[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH qemu 3/6] virtio-input: core code & base class
From: |
Michael S. Tsirkin |
Subject: |
Re: [Qemu-devel] [PATCH qemu 3/6] virtio-input: core code & base class |
Date: |
Thu, 10 Apr 2014 14:06:21 +0300 |
On Thu, Apr 10, 2014 at 11:07:51AM +0200, 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 <address@hidden>
> ---
> hw/input/Makefile.objs | 4 +
> hw/input/virtio-input.c | 253
> +++++++++++++++++++++++++++++++++++++++
> hw/virtio/virtio-pci.c | 36 ++++++
> hw/virtio/virtio-pci.h | 14 +++
> include/hw/virtio/virtio-input.h | 105 ++++++++++++++++
> include/hw/virtio/virtio.h | 1 +
> 6 files changed, 413 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..35f0cfc
> --- /dev/null
> +++ b/hw/input/virtio-input.c
> @@ -0,0 +1,253 @@
> +/*
> + * 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;
> + int len;
> +
> + if (!virtqueue_pop(vinput->evt, &elem)) {
> + fprintf(stderr, "%s: virtqueue empty, dropping event\n", __func__);
> + return;
Looks scary.
> + }
> + len = iov_from_buf(elem.in_sg, elem.in_num,
> + 0, event, sizeof(*event));
> + virtqueue_push(vinput->evt, &elem, len);
> +}
> +
> +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;
> +
> +
> + if (vic->unrealize) {
> + vic->realize(dev, errp);
> + if (error_is_set(errp)) {
> + return;
> + }
> + }
> +
> + virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SERIAL,
> + vinput->input.serial);
> + virtio_input_idstr_config(vinput, VIRTIO_INPUT_CFG_ID_SEAT,
> + vinput->input.seat);
> +
> + 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);
> +
> + if (vic->unrealize) {
> + vic->unrealize(dev, errp);
> + if (error_is_set(errp)) {
> + 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..5d37a70
> --- /dev/null
> +++ b/include/hw/virtio/virtio-input.h
> @@ -0,0 +1,105 @@
> +#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_ID_SEAT = 0x03,
> + 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;
> + char *seat;
> +};
> +
> +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;
> +
> + 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
- Re: [Qemu-devel] [PATCH qemu 6/6] virtio-input: evdev passthrough, (continued)
[Qemu-devel] [PATCH qemu 4/6] virtio-input: emulated devices, Gerd Hoffmann, 2014/04/10
[Qemu-devel] [PATCH qemu 3/6] virtio-input: core code & base class, Gerd Hoffmann, 2014/04/10
- Re: [Qemu-devel] [PATCH qemu 3/6] virtio-input: core code & base class,
Michael S. Tsirkin <=
[Qemu-devel] [PATCH spec] Add virtio input device specification, Gerd Hoffmann, 2014/04/10