diff mbox

[qemu,4/6] virtio-input: emulated devices

Message ID 1397120874-17166-7-git-send-email-kraxel@redhat.com
State New
Headers show

Commit Message

Gerd Hoffmann April 10, 2014, 9:07 a.m. UTC
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      | 486 +++++++++++++++++++++++++++++++++++++++
 hw/virtio/virtio-pci.c           |  68 ++++++
 hw/virtio/virtio-pci.h           |  13 ++
 include/hw/virtio/virtio-input.h |  22 ++
 5 files changed, 590 insertions(+)
 create mode 100644 hw/input/virtio-input-hid.c

Comments

Michael S. Tsirkin April 10, 2014, 10:55 a.m. UTC | #1
On Thu, Apr 10, 2014 at 11:07:52AM +0200, Gerd Hoffmann wrote:
> 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>

Hmm - that's interesting.
I was under the impression that a single pci function can be
a keyboard, mouse and tablet at the same time.

If they aren't why don't we assign distinct device IDs to them
after all?

> ---
>  hw/input/Makefile.objs           |   1 +
>  hw/input/virtio-input-hid.c      | 486 +++++++++++++++++++++++++++++++++++++++
>  hw/virtio/virtio-pci.c           |  68 ++++++
>  hw/virtio/virtio-pci.h           |  13 ++
>  include/hw/virtio/virtio-input.h |  22 ++
>  5 files changed, 590 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..2db993e
> --- /dev/null
> +++ b/hw/input/virtio-input-hid.c
> @@ -0,0 +1,486 @@
> +/*
> + * 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,
> +};

OK these are values send to guest, right?
And they are from linux/input.h, right? But are these
reasonable in a cross-platform device?

E.g. Linux is pretty good at backwards compatibility
but less good at versioning.

That header says "Most of the keys/buttons are modeled after USB HUT
1.12" but as far as I could see the codes are not from HUT, correct?

Would it be a good idea to use codes from HUT directly?
This way we could extend functionality without adding lots of
text to the spec, simply by referring to HUT.

Also what defines the subset selected?
I'm a bit worried that we'll get into an endless cycle of spec
updates with each new button missing above.

> +
> +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,
> +};
> +

In the future, it seems like a good idea to report raw
multi-touch events to guests - this would need a different
interface along the lines of
Documentation/input/multi-touch-protocol.txt

How would we handle such an extension?
Do MT devices generate ST events as well so it's ok to just
filter out everything we don't recognize?


> +/* ----------------------------------------------------------------- */
> +
> +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)
> +{
> +    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_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);


What does this do? notice led light up on one keyboard and propagate
state to all keyboards?

> +        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 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_INFO,
> +        .subsel    = 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)
> +{
> +    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 5518192..b421c01 100644
> --- a/hw/virtio/virtio-pci.c
> +++ b/hw/virtio/virtio-pci.c
> @@ -1534,6 +1534,13 @@ static const TypeInfo virtio_rng_pci_info = {
>  
>  /* virtio-input-pci */
>  
> +static Property virtio_input_hid_pci_properties[] = {
> +    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
> +    DEFINE_VIRTIO_INPUT_PROPERTIES(VirtIOInputPCI, vdev.input),
> +    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);
> @@ -1558,6 +1565,34 @@ static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
>      pcidev_k->class_id = PCI_CLASS_OTHERS;
>  }
>  
> +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_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,
> @@ -1566,6 +1601,35 @@ 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,
> +    .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,
> +    .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,
> @@ -1611,6 +1675,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 f1e75ad..f615ae6 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 */
>  
> @@ -213,6 +214,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 5d37a70..4b819c3 100644
> --- a/include/hw/virtio/virtio-input.h
> +++ b/include/hw/virtio/virtio-input.h
> @@ -57,9 +57,24 @@ 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), \
> +        DEFINE_PROP_STRING("seat", _state, _field.seat)
> +
>  typedef struct VirtIOInput VirtIOInput;
>  typedef struct VirtIOInputClass VirtIOInputClass;
>  typedef struct VirtIOInputConfig VirtIOInputConfig;
> +typedef struct VirtIOInputHID VirtIOInputHID;
>  
>  struct virtio_input_conf {
>      char *serial;
> @@ -94,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
Gerd Hoffmann April 10, 2014, 11:47 a.m. UTC | #2
On Do, 2014-04-10 at 13:55 +0300, Michael S. Tsirkin wrote:
> On Thu, Apr 10, 2014 at 11:07:52AM +0200, Gerd Hoffmann wrote:
> > 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>
> 
> Hmm - that's interesting.
> I was under the impression that a single pci function can be
> a keyboard, mouse and tablet at the same time.

It is possible to create a device supporting both keyboard and
mouse/tablet events.  Which will also show up as single input device in
the guest then.  People and software tends to not expect that though, so
I think it is better to keep them separate.

> If they aren't why don't we assign distinct device IDs to them
> after all?

pci device ids I assume?  Sure, we can do that.  Will make lspci output
a bit more informative (no need to check /proc/bus/input/devices to
figure what kind of input device it is).

> > +    [Q_KEY_CODE_META_L]              = KEY_LEFTMETA,
> > +    [Q_KEY_CODE_META_R]              = KEY_RIGHTMETA,
> > +    [Q_KEY_CODE_MENU]                = KEY_MENU,
> > +};
> 
> OK these are values send to guest, right?

Yes.

> And they are from linux/input.h, right? But are these
> reasonable in a cross-platform device?

Can't see strong reasons speaking against it.  It's kernel/userspace
API, therefore stable.  There are keycodes defined for pretty much
anything you can think of.

linux guest code is dead simple.  For other guests supporting it
shouldn't be that hard too, they basically need a mapping table to map
the linux KEY_* codes into their internal representation.

> E.g. Linux is pretty good at backwards compatibility
> but less good at versioning.

--verbose please.

> That header says "Most of the keys/buttons are modeled after USB HUT
> 1.12" but as far as I could see the codes are not from HUT, correct?

No, the codes are different.

> Would it be a good idea to use codes from HUT directly?
> This way we could extend functionality without adding lots of
> text to the spec, simply by referring to HUT.

I want to simply refer to linux/input.h in the spec.

> Also what defines the subset selected?

All keys in linux/input.h are supported by the virtio input protocol.

The current qemu kbd emulation covers all keys qemu knows (see QKeyCode
in qapi-schema.json).

> > +static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
> > +    [INPUT_AXIS_X]                   = ABS_X,
> > +    [INPUT_AXIS_Y]                   = ABS_Y,
> > +};
> > +
> 
> In the future, it seems like a good idea to report raw
> multi-touch events to guests - this would need a different
> interface along the lines of
> Documentation/input/multi-touch-protocol.txt

Should be no big deal.  Not looked at that deeply yet due to lack of
test hardware, but I think all we need is mapping the info from
EVIOCGMTSLOTS into config space, simliar to how it is done for
EVIOCGABS.

> Do MT devices generate ST events as well so it's ok to just
> filter out everything we don't recognize?

Yes, as far I know both mt and st events are generated.

> > +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);
> 
> What does this do? notice led light up on one keyboard and propagate
> state to all keyboards?

Notify everybody interested in about kbd led changes.  ps/2+usb kbd
emulations do the same.

It is used by vnc for example, to make sure capslock/numlock state
between guest and host stay in sync.

cheers,
  Gerd
Michael S. Tsirkin April 10, 2014, 3:14 p.m. UTC | #3
On Thu, Apr 10, 2014 at 01:47:08PM +0200, Gerd Hoffmann wrote:
> On Do, 2014-04-10 at 13:55 +0300, Michael S. Tsirkin wrote:
> > On Thu, Apr 10, 2014 at 11:07:52AM +0200, Gerd Hoffmann wrote:
> > > 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>
> > 
> > Hmm - that's interesting.
> > I was under the impression that a single pci function can be
> > a keyboard, mouse and tablet at the same time.
> 
> It is possible to create a device supporting both keyboard and
> mouse/tablet events.  Which will also show up as single input device in
> the guest then.  People and software tends to not expect that though, so
> I think it is better to keep them separate.
> 
> > If they aren't why don't we assign distinct device IDs to them
> > after all?
> 
> pci device ids I assume?  Sure, we can do that.  Will make lspci output
> a bit more informative (no need to check /proc/bus/input/devices to
> figure what kind of input device it is).

It's up to you I am just asking.

If drivers don't expect a mix of functionality, then separate IDs
seem better.
OTOH an advantage to using a custom identification scheme would be
if you want to allow a single device to change type
dynamically.

E.g. if guest knows about MT, act as an MT device otherwise
act as a mouse.

Or more interestingly, as we migrate from host with a mouse
to an MT one, switch from a mouse to a touch device?


> > > +    [Q_KEY_CODE_META_L]              = KEY_LEFTMETA,
> > > +    [Q_KEY_CODE_META_R]              = KEY_RIGHTMETA,
> > > +    [Q_KEY_CODE_MENU]                = KEY_MENU,
> > > +};
> > 
> > OK these are values send to guest, right?
> 
> Yes.
> 
> > And they are from linux/input.h, right? But are these
> > reasonable in a cross-platform device?
> 
> Can't see strong reasons speaking against it.  It's kernel/userspace
> API, therefore stable.  There are keycodes defined for pretty much
> anything you can think of.
> 
> linux guest code is dead simple.  For other guests supporting it
> shouldn't be that hard too, they basically need a mapping table to map
> the linux KEY_* codes into their internal representation.
> > E.g. Linux is pretty good at backwards compatibility
> > but less good at versioning.
> 
> --verbose please.

See below.


> > That header says "Most of the keys/buttons are modeled after USB HUT
> > 1.12" but as far as I could see the codes are not from HUT, correct?
> 
> No, the codes are different.
> 
> > Would it be a good idea to use codes from HUT directly?
> > This way we could extend functionality without adding lots of
> > text to the spec, simply by referring to HUT.
> 
> I want to simply refer to linux/input.h in the spec.


That's exactly the question.  Which version of linux/input.h?

> > Also what defines the subset selected?
> 
> All keys in linux/input.h are supported by the virtio input protocol.

I'm asking how is this versioned: assume that linux adds
a new keycode, existing guests don't expect it.
Don't we need any negotiation? Is it really always safe to
ignore keypresses?

> The current qemu kbd emulation covers all keys qemu knows (see QKeyCode
> in qapi-schema.json).

Hmm SDL can give us a bunch of codes like volume up/down
that you don't seem to include.



> > > +static const unsigned int axismap_abs[INPUT_AXIS_MAX] = {
> > > +    [INPUT_AXIS_X]                   = ABS_X,
> > > +    [INPUT_AXIS_Y]                   = ABS_Y,
> > > +};
> > > +
> > 
> > In the future, it seems like a good idea to report raw
> > multi-touch events to guests - this would need a different
> > interface along the lines of
> > Documentation/input/multi-touch-protocol.txt
> 
> Should be no big deal.  Not looked at that deeply yet due to lack of
> test hardware, but I think all we need is mapping the info from
> EVIOCGMTSLOTS into config space, simliar to how it is done for
> EVIOCGABS.
> > Do MT devices generate ST events as well so it's ok to just
> > filter out everything we don't recognize?
> 
> Yes, as far I know both mt and st events are generated.
> 
> > > +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);
> > 
> > What does this do? notice led light up on one keyboard and propagate
> > state to all keyboards?
> 
> Notify everybody interested in about kbd led changes.  ps/2+usb kbd
> emulations do the same.
> 
> It is used by vnc for example, to make sure capslock/numlock state
> between guest and host stay in sync.
> 
> cheers,
>   Gerd
>
diff mbox

Patch

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..2db993e
--- /dev/null
+++ b/hw/input/virtio-input-hid.c
@@ -0,0 +1,486 @@ 
+/*
+ * 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)
+{
+    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_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 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_INFO,
+        .subsel    = 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)
+{
+    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 5518192..b421c01 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1534,6 +1534,13 @@  static const TypeInfo virtio_rng_pci_info = {
 
 /* virtio-input-pci */
 
+static Property virtio_input_hid_pci_properties[] = {
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_VIRTIO_INPUT_PROPERTIES(VirtIOInputPCI, vdev.input),
+    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);
@@ -1558,6 +1565,34 @@  static void virtio_input_pci_class_init(ObjectClass *klass, void *data)
     pcidev_k->class_id = PCI_CLASS_OTHERS;
 }
 
+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_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,
@@ -1566,6 +1601,35 @@  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,
+    .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,
+    .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,
@@ -1611,6 +1675,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 f1e75ad..f615ae6 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 */
 
@@ -213,6 +214,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 5d37a70..4b819c3 100644
--- a/include/hw/virtio/virtio-input.h
+++ b/include/hw/virtio/virtio-input.h
@@ -57,9 +57,24 @@  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), \
+        DEFINE_PROP_STRING("seat", _state, _field.seat)
+
 typedef struct VirtIOInput VirtIOInput;
 typedef struct VirtIOInputClass VirtIOInputClass;
 typedef struct VirtIOInputConfig VirtIOInputConfig;
+typedef struct VirtIOInputHID VirtIOInputHID;
 
 struct virtio_input_conf {
     char *serial;
@@ -94,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);