xref: /qemu/hw/input/hid.c (revision af0f07df)
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  */
25d6454270SMarkus Armbruster 
260430891cSPeter Maydell #include "qemu/osdep.h"
2749ab747fSPaolo Bonzini #include "ui/console.h"
2849ab747fSPaolo Bonzini #include "qemu/timer.h"
2949ab747fSPaolo Bonzini #include "hw/input/hid.h"
30d6454270SMarkus Armbruster #include "migration/vmstate.h"
31c80276b4SGerd Hoffmann #include "trace.h"
3249ab747fSPaolo Bonzini 
3349ab747fSPaolo Bonzini #define HID_USAGE_ERROR_ROLLOVER        0x01
3449ab747fSPaolo Bonzini #define HID_USAGE_POSTFAIL              0x02
3549ab747fSPaolo Bonzini #define HID_USAGE_ERROR_UNDEFINED       0x03
3649ab747fSPaolo Bonzini 
3749ab747fSPaolo Bonzini /* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
3849ab747fSPaolo Bonzini  * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
3949ab747fSPaolo Bonzini static const uint8_t hid_usage_keys[0x100] = {
4049ab747fSPaolo Bonzini     0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
4149ab747fSPaolo Bonzini     0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
4249ab747fSPaolo Bonzini     0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
4349ab747fSPaolo Bonzini     0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
4449ab747fSPaolo Bonzini     0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
4549ab747fSPaolo Bonzini     0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
4649ab747fSPaolo Bonzini     0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
470ee4de58SDinar Valeev     0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
4849ab747fSPaolo Bonzini     0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
4949ab747fSPaolo Bonzini     0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
5086f3bf0eSPeter Korsgaard     0x5a, 0x5b, 0x62, 0x63, 0x46, 0x00, 0x64, 0x44,
5149ab747fSPaolo Bonzini     0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
5249ab747fSPaolo Bonzini     0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
5349ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
54f9c48124SKatsuhiro Ueno     0x88, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00,
55f9c48124SKatsuhiro Ueno     0x00, 0x8a, 0x00, 0x8b, 0x00, 0x89, 0xe7, 0x65,
5649ab747fSPaolo Bonzini 
5749ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x58, 0xe4, 0x00, 0x00,
61160997faSTao Wu     0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62160997faSTao Wu     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00,
63160997faSTao Wu     0x80, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
6449ab747fSPaolo Bonzini     0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6586f3bf0eSPeter Korsgaard     0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x48, 0x4a,
6649ab747fSPaolo Bonzini     0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
6749ab747fSPaolo Bonzini     0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
68160997faSTao Wu     0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x66, 0x00,
6949ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7049ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7149ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7249ab747fSPaolo Bonzini     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
7349ab747fSPaolo Bonzini };
7449ab747fSPaolo Bonzini 
hid_has_events(HIDState * hs)7549ab747fSPaolo Bonzini bool hid_has_events(HIDState *hs)
7649ab747fSPaolo Bonzini {
7749ab747fSPaolo Bonzini     return hs->n > 0 || hs->idle_pending;
7849ab747fSPaolo Bonzini }
7949ab747fSPaolo Bonzini 
hid_idle_timer(void * opaque)8049ab747fSPaolo Bonzini static void hid_idle_timer(void *opaque)
8149ab747fSPaolo Bonzini {
8249ab747fSPaolo Bonzini     HIDState *hs = opaque;
8349ab747fSPaolo Bonzini 
8449ab747fSPaolo Bonzini     hs->idle_pending = true;
8549ab747fSPaolo Bonzini     hs->event(hs);
8649ab747fSPaolo Bonzini }
8749ab747fSPaolo Bonzini 
hid_del_idle_timer(HIDState * hs)8849ab747fSPaolo Bonzini static void hid_del_idle_timer(HIDState *hs)
8949ab747fSPaolo Bonzini {
9049ab747fSPaolo Bonzini     if (hs->idle_timer) {
91bc72ad67SAlex Bligh         timer_free(hs->idle_timer);
9249ab747fSPaolo Bonzini         hs->idle_timer = NULL;
9349ab747fSPaolo Bonzini     }
9449ab747fSPaolo Bonzini }
9549ab747fSPaolo Bonzini 
hid_set_next_idle(HIDState * hs)9649ab747fSPaolo Bonzini void hid_set_next_idle(HIDState *hs)
9749ab747fSPaolo Bonzini {
9849ab747fSPaolo Bonzini     if (hs->idle) {
99bc72ad67SAlex Bligh         uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
10073bcb24dSRutuja Shah                                NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000;
10149ab747fSPaolo Bonzini         if (!hs->idle_timer) {
102bc72ad67SAlex Bligh             hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
10349ab747fSPaolo Bonzini         }
104bc72ad67SAlex Bligh         timer_mod_ns(hs->idle_timer, expire_time);
10549ab747fSPaolo Bonzini     } else {
10649ab747fSPaolo Bonzini         hid_del_idle_timer(hs);
10749ab747fSPaolo Bonzini     }
10849ab747fSPaolo Bonzini }
10949ab747fSPaolo Bonzini 
hid_pointer_event(DeviceState * dev,QemuConsole * src,InputEvent * evt)1108b84286fSGerd Hoffmann static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
1118b84286fSGerd Hoffmann                               InputEvent *evt)
11249ab747fSPaolo Bonzini {
1137fb1cf16SEric Blake     static const int bmap[INPUT_BUTTON__MAX] = {
1148b84286fSGerd Hoffmann         [INPUT_BUTTON_LEFT]   = 0x01,
1158b84286fSGerd Hoffmann         [INPUT_BUTTON_RIGHT]  = 0x02,
1168b84286fSGerd Hoffmann         [INPUT_BUTTON_MIDDLE] = 0x04,
1171e2d5558SNoah Bergbauer         [INPUT_BUTTON_SIDE] = 0x08,
1181e2d5558SNoah Bergbauer         [INPUT_BUTTON_EXTRA] = 0x10,
1198b84286fSGerd Hoffmann     };
1208b84286fSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
1218b84286fSGerd Hoffmann     HIDPointerEvent *e;
122b5a1b443SEric Blake     InputMoveEvent *move;
123b5a1b443SEric Blake     InputBtnEvent *btn;
12449ab747fSPaolo Bonzini 
1258b84286fSGerd Hoffmann     assert(hs->n < QUEUE_LENGTH);
1268b84286fSGerd Hoffmann     e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
1278b84286fSGerd Hoffmann 
128568c73a4SEric Blake     switch (evt->type) {
1298b84286fSGerd Hoffmann     case INPUT_EVENT_KIND_REL:
13032bafa8fSEric Blake         move = evt->u.rel.data;
131b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
132b5a1b443SEric Blake             e->xdx += move->value;
133b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
134b5a1b443SEric Blake             e->ydy += move->value;
1358b84286fSGerd Hoffmann         }
1368b84286fSGerd Hoffmann         break;
1378b84286fSGerd Hoffmann 
1388b84286fSGerd Hoffmann     case INPUT_EVENT_KIND_ABS:
13932bafa8fSEric Blake         move = evt->u.abs.data;
140b5a1b443SEric Blake         if (move->axis == INPUT_AXIS_X) {
141b5a1b443SEric Blake             e->xdx = move->value;
142b5a1b443SEric Blake         } else if (move->axis == INPUT_AXIS_Y) {
143b5a1b443SEric Blake             e->ydy = move->value;
1448b84286fSGerd Hoffmann         }
1458b84286fSGerd Hoffmann         break;
1468b84286fSGerd Hoffmann 
1478b84286fSGerd Hoffmann     case INPUT_EVENT_KIND_BTN:
14832bafa8fSEric Blake         btn = evt->u.btn.data;
149b5a1b443SEric Blake         if (btn->down) {
150b5a1b443SEric Blake             e->buttons_state |= bmap[btn->button];
151b5a1b443SEric Blake             if (btn->button == INPUT_BUTTON_WHEEL_UP) {
1528b84286fSGerd Hoffmann                 e->dz--;
153b5a1b443SEric Blake             } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
1548b84286fSGerd Hoffmann                 e->dz++;
1558b84286fSGerd Hoffmann             }
15649ab747fSPaolo Bonzini         } else {
157b5a1b443SEric Blake             e->buttons_state &= ~bmap[btn->button];
15849ab747fSPaolo Bonzini         }
1598b84286fSGerd Hoffmann         break;
1608b84286fSGerd Hoffmann 
1618b84286fSGerd Hoffmann     default:
1628b84286fSGerd Hoffmann         /* keep gcc happy */
1638b84286fSGerd Hoffmann         break;
16449ab747fSPaolo Bonzini     }
16549ab747fSPaolo Bonzini 
1668b84286fSGerd Hoffmann }
1678b84286fSGerd Hoffmann 
hid_pointer_sync(DeviceState * dev)1688b84286fSGerd Hoffmann static void hid_pointer_sync(DeviceState *dev)
16949ab747fSPaolo Bonzini {
1708b84286fSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
1718b84286fSGerd Hoffmann     HIDPointerEvent *prev, *curr, *next;
1728b84286fSGerd Hoffmann     bool event_compression = false;
17349ab747fSPaolo Bonzini 
1748b84286fSGerd Hoffmann     if (hs->n == QUEUE_LENGTH-1) {
1758b84286fSGerd Hoffmann         /*
1765d831be2SStefan Weil          * Queue full.  We are losing information, but we at least
1778b84286fSGerd Hoffmann          * keep track of most recent button state.
1788b84286fSGerd Hoffmann          */
1798b84286fSGerd Hoffmann         return;
18049ab747fSPaolo Bonzini     }
1818b84286fSGerd Hoffmann 
1828b84286fSGerd Hoffmann     prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
1838b84286fSGerd Hoffmann     curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
1848b84286fSGerd Hoffmann     next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
1858b84286fSGerd Hoffmann 
1868b84286fSGerd Hoffmann     if (hs->n > 0) {
1878b84286fSGerd Hoffmann         /*
1888b84286fSGerd Hoffmann          * No button state change between previous and current event
1898b84286fSGerd Hoffmann          * (and previous wasn't seen by the guest yet), so there is
1908b84286fSGerd Hoffmann          * motion information only and we can combine the two event
1918b84286fSGerd Hoffmann          * into one.
1928b84286fSGerd Hoffmann          */
1938b84286fSGerd Hoffmann         if (curr->buttons_state == prev->buttons_state) {
1948b84286fSGerd Hoffmann             event_compression = true;
1958b84286fSGerd Hoffmann         }
1968b84286fSGerd Hoffmann     }
1978b84286fSGerd Hoffmann 
1988b84286fSGerd Hoffmann     if (event_compression) {
1998b84286fSGerd Hoffmann         /* add current motion to previous, clear current */
2008b84286fSGerd Hoffmann         if (hs->kind == HID_MOUSE) {
2018b84286fSGerd Hoffmann             prev->xdx += curr->xdx;
2028b84286fSGerd Hoffmann             curr->xdx = 0;
20335e83d10SChristian Burger             prev->ydy += curr->ydy;
2048b84286fSGerd Hoffmann             curr->ydy = 0;
2058b84286fSGerd Hoffmann         } else {
2068b84286fSGerd Hoffmann             prev->xdx = curr->xdx;
2078b84286fSGerd Hoffmann             prev->ydy = curr->ydy;
2088b84286fSGerd Hoffmann         }
2098b84286fSGerd Hoffmann         prev->dz += curr->dz;
2108b84286fSGerd Hoffmann         curr->dz = 0;
2118b84286fSGerd Hoffmann     } else {
2129b4b4e51SMichael Tokarev         /* prepare next (clear rel, copy abs + btns) */
2138b84286fSGerd Hoffmann         if (hs->kind == HID_MOUSE) {
2148b84286fSGerd Hoffmann             next->xdx = 0;
2158b84286fSGerd Hoffmann             next->ydy = 0;
2168b84286fSGerd Hoffmann         } else {
2178b84286fSGerd Hoffmann             next->xdx = curr->xdx;
2188b84286fSGerd Hoffmann             next->ydy = curr->ydy;
2198b84286fSGerd Hoffmann         }
2208b84286fSGerd Hoffmann         next->dz = 0;
2218b84286fSGerd Hoffmann         next->buttons_state = curr->buttons_state;
2228b84286fSGerd Hoffmann         /* make current guest visible, notify guest */
2238b84286fSGerd Hoffmann         hs->n++;
22449ab747fSPaolo Bonzini         hs->event(hs);
22549ab747fSPaolo Bonzini     }
2268b84286fSGerd Hoffmann }
22749ab747fSPaolo Bonzini 
hid_keyboard_event(DeviceState * dev,QemuConsole * src,InputEvent * evt)2281ff5eeddSGerd Hoffmann static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
2291ff5eeddSGerd Hoffmann                                InputEvent *evt)
23049ab747fSPaolo Bonzini {
2311ff5eeddSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
2321ff5eeddSGerd Hoffmann     int scancodes[3], i, count;
23349ab747fSPaolo Bonzini     int slot;
23432bafa8fSEric Blake     InputKeyEvent *key = evt->u.key.data;
23549ab747fSPaolo Bonzini 
236b5a1b443SEric Blake     count = qemu_input_key_value_to_scancode(key->key,
237b5a1b443SEric Blake                                              key->down,
2381ff5eeddSGerd Hoffmann                                              scancodes);
2391ff5eeddSGerd Hoffmann     if (hs->n + count > QUEUE_LENGTH) {
240c80276b4SGerd Hoffmann         trace_hid_kbd_queue_full();
24149ab747fSPaolo Bonzini         return;
24249ab747fSPaolo Bonzini     }
2431ff5eeddSGerd Hoffmann     for (i = 0; i < count; i++) {
24449ab747fSPaolo Bonzini         slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
2451ff5eeddSGerd Hoffmann         hs->kbd.keycodes[slot] = scancodes[i];
2461ff5eeddSGerd Hoffmann     }
24749ab747fSPaolo Bonzini     hs->event(hs);
24849ab747fSPaolo Bonzini }
24949ab747fSPaolo Bonzini 
hid_keyboard_process_keycode(HIDState * hs)25049ab747fSPaolo Bonzini static void hid_keyboard_process_keycode(HIDState *hs)
25149ab747fSPaolo Bonzini {
252562f9375SPaolo Bonzini     uint8_t hid_code, index, key;
25349ab747fSPaolo Bonzini     int i, keycode, slot;
25449ab747fSPaolo Bonzini 
25549ab747fSPaolo Bonzini     if (hs->n == 0) {
25649ab747fSPaolo Bonzini         return;
25749ab747fSPaolo Bonzini     }
25849ab747fSPaolo Bonzini     slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
25949ab747fSPaolo Bonzini     keycode = hs->kbd.keycodes[slot];
26049ab747fSPaolo Bonzini 
2612222e0a6SAlexander Graf     if (!hs->n) {
2622222e0a6SAlexander Graf         trace_hid_kbd_queue_empty();
2632222e0a6SAlexander Graf     }
2642222e0a6SAlexander Graf 
26549ab747fSPaolo Bonzini     key = keycode & 0x7f;
266562f9375SPaolo Bonzini     index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
267562f9375SPaolo Bonzini     hid_code = hid_usage_keys[index];
26849ab747fSPaolo Bonzini     hs->kbd.modifiers &= ~(1 << 8);
26949ab747fSPaolo Bonzini 
27049ab747fSPaolo Bonzini     switch (hid_code) {
27149ab747fSPaolo Bonzini     case 0x00:
27249ab747fSPaolo Bonzini         return;
27349ab747fSPaolo Bonzini 
27449ab747fSPaolo Bonzini     case 0xe0:
275562f9375SPaolo Bonzini         assert(key == 0x1d);
27649ab747fSPaolo Bonzini         if (hs->kbd.modifiers & (1 << 9)) {
277562f9375SPaolo Bonzini             /* The hid_codes for the 0xe1/0x1d scancode sequence are 0xe9/0xe0.
278562f9375SPaolo Bonzini              * Here we're processing the second hid_code.  By dropping bit 9
279562f9375SPaolo Bonzini              * and setting bit 8, the scancode after 0x1d will access the
280562f9375SPaolo Bonzini              * second half of the table.
281562f9375SPaolo Bonzini              */
282562f9375SPaolo Bonzini             hs->kbd.modifiers ^= (1 << 8) | (1 << 9);
28349ab747fSPaolo Bonzini             return;
28449ab747fSPaolo Bonzini         }
285562f9375SPaolo Bonzini         /* fall through to process Ctrl_L */
28649ab747fSPaolo Bonzini     case 0xe1 ... 0xe7:
287562f9375SPaolo Bonzini         /* Ctrl_L/Ctrl_R, Shift_L/Shift_R, Alt_L/Alt_R, Win_L/Win_R.
288562f9375SPaolo Bonzini          * Handle releases here, or fall through to process presses.
289562f9375SPaolo Bonzini          */
29049ab747fSPaolo Bonzini         if (keycode & (1 << 7)) {
29149ab747fSPaolo Bonzini             hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
29249ab747fSPaolo Bonzini             return;
29349ab747fSPaolo Bonzini         }
294562f9375SPaolo Bonzini         /* fall through */
295562f9375SPaolo Bonzini     case 0xe8 ... 0xe9:
296562f9375SPaolo Bonzini         /* USB modifiers are just 1 byte long.  Bits 8 and 9 of
297562f9375SPaolo Bonzini          * hs->kbd.modifiers implement a state machine that detects the
298562f9375SPaolo Bonzini          * 0xe0 and 0xe1/0x1d sequences.  These bits do not follow the
299562f9375SPaolo Bonzini          * usual rules where bit 7 marks released keys; they are cleared
300562f9375SPaolo Bonzini          * elsewhere in the function as the state machine dictates.
301562f9375SPaolo Bonzini          */
30249ab747fSPaolo Bonzini         hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
30349ab747fSPaolo Bonzini         return;
304562f9375SPaolo Bonzini 
305562f9375SPaolo Bonzini     case 0xea ... 0xef:
306562f9375SPaolo Bonzini         abort();
307562f9375SPaolo Bonzini 
308562f9375SPaolo Bonzini     default:
309562f9375SPaolo Bonzini         break;
31049ab747fSPaolo Bonzini     }
31149ab747fSPaolo Bonzini 
31249ab747fSPaolo Bonzini     if (keycode & (1 << 7)) {
31349ab747fSPaolo Bonzini         for (i = hs->kbd.keys - 1; i >= 0; i--) {
31449ab747fSPaolo Bonzini             if (hs->kbd.key[i] == hid_code) {
31549ab747fSPaolo Bonzini                 hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
31649ab747fSPaolo Bonzini                 hs->kbd.key[hs->kbd.keys] = 0x00;
31749ab747fSPaolo Bonzini                 break;
31849ab747fSPaolo Bonzini             }
31949ab747fSPaolo Bonzini         }
32049ab747fSPaolo Bonzini         if (i < 0) {
32149ab747fSPaolo Bonzini             return;
32249ab747fSPaolo Bonzini         }
32349ab747fSPaolo Bonzini     } else {
32449ab747fSPaolo Bonzini         for (i = hs->kbd.keys - 1; i >= 0; i--) {
32549ab747fSPaolo Bonzini             if (hs->kbd.key[i] == hid_code) {
32649ab747fSPaolo Bonzini                 break;
32749ab747fSPaolo Bonzini             }
32849ab747fSPaolo Bonzini         }
32949ab747fSPaolo Bonzini         if (i < 0) {
33049ab747fSPaolo Bonzini             if (hs->kbd.keys < sizeof(hs->kbd.key)) {
33149ab747fSPaolo Bonzini                 hs->kbd.key[hs->kbd.keys++] = hid_code;
33249ab747fSPaolo Bonzini             }
33349ab747fSPaolo Bonzini         } else {
33449ab747fSPaolo Bonzini             return;
33549ab747fSPaolo Bonzini         }
33649ab747fSPaolo Bonzini     }
33749ab747fSPaolo Bonzini }
33849ab747fSPaolo Bonzini 
int_clamp(int val,int vmin,int vmax)33949ab747fSPaolo Bonzini static inline int int_clamp(int val, int vmin, int vmax)
34049ab747fSPaolo Bonzini {
34149ab747fSPaolo Bonzini     if (val < vmin) {
34249ab747fSPaolo Bonzini         return vmin;
34349ab747fSPaolo Bonzini     } else if (val > vmax) {
34449ab747fSPaolo Bonzini         return vmax;
34549ab747fSPaolo Bonzini     } else {
34649ab747fSPaolo Bonzini         return val;
34749ab747fSPaolo Bonzini     }
34849ab747fSPaolo Bonzini }
34949ab747fSPaolo Bonzini 
hid_pointer_activate(HIDState * hs)35049ab747fSPaolo Bonzini void hid_pointer_activate(HIDState *hs)
35149ab747fSPaolo Bonzini {
35249ab747fSPaolo Bonzini     if (!hs->ptr.mouse_grabbed) {
3538b84286fSGerd Hoffmann         qemu_input_handler_activate(hs->s);
35449ab747fSPaolo Bonzini         hs->ptr.mouse_grabbed = 1;
35549ab747fSPaolo Bonzini     }
35649ab747fSPaolo Bonzini }
35749ab747fSPaolo Bonzini 
hid_pointer_poll(HIDState * hs,uint8_t * buf,int len)35849ab747fSPaolo Bonzini int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
35949ab747fSPaolo Bonzini {
3608b84286fSGerd Hoffmann     int dx, dy, dz, l;
36149ab747fSPaolo Bonzini     int index;
36249ab747fSPaolo Bonzini     HIDPointerEvent *e;
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     hs->idle_pending = false;
36549ab747fSPaolo Bonzini 
36649ab747fSPaolo Bonzini     hid_pointer_activate(hs);
36749ab747fSPaolo Bonzini 
36849ab747fSPaolo Bonzini     /* When the buffer is empty, return the last event.  Relative
36949ab747fSPaolo Bonzini        movements will all be zero.  */
37049ab747fSPaolo Bonzini     index = (hs->n ? hs->head : hs->head - 1);
37149ab747fSPaolo Bonzini     e = &hs->ptr.queue[index & QUEUE_MASK];
37249ab747fSPaolo Bonzini 
37349ab747fSPaolo Bonzini     if (hs->kind == HID_MOUSE) {
37449ab747fSPaolo Bonzini         dx = int_clamp(e->xdx, -127, 127);
37549ab747fSPaolo Bonzini         dy = int_clamp(e->ydy, -127, 127);
37649ab747fSPaolo Bonzini         e->xdx -= dx;
37749ab747fSPaolo Bonzini         e->ydy -= dy;
37849ab747fSPaolo Bonzini     } else {
37949ab747fSPaolo Bonzini         dx = e->xdx;
38049ab747fSPaolo Bonzini         dy = e->ydy;
38149ab747fSPaolo Bonzini     }
38249ab747fSPaolo Bonzini     dz = int_clamp(e->dz, -127, 127);
38349ab747fSPaolo Bonzini     e->dz -= dz;
38449ab747fSPaolo Bonzini 
38549ab747fSPaolo Bonzini     if (hs->n &&
38649ab747fSPaolo Bonzini         !e->dz &&
38749ab747fSPaolo Bonzini         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
38849ab747fSPaolo Bonzini         /* that deals with this event */
38949ab747fSPaolo Bonzini         QUEUE_INCR(hs->head);
39049ab747fSPaolo Bonzini         hs->n--;
39149ab747fSPaolo Bonzini     }
39249ab747fSPaolo Bonzini 
39349ab747fSPaolo Bonzini     /* Appears we have to invert the wheel direction */
39449ab747fSPaolo Bonzini     dz = 0 - dz;
39549ab747fSPaolo Bonzini     l = 0;
39649ab747fSPaolo Bonzini     switch (hs->kind) {
39749ab747fSPaolo Bonzini     case HID_MOUSE:
39849ab747fSPaolo Bonzini         if (len > l) {
3998b84286fSGerd Hoffmann             buf[l++] = e->buttons_state;
40049ab747fSPaolo Bonzini         }
40149ab747fSPaolo Bonzini         if (len > l) {
40249ab747fSPaolo Bonzini             buf[l++] = dx;
40349ab747fSPaolo Bonzini         }
40449ab747fSPaolo Bonzini         if (len > l) {
40549ab747fSPaolo Bonzini             buf[l++] = dy;
40649ab747fSPaolo Bonzini         }
40749ab747fSPaolo Bonzini         if (len > l) {
40849ab747fSPaolo Bonzini             buf[l++] = dz;
40949ab747fSPaolo Bonzini         }
41049ab747fSPaolo Bonzini         break;
41149ab747fSPaolo Bonzini 
41249ab747fSPaolo Bonzini     case HID_TABLET:
41349ab747fSPaolo Bonzini         if (len > l) {
4148b84286fSGerd Hoffmann             buf[l++] = e->buttons_state;
41549ab747fSPaolo Bonzini         }
41649ab747fSPaolo Bonzini         if (len > l) {
41749ab747fSPaolo Bonzini             buf[l++] = dx & 0xff;
41849ab747fSPaolo Bonzini         }
41949ab747fSPaolo Bonzini         if (len > l) {
42049ab747fSPaolo Bonzini             buf[l++] = dx >> 8;
42149ab747fSPaolo Bonzini         }
42249ab747fSPaolo Bonzini         if (len > l) {
42349ab747fSPaolo Bonzini             buf[l++] = dy & 0xff;
42449ab747fSPaolo Bonzini         }
42549ab747fSPaolo Bonzini         if (len > l) {
42649ab747fSPaolo Bonzini             buf[l++] = dy >> 8;
42749ab747fSPaolo Bonzini         }
42849ab747fSPaolo Bonzini         if (len > l) {
42949ab747fSPaolo Bonzini             buf[l++] = dz;
43049ab747fSPaolo Bonzini         }
43149ab747fSPaolo Bonzini         break;
43249ab747fSPaolo Bonzini 
43349ab747fSPaolo Bonzini     default:
43449ab747fSPaolo Bonzini         abort();
43549ab747fSPaolo Bonzini     }
43649ab747fSPaolo Bonzini 
43749ab747fSPaolo Bonzini     return l;
43849ab747fSPaolo Bonzini }
43949ab747fSPaolo Bonzini 
hid_keyboard_poll(HIDState * hs,uint8_t * buf,int len)44049ab747fSPaolo Bonzini int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
44149ab747fSPaolo Bonzini {
44249ab747fSPaolo Bonzini     hs->idle_pending = false;
44349ab747fSPaolo Bonzini 
44449ab747fSPaolo Bonzini     if (len < 2) {
44549ab747fSPaolo Bonzini         return 0;
44649ab747fSPaolo Bonzini     }
44749ab747fSPaolo Bonzini 
44849ab747fSPaolo Bonzini     hid_keyboard_process_keycode(hs);
44949ab747fSPaolo Bonzini 
45049ab747fSPaolo Bonzini     buf[0] = hs->kbd.modifiers & 0xff;
45149ab747fSPaolo Bonzini     buf[1] = 0;
45249ab747fSPaolo Bonzini     if (hs->kbd.keys > 6) {
45349ab747fSPaolo Bonzini         memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
45449ab747fSPaolo Bonzini     } else {
45549ab747fSPaolo Bonzini         memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
45649ab747fSPaolo Bonzini     }
45749ab747fSPaolo Bonzini 
45849ab747fSPaolo Bonzini     return MIN(8, len);
45949ab747fSPaolo Bonzini }
46049ab747fSPaolo Bonzini 
hid_keyboard_write(HIDState * hs,uint8_t * buf,int len)46149ab747fSPaolo Bonzini int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
46249ab747fSPaolo Bonzini {
46349ab747fSPaolo Bonzini     if (len > 0) {
46449ab747fSPaolo Bonzini         int ledstate = 0;
46549ab747fSPaolo Bonzini         /* 0x01: Num Lock LED
46649ab747fSPaolo Bonzini          * 0x02: Caps Lock LED
46749ab747fSPaolo Bonzini          * 0x04: Scroll Lock LED
46849ab747fSPaolo Bonzini          * 0x08: Compose LED
46949ab747fSPaolo Bonzini          * 0x10: Kana LED */
47049ab747fSPaolo Bonzini         hs->kbd.leds = buf[0];
47149ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x04) {
47249ab747fSPaolo Bonzini             ledstate |= QEMU_SCROLL_LOCK_LED;
47349ab747fSPaolo Bonzini         }
47449ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x01) {
47549ab747fSPaolo Bonzini             ledstate |= QEMU_NUM_LOCK_LED;
47649ab747fSPaolo Bonzini         }
47749ab747fSPaolo Bonzini         if (hs->kbd.leds & 0x02) {
47849ab747fSPaolo Bonzini             ledstate |= QEMU_CAPS_LOCK_LED;
47949ab747fSPaolo Bonzini         }
48049ab747fSPaolo Bonzini         kbd_put_ledstate(ledstate);
48149ab747fSPaolo Bonzini     }
48249ab747fSPaolo Bonzini     return 0;
48349ab747fSPaolo Bonzini }
48449ab747fSPaolo Bonzini 
hid_reset(HIDState * hs)48549ab747fSPaolo Bonzini void hid_reset(HIDState *hs)
48649ab747fSPaolo Bonzini {
48749ab747fSPaolo Bonzini     switch (hs->kind) {
48849ab747fSPaolo Bonzini     case HID_KEYBOARD:
48949ab747fSPaolo Bonzini         memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
49049ab747fSPaolo Bonzini         memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
49149ab747fSPaolo Bonzini         hs->kbd.keys = 0;
49251dbea77SAlexander Graf         hs->kbd.modifiers = 0;
49349ab747fSPaolo Bonzini         break;
49449ab747fSPaolo Bonzini     case HID_MOUSE:
49549ab747fSPaolo Bonzini     case HID_TABLET:
49649ab747fSPaolo Bonzini         memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
49749ab747fSPaolo Bonzini         break;
49849ab747fSPaolo Bonzini     }
49949ab747fSPaolo Bonzini     hs->head = 0;
50049ab747fSPaolo Bonzini     hs->n = 0;
50149ab747fSPaolo Bonzini     hs->protocol = 1;
50249ab747fSPaolo Bonzini     hs->idle = 0;
50349ab747fSPaolo Bonzini     hs->idle_pending = false;
50449ab747fSPaolo Bonzini     hid_del_idle_timer(hs);
50549ab747fSPaolo Bonzini }
50649ab747fSPaolo Bonzini 
hid_free(HIDState * hs)50749ab747fSPaolo Bonzini void hid_free(HIDState *hs)
50849ab747fSPaolo Bonzini {
5091ff5eeddSGerd Hoffmann     qemu_input_handler_unregister(hs->s);
51049ab747fSPaolo Bonzini     hid_del_idle_timer(hs);
51149ab747fSPaolo Bonzini }
51249ab747fSPaolo Bonzini 
513b1be65f6SPhilippe Mathieu-Daudé static const QemuInputHandler hid_keyboard_handler = {
5141ff5eeddSGerd Hoffmann     .name  = "QEMU HID Keyboard",
5151ff5eeddSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_KEY,
5161ff5eeddSGerd Hoffmann     .event = hid_keyboard_event,
5171ff5eeddSGerd Hoffmann };
5181ff5eeddSGerd Hoffmann 
519b1be65f6SPhilippe Mathieu-Daudé static const QemuInputHandler hid_mouse_handler = {
5208b84286fSGerd Hoffmann     .name  = "QEMU HID Mouse",
5218b84286fSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
5228b84286fSGerd Hoffmann     .event = hid_pointer_event,
5238b84286fSGerd Hoffmann     .sync  = hid_pointer_sync,
5248b84286fSGerd Hoffmann };
5258b84286fSGerd Hoffmann 
526b1be65f6SPhilippe Mathieu-Daudé static const QemuInputHandler hid_tablet_handler = {
5278b84286fSGerd Hoffmann     .name  = "QEMU HID Tablet",
5288b84286fSGerd Hoffmann     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
5298b84286fSGerd Hoffmann     .event = hid_pointer_event,
5308b84286fSGerd Hoffmann     .sync  = hid_pointer_sync,
5318b84286fSGerd Hoffmann };
5328b84286fSGerd Hoffmann 
hid_init(HIDState * hs,int kind,HIDEventFunc event)53349ab747fSPaolo Bonzini void hid_init(HIDState *hs, int kind, HIDEventFunc event)
53449ab747fSPaolo Bonzini {
53549ab747fSPaolo Bonzini     hs->kind = kind;
53649ab747fSPaolo Bonzini     hs->event = event;
53749ab747fSPaolo Bonzini 
53849ab747fSPaolo Bonzini     if (hs->kind == HID_KEYBOARD) {
5391ff5eeddSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
5401ff5eeddSGerd Hoffmann                                             &hid_keyboard_handler);
5411ff5eeddSGerd Hoffmann         qemu_input_handler_activate(hs->s);
54249ab747fSPaolo Bonzini     } else if (hs->kind == HID_MOUSE) {
5438b84286fSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
5448b84286fSGerd Hoffmann                                             &hid_mouse_handler);
54549ab747fSPaolo Bonzini     } else if (hs->kind == HID_TABLET) {
5468b84286fSGerd Hoffmann         hs->s = qemu_input_handler_register((DeviceState *)hs,
5478b84286fSGerd Hoffmann                                             &hid_tablet_handler);
54849ab747fSPaolo Bonzini     }
54949ab747fSPaolo Bonzini }
55049ab747fSPaolo Bonzini 
hid_post_load(void * opaque,int version_id)55149ab747fSPaolo Bonzini static int hid_post_load(void *opaque, int version_id)
55249ab747fSPaolo Bonzini {
55349ab747fSPaolo Bonzini     HIDState *s = opaque;
55449ab747fSPaolo Bonzini 
55549ab747fSPaolo Bonzini     hid_set_next_idle(s);
556ba4d2606SGerd Hoffmann 
557ba4d2606SGerd Hoffmann     if (s->n == QUEUE_LENGTH && (s->kind == HID_TABLET ||
558ba4d2606SGerd Hoffmann                                  s->kind == HID_MOUSE)) {
559ba4d2606SGerd Hoffmann         /*
560ba4d2606SGerd Hoffmann          * Handle ptr device migration from old qemu with full queue.
561ba4d2606SGerd Hoffmann          *
562ba4d2606SGerd Hoffmann          * Throw away everything but the last event, so we propagate
563ba4d2606SGerd Hoffmann          * at least the current button state to the guest.  Also keep
564ba4d2606SGerd Hoffmann          * current position for the tablet, signal "no motion" for the
565ba4d2606SGerd Hoffmann          * mouse.
566ba4d2606SGerd Hoffmann          */
567ba4d2606SGerd Hoffmann         HIDPointerEvent evt;
568ba4d2606SGerd Hoffmann         evt = s->ptr.queue[(s->head+s->n) & QUEUE_MASK];
569ba4d2606SGerd Hoffmann         if (s->kind == HID_MOUSE) {
570ba4d2606SGerd Hoffmann             evt.xdx = 0;
571ba4d2606SGerd Hoffmann             evt.ydy = 0;
572ba4d2606SGerd Hoffmann         }
573ba4d2606SGerd Hoffmann         s->ptr.queue[0] = evt;
574ba4d2606SGerd Hoffmann         s->head = 0;
575ba4d2606SGerd Hoffmann         s->n = 1;
576ba4d2606SGerd Hoffmann     }
57749ab747fSPaolo Bonzini     return 0;
57849ab747fSPaolo Bonzini }
57949ab747fSPaolo Bonzini 
58049ab747fSPaolo Bonzini static const VMStateDescription vmstate_hid_ptr_queue = {
58149ab747fSPaolo Bonzini     .name = "HIDPointerEventQueue",
58249ab747fSPaolo Bonzini     .version_id = 1,
58349ab747fSPaolo Bonzini     .minimum_version_id = 1,
584*af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
58549ab747fSPaolo Bonzini         VMSTATE_INT32(xdx, HIDPointerEvent),
58649ab747fSPaolo Bonzini         VMSTATE_INT32(ydy, HIDPointerEvent),
58749ab747fSPaolo Bonzini         VMSTATE_INT32(dz, HIDPointerEvent),
58849ab747fSPaolo Bonzini         VMSTATE_INT32(buttons_state, HIDPointerEvent),
58949ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
59049ab747fSPaolo Bonzini     }
59149ab747fSPaolo Bonzini };
59249ab747fSPaolo Bonzini 
59349ab747fSPaolo Bonzini const VMStateDescription vmstate_hid_ptr_device = {
59449ab747fSPaolo Bonzini     .name = "HIDPointerDevice",
59549ab747fSPaolo Bonzini     .version_id = 1,
59649ab747fSPaolo Bonzini     .minimum_version_id = 1,
59749ab747fSPaolo Bonzini     .post_load = hid_post_load,
598*af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
59949ab747fSPaolo Bonzini         VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
60049ab747fSPaolo Bonzini                              vmstate_hid_ptr_queue, HIDPointerEvent),
60149ab747fSPaolo Bonzini         VMSTATE_UINT32(head, HIDState),
60249ab747fSPaolo Bonzini         VMSTATE_UINT32(n, HIDState),
60349ab747fSPaolo Bonzini         VMSTATE_INT32(protocol, HIDState),
60449ab747fSPaolo Bonzini         VMSTATE_UINT8(idle, HIDState),
60549ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST(),
60649ab747fSPaolo Bonzini     }
60749ab747fSPaolo Bonzini };
60849ab747fSPaolo Bonzini 
60949ab747fSPaolo Bonzini const VMStateDescription vmstate_hid_keyboard_device = {
61049ab747fSPaolo Bonzini     .name = "HIDKeyboardDevice",
61149ab747fSPaolo Bonzini     .version_id = 1,
61249ab747fSPaolo Bonzini     .minimum_version_id = 1,
61349ab747fSPaolo Bonzini     .post_load = hid_post_load,
614*af0f07dfSRichard Henderson     .fields = (const VMStateField[]) {
61549ab747fSPaolo Bonzini         VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
61649ab747fSPaolo Bonzini         VMSTATE_UINT32(head, HIDState),
61749ab747fSPaolo Bonzini         VMSTATE_UINT32(n, HIDState),
61849ab747fSPaolo Bonzini         VMSTATE_UINT16(kbd.modifiers, HIDState),
61949ab747fSPaolo Bonzini         VMSTATE_UINT8(kbd.leds, HIDState),
62049ab747fSPaolo Bonzini         VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
62149ab747fSPaolo Bonzini         VMSTATE_INT32(kbd.keys, HIDState),
62249ab747fSPaolo Bonzini         VMSTATE_INT32(protocol, HIDState),
62349ab747fSPaolo Bonzini         VMSTATE_UINT8(idle, HIDState),
62449ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST(),
62549ab747fSPaolo Bonzini     }
62649ab747fSPaolo Bonzini };
627