xref: /qemu/hw/input/hid.c (revision 1ff5eedd)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU HID devices
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2005 Fabrice Bellard
549ab747fSPaolo Bonzini  * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
849ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
949ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
1049ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1149ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1249ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1349ab747fSPaolo Bonzini  *
1449ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1549ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1649ab747fSPaolo Bonzini  *
1749ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1849ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1949ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
2049ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2149ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2249ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2349ab747fSPaolo Bonzini  * THE SOFTWARE.
2449ab747fSPaolo Bonzini  */
2549ab747fSPaolo Bonzini #include "hw/hw.h"
2649ab747fSPaolo Bonzini #include "ui/console.h"
2749ab747fSPaolo Bonzini #include "qemu/timer.h"
2849ab747fSPaolo Bonzini #include "hw/input/hid.h"
2949ab747fSPaolo Bonzini 
3049ab747fSPaolo Bonzini #define HID_USAGE_ERROR_ROLLOVER        0x01
3149ab747fSPaolo Bonzini #define HID_USAGE_POSTFAIL              0x02
3249ab747fSPaolo Bonzini #define HID_USAGE_ERROR_UNDEFINED       0x03
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini /* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
3549ab747fSPaolo Bonzini  * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
3649ab747fSPaolo Bonzini static const uint8_t hid_usage_keys[0x100] = {
3749ab747fSPaolo Bonzini     0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
3849ab747fSPaolo Bonzini     0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
3949ab747fSPaolo Bonzini     0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
4049ab747fSPaolo Bonzini     0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
4149ab747fSPaolo Bonzini     0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
4249ab747fSPaolo Bonzini     0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
4349ab747fSPaolo Bonzini     0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
4449ab747fSPaolo Bonzini     0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
4549ab747fSPaolo Bonzini     0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
4649ab747fSPaolo Bonzini     0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
4749ab747fSPaolo Bonzini     0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
4849ab747fSPaolo Bonzini     0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
4949ab747fSPaolo Bonzini     0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
5049ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
5149ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5249ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
5349ab747fSPaolo Bonzini 
5449ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5549ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5649ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5749ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
5849ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
5949ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6049ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
6149ab747fSPaolo Bonzini     0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6249ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
6349ab747fSPaolo Bonzini     0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
6449ab747fSPaolo Bonzini     0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
6549ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
6649ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6749ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6849ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6949ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7049ab747fSPaolo Bonzini };
7149ab747fSPaolo Bonzini 
7249ab747fSPaolo Bonzini bool hid_has_events(HIDState *hs)
7349ab747fSPaolo Bonzini {
7449ab747fSPaolo Bonzini     return hs->n > 0 || hs->idle_pending;
7549ab747fSPaolo Bonzini }
7649ab747fSPaolo Bonzini 
7749ab747fSPaolo Bonzini static void hid_idle_timer(void *opaque)
7849ab747fSPaolo Bonzini {
7949ab747fSPaolo Bonzini     HIDState *hs = opaque;
8049ab747fSPaolo Bonzini 
8149ab747fSPaolo Bonzini     hs->idle_pending = true;
8249ab747fSPaolo Bonzini     hs->event(hs);
8349ab747fSPaolo Bonzini }
8449ab747fSPaolo Bonzini 
8549ab747fSPaolo Bonzini static void hid_del_idle_timer(HIDState *hs)
8649ab747fSPaolo Bonzini {
8749ab747fSPaolo Bonzini     if (hs->idle_timer) {
88bc72ad67SAlex Bligh         timer_del(hs->idle_timer);
89bc72ad67SAlex Bligh         timer_free(hs->idle_timer);
9049ab747fSPaolo Bonzini         hs->idle_timer = NULL;
9149ab747fSPaolo Bonzini     }
9249ab747fSPaolo Bonzini }
9349ab747fSPaolo Bonzini 
9449ab747fSPaolo Bonzini void hid_set_next_idle(HIDState *hs)
9549ab747fSPaolo Bonzini {
9649ab747fSPaolo Bonzini     if (hs->idle) {
97bc72ad67SAlex Bligh         uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
9849ab747fSPaolo Bonzini                                get_ticks_per_sec() * hs->idle * 4 / 1000;
9949ab747fSPaolo Bonzini         if (!hs->idle_timer) {
100bc72ad67SAlex Bligh             hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
10149ab747fSPaolo Bonzini         }
102bc72ad67SAlex Bligh         timer_mod_ns(hs->idle_timer, expire_time);
10349ab747fSPaolo Bonzini     } else {
10449ab747fSPaolo Bonzini         hid_del_idle_timer(hs);
10549ab747fSPaolo Bonzini     }
10649ab747fSPaolo Bonzini }
10749ab747fSPaolo Bonzini 
10849ab747fSPaolo Bonzini static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
10949ab747fSPaolo Bonzini {
11049ab747fSPaolo Bonzini     e->xdx = e->ydy = e->dz = 0;
11149ab747fSPaolo Bonzini     e->buttons_state = buttons;
11249ab747fSPaolo Bonzini }
11349ab747fSPaolo Bonzini 
11449ab747fSPaolo Bonzini static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
11549ab747fSPaolo Bonzini                                       int x1, int y1, int z1) {
11649ab747fSPaolo Bonzini     if (xyrel) {
11749ab747fSPaolo Bonzini         e->xdx += x1;
11849ab747fSPaolo Bonzini         e->ydy += y1;
11949ab747fSPaolo Bonzini     } else {
12049ab747fSPaolo Bonzini         e->xdx = x1;
12149ab747fSPaolo Bonzini         e->ydy = y1;
12249ab747fSPaolo Bonzini         /* Windows drivers do not like the 0/0 position and ignore such
12349ab747fSPaolo Bonzini          * events. */
12449ab747fSPaolo Bonzini         if (!(x1 | y1)) {
12549ab747fSPaolo Bonzini             e->xdx = 1;
12649ab747fSPaolo Bonzini         }
12749ab747fSPaolo Bonzini     }
12849ab747fSPaolo Bonzini     e->dz += z1;
12949ab747fSPaolo Bonzini }
13049ab747fSPaolo Bonzini 
13149ab747fSPaolo Bonzini static void hid_pointer_event(void *opaque,
13249ab747fSPaolo Bonzini                               int x1, int y1, int z1, int buttons_state)
13349ab747fSPaolo Bonzini {
13449ab747fSPaolo Bonzini     HIDState *hs = opaque;
13549ab747fSPaolo Bonzini     unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
13649ab747fSPaolo Bonzini     unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
13749ab747fSPaolo Bonzini 
13849ab747fSPaolo Bonzini     /* We combine events where feasible to keep the queue small.  We shouldn't
13949ab747fSPaolo Bonzini      * combine anything with the first event of a particular button state, as
14049ab747fSPaolo Bonzini      * that would change the location of the button state change.  When the
14149ab747fSPaolo Bonzini      * queue is empty, a second event is needed because we don't know if
14249ab747fSPaolo Bonzini      * the first event changed the button state.  */
14349ab747fSPaolo Bonzini     if (hs->n == QUEUE_LENGTH) {
14449ab747fSPaolo Bonzini         /* Queue full.  Discard old button state, combine motion normally.  */
14549ab747fSPaolo Bonzini         hs->ptr.queue[use_slot].buttons_state = buttons_state;
14649ab747fSPaolo Bonzini     } else if (hs->n < 2 ||
14749ab747fSPaolo Bonzini                hs->ptr.queue[use_slot].buttons_state != buttons_state ||
14849ab747fSPaolo Bonzini                hs->ptr.queue[previous_slot].buttons_state !=
14949ab747fSPaolo Bonzini                hs->ptr.queue[use_slot].buttons_state) {
15049ab747fSPaolo Bonzini         /* Cannot or should not combine, so add an empty item to the queue.  */
15149ab747fSPaolo Bonzini         QUEUE_INCR(use_slot);
15249ab747fSPaolo Bonzini         hs->n++;
15349ab747fSPaolo Bonzini         hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
15449ab747fSPaolo Bonzini     }
15549ab747fSPaolo Bonzini     hid_pointer_event_combine(&hs->ptr.queue[use_slot],
15649ab747fSPaolo Bonzini                               hs->kind == HID_MOUSE,
15749ab747fSPaolo Bonzini                               x1, y1, z1);
15849ab747fSPaolo Bonzini     hs->event(hs);
15949ab747fSPaolo Bonzini }
16049ab747fSPaolo Bonzini 
161*1ff5eeddSGerd Hoffmann static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
162*1ff5eeddSGerd Hoffmann                                InputEvent *evt)
16349ab747fSPaolo Bonzini {
164*1ff5eeddSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
165*1ff5eeddSGerd Hoffmann     int scancodes[3], i, count;
16649ab747fSPaolo Bonzini     int slot;
16749ab747fSPaolo Bonzini 
168*1ff5eeddSGerd Hoffmann     count = qemu_input_key_value_to_scancode(evt->key->key,
169*1ff5eeddSGerd Hoffmann                                              evt->key->down,
170*1ff5eeddSGerd Hoffmann                                              scancodes);
171*1ff5eeddSGerd Hoffmann     if (hs->n + count > QUEUE_LENGTH) {
17249ab747fSPaolo Bonzini         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
17349ab747fSPaolo Bonzini         return;
17449ab747fSPaolo Bonzini     }
175*1ff5eeddSGerd Hoffmann     for (i = 0; i < count; i++) {
17649ab747fSPaolo Bonzini         slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
177*1ff5eeddSGerd Hoffmann         hs->kbd.keycodes[slot] = scancodes[i];
178*1ff5eeddSGerd Hoffmann     }
17949ab747fSPaolo Bonzini     hs->event(hs);
18049ab747fSPaolo Bonzini }
18149ab747fSPaolo Bonzini 
18249ab747fSPaolo Bonzini static void hid_keyboard_process_keycode(HIDState *hs)
18349ab747fSPaolo Bonzini {
18449ab747fSPaolo Bonzini     uint8_t hid_code, key;
18549ab747fSPaolo Bonzini     int i, keycode, slot;
18649ab747fSPaolo Bonzini 
18749ab747fSPaolo Bonzini     if (hs->n == 0) {
18849ab747fSPaolo Bonzini         return;
18949ab747fSPaolo Bonzini     }
19049ab747fSPaolo Bonzini     slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
19149ab747fSPaolo Bonzini     keycode = hs->kbd.keycodes[slot];
19249ab747fSPaolo Bonzini 
19349ab747fSPaolo Bonzini     key = keycode & 0x7f;
19449ab747fSPaolo Bonzini     hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
19549ab747fSPaolo Bonzini     hs->kbd.modifiers &= ~(1 << 8);
19649ab747fSPaolo Bonzini 
19749ab747fSPaolo Bonzini     switch (hid_code) {
19849ab747fSPaolo Bonzini     case 0x00:
19949ab747fSPaolo Bonzini         return;
20049ab747fSPaolo Bonzini 
20149ab747fSPaolo Bonzini     case 0xe0:
20249ab747fSPaolo Bonzini         if (hs->kbd.modifiers & (1 << 9)) {
20349ab747fSPaolo Bonzini             hs->kbd.modifiers ^= 3 << 8;
20449ab747fSPaolo Bonzini             return;
20549ab747fSPaolo Bonzini         }
20649ab747fSPaolo Bonzini     case 0xe1 ... 0xe7:
20749ab747fSPaolo Bonzini         if (keycode & (1 << 7)) {
20849ab747fSPaolo Bonzini             hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
20949ab747fSPaolo Bonzini             return;
21049ab747fSPaolo Bonzini         }
21149ab747fSPaolo Bonzini     case 0xe8 ... 0xef:
21249ab747fSPaolo Bonzini         hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
21349ab747fSPaolo Bonzini         return;
21449ab747fSPaolo Bonzini     }
21549ab747fSPaolo Bonzini 
21649ab747fSPaolo Bonzini     if (keycode & (1 << 7)) {
21749ab747fSPaolo Bonzini         for (i = hs->kbd.keys - 1; i >= 0; i--) {
21849ab747fSPaolo Bonzini             if (hs->kbd.key[i] == hid_code) {
21949ab747fSPaolo Bonzini                 hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
22049ab747fSPaolo Bonzini                 hs->kbd.key[hs->kbd.keys] = 0x00;
22149ab747fSPaolo Bonzini                 break;
22249ab747fSPaolo Bonzini             }
22349ab747fSPaolo Bonzini         }
22449ab747fSPaolo Bonzini         if (i < 0) {
22549ab747fSPaolo Bonzini             return;
22649ab747fSPaolo Bonzini         }
22749ab747fSPaolo Bonzini     } else {
22849ab747fSPaolo Bonzini         for (i = hs->kbd.keys - 1; i >= 0; i--) {
22949ab747fSPaolo Bonzini             if (hs->kbd.key[i] == hid_code) {
23049ab747fSPaolo Bonzini                 break;
23149ab747fSPaolo Bonzini             }
23249ab747fSPaolo Bonzini         }
23349ab747fSPaolo Bonzini         if (i < 0) {
23449ab747fSPaolo Bonzini             if (hs->kbd.keys < sizeof(hs->kbd.key)) {
23549ab747fSPaolo Bonzini                 hs->kbd.key[hs->kbd.keys++] = hid_code;
23649ab747fSPaolo Bonzini             }
23749ab747fSPaolo Bonzini         } else {
23849ab747fSPaolo Bonzini             return;
23949ab747fSPaolo Bonzini         }
24049ab747fSPaolo Bonzini     }
24149ab747fSPaolo Bonzini }
24249ab747fSPaolo Bonzini 
24349ab747fSPaolo Bonzini static inline int int_clamp(int val, int vmin, int vmax)
24449ab747fSPaolo Bonzini {
24549ab747fSPaolo Bonzini     if (val < vmin) {
24649ab747fSPaolo Bonzini         return vmin;
24749ab747fSPaolo Bonzini     } else if (val > vmax) {
24849ab747fSPaolo Bonzini         return vmax;
24949ab747fSPaolo Bonzini     } else {
25049ab747fSPaolo Bonzini         return val;
25149ab747fSPaolo Bonzini     }
25249ab747fSPaolo Bonzini }
25349ab747fSPaolo Bonzini 
25449ab747fSPaolo Bonzini void hid_pointer_activate(HIDState *hs)
25549ab747fSPaolo Bonzini {
25649ab747fSPaolo Bonzini     if (!hs->ptr.mouse_grabbed) {
25749ab747fSPaolo Bonzini         qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
25849ab747fSPaolo Bonzini         hs->ptr.mouse_grabbed = 1;
25949ab747fSPaolo Bonzini     }
26049ab747fSPaolo Bonzini }
26149ab747fSPaolo Bonzini 
26249ab747fSPaolo Bonzini int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
26349ab747fSPaolo Bonzini {
26449ab747fSPaolo Bonzini     int dx, dy, dz, b, l;
26549ab747fSPaolo Bonzini     int index;
26649ab747fSPaolo Bonzini     HIDPointerEvent *e;
26749ab747fSPaolo Bonzini 
26849ab747fSPaolo Bonzini     hs->idle_pending = false;
26949ab747fSPaolo Bonzini 
27049ab747fSPaolo Bonzini     hid_pointer_activate(hs);
27149ab747fSPaolo Bonzini 
27249ab747fSPaolo Bonzini     /* When the buffer is empty, return the last event.  Relative
27349ab747fSPaolo Bonzini        movements will all be zero.  */
27449ab747fSPaolo Bonzini     index = (hs->n ? hs->head : hs->head - 1);
27549ab747fSPaolo Bonzini     e = &hs->ptr.queue[index & QUEUE_MASK];
27649ab747fSPaolo Bonzini 
27749ab747fSPaolo Bonzini     if (hs->kind == HID_MOUSE) {
27849ab747fSPaolo Bonzini         dx = int_clamp(e->xdx, -127, 127);
27949ab747fSPaolo Bonzini         dy = int_clamp(e->ydy, -127, 127);
28049ab747fSPaolo Bonzini         e->xdx -= dx;
28149ab747fSPaolo Bonzini         e->ydy -= dy;
28249ab747fSPaolo Bonzini     } else {
28349ab747fSPaolo Bonzini         dx = e->xdx;
28449ab747fSPaolo Bonzini         dy = e->ydy;
28549ab747fSPaolo Bonzini     }
28649ab747fSPaolo Bonzini     dz = int_clamp(e->dz, -127, 127);
28749ab747fSPaolo Bonzini     e->dz -= dz;
28849ab747fSPaolo Bonzini 
28949ab747fSPaolo Bonzini     b = 0;
29049ab747fSPaolo Bonzini     if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
29149ab747fSPaolo Bonzini         b |= 0x01;
29249ab747fSPaolo Bonzini     }
29349ab747fSPaolo Bonzini     if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
29449ab747fSPaolo Bonzini         b |= 0x02;
29549ab747fSPaolo Bonzini     }
29649ab747fSPaolo Bonzini     if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
29749ab747fSPaolo Bonzini         b |= 0x04;
29849ab747fSPaolo Bonzini     }
29949ab747fSPaolo Bonzini 
30049ab747fSPaolo Bonzini     if (hs->n &&
30149ab747fSPaolo Bonzini         !e->dz &&
30249ab747fSPaolo Bonzini         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
30349ab747fSPaolo Bonzini         /* that deals with this event */
30449ab747fSPaolo Bonzini         QUEUE_INCR(hs->head);
30549ab747fSPaolo Bonzini         hs->n--;
30649ab747fSPaolo Bonzini     }
30749ab747fSPaolo Bonzini 
30849ab747fSPaolo Bonzini     /* Appears we have to invert the wheel direction */
30949ab747fSPaolo Bonzini     dz = 0 - dz;
31049ab747fSPaolo Bonzini     l = 0;
31149ab747fSPaolo Bonzini     switch (hs->kind) {
31249ab747fSPaolo Bonzini     case HID_MOUSE:
31349ab747fSPaolo Bonzini         if (len > l) {
31449ab747fSPaolo Bonzini             buf[l++] = b;
31549ab747fSPaolo Bonzini         }
31649ab747fSPaolo Bonzini         if (len > l) {
31749ab747fSPaolo Bonzini             buf[l++] = dx;
31849ab747fSPaolo Bonzini         }
31949ab747fSPaolo Bonzini         if (len > l) {
32049ab747fSPaolo Bonzini             buf[l++] = dy;
32149ab747fSPaolo Bonzini         }
32249ab747fSPaolo Bonzini         if (len > l) {
32349ab747fSPaolo Bonzini             buf[l++] = dz;
32449ab747fSPaolo Bonzini         }
32549ab747fSPaolo Bonzini         break;
32649ab747fSPaolo Bonzini 
32749ab747fSPaolo Bonzini     case HID_TABLET:
32849ab747fSPaolo Bonzini         if (len > l) {
32949ab747fSPaolo Bonzini             buf[l++] = b;
33049ab747fSPaolo Bonzini         }
33149ab747fSPaolo Bonzini         if (len > l) {
33249ab747fSPaolo Bonzini             buf[l++] = dx & 0xff;
33349ab747fSPaolo Bonzini         }
33449ab747fSPaolo Bonzini         if (len > l) {
33549ab747fSPaolo Bonzini             buf[l++] = dx >> 8;
33649ab747fSPaolo Bonzini         }
33749ab747fSPaolo Bonzini         if (len > l) {
33849ab747fSPaolo Bonzini             buf[l++] = dy & 0xff;
33949ab747fSPaolo Bonzini         }
34049ab747fSPaolo Bonzini         if (len > l) {
34149ab747fSPaolo Bonzini             buf[l++] = dy >> 8;
34249ab747fSPaolo Bonzini         }
34349ab747fSPaolo Bonzini         if (len > l) {
34449ab747fSPaolo Bonzini             buf[l++] = dz;
34549ab747fSPaolo Bonzini         }
34649ab747fSPaolo Bonzini         break;
34749ab747fSPaolo Bonzini 
34849ab747fSPaolo Bonzini     default:
34949ab747fSPaolo Bonzini         abort();
35049ab747fSPaolo Bonzini     }
35149ab747fSPaolo Bonzini 
35249ab747fSPaolo Bonzini     return l;
35349ab747fSPaolo Bonzini }
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
35649ab747fSPaolo Bonzini {
35749ab747fSPaolo Bonzini     hs->idle_pending = false;
35849ab747fSPaolo Bonzini 
35949ab747fSPaolo Bonzini     if (len < 2) {
36049ab747fSPaolo Bonzini         return 0;
36149ab747fSPaolo Bonzini     }
36249ab747fSPaolo Bonzini 
36349ab747fSPaolo Bonzini     hid_keyboard_process_keycode(hs);
36449ab747fSPaolo Bonzini 
36549ab747fSPaolo Bonzini     buf[0] = hs->kbd.modifiers & 0xff;
36649ab747fSPaolo Bonzini     buf[1] = 0;
36749ab747fSPaolo Bonzini     if (hs->kbd.keys > 6) {
36849ab747fSPaolo Bonzini         memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
36949ab747fSPaolo Bonzini     } else {
37049ab747fSPaolo Bonzini         memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
37149ab747fSPaolo Bonzini     }
37249ab747fSPaolo Bonzini 
37349ab747fSPaolo Bonzini     return MIN(8, len);
37449ab747fSPaolo Bonzini }
37549ab747fSPaolo Bonzini 
37649ab747fSPaolo Bonzini int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
37749ab747fSPaolo Bonzini {
37849ab747fSPaolo Bonzini     if (len > 0) {
37949ab747fSPaolo Bonzini         int ledstate = 0;
38049ab747fSPaolo Bonzini         /* 0x01: Num Lock LED
38149ab747fSPaolo Bonzini          * 0x02: Caps Lock LED
38249ab747fSPaolo Bonzini          * 0x04: Scroll Lock LED
38349ab747fSPaolo Bonzini          * 0x08: Compose LED
38449ab747fSPaolo Bonzini          * 0x10: Kana LED */
38549ab747fSPaolo Bonzini         hs->kbd.leds = buf[0];
38649ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x04) {
38749ab747fSPaolo Bonzini             ledstate |= QEMU_SCROLL_LOCK_LED;
38849ab747fSPaolo Bonzini         }
38949ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x01) {
39049ab747fSPaolo Bonzini             ledstate |= QEMU_NUM_LOCK_LED;
39149ab747fSPaolo Bonzini         }
39249ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x02) {
39349ab747fSPaolo Bonzini             ledstate |= QEMU_CAPS_LOCK_LED;
39449ab747fSPaolo Bonzini         }
39549ab747fSPaolo Bonzini         kbd_put_ledstate(ledstate);
39649ab747fSPaolo Bonzini     }
39749ab747fSPaolo Bonzini     return 0;
39849ab747fSPaolo Bonzini }
39949ab747fSPaolo Bonzini 
40049ab747fSPaolo Bonzini void hid_reset(HIDState *hs)
40149ab747fSPaolo Bonzini {
40249ab747fSPaolo Bonzini     switch (hs->kind) {
40349ab747fSPaolo Bonzini     case HID_KEYBOARD:
40449ab747fSPaolo Bonzini         memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
40549ab747fSPaolo Bonzini         memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
40649ab747fSPaolo Bonzini         hs->kbd.keys = 0;
40749ab747fSPaolo Bonzini         break;
40849ab747fSPaolo Bonzini     case HID_MOUSE:
40949ab747fSPaolo Bonzini     case HID_TABLET:
41049ab747fSPaolo Bonzini         memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
41149ab747fSPaolo Bonzini         break;
41249ab747fSPaolo Bonzini     }
41349ab747fSPaolo Bonzini     hs->head = 0;
41449ab747fSPaolo Bonzini     hs->n = 0;
41549ab747fSPaolo Bonzini     hs->protocol = 1;
41649ab747fSPaolo Bonzini     hs->idle = 0;
41749ab747fSPaolo Bonzini     hs->idle_pending = false;
41849ab747fSPaolo Bonzini     hid_del_idle_timer(hs);
41949ab747fSPaolo Bonzini }
42049ab747fSPaolo Bonzini 
42149ab747fSPaolo Bonzini void hid_free(HIDState *hs)
42249ab747fSPaolo Bonzini {
42349ab747fSPaolo Bonzini     switch (hs->kind) {
42449ab747fSPaolo Bonzini     case HID_KEYBOARD:
425*1ff5eeddSGerd Hoffmann         qemu_input_handler_unregister(hs->s);
42649ab747fSPaolo Bonzini         break;
42749ab747fSPaolo Bonzini     case HID_MOUSE:
42849ab747fSPaolo Bonzini     case HID_TABLET:
42949ab747fSPaolo Bonzini         qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
43049ab747fSPaolo Bonzini         break;
43149ab747fSPaolo Bonzini     }
43249ab747fSPaolo Bonzini     hid_del_idle_timer(hs);
43349ab747fSPaolo Bonzini }
43449ab747fSPaolo Bonzini 
435*1ff5eeddSGerd Hoffmann static QemuInputHandler hid_keyboard_handler = {
436*1ff5eeddSGerd Hoffmann     .name  = "QEMU HID Keyboard",
437*1ff5eeddSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
438*1ff5eeddSGerd Hoffmann     .event = hid_keyboard_event,
439*1ff5eeddSGerd Hoffmann };
440*1ff5eeddSGerd Hoffmann 
44149ab747fSPaolo Bonzini void hid_init(HIDState *hs, int kind, HIDEventFunc event)
44249ab747fSPaolo Bonzini {
44349ab747fSPaolo Bonzini     hs->kind = kind;
44449ab747fSPaolo Bonzini     hs->event = event;
44549ab747fSPaolo Bonzini 
44649ab747fSPaolo Bonzini     if (hs->kind == HID_KEYBOARD) {
447*1ff5eeddSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
448*1ff5eeddSGerd Hoffmann                                             &hid_keyboard_handler);
449*1ff5eeddSGerd Hoffmann         qemu_input_handler_activate(hs->s);
45049ab747fSPaolo Bonzini     } else if (hs->kind == HID_MOUSE) {
45149ab747fSPaolo Bonzini         hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
45249ab747fSPaolo Bonzini                                                         0, "QEMU HID Mouse");
45349ab747fSPaolo Bonzini     } else if (hs->kind == HID_TABLET) {
45449ab747fSPaolo Bonzini         hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
45549ab747fSPaolo Bonzini                                                         1, "QEMU HID Tablet");
45649ab747fSPaolo Bonzini     }
45749ab747fSPaolo Bonzini }
45849ab747fSPaolo Bonzini 
45949ab747fSPaolo Bonzini static int hid_post_load(void *opaque, int version_id)
46049ab747fSPaolo Bonzini {
46149ab747fSPaolo Bonzini     HIDState *s = opaque;
46249ab747fSPaolo Bonzini 
46349ab747fSPaolo Bonzini     hid_set_next_idle(s);
46449ab747fSPaolo Bonzini     return 0;
46549ab747fSPaolo Bonzini }
46649ab747fSPaolo Bonzini 
46749ab747fSPaolo Bonzini static const VMStateDescription vmstate_hid_ptr_queue = {
46849ab747fSPaolo Bonzini     .name = "HIDPointerEventQueue",
46949ab747fSPaolo Bonzini     .version_id = 1,
47049ab747fSPaolo Bonzini     .minimum_version_id = 1,
47149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
47249ab747fSPaolo Bonzini         VMSTATE_INT32(xdx, HIDPointerEvent),
47349ab747fSPaolo Bonzini         VMSTATE_INT32(ydy, HIDPointerEvent),
47449ab747fSPaolo Bonzini         VMSTATE_INT32(dz, HIDPointerEvent),
47549ab747fSPaolo Bonzini         VMSTATE_INT32(buttons_state, HIDPointerEvent),
47649ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
47749ab747fSPaolo Bonzini     }
47849ab747fSPaolo Bonzini };
47949ab747fSPaolo Bonzini 
48049ab747fSPaolo Bonzini const VMStateDescription vmstate_hid_ptr_device = {
48149ab747fSPaolo Bonzini     .name = "HIDPointerDevice",
48249ab747fSPaolo Bonzini     .version_id = 1,
48349ab747fSPaolo Bonzini     .minimum_version_id = 1,
48449ab747fSPaolo Bonzini     .post_load = hid_post_load,
48549ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
48649ab747fSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
48749ab747fSPaolo Bonzini                              vmstate_hid_ptr_queue, HIDPointerEvent),
48849ab747fSPaolo Bonzini         VMSTATE_UINT32(head, HIDState),
48949ab747fSPaolo Bonzini         VMSTATE_UINT32(n, HIDState),
49049ab747fSPaolo Bonzini         VMSTATE_INT32(protocol, HIDState),
49149ab747fSPaolo Bonzini         VMSTATE_UINT8(idle, HIDState),
49249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST(),
49349ab747fSPaolo Bonzini     }
49449ab747fSPaolo Bonzini };
49549ab747fSPaolo Bonzini 
49649ab747fSPaolo Bonzini const VMStateDescription vmstate_hid_keyboard_device = {
49749ab747fSPaolo Bonzini     .name = "HIDKeyboardDevice",
49849ab747fSPaolo Bonzini     .version_id = 1,
49949ab747fSPaolo Bonzini     .minimum_version_id = 1,
50049ab747fSPaolo Bonzini     .post_load = hid_post_load,
50149ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
50249ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
50349ab747fSPaolo Bonzini         VMSTATE_UINT32(head, HIDState),
50449ab747fSPaolo Bonzini         VMSTATE_UINT32(n, HIDState),
50549ab747fSPaolo Bonzini         VMSTATE_UINT16(kbd.modifiers, HIDState),
50649ab747fSPaolo Bonzini         VMSTATE_UINT8(kbd.leds, HIDState),
50749ab747fSPaolo Bonzini         VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
50849ab747fSPaolo Bonzini         VMSTATE_INT32(kbd.keys, HIDState),
50949ab747fSPaolo Bonzini         VMSTATE_INT32(protocol, HIDState),
51049ab747fSPaolo Bonzini         VMSTATE_UINT8(idle, HIDState),
51149ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST(),
51249ab747fSPaolo Bonzini     }
51349ab747fSPaolo Bonzini };
514