diff options
author | Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> | 2023-10-10 11:40:56 +0000 |
---|---|---|
committer | Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> | 2023-10-10 11:40:56 +0000 |
commit | e02cda008591317b1625707ff8e115a4841aa889 (patch) | |
tree | aee302e3cf8b59ec2d32ec481be3d1afddfc8968 /hw/input/virtio-input-hid.c | |
parent | cc668e6b7e0ffd8c9d130513d12053cf5eda1d3b (diff) |
Introduce Virtio-loopback epsilon release:
Epsilon release introduces a new compatibility layer which make virtio-loopback
design to work with QEMU and rust-vmm vhost-user backend without require any
changes.
Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
Change-Id: I52e57563e08a7d0bdc002f8e928ee61ba0c53dd9
Diffstat (limited to 'hw/input/virtio-input-hid.c')
-rw-r--r-- | hw/input/virtio-input-hid.c | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c new file mode 100644 index 000000000..a7a244a95 --- /dev/null +++ b/hw/input/virtio-input-hid.c @@ -0,0 +1,522 @@ +/* + * 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/osdep.h" +#include "qemu/iov.h" +#include "qemu/module.h" + +#include "hw/virtio/virtio.h" +#include "hw/qdev-properties.h" +#include "hw/virtio/virtio-input.h" + +#include "ui/console.h" + +#include "standard-headers/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 short 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, + [INPUT_BUTTON_SIDE] = BTN_SIDE, + [INPUT_BUTTON_EXTRA] = BTN_EXTRA, +}; + +static const unsigned short axismap_rel[INPUT_AXIS__MAX] = { + [INPUT_AXIS_X] = REL_X, + [INPUT_AXIS_Y] = REL_Y, +}; + +static const unsigned short 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 short *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) +{ + VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev); + VirtIOInput *vinput = VIRTIO_INPUT(dev); + virtio_input_event event; + int qcode; + InputKeyEvent *key; + InputMoveEvent *move; + InputBtnEvent *btn; + + switch (evt->type) { + case INPUT_EVENT_KIND_KEY: + key = evt->u.key.data; + qcode = qemu_input_key_value_to_qcode(key->key); + if (qcode < qemu_input_map_qcode_to_linux_len && + qemu_input_map_qcode_to_linux[qcode]) { + event.type = cpu_to_le16(EV_KEY); + event.code = cpu_to_le16(qemu_input_map_qcode_to_linux[qcode]); + event.value = cpu_to_le32(key->down ? 1 : 0); + virtio_input_send(vinput, &event); + } else { + if (key->down) { + fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__, + qcode, QKeyCode_str(qcode)); + } + } + break; + case INPUT_EVENT_KIND_BTN: + btn = evt->u.btn.data; + if (vhid->wheel_axis && + (btn->button == INPUT_BUTTON_WHEEL_UP || + btn->button == INPUT_BUTTON_WHEEL_DOWN) && + btn->down) { + event.type = cpu_to_le16(EV_REL); + event.code = cpu_to_le16(REL_WHEEL); + event.value = cpu_to_le32(btn->button == INPUT_BUTTON_WHEEL_UP + ? 1 : -1); + virtio_input_send(vinput, &event); + } else if (keymap_button[btn->button]) { + event.type = cpu_to_le16(EV_KEY); + event.code = cpu_to_le16(keymap_button[btn->button]); + event.value = cpu_to_le32(btn->down ? 1 : 0); + virtio_input_send(vinput, &event); + } else { + if (btn->down) { + fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__, + btn->button, + InputButton_str(btn->button)); + } + } + break; + case INPUT_EVENT_KIND_REL: + move = evt->u.rel.data; + event.type = cpu_to_le16(EV_REL); + event.code = cpu_to_le16(axismap_rel[move->axis]); + event.value = cpu_to_le32(move->value); + virtio_input_send(vinput, &event); + break; + case INPUT_EVENT_KIND_ABS: + move = evt->u.abs.data; + event.type = cpu_to_le16(EV_ABS); + event.code = cpu_to_le16(axismap_abs[move->axis]); + event.value = cpu_to_le32(move->value); + virtio_input_send(vinput, &event); + break; + default: + /* keep gcc happy */ + break; + } +} + +static void virtio_input_handle_sync(DeviceState *dev) +{ + VirtIOInput *vinput = VIRTIO_INPUT(dev); + virtio_input_event event = { + .type = cpu_to_le16(EV_SYN), + .code = cpu_to_le16(SYN_REPORT), + .value = 0, + }; + + virtio_input_send(vinput, &event); +} + +static void virtio_input_hid_realize(DeviceState *dev, Error **errp) +{ + VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev); + + vhid->hs = qemu_input_handler_register(dev, vhid->handler); + if (vhid->display && vhid->hs) { + qemu_input_handler_bind(vhid->hs, vhid->display, vhid->head, NULL); + } +} + +static void virtio_input_hid_unrealize(DeviceState *dev) +{ + 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 Property virtio_input_hid_properties[] = { + DEFINE_PROP_STRING("display", VirtIOInputHID, display), + DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_input_hid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); + + device_class_set_props(dc, virtio_input_hid_properties); + 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_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = const_le16(BUS_VIRTUAL), + .vendor = const_le16(0x0627), /* same we use for usb hid devices */ + .product = const_le16(0x0001), + .version = const_le16(0x0001), + }, + },{ + .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, qemu_input_map_qcode_to_linux, + qemu_input_map_qcode_to_linux_len); +} + +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_v1[] = { + { + .select = VIRTIO_INPUT_CFG_ID_NAME, + .size = sizeof(VIRTIO_ID_NAME_MOUSE), + .u.string = VIRTIO_ID_NAME_MOUSE, + },{ + .select = VIRTIO_INPUT_CFG_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = const_le16(BUS_VIRTUAL), + .vendor = const_le16(0x0627), /* same we use for usb hid devices */ + .product = const_le16(0x0002), + .version = const_le16(0x0001), + }, + },{ + .select = VIRTIO_INPUT_CFG_EV_BITS, + .subsel = EV_REL, + .size = 1, + .u.bitmap = { + (1 << REL_X) | (1 << REL_Y), + }, + }, + { /* end of list */ }, +}; + +static struct virtio_input_config virtio_mouse_config_v2[] = { + { + .select = VIRTIO_INPUT_CFG_ID_NAME, + .size = sizeof(VIRTIO_ID_NAME_MOUSE), + .u.string = VIRTIO_ID_NAME_MOUSE, + },{ + .select = VIRTIO_INPUT_CFG_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = const_le16(BUS_VIRTUAL), + .vendor = const_le16(0x0627), /* same we use for usb hid devices */ + .product = const_le16(0x0002), + .version = const_le16(0x0002), + }, + },{ + .select = VIRTIO_INPUT_CFG_EV_BITS, + .subsel = EV_REL, + .size = 2, + .u.bitmap = { + (1 << REL_X) | (1 << REL_Y), + (1 << (REL_WHEEL - 8)) + }, + }, + { /* end of list */ }, +}; + +static Property virtio_mouse_properties[] = { + DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_mouse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, virtio_mouse_properties); +} + +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, vhid->wheel_axis + ? virtio_mouse_config_v2 + : virtio_mouse_config_v1); + 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, + .class_init = virtio_mouse_class_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_v1[] = { + { + .select = VIRTIO_INPUT_CFG_ID_NAME, + .size = sizeof(VIRTIO_ID_NAME_TABLET), + .u.string = VIRTIO_ID_NAME_TABLET, + },{ + .select = VIRTIO_INPUT_CFG_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = const_le16(BUS_VIRTUAL), + .vendor = const_le16(0x0627), /* same we use for usb hid devices */ + .product = const_le16(0x0003), + .version = const_le16(0x0001), + }, + },{ + .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), + .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX), + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_Y, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX), + }, + { /* end of list */ }, +}; + +static struct virtio_input_config virtio_tablet_config_v2[] = { + { + .select = VIRTIO_INPUT_CFG_ID_NAME, + .size = sizeof(VIRTIO_ID_NAME_TABLET), + .u.string = VIRTIO_ID_NAME_TABLET, + },{ + .select = VIRTIO_INPUT_CFG_ID_DEVIDS, + .size = sizeof(struct virtio_input_devids), + .u.ids = { + .bustype = const_le16(BUS_VIRTUAL), + .vendor = const_le16(0x0627), /* same we use for usb hid devices */ + .product = const_le16(0x0003), + .version = const_le16(0x0002), + }, + },{ + .select = VIRTIO_INPUT_CFG_EV_BITS, + .subsel = EV_ABS, + .size = 1, + .u.bitmap = { + (1 << ABS_X) | (1 << ABS_Y), + }, + },{ + .select = VIRTIO_INPUT_CFG_EV_BITS, + .subsel = EV_REL, + .size = 2, + .u.bitmap = { + 0, + (1 << (REL_WHEEL - 8)) + }, + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_X, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX), + },{ + .select = VIRTIO_INPUT_CFG_ABS_INFO, + .subsel = ABS_Y, + .size = sizeof(virtio_input_absinfo), + .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN), + .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX), + }, + { /* end of list */ }, +}; + +static Property virtio_tablet_properties[] = { + DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_tablet_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_props(dc, virtio_tablet_properties); +} + +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, vhid->wheel_axis + ? virtio_tablet_config_v2 + : virtio_tablet_config_v1); + 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, + .class_init = virtio_tablet_class_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) |