X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/0e4e9695d5c4a5923094906433b5f7b431ba35e4..c0a93a9efabae3c9a8500bf2f14ffb06e313dc73:/hw/usb-hid.c diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 74d17fc26f..89c293c466 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -26,7 +26,7 @@ #include "console.h" #include "usb.h" #include "usb-desc.h" -#include "sysemu.h" +#include "qemu-timer.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -45,18 +45,27 @@ #define USB_TABLET 2 #define USB_KEYBOARD 3 +typedef struct USBPointerEvent { + int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ + int32_t dz, buttons_state; +} USBPointerEvent; + +#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ +#define QUEUE_MASK (QUEUE_LENGTH-1u) +#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) + typedef struct USBMouseState { - int dx, dy, dz, buttons_state; - int x, y; + USBPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; QEMUPutMouseEntry *eh_entry; } USBMouseState; typedef struct USBKeyboardState { + uint32_t keycodes[QUEUE_LENGTH]; uint16_t modifiers; uint8_t leds; uint8_t key[16]; - int keys; + int32_t keys; } USBKeyboardState; typedef struct USBHIDState { @@ -65,8 +74,10 @@ typedef struct USBHIDState { USBMouseState ptr; USBKeyboardState kbd; }; + uint32_t head; /* index into circular queue */ + uint32_t n; int kind; - int protocol; + int32_t protocol; uint8_t idle; int64_t next_idle_clock; int changed; @@ -90,7 +101,7 @@ static const USBDescStrings desc_strings = { [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", [STR_PRODUCT_TABLET] = "QEMU USB Tablet", [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", - [STR_SERIALNUMBER] = "1", + [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ [STR_CONFIG_MOUSE] = "HID Mouse", [STR_CONFIG_TABLET] = "HID Tablet", [STR_CONFIG_KEYBOARD] = "HID Keyboard", @@ -416,7 +427,7 @@ static const uint8_t usb_hid_usage_keys[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -429,42 +440,83 @@ static void usb_hid_changed(USBHIDState *hs) if (hs->datain) hs->datain(hs->datain_opaque); + + usb_wakeup(&hs->dev); +} + +static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) { + e->xdx = e->ydy = e->dz = 0; + e->buttons_state = buttons; } -static void usb_mouse_event(void *opaque, - int dx1, int dy1, int dz1, int buttons_state) +static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, + int x1, int y1, int z1) { + if (xyrel) { + e->xdx += x1; + e->ydy += y1; + } else { + e->xdx = x1; + e->ydy = y1; + } + e->dz += z1; +} + +static void usb_pointer_event(void *opaque, + int x1, int y1, int z1, int buttons_state) { USBHIDState *hs = opaque; USBMouseState *s = &hs->ptr; - - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; - + unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; + unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; + + /* We combine events where feasible to keep the queue small. We shouldn't + * combine anything with the first event of a particular button state, as + * that would change the location of the button state change. When the + * queue is empty, a second event is needed because we don't know if + * the first event changed the button state. */ + if (hs->n == QUEUE_LENGTH) { + /* Queue full. Discard old button state, combine motion normally. */ + s->queue[use_slot].buttons_state = buttons_state; + } else if (hs->n < 2 || + s->queue[use_slot].buttons_state != buttons_state || + s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) { + /* Cannot or should not combine, so add an empty item to the queue. */ + QUEUE_INCR(use_slot); + hs->n++; + usb_pointer_event_clear(&s->queue[use_slot], buttons_state); + } + usb_pointer_event_combine(&s->queue[use_slot], + hs->kind == USB_MOUSE, + x1, y1, z1); usb_hid_changed(hs); } -static void usb_tablet_event(void *opaque, - int x, int y, int dz, int buttons_state) +static void usb_keyboard_event(void *opaque, int keycode) { USBHIDState *hs = opaque; - USBMouseState *s = &hs->ptr; - - s->x = x; - s->y = y; - s->dz += dz; - s->buttons_state = buttons_state; + USBKeyboardState *s = &hs->kbd; + int slot; + if (hs->n == QUEUE_LENGTH) { + fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + return; + } + slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; + s->keycodes[slot] = keycode; usb_hid_changed(hs); } -static void usb_keyboard_event(void *opaque, int keycode) +static void usb_keyboard_process_keycode(USBHIDState *hs) { - USBHIDState *hs = opaque; USBKeyboardState *s = &hs->kbd; uint8_t hid_code, key; - int i; + int i, keycode, slot; + + if (hs->n == 0) { + return; + } + slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; + keycode = s->keycodes[slot]; key = keycode & 0x7f; hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; @@ -477,15 +529,18 @@ static void usb_keyboard_event(void *opaque, int keycode) case 0xe0: if (s->modifiers & (1 << 9)) { s->modifiers ^= 3 << 8; + usb_hid_changed(hs); return; } case 0xe1 ... 0xe7: if (keycode & (1 << 7)) { s->modifiers &= ~(1 << (hid_code & 0x0f)); + usb_hid_changed(hs); return; } case 0xe8 ... 0xef: s->modifiers |= 1 << (hid_code & 0x0f); + usb_hid_changed(hs); return; } @@ -494,7 +549,6 @@ static void usb_keyboard_event(void *opaque, int keycode) if (s->key[i] == hid_code) { s->key[i] = s->key[-- s->keys]; s->key[s->keys] = 0x00; - usb_hid_changed(hs); break; } if (i < 0) @@ -509,8 +563,6 @@ static void usb_keyboard_event(void *opaque, int keycode) } else return; } - - usb_hid_changed(hs); } static inline int int_clamp(int val, int vmin, int vmax) @@ -523,86 +575,96 @@ static inline int int_clamp(int val, int vmin, int vmax) return val; } -static int usb_mouse_poll(USBHIDState *hs, uint8_t *buf, int len) +static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) { int dx, dy, dz, b, l; + int index; USBMouseState *s = &hs->ptr; + USBPointerEvent *e; if (!s->mouse_grabbed) { qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; + s->mouse_grabbed = 1; } - dx = int_clamp(s->dx, -127, 127); - dy = int_clamp(s->dy, -127, 127); - dz = int_clamp(s->dz, -127, 127); - - s->dx -= dx; - s->dy -= dy; - s->dz -= dz; + /* When the buffer is empty, return the last event. Relative + movements will all be zero. */ + index = (hs->n ? hs->head : hs->head - 1); + e = &s->queue[index & QUEUE_MASK]; - /* Appears we have to invert the wheel direction */ - dz = 0 - dz; + if (hs->kind == USB_MOUSE) { + dx = int_clamp(e->xdx, -127, 127); + dy = int_clamp(e->ydy, -127, 127); + e->xdx -= dx; + e->ydy -= dy; + } else { + dx = e->xdx; + dy = e->ydy; + } + dz = int_clamp(e->dz, -127, 127); + e->dz -= dz; b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) + if (e->buttons_state & MOUSE_EVENT_LBUTTON) b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) + if (e->buttons_state & MOUSE_EVENT_RBUTTON) b |= 0x02; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) + if (e->buttons_state & MOUSE_EVENT_MBUTTON) b |= 0x04; - l = 0; - if (len > l) - buf[l ++] = b; - if (len > l) - buf[l ++] = dx; - if (len > l) - buf[l ++] = dy; - if (len > l) - buf[l ++] = dz; - return l; -} - -static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len) -{ - int dz, b, l; - USBMouseState *s = &hs->ptr; - - if (!s->mouse_grabbed) { - qemu_activate_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 1; + if (hs->n && + !e->dz && + (hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) { + /* that deals with this event */ + QUEUE_INCR(hs->head); + hs->n--; } - dz = int_clamp(s->dz, -127, 127); - s->dz -= dz; - /* Appears we have to invert the wheel direction */ dz = 0 - dz; - b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x02; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x04; + l = 0; + switch (hs->kind) { + case USB_MOUSE: + if (len > l) + buf[l++] = b; + if (len > l) + buf[l++] = dx; + if (len > l) + buf[l++] = dy; + if (len > l) + buf[l++] = dz; + break; - buf[0] = b; - buf[1] = s->x & 0xff; - buf[2] = s->x >> 8; - buf[3] = s->y & 0xff; - buf[4] = s->y >> 8; - buf[5] = dz; - l = 6; + case USB_TABLET: + if (len > l) + buf[l++] = b; + if (len > l) + buf[l++] = dx & 0xff; + if (len > l) + buf[l++] = dx >> 8; + if (len > l) + buf[l++] = dy & 0xff; + if (len > l) + buf[l++] = dy >> 8; + if (len > l) + buf[l++] = dz; + break; + + default: + abort(); + } return l; } -static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len) +static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len) { + USBKeyboardState *s = &hs->kbd; if (len < 2) return 0; + usb_keyboard_process_keycode(hs); + buf[0] = s->modifiers & 0xff; buf[1] = 0; if (s->keys > 6) @@ -638,12 +700,9 @@ static void usb_mouse_handle_reset(USBDevice *dev) { USBHIDState *s = (USBHIDState *)dev; - s->ptr.dx = 0; - s->ptr.dy = 0; - s->ptr.dz = 0; - s->ptr.x = 0; - s->ptr.y = 0; - s->ptr.buttons_state = 0; + memset(s->ptr.queue, 0, sizeof (s->ptr.queue)); + s->head = 0; + s->n = 0; s->protocol = 1; } @@ -652,6 +711,11 @@ static void usb_keyboard_handle_reset(USBDevice *dev) USBHIDState *s = (USBHIDState *)dev; qemu_add_kbd_event_handler(usb_keyboard_event, s); + memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes)); + s->head = 0; + s->n = 0; + memset(s->kbd.key, 0, sizeof (s->kbd.key)); + s->kbd.keys = 0; s->protocol = 1; } @@ -673,39 +737,6 @@ static int usb_hid_handle_control(USBDevice *dev, int request, int value, ret = 0; switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -736,12 +767,10 @@ static int usb_hid_handle_control(USBDevice *dev, int request, int value, } break; case GET_REPORT: - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, data, length); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, data, length); + if (s->kind == USB_MOUSE || s->kind == USB_TABLET) + ret = usb_pointer_poll(s, data, length); else if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_poll(&s->kbd, data, length); + ret = usb_keyboard_poll(s, data, length); break; case SET_REPORT: if (s->kind == USB_KEYBOARD) @@ -767,7 +796,7 @@ static int usb_hid_handle_control(USBDevice *dev, int request, int value, break; case SET_IDLE: s->idle = (uint8_t) (value >> 8); - usb_hid_set_next_idle(s, qemu_get_clock(vm_clock)); + usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); ret = 0; break; default: @@ -786,17 +815,17 @@ static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) switch(p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { - int64_t curtime = qemu_get_clock(vm_clock); + int64_t curtime = qemu_get_clock_ns(vm_clock); if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0)) return USB_RET_NAK; usb_hid_set_next_idle(s, curtime); - s->changed = 0; - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, p->data, p->len); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, p->data, p->len); - else if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_poll(&s->kbd, p->data, p->len); + if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { + ret = usb_pointer_poll(s, p->data, p->len); + } + else if (s->kind == USB_KEYBOARD) { + ret = usb_keyboard_poll(s, p->data, p->len); + } + s->changed = s->n > 0; } else { goto fail; } @@ -826,17 +855,18 @@ static void usb_hid_handle_destroy(USBDevice *dev) static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + + usb_desc_init(dev); s->kind = kind; if (s->kind == USB_MOUSE) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, + s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, 0, "QEMU USB Mouse"); } else if (s->kind == USB_TABLET) { - s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, s, + s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, 1, "QEMU USB Tablet"); } - + /* Force poll routine to be run and grab input the first time. */ s->changed = 1; return 0; @@ -865,12 +895,72 @@ void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)) s->datain = datain; } +static int usb_hid_post_load(void *opaque, int version_id) +{ + USBHIDState *s = opaque; + + if (s->idle) { + usb_hid_set_next_idle(s, qemu_get_clock_ns(vm_clock)); + } + return 0; +} + +static const VMStateDescription vmstate_usb_ptr_queue = { + .name = "usb-ptr-queue", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_INT32(xdx, USBPointerEvent), + VMSTATE_INT32(ydy, USBPointerEvent), + VMSTATE_INT32(dz, USBPointerEvent), + VMSTATE_INT32(buttons_state, USBPointerEvent), + VMSTATE_END_OF_LIST() + } +}; +static const VMStateDescription vmstate_usb_ptr = { + .name = "usb-ptr", + .version_id = 1, + .minimum_version_id = 1, + .post_load = usb_hid_post_load, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, USBHIDState), + VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0, + vmstate_usb_ptr_queue, USBPointerEvent), + VMSTATE_UINT32(head, USBHIDState), + VMSTATE_UINT32(n, USBHIDState), + VMSTATE_INT32(protocol, USBHIDState), + VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_usb_kbd = { + .name = "usb-kbd", + .version_id = 1, + .minimum_version_id = 1, + .post_load = usb_hid_post_load, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, USBHIDState), + VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH), + VMSTATE_UINT32(head, USBHIDState), + VMSTATE_UINT32(n, USBHIDState), + VMSTATE_UINT16(kbd.modifiers, USBHIDState), + VMSTATE_UINT8(kbd.leds, USBHIDState), + VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16), + VMSTATE_INT32(kbd.keys, USBHIDState), + VMSTATE_INT32(protocol, USBHIDState), + VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_END_OF_LIST() + } +}; + static struct USBDeviceInfo hid_info[] = { { .product_desc = "QEMU USB Tablet", .qdev.name = "usb-tablet", .usbdevice_name = "tablet", .qdev.size = sizeof(USBHIDState), + .qdev.vmsd = &vmstate_usb_ptr, .usb_desc = &desc_tablet, .init = usb_tablet_initfn, .handle_packet = usb_generic_handle_packet, @@ -883,6 +973,7 @@ static struct USBDeviceInfo hid_info[] = { .qdev.name = "usb-mouse", .usbdevice_name = "mouse", .qdev.size = sizeof(USBHIDState), + .qdev.vmsd = &vmstate_usb_ptr, .usb_desc = &desc_mouse, .init = usb_mouse_initfn, .handle_packet = usb_generic_handle_packet, @@ -895,6 +986,7 @@ static struct USBDeviceInfo hid_info[] = { .qdev.name = "usb-kbd", .usbdevice_name = "keyboard", .qdev.size = sizeof(USBHIDState), + .qdev.vmsd = &vmstate_usb_kbd, .usb_desc = &desc_keyboard, .init = usb_keyboard_initfn, .handle_packet = usb_generic_handle_packet,