new file mode 100644
@@ -0,0 +1,50 @@
+
+virtio-input protocol
+=====================
+
+TL;DR It is basically the linux input layer event API in virtio.
+ioctls to query device capabilities map to config space. Events
+travel via virtqueues.
+
+
+config space
+------------
+
+Device config space (see include/hw/virtio/virtio-input.h, struct
+virtio_input_config) has a four byte header.
+
+ * First byte (select) can be written by the guest to pick the piece of
+ information it wants query.
+
+ * Third byte (size) is read-only and returns the size of the
+ information. If there isn't any (for example when checking mouse
+ axis information on a virtual keyboard) size will be zero.
+
+ * Second and fourth byte are reserved and should not be written by the
+ guest.
+
+
+After the header the actual information is stored:
+
+VIRTIO_INPUT_CFG_ID_NAME -- u.id_name
+ name of the device
+
+VIRTIO_INPUT_CFG_FL_REPEAT -- u.flag
+ true if autorepeat should be enabled for the device
+
+VIRTIO_INPUT_CFG_EV_* -- u.ev_bits
+ bitmap for the supported event codes
+
+VIRTIO_INPUT_CFG_ABS_BASE -- u.abs
+ struct virtio_input_absinfo for each absolute axis.
+ select = VIRTIO_INPUT_CFG_ABS_BASE + ABS_$axis
+
+
+virtqueues
+==========
+
+One queue (#0) for input events (KEY, ABS, REL + friends).
+One queue (#1) for status reports (LED).
+
+Both carry virtio_input_event structs.
+#0 is host->guest, #1 is guest->host.
@@ -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
new file mode 100644
@@ -0,0 +1,559 @@
+/*
+ * 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 <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,
+};
+
+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_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;
+ }
+ 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_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)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(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);
+ virtio_notify(vdev, vinput->evt);
+}
+
+static void virtio_input_handle_evt(VirtIODevice *vdev, VirtQueue *vq)
+{
+ /* nothing */
+}
+
+static void virtio_input_handle_sts(VirtIODevice *vdev, VirtQueue *vq)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+ virtio_input_event event;
+ VirtQueueElement elem;
+ int len;
+
+ fprintf(stderr, "%s:\n", __func__);
+ 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));
+ switch (le16_to_cpu(event.type)) {
+ default:
+ fprintf(stderr, "%s: unknown type %d\n", __func__,
+ le16_to_cpu(event.type));
+ break;
+ }
+ virtqueue_push(vinput->sts, &elem, len);
+ }
+ virtio_notify(vdev, vinput->sts);
+}
+
+static virtio_input_config *virtio_input_find_config(VirtIOInput *vinput,
+ uint16_t select)
+{
+ int i = 0;
+
+ while (vinput->config && vinput->config[i].select) {
+ if (vinput->config[i].select == select) {
+ return &vinput->config[i];
+ }
+ i++;
+ }
+ return NULL;
+}
+
+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;
+
+ keys = virtio_input_find_config(vinput, VIRTIO_INPUT_CFG_EV_KEY);
+ assert(keys != NULL);
+ for (i = 0; i < mapsize; i++) {
+ bit = keymap[i];
+ if (!bit) {
+ continue;
+ }
+ byte = bit / 8;
+ bit = bit % 8;
+ keys->u.ev_bits[byte] |= (1 << bit);
+ if (bmax < byte+1) {
+ bmax = byte+1;
+ }
+ }
+ keys->size = bmax;
+}
+
+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);
+ if (config) {
+ memcpy(config_data, config, sizeof(*config));
+ } else {
+ memset(config_data, 0, sizeof(*config));
+ }
+}
+
+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;
+ 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)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+
+ if (val & VIRTIO_CONFIG_S_DRIVER_OK) {
+ if (!vinput->active) {
+ qemu_input_handler_activate(vinput->hs);
+ vinput->active = true;
+ }
+ }
+}
+
+static void virtio_input_reset(VirtIODevice *vdev)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(vdev);
+
+ if (vinput->active) {
+ qemu_input_handler_deactivate(vinput->hs);
+ vinput->active = false;
+ }
+}
+
+static void virtio_input_device_realize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+
+ virtio_init(vdev, "virtio-input", VIRTIO_ID_INPUT,
+ sizeof(virtio_input_config));
+ vinput->evt = virtio_add_queue(vdev, 64, virtio_input_handle_evt);
+ vinput->sts = virtio_add_queue(vdev, 64, virtio_input_handle_sts);
+ vinput->hs = qemu_input_handler_register(dev, vinput->handler);
+}
+
+static void virtio_input_device_unrealize(DeviceState *dev, Error **errp)
+{
+ VirtIODevice *vdev = VIRTIO_DEVICE(dev);
+ VirtIOInput *vinput = VIRTIO_INPUT(dev);
+
+ qemu_input_handler_unregister(vinput->hs);
+ 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_init = virtio_input_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.id_name = VIRTIO_ID_NAME_KEYBOARD,
+ },{
+ .select = VIRTIO_INPUT_CFG_FL_REPEAT,
+ .size = 1,
+ .u.flag = true,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_KEY,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_LED,
+ .size = 1,
+ .u.ev_bits = {
+ (1 << LED_NUML) | (1 << LED_CAPSL) | (1 << LED_SCROLLL),
+ },
+ },
+ { /* end of list */ },
+};
+
+static void virtio_keyboard_init(Object *obj)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vinput->config = virtio_keyboard_config;
+ vinput->handler = &virtio_keyboard_handler;
+ 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,
+ .instance_size = sizeof(VirtIOInput),
+ .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.id_name = VIRTIO_ID_NAME_MOUSE,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_KEY,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_REL,
+ .size = 1,
+ .u.ev_bits = {
+ (1 << REL_X) | (1 << REL_Y),
+ },
+ },
+ { /* end of list */ },
+};
+
+static void virtio_mouse_init(Object *obj)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vinput->config = virtio_mouse_config;
+ vinput->handler = &virtio_mouse_handler;
+ 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,
+ .instance_size = sizeof(VirtIOInput),
+ .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.id_name = VIRTIO_ID_NAME_TABLET,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_KEY,
+ },{
+ .select = VIRTIO_INPUT_CFG_EV_ABS,
+ .size = 1,
+ .u.ev_bits = {
+ (1 << ABS_X) | (1 << ABS_Y),
+ },
+ },{
+ .select = VIRTIO_INPUT_CFG_ABS_BASE + ABS_X,
+ .size = sizeof(virtio_input_absinfo),
+#if 0
+ /* FIXME */
+ .u.abs.max = cpu_to_le32(INPUT_EVENT_ABS_SIZE),
+#else
+ .u.abs.max = INPUT_EVENT_ABS_SIZE,
+#endif
+ },{
+ .select = VIRTIO_INPUT_CFG_ABS_BASE + ABS_Y,
+ .size = sizeof(virtio_input_absinfo),
+#if 0
+ /* FIXME */
+ .u.abs.max = cpu_to_le32(INPUT_EVENT_ABS_SIZE),
+#else
+ .u.abs.max = INPUT_EVENT_ABS_SIZE,
+#endif
+ },
+ { /* end of list */ },
+};
+
+static void virtio_tablet_init(Object *obj)
+{
+ VirtIOInput *vinput = VIRTIO_INPUT(obj);
+
+ vinput->config = virtio_tablet_config;
+ vinput->handler = &virtio_tablet_handler;
+ 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,
+ .instance_size = sizeof(VirtIOInput),
+ .instance_init = virtio_tablet_init,
+};
+
+/* ----------------------------------------------------------------- */
+
+static void virtio_register_types(void)
+{
+ type_register_static(&virtio_input_info);
+ type_register_static(&virtio_keyboard_info);
+ type_register_static(&virtio_mouse_info);
+ type_register_static(&virtio_tablet_info);
+}
+
+type_init(virtio_register_types)
@@ -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"
@@ -1529,6 +1530,89 @@ static const TypeInfo virtio_rng_pci_info = {
.class_init = virtio_rng_pci_class_init,
};
+/* virtio-input-pci */
+
+static Property virtio_input_pci_properties[] = {
+ DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+ DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+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);
+ dc->props = virtio_input_pci_properties;
+
+ 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 void virtio_keyboard_initfn(Object *obj)
+{
+ VirtIOInputPCI *dev = VIRTIO_INPUT_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)
+{
+ VirtIOInputPCI *dev = VIRTIO_INPUT_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)
+{
+ VirtIOInputPCI *dev = VIRTIO_INPUT_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,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .class_init = virtio_input_pci_class_init,
+ .abstract = true,
+};
+
+static const TypeInfo virtio_keyboard_pci_info = {
+ .name = TYPE_VIRTIO_KEYBOARD_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .instance_init = virtio_keyboard_initfn,
+};
+
+static const TypeInfo virtio_mouse_pci_info = {
+ .name = TYPE_VIRTIO_MOUSE_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .instance_init = virtio_mouse_initfn,
+};
+
+static const TypeInfo virtio_tablet_pci_info = {
+ .name = TYPE_VIRTIO_TABLET_PCI,
+ .parent = TYPE_VIRTIO_INPUT_PCI,
+ .instance_size = sizeof(VirtIOInputPCI),
+ .instance_init = virtio_tablet_initfn,
+};
+
/* virtio-pci-bus */
static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size,
@@ -1573,6 +1657,10 @@ 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_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
@@ -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,22 @@ struct VirtIORngPCI {
VirtIORNG vdev;
};
+/*
+ * virtio-keyboard-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)
+
+#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci"
+#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci"
+#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-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
@@ -80,6 +80,7 @@
#define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004
#define PCI_DEVICE_ID_VIRTIO_RNG 0x1005
#define PCI_DEVICE_ID_VIRTIO_9P 0x1009
+#define PCI_DEVICE_ID_VIRTIO_INPUT 0x1021
#define PCI_VENDOR_ID_REDHAT 0x1b36
#define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001
new file mode 100644
@@ -0,0 +1,71 @@
+#ifndef _QEMU_VIRTIO_INPUT_H
+#define _QEMU_VIRTIO_INPUT_H
+
+#include "ui/input.h"
+
+#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 TYPE_VIRTIO_KEYBOARD "virtio-keyboard"
+#define TYPE_VIRTIO_MOUSE "virtio-mouse"
+#define TYPE_VIRTIO_TABLET "virtio-tablet"
+
+/* The Virtio ID for the virtio input device */
+#define VIRTIO_ID_INPUT 0x21
+
+enum virtio_input_config_select {
+ VIRTIO_INPUT_CFG_UNSET = 0x00,
+ VIRTIO_INPUT_CFG_ID_NAME = 0x01,
+ VIRTIO_INPUT_CFG_FL_REPEAT = 0x08,
+
+ VIRTIO_INPUT_CFG_EV_KEY = 0x10,
+ VIRTIO_INPUT_CFG_EV_REL,
+ VIRTIO_INPUT_CFG_EV_ABS,
+ VIRTIO_INPUT_CFG_EV_MSC,
+ VIRTIO_INPUT_CFG_EV_SW,
+
+ VIRTIO_INPUT_CFG_EV_LED = 0x20,
+
+ VIRTIO_INPUT_CFG_ABS_BASE = 0x30,
+};
+
+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 reserved1;
+ uint8_t size;
+ uint8_t reserved2;
+ union {
+ char id_name[128];
+ uint8_t flag;
+ uint8_t ev_bits[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;
+
+typedef struct VirtIOInput {
+ VirtIODevice parent_obj;
+ uint8_t cfg_select;
+ virtio_input_config *config;
+ VirtQueue *evt, *sts;
+ QemuInputHandler *handler;
+ QemuInputHandlerState *hs;
+ bool active;
+} VirtIOInput;
+
+#endif /* _QEMU_VIRTIO_INPUT_H */
Experimental virtio protocol implementation for input devices. See docs/specs/virtio-input.txt (+code) for more info. Detailed specs are to be written. Guest bits: http://www.kraxel.org/cgit/linux/log/?h=virtio-input Config isn't exactly small, so I tend to make that a pure virtio 1.0 device where virtio config space can live in mmio and doesn't waste io address space. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> --- docs/specs/virtio-input.txt | 50 ++++ hw/input/Makefile.objs | 4 + hw/input/virtio-input.c | 559 +++++++++++++++++++++++++++++++++++++++ hw/virtio/virtio-pci.c | 88 ++++++ hw/virtio/virtio-pci.h | 18 ++ include/hw/pci/pci.h | 1 + include/hw/virtio/virtio-input.h | 71 +++++ 7 files changed, 791 insertions(+) create mode 100644 docs/specs/virtio-input.txt create mode 100644 hw/input/virtio-input.c create mode 100644 include/hw/virtio/virtio-input.h