/* * 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 "hw/qdev.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-input.h" #undef CONFIG_CURSES #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, 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 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); dc->props = 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); dc->props = 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); dc->props = 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)