xref: /qemu/hw/input/hid.c (revision 35e83d10)
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 
1088b84286fSGerd Hoffmann static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
1098b84286fSGerd Hoffmann                               InputEvent *evt)
11049ab747fSPaolo Bonzini {
1118b84286fSGerd Hoffmann     static const int bmap[INPUT_BUTTON_MAX] = {
1128b84286fSGerd Hoffmann         [INPUT_BUTTON_LEFT]   = 0x01,
1138b84286fSGerd Hoffmann         [INPUT_BUTTON_RIGHT]  = 0x02,
1148b84286fSGerd Hoffmann         [INPUT_BUTTON_MIDDLE] = 0x04,
1158b84286fSGerd Hoffmann     };
1168b84286fSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
1178b84286fSGerd Hoffmann     HIDPointerEvent *e;
11849ab747fSPaolo Bonzini 
1198b84286fSGerd Hoffmann     assert(hs->n < QUEUE_LENGTH);
1208b84286fSGerd Hoffmann     e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
1218b84286fSGerd Hoffmann 
1228b84286fSGerd Hoffmann     switch (evt->kind) {
1238b84286fSGerd Hoffmann     case INPUT_EVENT_KIND_REL:
1248b84286fSGerd Hoffmann         if (evt->rel->axis == INPUT_AXIS_X) {
1258b84286fSGerd Hoffmann             e->xdx += evt->rel->value;
1268b84286fSGerd Hoffmann         } else if (evt->rel->axis == INPUT_AXIS_Y) {
127*35e83d10SChristian Burger             e->ydy += evt->rel->value;
1288b84286fSGerd Hoffmann         }
1298b84286fSGerd Hoffmann         break;
1308b84286fSGerd Hoffmann 
1318b84286fSGerd Hoffmann     case INPUT_EVENT_KIND_ABS:
1328b84286fSGerd Hoffmann         if (evt->rel->axis == INPUT_AXIS_X) {
1338b84286fSGerd Hoffmann             e->xdx = evt->rel->value;
1348b84286fSGerd Hoffmann         } else if (evt->rel->axis == INPUT_AXIS_Y) {
1358b84286fSGerd Hoffmann             e->ydy = evt->rel->value;
1368b84286fSGerd Hoffmann         }
1378b84286fSGerd Hoffmann         break;
1388b84286fSGerd Hoffmann 
1398b84286fSGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
1408b84286fSGerd Hoffmann         if (evt->btn->down) {
1418b84286fSGerd Hoffmann             e->buttons_state |= bmap[evt->btn->button];
1428b84286fSGerd Hoffmann             if (evt->btn->button == INPUT_BUTTON_WHEEL_UP) {
1438b84286fSGerd Hoffmann                 e->dz--;
1448b84286fSGerd Hoffmann             } else if (evt->btn->button == INPUT_BUTTON_WHEEL_DOWN) {
1458b84286fSGerd Hoffmann                 e->dz++;
1468b84286fSGerd Hoffmann             }
14749ab747fSPaolo Bonzini         } else {
1488b84286fSGerd Hoffmann             e->buttons_state &= ~bmap[evt->btn->button];
14949ab747fSPaolo Bonzini         }
1508b84286fSGerd Hoffmann         break;
1518b84286fSGerd Hoffmann 
1528b84286fSGerd Hoffmann     default:
1538b84286fSGerd Hoffmann         /* keep gcc happy */
1548b84286fSGerd Hoffmann         break;
15549ab747fSPaolo Bonzini     }
15649ab747fSPaolo Bonzini 
1578b84286fSGerd Hoffmann }
1588b84286fSGerd Hoffmann 
1598b84286fSGerd Hoffmann static void hid_pointer_sync(DeviceState *dev)
16049ab747fSPaolo Bonzini {
1618b84286fSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
1628b84286fSGerd Hoffmann     HIDPointerEvent *prev, *curr, *next;
1638b84286fSGerd Hoffmann     bool event_compression = false;
16449ab747fSPaolo Bonzini 
1658b84286fSGerd Hoffmann     if (hs->n == QUEUE_LENGTH-1) {
1668b84286fSGerd Hoffmann         /*
1675d831be2SStefan Weil          * Queue full.  We are losing information, but we at least
1688b84286fSGerd Hoffmann          * keep track of most recent button state.
1698b84286fSGerd Hoffmann          */
1708b84286fSGerd Hoffmann         return;
17149ab747fSPaolo Bonzini     }
1728b84286fSGerd Hoffmann 
1738b84286fSGerd Hoffmann     prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
1748b84286fSGerd Hoffmann     curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
1758b84286fSGerd Hoffmann     next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
1768b84286fSGerd Hoffmann 
1778b84286fSGerd Hoffmann     if (hs->n > 0) {
1788b84286fSGerd Hoffmann         /*
1798b84286fSGerd Hoffmann          * No button state change between previous and current event
1808b84286fSGerd Hoffmann          * (and previous wasn't seen by the guest yet), so there is
1818b84286fSGerd Hoffmann          * motion information only and we can combine the two event
1828b84286fSGerd Hoffmann          * into one.
1838b84286fSGerd Hoffmann          */
1848b84286fSGerd Hoffmann         if (curr->buttons_state == prev->buttons_state) {
1858b84286fSGerd Hoffmann             event_compression = true;
1868b84286fSGerd Hoffmann         }
1878b84286fSGerd Hoffmann     }
1888b84286fSGerd Hoffmann 
1898b84286fSGerd Hoffmann     if (event_compression) {
1908b84286fSGerd Hoffmann         /* add current motion to previous, clear current */
1918b84286fSGerd Hoffmann         if (hs->kind == HID_MOUSE) {
1928b84286fSGerd Hoffmann             prev->xdx += curr->xdx;
1938b84286fSGerd Hoffmann             curr->xdx = 0;
194*35e83d10SChristian Burger             prev->ydy += curr->ydy;
1958b84286fSGerd Hoffmann             curr->ydy = 0;
1968b84286fSGerd Hoffmann         } else {
1978b84286fSGerd Hoffmann             prev->xdx = curr->xdx;
1988b84286fSGerd Hoffmann             prev->ydy = curr->ydy;
1998b84286fSGerd Hoffmann         }
2008b84286fSGerd Hoffmann         prev->dz += curr->dz;
2018b84286fSGerd Hoffmann         curr->dz = 0;
2028b84286fSGerd Hoffmann     } else {
2038b84286fSGerd Hoffmann         /* prepate next (clear rel, copy abs + btns) */
2048b84286fSGerd Hoffmann         if (hs->kind == HID_MOUSE) {
2058b84286fSGerd Hoffmann             next->xdx = 0;
2068b84286fSGerd Hoffmann             next->ydy = 0;
2078b84286fSGerd Hoffmann         } else {
2088b84286fSGerd Hoffmann             next->xdx = curr->xdx;
2098b84286fSGerd Hoffmann             next->ydy = curr->ydy;
2108b84286fSGerd Hoffmann         }
2118b84286fSGerd Hoffmann         next->dz = 0;
2128b84286fSGerd Hoffmann         next->buttons_state = curr->buttons_state;
2138b84286fSGerd Hoffmann         /* make current guest visible, notify guest */
2148b84286fSGerd Hoffmann         hs->n++;
21549ab747fSPaolo Bonzini         hs->event(hs);
21649ab747fSPaolo Bonzini     }
2178b84286fSGerd Hoffmann }
21849ab747fSPaolo Bonzini 
2191ff5eeddSGerd Hoffmann static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
2201ff5eeddSGerd Hoffmann                                InputEvent *evt)
22149ab747fSPaolo Bonzini {
2221ff5eeddSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
2231ff5eeddSGerd Hoffmann     int scancodes[3], i, count;
22449ab747fSPaolo Bonzini     int slot;
22549ab747fSPaolo Bonzini 
2261ff5eeddSGerd Hoffmann     count = qemu_input_key_value_to_scancode(evt->key->key,
2271ff5eeddSGerd Hoffmann                                              evt->key->down,
2281ff5eeddSGerd Hoffmann                                              scancodes);
2291ff5eeddSGerd Hoffmann     if (hs->n + count > QUEUE_LENGTH) {
23049ab747fSPaolo Bonzini         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
23149ab747fSPaolo Bonzini         return;
23249ab747fSPaolo Bonzini     }
2331ff5eeddSGerd Hoffmann     for (i = 0; i < count; i++) {
23449ab747fSPaolo Bonzini         slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
2351ff5eeddSGerd Hoffmann         hs->kbd.keycodes[slot] = scancodes[i];
2361ff5eeddSGerd Hoffmann     }
23749ab747fSPaolo Bonzini     hs->event(hs);
23849ab747fSPaolo Bonzini }
23949ab747fSPaolo Bonzini 
24049ab747fSPaolo Bonzini static void hid_keyboard_process_keycode(HIDState *hs)
24149ab747fSPaolo Bonzini {
24249ab747fSPaolo Bonzini     uint8_t hid_code, key;
24349ab747fSPaolo Bonzini     int i, keycode, slot;
24449ab747fSPaolo Bonzini 
24549ab747fSPaolo Bonzini     if (hs->n == 0) {
24649ab747fSPaolo Bonzini         return;
24749ab747fSPaolo Bonzini     }
24849ab747fSPaolo Bonzini     slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
24949ab747fSPaolo Bonzini     keycode = hs->kbd.keycodes[slot];
25049ab747fSPaolo Bonzini 
25149ab747fSPaolo Bonzini     key = keycode & 0x7f;
25249ab747fSPaolo Bonzini     hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
25349ab747fSPaolo Bonzini     hs->kbd.modifiers &= ~(1 << 8);
25449ab747fSPaolo Bonzini 
25549ab747fSPaolo Bonzini     switch (hid_code) {
25649ab747fSPaolo Bonzini     case 0x00:
25749ab747fSPaolo Bonzini         return;
25849ab747fSPaolo Bonzini 
25949ab747fSPaolo Bonzini     case 0xe0:
26049ab747fSPaolo Bonzini         if (hs->kbd.modifiers & (1 << 9)) {
26149ab747fSPaolo Bonzini             hs->kbd.modifiers ^= 3 << 8;
26249ab747fSPaolo Bonzini             return;
26349ab747fSPaolo Bonzini         }
26449ab747fSPaolo Bonzini     case 0xe1 ... 0xe7:
26549ab747fSPaolo Bonzini         if (keycode & (1 << 7)) {
26649ab747fSPaolo Bonzini             hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
26749ab747fSPaolo Bonzini             return;
26849ab747fSPaolo Bonzini         }
26949ab747fSPaolo Bonzini     case 0xe8 ... 0xef:
27049ab747fSPaolo Bonzini         hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
27149ab747fSPaolo Bonzini         return;
27249ab747fSPaolo Bonzini     }
27349ab747fSPaolo Bonzini 
27449ab747fSPaolo Bonzini     if (keycode & (1 << 7)) {
27549ab747fSPaolo Bonzini         for (i = hs->kbd.keys - 1; i >= 0; i--) {
27649ab747fSPaolo Bonzini             if (hs->kbd.key[i] == hid_code) {
27749ab747fSPaolo Bonzini                 hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
27849ab747fSPaolo Bonzini                 hs->kbd.key[hs->kbd.keys] = 0x00;
27949ab747fSPaolo Bonzini                 break;
28049ab747fSPaolo Bonzini             }
28149ab747fSPaolo Bonzini         }
28249ab747fSPaolo Bonzini         if (i < 0) {
28349ab747fSPaolo Bonzini             return;
28449ab747fSPaolo Bonzini         }
28549ab747fSPaolo Bonzini     } else {
28649ab747fSPaolo Bonzini         for (i = hs->kbd.keys - 1; i >= 0; i--) {
28749ab747fSPaolo Bonzini             if (hs->kbd.key[i] == hid_code) {
28849ab747fSPaolo Bonzini                 break;
28949ab747fSPaolo Bonzini             }
29049ab747fSPaolo Bonzini         }
29149ab747fSPaolo Bonzini         if (i < 0) {
29249ab747fSPaolo Bonzini             if (hs->kbd.keys < sizeof(hs->kbd.key)) {
29349ab747fSPaolo Bonzini                 hs->kbd.key[hs->kbd.keys++] = hid_code;
29449ab747fSPaolo Bonzini             }
29549ab747fSPaolo Bonzini         } else {
29649ab747fSPaolo Bonzini             return;
29749ab747fSPaolo Bonzini         }
29849ab747fSPaolo Bonzini     }
29949ab747fSPaolo Bonzini }
30049ab747fSPaolo Bonzini 
30149ab747fSPaolo Bonzini static inline int int_clamp(int val, int vmin, int vmax)
30249ab747fSPaolo Bonzini {
30349ab747fSPaolo Bonzini     if (val < vmin) {
30449ab747fSPaolo Bonzini         return vmin;
30549ab747fSPaolo Bonzini     } else if (val > vmax) {
30649ab747fSPaolo Bonzini         return vmax;
30749ab747fSPaolo Bonzini     } else {
30849ab747fSPaolo Bonzini         return val;
30949ab747fSPaolo Bonzini     }
31049ab747fSPaolo Bonzini }
31149ab747fSPaolo Bonzini 
31249ab747fSPaolo Bonzini void hid_pointer_activate(HIDState *hs)
31349ab747fSPaolo Bonzini {
31449ab747fSPaolo Bonzini     if (!hs->ptr.mouse_grabbed) {
3158b84286fSGerd Hoffmann         qemu_input_handler_activate(hs->s);
31649ab747fSPaolo Bonzini         hs->ptr.mouse_grabbed = 1;
31749ab747fSPaolo Bonzini     }
31849ab747fSPaolo Bonzini }
31949ab747fSPaolo Bonzini 
32049ab747fSPaolo Bonzini int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
32149ab747fSPaolo Bonzini {
3228b84286fSGerd Hoffmann     int dx, dy, dz, l;
32349ab747fSPaolo Bonzini     int index;
32449ab747fSPaolo Bonzini     HIDPointerEvent *e;
32549ab747fSPaolo Bonzini 
32649ab747fSPaolo Bonzini     hs->idle_pending = false;
32749ab747fSPaolo Bonzini 
32849ab747fSPaolo Bonzini     hid_pointer_activate(hs);
32949ab747fSPaolo Bonzini 
33049ab747fSPaolo Bonzini     /* When the buffer is empty, return the last event.  Relative
33149ab747fSPaolo Bonzini        movements will all be zero.  */
33249ab747fSPaolo Bonzini     index = (hs->n ? hs->head : hs->head - 1);
33349ab747fSPaolo Bonzini     e = &hs->ptr.queue[index & QUEUE_MASK];
33449ab747fSPaolo Bonzini 
33549ab747fSPaolo Bonzini     if (hs->kind == HID_MOUSE) {
33649ab747fSPaolo Bonzini         dx = int_clamp(e->xdx, -127, 127);
33749ab747fSPaolo Bonzini         dy = int_clamp(e->ydy, -127, 127);
33849ab747fSPaolo Bonzini         e->xdx -= dx;
33949ab747fSPaolo Bonzini         e->ydy -= dy;
34049ab747fSPaolo Bonzini     } else {
34149ab747fSPaolo Bonzini         dx = e->xdx;
34249ab747fSPaolo Bonzini         dy = e->ydy;
34349ab747fSPaolo Bonzini     }
34449ab747fSPaolo Bonzini     dz = int_clamp(e->dz, -127, 127);
34549ab747fSPaolo Bonzini     e->dz -= dz;
34649ab747fSPaolo Bonzini 
34749ab747fSPaolo Bonzini     if (hs->n &&
34849ab747fSPaolo Bonzini         !e->dz &&
34949ab747fSPaolo Bonzini         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
35049ab747fSPaolo Bonzini         /* that deals with this event */
35149ab747fSPaolo Bonzini         QUEUE_INCR(hs->head);
35249ab747fSPaolo Bonzini         hs->n--;
35349ab747fSPaolo Bonzini     }
35449ab747fSPaolo Bonzini 
35549ab747fSPaolo Bonzini     /* Appears we have to invert the wheel direction */
35649ab747fSPaolo Bonzini     dz = 0 - dz;
35749ab747fSPaolo Bonzini     l = 0;
35849ab747fSPaolo Bonzini     switch (hs->kind) {
35949ab747fSPaolo Bonzini     case HID_MOUSE:
36049ab747fSPaolo Bonzini         if (len > l) {
3618b84286fSGerd Hoffmann             buf[l++] = e->buttons_state;
36249ab747fSPaolo Bonzini         }
36349ab747fSPaolo Bonzini         if (len > l) {
36449ab747fSPaolo Bonzini             buf[l++] = dx;
36549ab747fSPaolo Bonzini         }
36649ab747fSPaolo Bonzini         if (len > l) {
36749ab747fSPaolo Bonzini             buf[l++] = dy;
36849ab747fSPaolo Bonzini         }
36949ab747fSPaolo Bonzini         if (len > l) {
37049ab747fSPaolo Bonzini             buf[l++] = dz;
37149ab747fSPaolo Bonzini         }
37249ab747fSPaolo Bonzini         break;
37349ab747fSPaolo Bonzini 
37449ab747fSPaolo Bonzini     case HID_TABLET:
37549ab747fSPaolo Bonzini         if (len > l) {
3768b84286fSGerd Hoffmann             buf[l++] = e->buttons_state;
37749ab747fSPaolo Bonzini         }
37849ab747fSPaolo Bonzini         if (len > l) {
37949ab747fSPaolo Bonzini             buf[l++] = dx & 0xff;
38049ab747fSPaolo Bonzini         }
38149ab747fSPaolo Bonzini         if (len > l) {
38249ab747fSPaolo Bonzini             buf[l++] = dx >> 8;
38349ab747fSPaolo Bonzini         }
38449ab747fSPaolo Bonzini         if (len > l) {
38549ab747fSPaolo Bonzini             buf[l++] = dy & 0xff;
38649ab747fSPaolo Bonzini         }
38749ab747fSPaolo Bonzini         if (len > l) {
38849ab747fSPaolo Bonzini             buf[l++] = dy >> 8;
38949ab747fSPaolo Bonzini         }
39049ab747fSPaolo Bonzini         if (len > l) {
39149ab747fSPaolo Bonzini             buf[l++] = dz;
39249ab747fSPaolo Bonzini         }
39349ab747fSPaolo Bonzini         break;
39449ab747fSPaolo Bonzini 
39549ab747fSPaolo Bonzini     default:
39649ab747fSPaolo Bonzini         abort();
39749ab747fSPaolo Bonzini     }
39849ab747fSPaolo Bonzini 
39949ab747fSPaolo Bonzini     return l;
40049ab747fSPaolo Bonzini }
40149ab747fSPaolo Bonzini 
40249ab747fSPaolo Bonzini int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
40349ab747fSPaolo Bonzini {
40449ab747fSPaolo Bonzini     hs->idle_pending = false;
40549ab747fSPaolo Bonzini 
40649ab747fSPaolo Bonzini     if (len < 2) {
40749ab747fSPaolo Bonzini         return 0;
40849ab747fSPaolo Bonzini     }
40949ab747fSPaolo Bonzini 
41049ab747fSPaolo Bonzini     hid_keyboard_process_keycode(hs);
41149ab747fSPaolo Bonzini 
41249ab747fSPaolo Bonzini     buf[0] = hs->kbd.modifiers & 0xff;
41349ab747fSPaolo Bonzini     buf[1] = 0;
41449ab747fSPaolo Bonzini     if (hs->kbd.keys > 6) {
41549ab747fSPaolo Bonzini         memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
41649ab747fSPaolo Bonzini     } else {
41749ab747fSPaolo Bonzini         memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
41849ab747fSPaolo Bonzini     }
41949ab747fSPaolo Bonzini 
42049ab747fSPaolo Bonzini     return MIN(8, len);
42149ab747fSPaolo Bonzini }
42249ab747fSPaolo Bonzini 
42349ab747fSPaolo Bonzini int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
42449ab747fSPaolo Bonzini {
42549ab747fSPaolo Bonzini     if (len > 0) {
42649ab747fSPaolo Bonzini         int ledstate = 0;
42749ab747fSPaolo Bonzini         /* 0x01: Num Lock LED
42849ab747fSPaolo Bonzini          * 0x02: Caps Lock LED
42949ab747fSPaolo Bonzini          * 0x04: Scroll Lock LED
43049ab747fSPaolo Bonzini          * 0x08: Compose LED
43149ab747fSPaolo Bonzini          * 0x10: Kana LED */
43249ab747fSPaolo Bonzini         hs->kbd.leds = buf[0];
43349ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x04) {
43449ab747fSPaolo Bonzini             ledstate |= QEMU_SCROLL_LOCK_LED;
43549ab747fSPaolo Bonzini         }
43649ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x01) {
43749ab747fSPaolo Bonzini             ledstate |= QEMU_NUM_LOCK_LED;
43849ab747fSPaolo Bonzini         }
43949ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x02) {
44049ab747fSPaolo Bonzini             ledstate |= QEMU_CAPS_LOCK_LED;
44149ab747fSPaolo Bonzini         }
44249ab747fSPaolo Bonzini         kbd_put_ledstate(ledstate);
44349ab747fSPaolo Bonzini     }
44449ab747fSPaolo Bonzini     return 0;
44549ab747fSPaolo Bonzini }
44649ab747fSPaolo Bonzini 
44749ab747fSPaolo Bonzini void hid_reset(HIDState *hs)
44849ab747fSPaolo Bonzini {
44949ab747fSPaolo Bonzini     switch (hs->kind) {
45049ab747fSPaolo Bonzini     case HID_KEYBOARD:
45149ab747fSPaolo Bonzini         memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
45249ab747fSPaolo Bonzini         memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
45349ab747fSPaolo Bonzini         hs->kbd.keys = 0;
45449ab747fSPaolo Bonzini         break;
45549ab747fSPaolo Bonzini     case HID_MOUSE:
45649ab747fSPaolo Bonzini     case HID_TABLET:
45749ab747fSPaolo Bonzini         memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
45849ab747fSPaolo Bonzini         break;
45949ab747fSPaolo Bonzini     }
46049ab747fSPaolo Bonzini     hs->head = 0;
46149ab747fSPaolo Bonzini     hs->n = 0;
46249ab747fSPaolo Bonzini     hs->protocol = 1;
46349ab747fSPaolo Bonzini     hs->idle = 0;
46449ab747fSPaolo Bonzini     hs->idle_pending = false;
46549ab747fSPaolo Bonzini     hid_del_idle_timer(hs);
46649ab747fSPaolo Bonzini }
46749ab747fSPaolo Bonzini 
46849ab747fSPaolo Bonzini void hid_free(HIDState *hs)
46949ab747fSPaolo Bonzini {
4701ff5eeddSGerd Hoffmann     qemu_input_handler_unregister(hs->s);
47149ab747fSPaolo Bonzini     hid_del_idle_timer(hs);
47249ab747fSPaolo Bonzini }
47349ab747fSPaolo Bonzini 
4741ff5eeddSGerd Hoffmann static QemuInputHandler hid_keyboard_handler = {
4751ff5eeddSGerd Hoffmann     .name  = "QEMU HID Keyboard",
4761ff5eeddSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
4771ff5eeddSGerd Hoffmann     .event = hid_keyboard_event,
4781ff5eeddSGerd Hoffmann };
4791ff5eeddSGerd Hoffmann 
4808b84286fSGerd Hoffmann static QemuInputHandler hid_mouse_handler = {
4818b84286fSGerd Hoffmann     .name  = "QEMU HID Mouse",
4828b84286fSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
4838b84286fSGerd Hoffmann     .event = hid_pointer_event,
4848b84286fSGerd Hoffmann     .sync  = hid_pointer_sync,
4858b84286fSGerd Hoffmann };
4868b84286fSGerd Hoffmann 
4878b84286fSGerd Hoffmann static QemuInputHandler hid_tablet_handler = {
4888b84286fSGerd Hoffmann     .name  = "QEMU HID Tablet",
4898b84286fSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
4908b84286fSGerd Hoffmann     .event = hid_pointer_event,
4918b84286fSGerd Hoffmann     .sync  = hid_pointer_sync,
4928b84286fSGerd Hoffmann };
4938b84286fSGerd Hoffmann 
49449ab747fSPaolo Bonzini void hid_init(HIDState *hs, int kind, HIDEventFunc event)
49549ab747fSPaolo Bonzini {
49649ab747fSPaolo Bonzini     hs->kind = kind;
49749ab747fSPaolo Bonzini     hs->event = event;
49849ab747fSPaolo Bonzini 
49949ab747fSPaolo Bonzini     if (hs->kind == HID_KEYBOARD) {
5001ff5eeddSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
5011ff5eeddSGerd Hoffmann                                             &hid_keyboard_handler);
5021ff5eeddSGerd Hoffmann         qemu_input_handler_activate(hs->s);
50349ab747fSPaolo Bonzini     } else if (hs->kind == HID_MOUSE) {
5048b84286fSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
5058b84286fSGerd Hoffmann                                             &hid_mouse_handler);
50649ab747fSPaolo Bonzini     } else if (hs->kind == HID_TABLET) {
5078b84286fSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
5088b84286fSGerd Hoffmann                                             &hid_tablet_handler);
50949ab747fSPaolo Bonzini     }
51049ab747fSPaolo Bonzini }
51149ab747fSPaolo Bonzini 
51249ab747fSPaolo Bonzini static int hid_post_load(void *opaque, int version_id)
51349ab747fSPaolo Bonzini {
51449ab747fSPaolo Bonzini     HIDState *s = opaque;
51549ab747fSPaolo Bonzini 
51649ab747fSPaolo Bonzini     hid_set_next_idle(s);
51749ab747fSPaolo Bonzini     return 0;
51849ab747fSPaolo Bonzini }
51949ab747fSPaolo Bonzini 
52049ab747fSPaolo Bonzini static const VMStateDescription vmstate_hid_ptr_queue = {
52149ab747fSPaolo Bonzini     .name = "HIDPointerEventQueue",
52249ab747fSPaolo Bonzini     .version_id = 1,
52349ab747fSPaolo Bonzini     .minimum_version_id = 1,
52449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
52549ab747fSPaolo Bonzini         VMSTATE_INT32(xdx, HIDPointerEvent),
52649ab747fSPaolo Bonzini         VMSTATE_INT32(ydy, HIDPointerEvent),
52749ab747fSPaolo Bonzini         VMSTATE_INT32(dz, HIDPointerEvent),
52849ab747fSPaolo Bonzini         VMSTATE_INT32(buttons_state, HIDPointerEvent),
52949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
53049ab747fSPaolo Bonzini     }
53149ab747fSPaolo Bonzini };
53249ab747fSPaolo Bonzini 
53349ab747fSPaolo Bonzini const VMStateDescription vmstate_hid_ptr_device = {
53449ab747fSPaolo Bonzini     .name = "HIDPointerDevice",
53549ab747fSPaolo Bonzini     .version_id = 1,
53649ab747fSPaolo Bonzini     .minimum_version_id = 1,
53749ab747fSPaolo Bonzini     .post_load = hid_post_load,
53849ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
53949ab747fSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
54049ab747fSPaolo Bonzini                              vmstate_hid_ptr_queue, HIDPointerEvent),
54149ab747fSPaolo Bonzini         VMSTATE_UINT32(head, HIDState),
54249ab747fSPaolo Bonzini         VMSTATE_UINT32(n, HIDState),
54349ab747fSPaolo Bonzini         VMSTATE_INT32(protocol, HIDState),
54449ab747fSPaolo Bonzini         VMSTATE_UINT8(idle, HIDState),
54549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST(),
54649ab747fSPaolo Bonzini     }
54749ab747fSPaolo Bonzini };
54849ab747fSPaolo Bonzini 
54949ab747fSPaolo Bonzini const VMStateDescription vmstate_hid_keyboard_device = {
55049ab747fSPaolo Bonzini     .name = "HIDKeyboardDevice",
55149ab747fSPaolo Bonzini     .version_id = 1,
55249ab747fSPaolo Bonzini     .minimum_version_id = 1,
55349ab747fSPaolo Bonzini     .post_load = hid_post_load,
55449ab747fSPaolo Bonzini     .fields = (VMStateField[]) {
55549ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
55649ab747fSPaolo Bonzini         VMSTATE_UINT32(head, HIDState),
55749ab747fSPaolo Bonzini         VMSTATE_UINT32(n, HIDState),
55849ab747fSPaolo Bonzini         VMSTATE_UINT16(kbd.modifiers, HIDState),
55949ab747fSPaolo Bonzini         VMSTATE_UINT8(kbd.leds, HIDState),
56049ab747fSPaolo Bonzini         VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
56149ab747fSPaolo Bonzini         VMSTATE_INT32(kbd.keys, HIDState),
56249ab747fSPaolo Bonzini         VMSTATE_INT32(protocol, HIDState),
56349ab747fSPaolo Bonzini         VMSTATE_UINT8(idle, HIDState),
56449ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST(),
56549ab747fSPaolo Bonzini     }
56649ab747fSPaolo Bonzini };
567