All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/4] virtio: add input device
@ 2015-03-18 14:00 Gerd Hoffmann
  2015-03-18 14:00 ` [Qemu-devel] [PATCH 1/4] pci: add PCI_CLASS_INPUT_* Gerd Hoffmann
                   ` (3 more replies)
  0 siblings, 4 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2015-03-18 14:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann

  Hi,

This patch series adds virtio input devices.  It's basically sending
linux evdev events over virtio.  There is support for emulated hid
devices (i.e. send usual input to virtio device instead of usb or ps2
device) and for evdev device pass-through.

This depends on mst's virtio-1.0 patches.

Guest driver:
  https://www.kraxel.org/cgit/linux/log/?h=virtio-input

Specification:
  https://www.kraxel.org/cgit/virtio-spec/log/?h=virtio-input
  https://www.kraxel.org/virtio/virtio-v1.0-csprd03-virtio-input.html#x1-2640007

Gerd Hoffmann (4):
  pci: add  PCI_CLASS_INPUT_*
  virtio-input: core code & base class
  virtio-input: emulated devices
  virtio-input: evdev passthrough

 hw/input/Makefile.objs           |   6 +
 hw/input/virtio-input-hid.c      | 482 +++++++++++++++++++++++++++++++++++++++
 hw/input/virtio-input-host.c     | 172 ++++++++++++++
 hw/input/virtio-input.c          | 284 +++++++++++++++++++++++
 hw/virtio/virtio-pci.c           | 150 ++++++++++++
 hw/virtio/virtio-pci.h           |  37 +++
 include/hw/pci/pci_ids.h         |   7 +
 include/hw/virtio/virtio-input.h | 140 ++++++++++++
 include/hw/virtio/virtio.h       |   1 +
 9 files changed, 1279 insertions(+)
 create mode 100644 hw/input/virtio-input-hid.c
 create mode 100644 hw/input/virtio-input-host.c
 create mode 100644 hw/input/virtio-input.c
 create mode 100644 include/hw/virtio/virtio-input.h

-- 
1.8.3.1

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [Qemu-devel] [PATCH 1/4] pci: add  PCI_CLASS_INPUT_*
  2015-03-18 14:00 [Qemu-devel] [PATCH 0/4] virtio: add input device Gerd Hoffmann
@ 2015-03-18 14:00 ` Gerd Hoffmann
  2015-03-18 14:00 ` [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class Gerd Hoffmann
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2015-03-18 14:00 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Michael S. Tsirkin

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 include/hw/pci/pci_ids.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index d7be386..dfccefc 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -46,6 +46,13 @@
 #define PCI_CLASS_COMMUNICATION_SERIAL   0x0700
 #define PCI_CLASS_COMMUNICATION_OTHER    0x0780
 
+#define PCI_CLASS_INPUT_KEYBOARD         0x0900
+#define PCI_CLASS_INPUT_PEN              0x0901
+#define PCI_CLASS_INPUT_MOUSE            0x0902
+#define PCI_CLASS_INPUT_SCANNER          0x0903
+#define PCI_CLASS_INPUT_GAMEPORT         0x0904
+#define PCI_CLASS_INPUT_OTHER            0x0980
+
 #define PCI_CLASS_PROCESSOR_CO           0x0b40
 #define PCI_CLASS_PROCESSOR_POWERPC      0x0b20
 
-- 
1.8.3.1

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-18 14:00 ` [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class Gerd Hoffmann
@ 2015-03-18 14:31   ` Michael S. Tsirkin
  2015-03-18 15:31     ` Gerd Hoffmann
  0 siblings, 1 reply; 15+ messages in thread
From: Michael S. Tsirkin @ 2015-03-18 14:31 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Wed, Mar 18, 2015 at 03:00:12PM +0100, Gerd Hoffmann wrote:
> 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>

Two questions before I looked at code:
- can you do a writeup for the virtio spec?
  might make it easier to review.
- does linux need to support this?
  if yes we'll eventually want to take
  the header from there.
  maybe split out guest/host ABI?
  also might be a good idea to make style there
  linux-compliant (e.g. no typedefs, add typedefs
  in another header).

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-18 14:31   ` Michael S. Tsirkin
@ 2015-03-18 15:31     ` Gerd Hoffmann
  2015-03-18 16:58       ` Michael S. Tsirkin
  0 siblings, 1 reply; 15+ messages in thread
From: Gerd Hoffmann @ 2015-03-18 15:31 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel

On Mi, 2015-03-18 at 15:31 +0100, Michael S. Tsirkin wrote:
> On Wed, Mar 18, 2015 at 03:00:12PM +0100, Gerd Hoffmann wrote:
> > 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>
> 
> Two questions before I looked at code:
> - can you do a writeup for the virtio spec?
>   might make it easier to review.

>From cover letter:

<quote>

Guest driver:
  https://www.kraxel.org/cgit/linux/log/?h=virtio-input

Specification:
  https://www.kraxel.org/cgit/virtio-spec/log/?h=virtio-input
  https://www.kraxel.org/virtio/virtio-v1.0-csprd03-virtio-input.html#x1-2640007

</quote>

Guess you havn't seen that (yet) because get_maintainers.pl doesn't
really work for the cover letter, only the patches, and I forgot to cc
you for the whole series.  [ anyone has a solution for this btw? ]

> - does linux need to support this?

For pass-through (patch 4) yes, emulated hid devices (patch 3) not
really.  It is linux only for now because the code simply #includes the
system header files for the linux input layer, so it wouldn't compile on
something else.

>   if yes we'll eventually want to take
>   the header from there.

Yes, we can do that, for both linux input layer and virtio-input (once
merged upstream) headers, then go build this on non-linux hosts too.

I'd suggest to do that as incremental patch, after guest driver merge.

>   maybe split out guest/host ABI?

When copying over virtio-input header from linux kernel that needs to
happen anyway.

>   also might be a good idea to make style there
>   linux-compliant (e.g. no typedefs, add typedefs
>   in another header).

Linux driver linked above already has that.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-18 15:31     ` Gerd Hoffmann
@ 2015-03-18 16:58       ` Michael S. Tsirkin
  2015-03-19  7:45         ` Gerd Hoffmann
  0 siblings, 1 reply; 15+ messages in thread
From: Michael S. Tsirkin @ 2015-03-18 16:58 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Wed, Mar 18, 2015 at 04:31:53PM +0100, Gerd Hoffmann wrote:
> On Mi, 2015-03-18 at 15:31 +0100, Michael S. Tsirkin wrote:
> > On Wed, Mar 18, 2015 at 03:00:12PM +0100, Gerd Hoffmann wrote:
> > > 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>
> > 
> > Two questions before I looked at code:
> > - can you do a writeup for the virtio spec?
> >   might make it easier to review.
> 
> >From cover letter:
> 
> <quote>
> 
> Guest driver:
>   https://www.kraxel.org/cgit/linux/log/?h=virtio-input
> 
> Specification:
>   https://www.kraxel.org/cgit/virtio-spec/log/?h=virtio-input
>   https://www.kraxel.org/virtio/virtio-v1.0-csprd03-virtio-input.html#x1-2640007
> 
> </quote>
> 
> Guess you havn't seen that (yet) because get_maintainers.pl doesn't
> really work for the cover letter, only the patches, and I forgot to cc
> you for the whole series.

Right. I didn't review yet, just saw it in my inbox and asked some
questions. I assumed there's no cover letter.

>  [ anyone has a solution for this btw? ]

I have a patch to git that makes it Cc everyone who's
copied on cover letter. This let you supply people
to Cc while editing the cover letter.

> > - does linux need to support this?
> 
> For pass-through (patch 4) yes, emulated hid devices (patch 3) not
> really.  It is linux only for now because the code simply #includes the
> system header files for the linux input layer, so it wouldn't compile on
> something else.
> 
> >   if yes we'll eventually want to take
> >   the header from there.
> 
> Yes, we can do that, for both linux input layer and virtio-input (once
> merged upstream) headers, then go build this on non-linux hosts too.
> 
> I'd suggest to do that as incremental patch, after guest driver merge.

Hmm but why? It's just code churn.

> >   maybe split out guest/host ABI?
> 
> When copying over virtio-input header from linux kernel that needs to
> happen anyway.
> 
> >   also might be a good idea to make style there
> >   linux-compliant (e.g. no typedefs, add typedefs
> >   in another header).
> 
> Linux driver linked above already has that.
> 
> cheers,
>   Gerd
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-18 16:58       ` Michael S. Tsirkin
@ 2015-03-19  7:45         ` Gerd Hoffmann
  2015-03-19  9:24           ` Michael S. Tsirkin
  0 siblings, 1 reply; 15+ messages in thread
From: Gerd Hoffmann @ 2015-03-19  7:45 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel

  Hi,

> > >   if yes we'll eventually want to take
> > >   the header from there.
> > 
> > Yes, we can do that, for both linux input layer and virtio-input (once
> > merged upstream) headers, then go build this on non-linux hosts too.
> > 
> > I'd suggest to do that as incremental patch, after guest driver merge.
> 
> Hmm but why? It's just code churn.

Adding not-yet upstream files to the list of headers to sync over from
linux isn't sane IMHO.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-19  7:45         ` Gerd Hoffmann
@ 2015-03-19  9:24           ` Michael S. Tsirkin
  2015-03-19 11:33             ` Gerd Hoffmann
  0 siblings, 1 reply; 15+ messages in thread
From: Michael S. Tsirkin @ 2015-03-19  9:24 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Thu, Mar 19, 2015 at 08:45:29AM +0100, Gerd Hoffmann wrote:
>   Hi,
> 
> > > >   if yes we'll eventually want to take
> > > >   the header from there.
> > > 
> > > Yes, we can do that, for both linux input layer and virtio-input (once
> > > merged upstream) headers, then go build this on non-linux hosts too.
> > > 
> > > I'd suggest to do that as incremental patch, after guest driver merge.
> > 
> > Hmm but why? It's just code churn.
> 
> Adding not-yet upstream files to the list of headers to sync over from
> linux isn't sane IMHO.
> 
> cheers,
>   Gerd
> 

Of course it isn't. But you can sync it manually. Once it's upstream,
add it to list for automated sync.
Copy-pasting some defs from there in an unrelated header
seems worse.

-- 
MST

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-19  9:24           ` Michael S. Tsirkin
@ 2015-03-19 11:33             ` Gerd Hoffmann
  2015-03-19 11:37               ` Michael S. Tsirkin
  0 siblings, 1 reply; 15+ messages in thread
From: Gerd Hoffmann @ 2015-03-19 11:33 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel

  Hi,

> > Adding not-yet upstream files to the list of headers to sync over from
> > linux isn't sane IMHO.
> > 
> > cheers,
> >   Gerd
> > 
> 
> Of course it isn't. But you can sync it manually. Once it's upstream,
> add it to list for automated sync.

Hmm, well, not that simple, there is no whitelist for virtio headers but
a "*virtio*.h" glob.  So it'll simply sync automatic once landed
upstream, and may get deleted by accident before.

I've sent the guest kernel driver meanwhile, maybe this goes in quickly
and and renders the whole discussion moot ;)

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-19 11:33             ` Gerd Hoffmann
@ 2015-03-19 11:37               ` Michael S. Tsirkin
  2015-03-19 14:38                 ` Gerd Hoffmann
  0 siblings, 1 reply; 15+ messages in thread
From: Michael S. Tsirkin @ 2015-03-19 11:37 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Thu, Mar 19, 2015 at 12:33:01PM +0100, Gerd Hoffmann wrote:
>   Hi,
> 
> > > Adding not-yet upstream files to the list of headers to sync over from
> > > linux isn't sane IMHO.
> > > 
> > > cheers,
> > >   Gerd
> > > 
> > 
> > Of course it isn't. But you can sync it manually. Once it's upstream,
> > add it to list for automated sync.
> 
> Hmm, well, not that simple, there is no whitelist for virtio headers but
> a "*virtio*.h" glob.  So it'll simply sync automatic once landed
> upstream, and may get deleted by accident before.

Well no - by design we only update headers, never delete them.
If someone does manage to delete a header it's just a question of running
make before pushing upstream, isn't it?

> I've sent the guest kernel driver meanwhile, maybe this goes in quickly
> and and renders the whole discussion moot ;)
> 
> cheers,
>   Gerd
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-19 11:37               ` Michael S. Tsirkin
@ 2015-03-19 14:38                 ` Gerd Hoffmann
  2015-03-19 15:56                   ` Michael S. Tsirkin
  0 siblings, 1 reply; 15+ messages in thread
From: Gerd Hoffmann @ 2015-03-19 14:38 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: qemu-devel

On Do, 2015-03-19 at 12:37 +0100, Michael S. Tsirkin wrote:
> On Thu, Mar 19, 2015 at 12:33:01PM +0100, Gerd Hoffmann wrote:
> >   Hi,
> > 
> > > > Adding not-yet upstream files to the list of headers to sync over from
> > > > linux isn't sane IMHO.
> > > > 
> > > > cheers,
> > > >   Gerd
> > > > 
> > > 
> > > Of course it isn't. But you can sync it manually. Once it's upstream,
> > > add it to list for automated sync.
> > 
> > Hmm, well, not that simple, there is no whitelist for virtio headers but
> > a "*virtio*.h" glob.  So it'll simply sync automatic once landed
> > upstream, and may get deleted by accident before.
> 
> Well no - by design we only update headers, never delete them.

Hmm?

I'm talking about scripts/update-linux-headers.sh in the qemu tree.
That does a "rm -rf" followed by copying over the bits from linux
kernel.  So when switching from my virtio-input branch back to master
(in the linux kernel source tree) and re-running the script the header
file will disappear ...

Sounds like you are talking about something completely different.

cheers,
  Gerd

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2015-03-19 14:38                 ` Gerd Hoffmann
@ 2015-03-19 15:56                   ` Michael S. Tsirkin
  0 siblings, 0 replies; 15+ messages in thread
From: Michael S. Tsirkin @ 2015-03-19 15:56 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: qemu-devel

On Thu, Mar 19, 2015 at 03:38:59PM +0100, Gerd Hoffmann wrote:
> On Do, 2015-03-19 at 12:37 +0100, Michael S. Tsirkin wrote:
> > On Thu, Mar 19, 2015 at 12:33:01PM +0100, Gerd Hoffmann wrote:
> > >   Hi,
> > > 
> > > > > Adding not-yet upstream files to the list of headers to sync over from
> > > > > linux isn't sane IMHO.
> > > > > 
> > > > > cheers,
> > > > >   Gerd
> > > > > 
> > > > 
> > > > Of course it isn't. But you can sync it manually. Once it's upstream,
> > > > add it to list for automated sync.
> > > 
> > > Hmm, well, not that simple, there is no whitelist for virtio headers but
> > > a "*virtio*.h" glob.  So it'll simply sync automatic once landed
> > > upstream, and may get deleted by accident before.
> > 
> > Well no - by design we only update headers, never delete them.
> 
> Hmm?
> 
> I'm talking about scripts/update-linux-headers.sh in the qemu tree.
> That does a "rm -rf" followed by copying over the bits from linux
> kernel.  So when switching from my virtio-input branch back to master
> (in the linux kernel source tree) and re-running the script the header
> file will disappear ...
> 
> Sounds like you are talking about something completely different.
> 
> cheers,
>   Gerd

What I referred to is that we don't do any of git add/git rm.
User must manually run these to add/remove new headers.

-- 
MST

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class
  2014-06-03  8:42 [Qemu-devel] [PATCH 0/4] input: add virtio input devices Gerd Hoffmann
@ 2014-06-03  8:42 ` Gerd Hoffmann
  0 siblings, 0 replies; 15+ messages in thread
From: Gerd Hoffmann @ 2014-06-03  8:42 UTC (permalink / raw)
  To: qemu-devel; +Cc: Gerd Hoffmann, Anthony Liguori, 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..9571de5
--- /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 uint32_t virtio_input_get_features(VirtIODevice *vdev, uint32_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 ce97514..5518192 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -23,6 +23,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"
@@ -1531,6 +1532,40 @@ static const TypeInfo virtio_rng_pci_info = {
     .class_init    = virtio_rng_pci_class_init,
 };
 
+/* virtio-input-pci */
+
+static int virtio_input_pci_init(VirtIOPCIProxy *vpci_dev)
+{
+    VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev);
+    DeviceState *vdev = DEVICE(&vinput->vdev);
+
+    qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus));
+    return qdev_init(vdev);
+}
+
+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->init = virtio_input_pci_init;
+    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+
+    pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_INPUT;
+    pcidev_k->revision = VIRTIO_PCI_ABI_VERSION;
+    pcidev_k->class_id = PCI_CLASS_OTHERS;
+}
+
+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,
@@ -1575,6 +1610,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 dc332ae..f1e75ad 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 */
 
@@ -199,6 +201,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 3e54e90..93d1607 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -222,6 +222,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
                               struct virtio_net_conf *net,
                               uint32_t host_features);
 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] 15+ messages in thread

end of thread, other threads:[~2015-03-19 15:56 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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:31   ` Michael S. Tsirkin
2015-03-18 15:31     ` Gerd Hoffmann
2015-03-18 16:58       ` Michael S. Tsirkin
2015-03-19  7:45         ` Gerd Hoffmann
2015-03-19  9:24           ` Michael S. Tsirkin
2015-03-19 11:33             ` Gerd Hoffmann
2015-03-19 11:37               ` Michael S. Tsirkin
2015-03-19 14:38                 ` Gerd Hoffmann
2015-03-19 15:56                   ` 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
  -- strict thread matches above, loose matches on Subject: below --
2014-06-03  8:42 [Qemu-devel] [PATCH 0/4] input: add virtio input devices Gerd Hoffmann
2014-06-03  8:42 ` [Qemu-devel] [PATCH 2/4] virtio-input: core code & base class Gerd Hoffmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.