* [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
2015-03-18 14:00 [Qemu-devel] [PATCH 0/4] virtio: add input device Gerd Hoffmann
2015-03-18 14:00 ` [Qemu-devel] [PATCH 1/4] pci: add PCI_CLASS_INPUT_* Gerd Hoffmann
@ 2015-03-18 14:00 ` Gerd Hoffmann
2015-03-18 14:31 ` Michael S. Tsirkin
2015-03-18 14:00 ` [Qemu-devel] [PATCH 3/4] virtio-input: emulated devices Gerd Hoffmann
2015-03-18 14:00 ` [Qemu-devel] [PATCH 4/4] virtio-input: evdev passthrough Gerd Hoffmann
3 siblings, 1 reply; 14+ messages in thread
From: Gerd Hoffmann @ 2015-03-18 14:00 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann, Michael S. Tsirkin
This patch adds virtio-input support to qemu. It brings a abstract
base class providing core support, other classes can build on it to
actually implement input devices.
virtio-input basically sends linux input layer events (evdev) over
virtio.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
hw/input/Makefile.objs | 4 +
hw/input/virtio-input.c | 284 +++++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-pci.c | 36 +++++
hw/virtio/virtio-pci.h | 14 ++
include/hw/virtio/virtio-input.h | 106 +++++++++++++++
include/hw/virtio/virtio.h | 1 +
6 files changed, 445 insertions(+)
create mode 100644 hw/input/virtio-input.c
create mode 100644 include/hw/virtio/virtio-input.h
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index e8c80b9..ee8bba9 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -8,6 +8,10 @@ common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
+ifeq ($(CONFIG_LINUX),y)
+common-obj-$(CONFIG_VIRTIO) += virtio-input.o
+endif
+
obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
obj-$(CONFIG_PXA2XX) += pxa2xx_keypad.o
obj-$(CONFIG_TSC210X) += tsc210x.o
diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c
new file mode 100644
index 0000000..8df7afb
--- /dev/null
+++ b/hw/input/virtio-input.c
@@ -0,0 +1,284 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/iov.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#include "ui/console.h"
+
+#include <linux/input.h>
+
+/* ----------------------------------------------------------------- */
+
+void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event)
+{
+ VirtQueueElement elem;
+ unsigned have, need;
+ int i, len;
+
+ /* queue up events ... */
+ if (vinput->qindex == vinput->qsize) {
+ vinput->qsize++;
+ vinput->queue = realloc(vinput->queue, vinput->qsize *
+ sizeof(virtio_input_event));
+ }
+ vinput->queue[vinput->qindex++] = *event;
+
+ /* ... until we see a report sync ... */
+ if (event->type != cpu_to_le16(EV_SYN) ||
+ event->code != cpu_to_le16(SYN_REPORT)) {
+ return;
+ }
+
+ /* ... then check available space ... */
+ need = sizeof(virtio_input_event) * vinput->qindex;
+ virtqueue_get_avail_bytes(vinput->evt, &have, NULL, need, 0);
+ if (have < need) {
+ vinput->qindex = 0;
+ fprintf(stderr, "%s: ENOSPC in vq, dropping events\n", __func__);
+ return;
+ }
+
+ /* ... and finally pass them to the guest */
+ for (i = 0; i < vinput->qindex; i++) {
+ if (!virtqueue_pop(vinput->evt, &elem)) {
+ /* should not happen, we've checked for space beforehand */
+ fprintf(stderr, "%s: Huh? No vq elem available ...\n", __func__);
+ return;
+ }
+ len = iov_from_buf(elem.in_sg, elem.in_num,
+ 0, vinput->queue+i, sizeof(virtio_input_event));
+ virtqueue_push(vinput->evt, &elem, len);
+ }
+ virtio_notify(VIRTIO_DEVICE(vinput), vinput->evt);
+ vinput->qindex = 0;
+}
+
+static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /* nothing */
+}
+
+static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_event event;
+ VirtQueueElement elem;
+ int len;
+
+ while (virtqueue_pop(vinput->sts, &elem)) {
+ memset(&event, 0, sizeof(event));
+ len = iov_to_buf(elem.out_sg, elem.out_num,
+ 0, &event, sizeof(event));
+ if (vic->handle_status) {
+ vic->handle_status(vinput, &event);
+ }
+ virtqueue_push(vinput->sts, &elem, len);
+ }
+ virtio_notify(vdev, vinput->sts);
+}
+
+static virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
+ uint8_t select,
+ uint8_t subsel)
+{
+ VirtIOInputConfig *cfg;
+
+ QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
+ if (select == cfg->config.select &&
+ subsel == cfg->config.subsel) {
+ return &cfg->config;
+ }
+ }
+ return NULL;
+}
+
+void virtio_input_add_config(VirtIOInput *vinput,
+ virtio_input_config *config)
+{
+ VirtIOInputConfig *cfg;
+
+ if (virtio_input_find_config(vinput, config->select, config->subsel)) {
+ /* should not happen */
+ fprintf(stderr, "%s: duplicate config: %d/%d\n",
+ __func__, config->select, config->subsel);
+ abort();
+ }
+
+ cfg = g_new0(VirtIOInputConfig, 1);
+ cfg->config = *config;
+ QTAILQ_INSERT_TAIL(&vinput->cfg_list, cfg, node);
+}
+
+void virtio_input_init_config(VirtIOInput *vinput,
+ virtio_input_config *config)
+{
+ int i = 0;
+
+ QTAILQ_INIT(&vinput->cfg_list);
+ while (config[i].select) {
+ virtio_input_add_config(vinput, config + i);
+ i++;
+ }
+}
+
+void virtio_input_idstr_config(VirtIOInput *vinput,
+ uint8_t select, const char *string)
+{
+ virtio_input_config id;
+
+ if (!string) {
+ return;
+ }
+ memset(&id, 0, sizeof(id));
+ id.select = select;
+ id.size = snprintf(id.u.string, sizeof(id.u.string), "%s", string);
+ virtio_input_add_config(vinput, &id);
+}
+
+static void virtio_input_get_config(VirtIODevice *vdev, uint8_t *config_data)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_config *config;
+
+ config = virtio_input_find_config(vinput, vinput->cfg_select,
+ vinput->cfg_subsel);
+ if (config) {
+ memcpy(config_data, config, vinput->cfg_size);
+ } else {
+ memset(config_data, 0, vinput->cfg_size);
+ }
+}
+
+static void virtio_input_set_config(VirtIODevice *vdev,
+ const uint8_t *config_data)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_config *config = (virtio_input_config *)config_data;
+
+ vinput->cfg_select = config->select;
+ vinput->cfg_subsel = config->subsel;
+ virtio_notify_config(vdev);
+}
+
+static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f)
+{
+ return f;
+}
+
+static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+
+ if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
+ if (!vinput->active) {
+ vinput->active = true;
+ if (vic->change_active) {
+ vic->change_active(vinput);
+ }
+ }
+ }
+}
+
+static void virtio_input_reset(VirtIODevice *vdev)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev);
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+
+ if (vinput->active) {
+ vinput->active = false;
+ if (vic->change_active) {
+ vic->change_active(vinput);
+ }
+ }
+}
+
+static void virtio_input_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ VirtIOInputConfig *cfg;
+ Error *local_err = NULL;
+
+ if (vic->realize) {
+ vic->realize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
+ virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
+ vinput->input.serial);
+
+ QTAILQ_FOREACH(cfg, &vinput->cfg_list, node) {
+ if (vinput->cfg_size < cfg->config.size) {
+ vinput->cfg_size = cfg->config.size;
+ }
+ }
+ vinput->cfg_size += 4;
+ assert(vinput->cfg_size <= sizeof(virtio_input_config));
+
+ virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
+ vinput->cfg_size);
+ vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
+ vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
+}
+
+static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(dev);
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ Error *local_err = NULL;
+
+ if (vic->unrealize) {
+ vic->unrealize(dev, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+ virtio_cleanup(vdev);
+}
+
+static void virtio_input_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
+
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+ vdc->realize = virtio_input_device_realize;
+ vdc->unrealize = virtio_input_device_unrealize;
+ vdc->get_config = virtio_input_get_config;
+ vdc->set_config = virtio_input_set_config;
+ vdc->get_features = virtio_input_get_features;
+ vdc->set_status = virtio_input_set_status;
+ vdc->reset = virtio_input_reset;
+}
+
+static const TypeInfo virtio_input_info = {
+ .name = TYPE_VIRTIO_INPUT,
+ .parent = TYPE_VIRTIO_DEVICE,
+ .instance_size = sizeof(VirtIOInput),
+ .class_size = sizeof(VirtIOInputClass),
+ .class_init = virtio_input_class_init,
+ .abstract = true,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 4c653fe..4bfd927 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -24,6 +24,7 @@
#include "hw/virtio/virtio-serial.h"
#include "hw/virtio/virtio-scsi.h"
#include "hw/virtio/virtio-balloon.h"
+#include "hw/virtio/virtio-input.h"
#include "hw/pci/pci.h"
#include "qemu/error-report.h"
#include "hw/pci/msi.h"
@@ -1888,6 +1889,40 @@ static const TypeInfo virtio_rng_pci_info = {
.class_init = virtio_rng_pci_class_init,
};
+/* virtio-input-pci */
+
+static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
+{
+ VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
+ DeviceState *vdev = DEVICE(&vinput->vdev);
+
+ qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+ /* force virtio-1.0 */
+ vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
+ vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
+ object_property_set_bool(OBJECT(vdev), true, "realized", errp);
+}
+
+static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass);
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = virtio_input_pci_realize;
+ set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_OTHER;
+}
+
+static const TypeInfo virtio_input_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_PCI,
+ .parent = TYPE_VIRTIO_PCI,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .class_init = virtio_input_pci_class_init,
+ .abstract = true,
+};
+
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1929,6 +1964,7 @@ static const TypeInfo virtio_pci_bus_info = {
static void virtio_pci_register_types(void)
{
type_register_static(&virtio_rng_pci_info);
+ type_register_static(&virtio_input_pci_info);
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
#ifdef CONFIG_VIRTFS
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index b382688..8a744eb 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -24,6 +24,7 @@
#include "hw/virtio/virtio-balloon.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-9p.h"
+#include "hw/virtio/virtio-input.h"
#ifdef CONFIG_VIRTFS
#include "hw/9pfs/virtio-9p.h"
#endif
@@ -39,6 +40,7 @@ typedef struct VirtIOSerialPCI VirtIOSerialPCI;
typedef struct VirtIONetPCI VirtIONetPCI;
typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
+typedef struct VirtIOInputPCI VirtIOInputPCI;
/* virtio-pci-bus */
@@ -225,6 +227,18 @@ struct VirtIORngPCI {
VirtIORNG vdev;
};
+/*
+ * virtio-input-pci: This extends VirtioPCIProxy.
+ */
+#define TYPE_VIRTIO_INPUT_PCI "virtio-input-pci"
+#define VIRTIO_INPUT_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI)
+
+struct VirtIOInputPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInput vdev;
+};
+
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h
new file mode 100644
index 0000000..718ebfd
--- /dev/null
+++ b/include/hw/virtio/virtio-input.h
@@ -0,0 +1,106 @@
+#ifndef _QEMU_VIRTIO_INPUT_H
+#define _QEMU_VIRTIO_INPUT_H
+
+#include "ui/input.h"
+
+/* ----------------------------------------------------------------- */
+/* virtio input protocol */
+
+/* The Virtio ID for the virtio input device */
+#define VIRTIO_ID_INPUT 18
+
+enum virtio_input_config_select {
+ VIRTIO_INPUT_CFG_UNSET = 0x00,
+ VIRTIO_INPUT_CFG_ID_NAME = 0x01,
+ VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
+ VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
+ VIRTIO_INPUT_CFG_EV_BITS = 0x11,
+ VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
+};
+
+typedef struct virtio_input_absinfo {
+ uint32_t min;
+ uint32_t max;
+ uint32_t fuzz;
+ uint32_t flat;
+} virtio_input_absinfo;
+
+typedef struct virtio_input_config {
+ uint8_t select;
+ uint8_t subsel;
+ uint8_t size;
+ uint8_t reserved;
+ union {
+ char string[128];
+ uint8_t bitmap[128];
+ virtio_input_absinfo abs;
+ } u;
+} virtio_input_config;
+
+typedef struct virtio_input_event {
+ uint16_t type;
+ uint16_t code;
+ int32_t value;
+} virtio_input_event;
+
+/* ----------------------------------------------------------------- */
+/* qemu internals */
+
+#define TYPE_VIRTIO_INPUT "virtio-input-device"
+#define VIRTIO_INPUT(obj) \
+ OBJECT_CHECK(VirtIOInput, (obj), TYPE_VIRTIO_INPUT)
+#define VIRTIO_INPUT_GET_PARENT_CLASS(obj) \
+ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT)
+#define VIRTIO_INPUT_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(VirtIOInputClass, obj, TYPE_VIRTIO_INPUT)
+#define VIRTIO_INPUT_CLASS(klass) \
+ OBJECT_CLASS_CHECK(VirtIOInputClass, klass, TYPE_VIRTIO_INPUT)
+
+typedef struct VirtIOInput VirtIOInput;
+typedef struct VirtIOInputClass VirtIOInputClass;
+typedef struct VirtIOInputConfig VirtIOInputConfig;
+
+struct virtio_input_conf {
+ char *serial;
+};
+
+struct VirtIOInputConfig {
+ virtio_input_config config;
+ QTAILQ_ENTRY(VirtIOInputConfig) node;
+};
+
+struct VirtIOInput {
+ VirtIODevice parent_obj;
+ uint8_t cfg_select;
+ uint8_t cfg_subsel;
+ uint32_t cfg_size;
+ QTAILQ_HEAD(, VirtIOInputConfig) cfg_list;
+ VirtQueue *evt, *sts;
+ virtio_input_conf input;
+
+ virtio_input_event *queue;
+ uint32_t qindex, qsize;
+
+ bool active;
+};
+
+struct VirtIOInputClass {
+ /*< private >*/
+ VirtioDeviceClass parent;
+ /*< public >*/
+
+ DeviceRealize realize;
+ DeviceUnrealize unrealize;
+ void (*change_active)(VirtIOInput *vinput);
+ void (*handle_status)(VirtIOInput *vinput, virtio_input_event *event);
+};
+
+void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event);
+void virtio_input_init_config(VirtIOInput *vinput,
+ virtio_input_config *config);
+void virtio_input_add_config(VirtIOInput *vinput,
+ virtio_input_config *config);
+void virtio_input_idstr_config(VirtIOInput *vinput,
+ uint8_t select, const char *string);
+
+#endif /* _QEMU_VIRTIO_INPUT_H */
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index a4ade5c..8e33fe4 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -201,6 +201,7 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val);
typedef struct VirtIOBlkConf VirtIOBlkConf;
struct virtio_net_conf;
typedef struct virtio_serial_conf virtio_serial_conf;
+typedef struct virtio_input_conf virtio_input_conf;
typedef struct VirtIOSCSIConf VirtIOSCSIConf;
typedef struct VirtIORNGConf VirtIORNGConf;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 3/4] virtio-input: emulated devices
2015-03-18 14:00 [Qemu-devel] [PATCH 0/4] virtio: add input device Gerd Hoffmann
2015-03-18 14:00 ` [Qemu-devel] [PATCH 1/4] pci: add PCI_CLASS_INPUT_* Gerd Hoffmann
2015-03-18 14:00 ` [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class Gerd Hoffmann
@ 2015-03-18 14:00 ` Gerd Hoffmann
2015-03-18 14:00 ` [Qemu-devel] [PATCH 4/4] virtio-input: evdev passthrough Gerd Hoffmann
3 siblings, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2015-03-18 14:00 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann, Michael S. Tsirkin
This patch adds the virtio-input-hid base class and
virtio-{keyboard,mouse,tablet} subclasses building on the base class.
They are hooked up to the qemu input core and deliver input events
to the guest like all other hid devices (ps/2 kbd, usb tablet, ...).
Using them is as simple as adding "-device virtio-tablet-pci" to your
command line. If you want add multiple devices but don't want waste
a pci slot for each you can compose a multifunction device this way:
qemu -device virtio-keyboard-pci,addr=0d.0,multifunction=on \
-device virtio-tablet-pci,addr=0d.1,multifunction=on
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
hw/input/Makefile.objs | 1 +
hw/input/virtio-input-hid.c | 482 +++++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-pci.c | 84 +++++++
hw/virtio/virtio-pci.h | 13 ++
include/hw/virtio/virtio-input.h | 21 ++
5 files changed, 601 insertions(+)
create mode 100644 hw/input/virtio-input-hid.c
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index ee8bba9..0dae710 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -10,6 +10,7 @@ common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input.o
+common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
endif
obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c
new file mode 100644
index 0000000..945e4f3
--- /dev/null
+++ b/hw/input/virtio-input-hid.c
@@ -0,0 +1,482 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu/iov.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#include "ui/console.h"
+
+#include <linux/input.h>
+
+#define VIRTIO_ID_NAME_KEYBOARD "QEMU Virtio Keyboard"
+#define VIRTIO_ID_NAME_MOUSE "QEMU Virtio Mouse"
+#define VIRTIO_ID_NAME_TABLET "QEMU Virtio Tablet"
+
+/* ----------------------------------------------------------------- */
+
+static const unsigned int keymap_qcode[Q_KEY_CODE_MAX] = {
+ [Q_KEY_CODE_ESC] = KEY_ESC,
+ [Q_KEY_CODE_1] = KEY_1,
+ [Q_KEY_CODE_2] = KEY_2,
+ [Q_KEY_CODE_3] = KEY_3,
+ [Q_KEY_CODE_4] = KEY_4,
+ [Q_KEY_CODE_5] = KEY_5,
+ [Q_KEY_CODE_6] = KEY_6,
+ [Q_KEY_CODE_7] = KEY_7,
+ [Q_KEY_CODE_8] = KEY_8,
+ [Q_KEY_CODE_9] = KEY_9,
+ [Q_KEY_CODE_0] = KEY_0,
+ [Q_KEY_CODE_MINUS] = KEY_MINUS,
+ [Q_KEY_CODE_EQUAL] = KEY_EQUAL,
+ [Q_KEY_CODE_BACKSPACE] = KEY_BACKSPACE,
+
+ [Q_KEY_CODE_TAB] = KEY_TAB,
+ [Q_KEY_CODE_Q] = KEY_Q,
+ [Q_KEY_CODE_W] = KEY_W,
+ [Q_KEY_CODE_E] = KEY_E,
+ [Q_KEY_CODE_R] = KEY_R,
+ [Q_KEY_CODE_T] = KEY_T,
+ [Q_KEY_CODE_Y] = KEY_Y,
+ [Q_KEY_CODE_U] = KEY_U,
+ [Q_KEY_CODE_I] = KEY_I,
+ [Q_KEY_CODE_O] = KEY_O,
+ [Q_KEY_CODE_P] = KEY_P,
+ [Q_KEY_CODE_BRACKET_LEFT] = KEY_LEFTBRACE,
+ [Q_KEY_CODE_BRACKET_RIGHT] = KEY_RIGHTBRACE,
+ [Q_KEY_CODE_RET] = KEY_ENTER,
+
+ [Q_KEY_CODE_CTRL] = KEY_LEFTCTRL,
+ [Q_KEY_CODE_A] = KEY_A,
+ [Q_KEY_CODE_S] = KEY_S,
+ [Q_KEY_CODE_D] = KEY_D,
+ [Q_KEY_CODE_F] = KEY_F,
+ [Q_KEY_CODE_G] = KEY_G,
+ [Q_KEY_CODE_H] = KEY_H,
+ [Q_KEY_CODE_J] = KEY_J,
+ [Q_KEY_CODE_K] = KEY_K,
+ [Q_KEY_CODE_L] = KEY_L,
+ [Q_KEY_CODE_SEMICOLON] = KEY_SEMICOLON,
+ [Q_KEY_CODE_APOSTROPHE] = KEY_APOSTROPHE,
+ [Q_KEY_CODE_GRAVE_ACCENT] = KEY_GRAVE,
+
+ [Q_KEY_CODE_SHIFT] = KEY_LEFTSHIFT,
+ [Q_KEY_CODE_BACKSLASH] = KEY_BACKSLASH,
+ [Q_KEY_CODE_LESS] = KEY_102ND,
+ [Q_KEY_CODE_Z] = KEY_Z,
+ [Q_KEY_CODE_X] = KEY_X,
+ [Q_KEY_CODE_C] = KEY_C,
+ [Q_KEY_CODE_V] = KEY_V,
+ [Q_KEY_CODE_B] = KEY_B,
+ [Q_KEY_CODE_N] = KEY_N,
+ [Q_KEY_CODE_M] = KEY_M,
+ [Q_KEY_CODE_COMMA] = KEY_COMMA,
+ [Q_KEY_CODE_DOT] = KEY_DOT,
+ [Q_KEY_CODE_SLASH] = KEY_SLASH,
+ [Q_KEY_CODE_SHIFT_R] = KEY_RIGHTSHIFT,
+
+ [Q_KEY_CODE_ALT] = KEY_LEFTALT,
+ [Q_KEY_CODE_SPC] = KEY_SPACE,
+ [Q_KEY_CODE_CAPS_LOCK] = KEY_CAPSLOCK,
+
+ [Q_KEY_CODE_F1] = KEY_F1,
+ [Q_KEY_CODE_F2] = KEY_F2,
+ [Q_KEY_CODE_F3] = KEY_F3,
+ [Q_KEY_CODE_F4] = KEY_F4,
+ [Q_KEY_CODE_F5] = KEY_F5,
+ [Q_KEY_CODE_F6] = KEY_F6,
+ [Q_KEY_CODE_F7] = KEY_F7,
+ [Q_KEY_CODE_F8] = KEY_F8,
+ [Q_KEY_CODE_F9] = KEY_F9,
+ [Q_KEY_CODE_F10] = KEY_F10,
+ [Q_KEY_CODE_NUM_LOCK] = KEY_NUMLOCK,
+ [Q_KEY_CODE_SCROLL_LOCK] = KEY_SCROLLLOCK,
+
+ [Q_KEY_CODE_KP_0] = KEY_KP0,
+ [Q_KEY_CODE_KP_1] = KEY_KP1,
+ [Q_KEY_CODE_KP_2] = KEY_KP2,
+ [Q_KEY_CODE_KP_3] = KEY_KP3,
+ [Q_KEY_CODE_KP_4] = KEY_KP4,
+ [Q_KEY_CODE_KP_5] = KEY_KP5,
+ [Q_KEY_CODE_KP_6] = KEY_KP6,
+ [Q_KEY_CODE_KP_7] = KEY_KP7,
+ [Q_KEY_CODE_KP_8] = KEY_KP8,
+ [Q_KEY_CODE_KP_9] = KEY_KP9,
+ [Q_KEY_CODE_KP_SUBTRACT] = KEY_KPMINUS,
+ [Q_KEY_CODE_KP_ADD] = KEY_KPPLUS,
+ [Q_KEY_CODE_KP_DECIMAL] = KEY_KPDOT,
+ [Q_KEY_CODE_KP_ENTER] = KEY_KPENTER,
+ [Q_KEY_CODE_KP_DIVIDE] = KEY_KPSLASH,
+ [Q_KEY_CODE_KP_MULTIPLY] = KEY_KPASTERISK,
+
+ [Q_KEY_CODE_F11] = KEY_F11,
+ [Q_KEY_CODE_F12] = KEY_F12,
+
+ [Q_KEY_CODE_CTRL_R] = KEY_RIGHTCTRL,
+ [Q_KEY_CODE_SYSRQ] = KEY_SYSRQ,
+ [Q_KEY_CODE_ALT_R] = KEY_RIGHTALT,
+
+ [Q_KEY_CODE_HOME] = KEY_HOME,
+ [Q_KEY_CODE_UP] = KEY_UP,
+ [Q_KEY_CODE_PGUP] = KEY_PAGEUP,
+ [Q_KEY_CODE_LEFT] = KEY_LEFT,
+ [Q_KEY_CODE_RIGHT] = KEY_RIGHT,
+ [Q_KEY_CODE_END] = KEY_END,
+ [Q_KEY_CODE_DOWN] = KEY_DOWN,
+ [Q_KEY_CODE_PGDN] = KEY_PAGEDOWN,
+ [Q_KEY_CODE_INSERT] = KEY_INSERT,
+ [Q_KEY_CODE_DELETE] = KEY_DELETE,
+
+ [Q_KEY_CODE_META_L] = KEY_LEFTMETA,
+ [Q_KEY_CODE_META_R] = KEY_RIGHTMETA,
+ [Q_KEY_CODE_MENU] = KEY_MENU,
+};
+
+static const unsigned int keymap_button[INPUT_BUTTON_MAX] = {
+ [INPUT_BUTTON_LEFT] = BTN_LEFT,
+ [INPUT_BUTTON_RIGHT] = BTN_RIGHT,
+ [INPUT_BUTTON_MIDDLE] = BTN_MIDDLE,
+ [INPUT_BUTTON_WHEEL_UP] = BTN_GEAR_UP,
+ [INPUT_BUTTON_WHEEL_DOWN] = BTN_GEAR_DOWN,
+};
+
+static const unsigned int axismap_rel[INPUT_AXIS_MAX] = {
+ [INPUT_AXIS_X] = REL_X,
+ [INPUT_AXIS_Y] = REL_Y,
+};
+
+static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
+ [INPUT_AXIS_X] = ABS_X,
+ [INPUT_AXIS_Y] = ABS_Y,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_input_key_config(VirtIOInput *vinput,
+ const unsigned int *keymap,
+ size_t mapsize)
+{
+ virtio_input_config keys;
+ int i, bit, byte, bmax = 0;
+
+ memset(&keys, 0, sizeof(keys));
+ for (i = 0; i < mapsize; i++) {
+ bit = keymap[i];
+ if (!bit) {
+ continue;
+ }
+ byte = bit / 8;
+ bit = bit % 8;
+ keys.u.bitmap[byte] |= (1 << bit);
+ if (bmax < byte+1) {
+ bmax = byte+1;
+ }
+ }
+ keys.select = VIRTIO_INPUT_CFG_EV_BITS;
+ keys.subsel = EV_KEY;
+ keys.size = bmax;
+ virtio_input_add_config(vinput, &keys);
+}
+
+static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
+ InputEvent *evt)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ virtio_input_event event;
+ int qcode;
+
+ switch (evt->kind) {
+ case INPUT_EVENT_KIND_KEY:
+ qcode = qemu_input_key_value_to_qcode(evt->key->key);
+ if (qcode && keymap_qcode[qcode]) {
+ event.type = cpu_to_le16(EV_KEY);
+ event.code = cpu_to_le16(keymap_qcode[qcode]);
+ event.value = cpu_to_le32(evt->key->down ? 1 : 0);
+ virtio_input_send(vinput, &event);
+ } else {
+ if (evt->key->down) {
+ fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
+ qcode, QKeyCode_lookup[qcode]);
+ }
+ }
+ break;
+ case INPUT_EVENT_KIND_BTN:
+ if (keymap_button[evt->btn->button]) {
+ event.type = cpu_to_le16(EV_KEY);
+ event.code = cpu_to_le16(keymap_button[evt->btn->button]);
+ event.value = cpu_to_le32(evt->btn->down ? 1 : 0);
+ virtio_input_send(vinput, &event);
+ } else {
+ if (evt->btn->down) {
+ fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
+ evt->btn->button, InputButton_lookup[evt->btn->button]);
+ }
+ }
+ break;
+ case INPUT_EVENT_KIND_REL:
+ event.type = cpu_to_le16(EV_REL);
+ event.code = cpu_to_le16(axismap_rel[evt->rel->axis]);
+ event.value = cpu_to_le32(evt->rel->value);
+ virtio_input_send(vinput, &event);
+ break;
+ case INPUT_EVENT_KIND_ABS:
+ event.type = cpu_to_le16(EV_ABS);
+ event.code = cpu_to_le16(axismap_abs[evt->abs->axis]);
+ event.value = cpu_to_le32(evt->abs->value);
+ virtio_input_send(vinput, &event);
+ break;
+ default:
+ /* keep gcc happy */
+ break;
+ }
+}
+
+static void virtio_input_handle_sync(DeviceState *dev)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ virtio_input_event event = {
+ .type = cpu_to_le16(EV_SYN),
+ .code = cpu_to_le16(SYN_REPORT),
+ .value = 0,
+ };
+
+ virtio_input_send(vinput, &event);
+}
+
+static void virtio_input_hid_realize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
+ vhid->hs = qemu_input_handler_register(dev, vhid->handler);
+}
+
+static void virtio_input_hid_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
+ qemu_input_handler_unregister(vhid->hs);
+}
+
+static void virtio_input_hid_change_active(VirtIOInput *vinput)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput);
+
+ if (vinput->active) {
+ qemu_input_handler_activate(vhid->hs);
+ } else {
+ qemu_input_handler_deactivate(vhid->hs);
+ }
+}
+
+static void virtio_input_hid_handle_status(VirtIOInput *vinput,
+ virtio_input_event *event)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput);
+ int ledbit = 0;
+
+ switch (le16_to_cpu(event->type)) {
+ case EV_LED:
+ if (event->code == LED_NUML) {
+ ledbit = QEMU_NUM_LOCK_LED;
+ } else if (event->code == LED_CAPSL) {
+ ledbit = QEMU_CAPS_LOCK_LED;
+ } else if (event->code == LED_SCROLLL) {
+ ledbit = QEMU_SCROLL_LOCK_LED;
+ }
+ if (event->value) {
+ vhid->ledstate |= ledbit;
+ } else {
+ vhid->ledstate &= ~ledbit;
+ }
+ kbd_put_ledstate(vhid->ledstate);
+ break;
+ default:
+ fprintf(stderr, "%s: unknown type %d\n", __func__,
+ le16_to_cpu(event->type));
+ break;
+ }
+}
+
+static void virtio_input_hid_class_init(ObjectClass *klass, void *data)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
+
+ vic->realize = virtio_input_hid_realize;
+ vic->unrealize = virtio_input_hid_unrealize;
+ vic->change_active = virtio_input_hid_change_active;
+ vic->handle_status = virtio_input_hid_handle_status;
+}
+
+static const TypeInfo virtio_input_hid_info = {
+ .name = TYPE_VIRTIO_INPUT_HID,
+ .parent = TYPE_VIRTIO_INPUT,
+ .instance_size = sizeof(VirtIOInputHID),
+ .class_init = virtio_input_hid_class_init,
+ .abstract = true,
+};
+
+/* ----------------------------------------------------------------- */
+
+static QemuInputHandler virtio_keyboard_handler = {
+ .name = VIRTIO_ID_NAME_KEYBOARD,
+ .mask = INPUT_EVENT_MASK_KEY,
+ .event = virtio_input_handle_event,
+ .sync = virtio_input_handle_sync,
+};
+
+static struct virtio_input_config virtio_keyboard_config[] = {
+ {
+ .select = VIRTIO_INPUT_CFG_ID_NAME,
+ .size = sizeof(VIRTIO_ID_NAME_KEYBOARD),
+ .u.string = VIRTIO_ID_NAME_KEYBOARD,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_REP,
+ .size = 1,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_LED,
+ .size = 1,
+ .u.bitmap = {
+ (1 << LED_NUML) | (1 << LED_CAPSL) | (1 << LED_SCROLLL),
+ },
+ },
+ { /* end of list */ },
+};
+
+static void virtio_keyboard_init(Object *obj)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vhid->handler = &virtio_keyboard_handler;
+ virtio_input_init_config(vinput, virtio_keyboard_config);
+ virtio_input_key_config(vinput, keymap_qcode,
+ ARRAY_SIZE(keymap_qcode));
+}
+
+static const TypeInfo virtio_keyboard_info = {
+ .name = TYPE_VIRTIO_KEYBOARD,
+ .parent = TYPE_VIRTIO_INPUT_HID,
+ .instance_size = sizeof(VirtIOInputHID),
+ .instance_init = virtio_keyboard_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static QemuInputHandler virtio_mouse_handler = {
+ .name = VIRTIO_ID_NAME_MOUSE,
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
+ .event = virtio_input_handle_event,
+ .sync = virtio_input_handle_sync,
+};
+
+static struct virtio_input_config virtio_mouse_config[] = {
+ {
+ .select = VIRTIO_INPUT_CFG_ID_NAME,
+ .size = sizeof(VIRTIO_ID_NAME_MOUSE),
+ .u.string = VIRTIO_ID_NAME_MOUSE,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_REL,
+ .size = 1,
+ .u.bitmap = {
+ (1 << REL_X) | (1 << REL_Y),
+ },
+ },
+ { /* end of list */ },
+};
+
+static void virtio_mouse_init(Object *obj)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vhid->handler = &virtio_mouse_handler;
+ virtio_input_init_config(vinput, virtio_mouse_config);
+ virtio_input_key_config(vinput, keymap_button,
+ ARRAY_SIZE(keymap_button));
+}
+
+static const TypeInfo virtio_mouse_info = {
+ .name = TYPE_VIRTIO_MOUSE,
+ .parent = TYPE_VIRTIO_INPUT_HID,
+ .instance_size = sizeof(VirtIOInputHID),
+ .instance_init = virtio_mouse_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static QemuInputHandler virtio_tablet_handler = {
+ .name = VIRTIO_ID_NAME_TABLET,
+ .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
+ .event = virtio_input_handle_event,
+ .sync = virtio_input_handle_sync,
+};
+
+static struct virtio_input_config virtio_tablet_config[] = {
+ {
+ .select = VIRTIO_INPUT_CFG_ID_NAME,
+ .size = sizeof(VIRTIO_ID_NAME_TABLET),
+ .u.string = VIRTIO_ID_NAME_TABLET,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_BITS,
+ .subsel = EV_ABS,
+ .size = 1,
+ .u.bitmap = {
+ (1 << ABS_X) | (1 << ABS_Y),
+ },
+ },{
+ .select = VIRTIO_INPUT_CFG_ABS_INFO,
+ .subsel = ABS_X,
+ .size = sizeof(virtio_input_absinfo),
+#if defined(HOST_WORDS_BIGENDIAN)
+ .u.abs.max = bswap32(INPUT_EVENT_ABS_SIZE),
+#else
+ .u.abs.max = INPUT_EVENT_ABS_SIZE,
+#endif
+ },{
+ .select = VIRTIO_INPUT_CFG_ABS_INFO,
+ .subsel = ABS_Y,
+ .size = sizeof(virtio_input_absinfo),
+#if defined(HOST_WORDS_BIGENDIAN)
+ .u.abs.max = bswap32(INPUT_EVENT_ABS_SIZE),
+#else
+ .u.abs.max = INPUT_EVENT_ABS_SIZE,
+#endif
+ },
+ { /* end of list */ },
+};
+
+static void virtio_tablet_init(Object *obj)
+{
+ VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vhid->handler = &virtio_tablet_handler;
+ virtio_input_init_config(vinput, virtio_tablet_config);
+ virtio_input_key_config(vinput, keymap_button,
+ ARRAY_SIZE(keymap_button));
+}
+
+static const TypeInfo virtio_tablet_info = {
+ .name = TYPE_VIRTIO_TABLET,
+ .parent = TYPE_VIRTIO_INPUT_HID,
+ .instance_size = sizeof(VirtIOInputHID),
+ .instance_init = virtio_tablet_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_hid_info);
+ type_register_static(&virtio_keyboard_info);
+ type_register_static(&virtio_mouse_info);
+ type_register_static(&virtio_tablet_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 4bfd927..f7d6b55 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1891,6 +1891,12 @@ static const TypeInfo virtio_rng_pci_info = {
/* virtio-input-pci */
+static Property virtio_input_hid_pci_properties[] = {
+ DEFINE_VIRTIO_INPUT_PROPERTIES(VirtIOInputPCI, vdev.input),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
@@ -1915,6 +1921,49 @@ static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
pcidev_k->class_id = PCI_CLASS_INPUT_OTHER;
}
+static void virtio_input_hid_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = virtio_input_hid_pci_properties;
+}
+
+static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data)
+{
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD;
+}
+
+static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass,
+ void *data)
+{
+ PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass);
+
+ pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE;
+}
+
+static void virtio_keyboard_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_KEYBOARD);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static void virtio_mouse_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_MOUSE);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
+static void virtio_tablet_initfn(Object *obj)
+{
+ VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_TABLET);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
static const TypeInfo virtio_input_pci_info = {
.name = TYPE_VIRTIO_INPUT_PCI,
.parent = TYPE_VIRTIO_PCI,
@@ -1923,6 +1972,37 @@ static const TypeInfo virtio_input_pci_info = {
.abstract = true,
};
+static const TypeInfo virtio_input_hid_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_HID_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .class_init = virtio_input_hid_pci_class_init,
+ .abstract = true,
+};
+
+static const TypeInfo virtio_keyboard_pci_info = {
+ .name = TYPE_VIRTIO_KEYBOARD_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .class_init = virtio_input_hid_kbd_pci_class_init,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_keyboard_initfn,
+};
+
+static const TypeInfo virtio_mouse_pci_info = {
+ .name = TYPE_VIRTIO_MOUSE_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .class_init = virtio_input_hid_mouse_pci_class_init,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_mouse_initfn,
+};
+
+static const TypeInfo virtio_tablet_pci_info = {
+ .name = TYPE_VIRTIO_TABLET_PCI,
+ .parent = TYPE_VIRTIO_INPUT_HID_PCI,
+ .instance_size = sizeof(VirtIOInputHIDPCI),
+ .instance_init = virtio_tablet_initfn,
+};
+
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1965,6 +2045,10 @@ static void virtio_pci_register_types(void)
{
type_register_static(&virtio_rng_pci_info);
type_register_static(&virtio_input_pci_info);
+ type_register_static(&virtio_input_hid_pci_info);
+ type_register_static(&virtio_keyboard_pci_info);
+ type_register_static(&virtio_mouse_pci_info);
+ type_register_static(&virtio_tablet_pci_info);
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
#ifdef CONFIG_VIRTFS
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index 8a744eb..dbaca2b 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -41,6 +41,7 @@ typedef struct VirtIONetPCI VirtIONetPCI;
typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
typedef struct VirtIOInputPCI VirtIOInputPCI;
+typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
/* virtio-pci-bus */
@@ -239,6 +240,18 @@ struct VirtIOInputPCI {
VirtIOInput vdev;
};
+#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci"
+#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci"
+#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci"
+#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci"
+#define VIRTIO_INPUT_HID_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI)
+
+struct VirtIOInputHIDPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInputHID vdev;
+};
+
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h
index 718ebfd..ca5bcee 100644
--- a/include/hw/virtio/virtio-input.h
+++ b/include/hw/virtio/virtio-input.h
@@ -56,9 +56,23 @@ typedef struct virtio_input_event {
#define VIRTIO_INPUT_CLASS(klass) \
OBJECT_CLASS_CHECK(VirtIOInputClass, klass, TYPE_VIRTIO_INPUT)
+#define TYPE_VIRTIO_INPUT_HID "virtio-input-hid"
+#define TYPE_VIRTIO_KEYBOARD "virtio-keyboard"
+#define TYPE_VIRTIO_MOUSE "virtio-mouse"
+#define TYPE_VIRTIO_TABLET "virtio-tablet"
+
+#define VIRTIO_INPUT_HID(obj) \
+ OBJECT_CHECK(VirtIOInputHID, (obj), TYPE_VIRTIO_INPUT_HID)
+#define VIRTIO_INPUT_HID_GET_PARENT_CLASS(obj) \
+ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT_HID)
+
+#define DEFINE_VIRTIO_INPUT_PROPERTIES(_state, _field) \
+ DEFINE_PROP_STRING("serial", _state, _field.serial)
+
typedef struct VirtIOInput VirtIOInput;
typedef struct VirtIOInputClass VirtIOInputClass;
typedef struct VirtIOInputConfig VirtIOInputConfig;
+typedef struct VirtIOInputHID VirtIOInputHID;
struct virtio_input_conf {
char *serial;
@@ -95,6 +109,13 @@ struct VirtIOInputClass {
void (*handle_status)(VirtIOInput *vinput, virtio_input_event *event);
};
+struct VirtIOInputHID {
+ VirtIOInput parent_obj;
+ QemuInputHandler *handler;
+ QemuInputHandlerState *hs;
+ int ledstate;
+};
+
void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event);
void virtio_input_init_config(VirtIOInput *vinput,
virtio_input_config *config);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [PATCH 4/4] virtio-input: evdev passthrough
2015-03-18 14:00 [Qemu-devel] [PATCH 0/4] virtio: add input device Gerd Hoffmann
` (2 preceding siblings ...)
2015-03-18 14:00 ` [Qemu-devel] [PATCH 3/4] virtio-input: emulated devices Gerd Hoffmann
@ 2015-03-18 14:00 ` Gerd Hoffmann
3 siblings, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2015-03-18 14:00 UTC (permalink / raw)
To: qemu-devel; +Cc: Gerd Hoffmann, Michael S. Tsirkin
This allows to assign host input devices to the guest:
qemu -device virto-input-host-pci,evdev=/dev/input/event<nr>
The guest gets exclusive access to the input device, so be careful
with assigning the keyboard if you have only one connected to your
machine.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
hw/input/Makefile.objs | 1 +
hw/input/virtio-input-host.c | 172 +++++++++++++++++++++++++++++++++++++++
hw/virtio/virtio-pci.c | 30 +++++++
hw/virtio/virtio-pci.h | 10 +++
include/hw/virtio/virtio-input.h | 13 +++
5 files changed, 226 insertions(+)
create mode 100644 hw/input/virtio-input-host.c
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index 0dae710..624ba7e 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -11,6 +11,7 @@ common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input.o
common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
+common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
endif
obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o
diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c
new file mode 100644
index 0000000..30d5c1c
--- /dev/null
+++ b/hw/input/virtio-input-host.c
@@ -0,0 +1,172 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version. See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu/sockets.h"
+
+#include "hw/qdev.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-input.h"
+
+#include "ui/console.h"
+
+#include <linux/input.h>
+
+/* ----------------------------------------------------------------- */
+
+static struct virtio_input_config virtio_input_host_config[] = {
+ { /* empty list */ },
+};
+
+static void virtio_input_host_event(void *opaque)
+{
+ VirtIOInputHost *vih = opaque;
+ VirtIOInput *vinput = VIRTIO_INPUT(vih);
+ struct virtio_input_event virtio;
+ struct input_event evdev;
+ int rc;
+
+ for (;;) {
+ rc = read(vih->fd, &evdev, sizeof(evdev));
+ if (rc != sizeof(evdev)) {
+ break;
+ }
+
+ virtio.type = cpu_to_le16(evdev.type);
+ virtio.code = cpu_to_le16(evdev.code);
+ virtio.value = cpu_to_le32(evdev.value);
+ virtio_input_send(vinput, &virtio);
+ }
+}
+
+static void virtio_input_bits_config(VirtIOInputHost *vih,
+ int type, int count)
+{
+ virtio_input_config bits;
+ int rc, i, size = 0;
+
+ memset(&bits, 0, sizeof(bits));
+ rc = ioctl(vih->fd, EVIOCGBIT(type, count/8), bits.u.bitmap);
+ if (rc < 0) {
+ return;
+ }
+
+ for (i = 0; i < count/8; i++) {
+ if (bits.u.bitmap[i]) {
+ size = i+1;
+ }
+ }
+ if (size == 0) {
+ return;
+ }
+
+ bits.select = VIRTIO_INPUT_CFG_EV_BITS;
+ bits.subsel = type;
+ bits.size = size;
+ virtio_input_add_config(VIRTIO_INPUT(vih), &bits);
+}
+
+static void virtio_input_host_realize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+ virtio_input_config id;
+ int rc, ver;
+
+ if (!vih->evdev) {
+ error_setg(errp, "evdev property is required");
+ return;
+ }
+
+ vih->fd = open(vih->evdev, O_RDWR);
+ if (vih->fd < 0) {
+ error_setg_file_open(errp, errno, vih->evdev);
+ return;
+ }
+ qemu_set_nonblock(vih->fd);
+
+ rc = ioctl(vih->fd, EVIOCGVERSION, &ver);
+ if (rc < 0) {
+ error_setg(errp, "%s: is not an evdev device", vih->evdev);
+ goto err_close;
+ }
+
+ rc = ioctl(vih->fd, EVIOCGRAB, 1);
+ if (rc < 0) {
+ error_setg_errno(errp, errno, "%s: failed to get exclusive access",
+ vih->evdev);
+ goto err_close;
+ }
+
+ memset(&id, 0, sizeof(id));
+ ioctl(vih->fd, EVIOCGNAME(sizeof(id.u.string)-1), id.u.string);
+ id.select = VIRTIO_INPUT_CFG_ID_NAME;
+ id.size = strlen(id.u.string);
+ virtio_input_add_config(vinput, &id);
+
+ virtio_input_bits_config(vih, EV_KEY, KEY_CNT);
+ virtio_input_bits_config(vih, EV_REL, REL_CNT);
+ virtio_input_bits_config(vih, EV_ABS, ABS_CNT);
+ virtio_input_bits_config(vih, EV_MSC, MSC_CNT);
+ virtio_input_bits_config(vih, EV_SW, SW_CNT);
+
+ qemu_set_fd_handler(vih->fd, virtio_input_host_event, NULL, vih);
+ return;
+
+err_close:
+ close(vih->fd);
+ vih->fd = -1;
+ return;
+}
+
+static void virtio_input_host_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIOInputHost *vih = VIRTIO_INPUT_HOST(dev);
+
+ if (vih->fd > 0) {
+ qemu_set_fd_handler(vih->fd, NULL, NULL, NULL);
+ close(vih->fd);
+ }
+}
+
+static const VMStateDescription vmstate_virtio_input_host = {
+ .name = "virtio-input-host",
+ .unmigratable = 1,
+};
+
+static void virtio_input_host_class_init(ObjectClass *klass, void *data)
+{
+ VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_virtio_input_host;
+ vic->realize = virtio_input_host_realize;
+ vic->unrealize = virtio_input_host_unrealize;
+}
+
+static void virtio_input_host_init(Object *obj)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ virtio_input_init_config(vinput, virtio_input_host_config);
+}
+
+static const TypeInfo virtio_input_host_info = {
+ .name = TYPE_VIRTIO_INPUT_HOST,
+ .parent = TYPE_VIRTIO_INPUT,
+ .instance_size = sizeof(VirtIOInputHost),
+ .instance_init = virtio_input_host_init,
+ .class_init = virtio_input_host_class_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_host_info);
+}
+
+type_init(virtio_register_types)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index f7d6b55..d1b02b3 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1897,6 +1897,13 @@ static Property virtio_input_hid_pci_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
+static Property virtio_input_host_pci_properties[] = {
+ DEFINE_VIRTIO_INPUT_PROPERTIES(VirtIOInputPCI, vdev.input),
+ DEFINE_PROP_STRING("evdev", VirtIOInputHostPCI, vdev.evdev),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
{
VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
@@ -1943,6 +1950,13 @@ static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass,
pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE;
}
+static void virtio_input_host_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->props = virtio_input_host_pci_properties;
+}
+
static void virtio_keyboard_initfn(Object *obj)
{
VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj);
@@ -1964,6 +1978,13 @@ static void virtio_tablet_initfn(Object *obj)
object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
}
+static void virtio_host_initfn(Object *obj)
+{
+ VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
+ object_initialize(&dev->vdev, sizeof(dev->vdev), TYPE_VIRTIO_INPUT_HOST);
+ object_property_add_child(obj, "virtio-backend", OBJECT(&dev->vdev), NULL);
+}
+
static const TypeInfo virtio_input_pci_info = {
.name = TYPE_VIRTIO_INPUT_PCI,
.parent = TYPE_VIRTIO_PCI,
@@ -2003,6 +2024,14 @@ static const TypeInfo virtio_tablet_pci_info = {
.instance_init = virtio_tablet_initfn,
};
+static const TypeInfo virtio_host_pci_info = {
+ .name = TYPE_VIRTIO_INPUT_HOST_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputHostPCI),
+ .instance_init = virtio_host_initfn,
+ .class_init = virtio_input_host_pci_class_init,
+};
+
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -2049,6 +2078,7 @@ static void virtio_pci_register_types(void)
type_register_static(&virtio_keyboard_pci_info);
type_register_static(&virtio_mouse_pci_info);
type_register_static(&virtio_tablet_pci_info);
+ type_register_static(&virtio_host_pci_info);
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
#ifdef CONFIG_VIRTFS
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index dbaca2b..fc58539 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -42,6 +42,7 @@ typedef struct VHostSCSIPCI VHostSCSIPCI;
typedef struct VirtIORngPCI VirtIORngPCI;
typedef struct VirtIOInputPCI VirtIOInputPCI;
typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI;
+typedef struct VirtIOInputHostPCI VirtIOInputHostPCI;
/* virtio-pci-bus */
@@ -252,6 +253,15 @@ struct VirtIOInputHIDPCI {
VirtIOInputHID vdev;
};
+#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci"
+#define VIRTIO_INPUT_HOST_PCI(obj) \
+ OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI)
+
+struct VirtIOInputHostPCI {
+ VirtIOPCIProxy parent_obj;
+ VirtIOInputHost vdev;
+};
+
/* Virtio ABI version, if we increment this, we break the guest driver. */
#define VIRTIO_PCI_ABI_VERSION 0
diff --git a/include/hw/virtio/virtio-input.h b/include/hw/virtio/virtio-input.h
index ca5bcee..eb870bb 100644
--- a/include/hw/virtio/virtio-input.h
+++ b/include/hw/virtio/virtio-input.h
@@ -66,6 +66,12 @@ typedef struct virtio_input_event {
#define VIRTIO_INPUT_HID_GET_PARENT_CLASS(obj) \
OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT_HID)
+#define TYPE_VIRTIO_INPUT_HOST "virtio-input-host"
+#define VIRTIO_INPUT_HOST(obj) \
+ OBJECT_CHECK(VirtIOInputHost, (obj), TYPE_VIRTIO_INPUT_HOST)
+#define VIRTIO_INPUT_HOST_GET_PARENT_CLASS(obj) \
+ OBJECT_GET_PARENT_CLASS(obj, TYPE_VIRTIO_INPUT_HOST)
+
#define DEFINE_VIRTIO_INPUT_PROPERTIES(_state, _field) \
DEFINE_PROP_STRING("serial", _state, _field.serial)
@@ -73,6 +79,7 @@ typedef struct VirtIOInput VirtIOInput;
typedef struct VirtIOInputClass VirtIOInputClass;
typedef struct VirtIOInputConfig VirtIOInputConfig;
typedef struct VirtIOInputHID VirtIOInputHID;
+typedef struct VirtIOInputHost VirtIOInputHost;
struct virtio_input_conf {
char *serial;
@@ -116,6 +123,12 @@ struct VirtIOInputHID {
int ledstate;
};
+struct VirtIOInputHost {
+ VirtIOInput parent_obj;
+ char *evdev;
+ int fd;
+};
+
void virtio_input_send(VirtIOInput *vinput, virtio_input_event *event);
void virtio_input_init_config(VirtIOInput *vinput,
virtio_input_config *config);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 14+ messages in thread