xref: /qemu/hw/input/hid.c (revision 1ff5eedd1d0facf94b2f272058b83856b361b079)
1dcfda673SGerd Hoffmann /*
2dcfda673SGerd Hoffmann  * QEMU HID devices
3dcfda673SGerd Hoffmann  *
4dcfda673SGerd Hoffmann  * Copyright (c) 2005 Fabrice Bellard
5dcfda673SGerd Hoffmann  * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
6dcfda673SGerd Hoffmann  *
7dcfda673SGerd Hoffmann  * Permission is hereby granted, free of charge, to any person obtaining a copy
8dcfda673SGerd Hoffmann  * of this software and associated documentation files (the "Software"), to deal
9dcfda673SGerd Hoffmann  * in the Software without restriction, including without limitation the rights
10dcfda673SGerd Hoffmann  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11dcfda673SGerd Hoffmann  * copies of the Software, and to permit persons to whom the Software is
12dcfda673SGerd Hoffmann  * furnished to do so, subject to the following conditions:
13dcfda673SGerd Hoffmann  *
14dcfda673SGerd Hoffmann  * The above copyright notice and this permission notice shall be included in
15dcfda673SGerd Hoffmann  * all copies or substantial portions of the Software.
16dcfda673SGerd Hoffmann  *
17dcfda673SGerd Hoffmann  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18dcfda673SGerd Hoffmann  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19dcfda673SGerd Hoffmann  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20dcfda673SGerd Hoffmann  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21dcfda673SGerd Hoffmann  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22dcfda673SGerd Hoffmann  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23dcfda673SGerd Hoffmann  * THE SOFTWARE.
24dcfda673SGerd Hoffmann  */
2583c9f4caSPaolo Bonzini #include "hw/hw.h"
2628ecbaeeSPaolo Bonzini #include "ui/console.h"
271de7afc9SPaolo Bonzini #include "qemu/timer.h"
280d09e41aSPaolo Bonzini #include "hw/input/hid.h"
29dcfda673SGerd Hoffmann 
30dcfda673SGerd Hoffmann #define HID_USAGE_ERROR_ROLLOVER        0x01
31dcfda673SGerd Hoffmann #define HID_USAGE_POSTFAIL              0x02
32dcfda673SGerd Hoffmann #define HID_USAGE_ERROR_UNDEFINED       0x03
33dcfda673SGerd Hoffmann 
34dcfda673SGerd Hoffmann /* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
35dcfda673SGerd Hoffmann  * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
36dcfda673SGerd Hoffmann static const uint8_t hid_usage_keys[0x100] = {
37dcfda673SGerd Hoffmann     0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
38dcfda673SGerd Hoffmann     0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
39dcfda673SGerd Hoffmann     0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
40dcfda673SGerd Hoffmann     0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
41dcfda673SGerd Hoffmann     0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
42dcfda673SGerd Hoffmann     0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
43dcfda673SGerd Hoffmann     0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
44dcfda673SGerd Hoffmann     0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
45dcfda673SGerd Hoffmann     0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
46dcfda673SGerd Hoffmann     0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
47dcfda673SGerd Hoffmann     0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
48dcfda673SGerd Hoffmann     0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
49dcfda673SGerd Hoffmann     0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
50dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
51dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
53dcfda673SGerd Hoffmann 
54dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
58dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
61dcfda673SGerd Hoffmann     0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
62dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
63dcfda673SGerd Hoffmann     0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
64dcfda673SGerd Hoffmann     0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
65dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
66dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69dcfda673SGerd Hoffmann     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70dcfda673SGerd Hoffmann };
71dcfda673SGerd Hoffmann 
72dcfda673SGerd Hoffmann bool hid_has_events(HIDState *hs)
73dcfda673SGerd Hoffmann {
74027c03f7SHans de Goede     return hs->n > 0 || hs->idle_pending;
75dcfda673SGerd Hoffmann }
76dcfda673SGerd Hoffmann 
77027c03f7SHans de Goede static void hid_idle_timer(void *opaque)
78b069d348SGerd Hoffmann {
79027c03f7SHans de Goede     HIDState *hs = opaque;
80027c03f7SHans de Goede 
81027c03f7SHans de Goede     hs->idle_pending = true;
82027c03f7SHans de Goede     hs->event(hs);
83027c03f7SHans de Goede }
84027c03f7SHans de Goede 
85027c03f7SHans de Goede static void hid_del_idle_timer(HIDState *hs)
86027c03f7SHans de Goede {
87027c03f7SHans de Goede     if (hs->idle_timer) {
88bc72ad67SAlex Bligh         timer_del(hs->idle_timer);
89bc72ad67SAlex Bligh         timer_free(hs->idle_timer);
90027c03f7SHans de Goede         hs->idle_timer = NULL;
91027c03f7SHans de Goede     }
92027c03f7SHans de Goede }
93027c03f7SHans de Goede 
94027c03f7SHans de Goede void hid_set_next_idle(HIDState *hs)
95027c03f7SHans de Goede {
96027c03f7SHans de Goede     if (hs->idle) {
97bc72ad67SAlex Bligh         uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
98027c03f7SHans de Goede                                get_ticks_per_sec() * hs->idle * 4 / 1000;
99027c03f7SHans de Goede         if (!hs->idle_timer) {
100bc72ad67SAlex Bligh             hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
101027c03f7SHans de Goede         }
102bc72ad67SAlex Bligh         timer_mod_ns(hs->idle_timer, expire_time);
103027c03f7SHans de Goede     } else {
104027c03f7SHans de Goede         hid_del_idle_timer(hs);
105027c03f7SHans de Goede     }
106b069d348SGerd Hoffmann }
107b069d348SGerd Hoffmann 
108dcfda673SGerd Hoffmann static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
109dcfda673SGerd Hoffmann {
110dcfda673SGerd Hoffmann     e->xdx = e->ydy = e->dz = 0;
111dcfda673SGerd Hoffmann     e->buttons_state = buttons;
112dcfda673SGerd Hoffmann }
113dcfda673SGerd Hoffmann 
114dcfda673SGerd Hoffmann static void hid_pointer_event_combine(HIDPointerEvent *e, int xyrel,
115dcfda673SGerd Hoffmann                                       int x1, int y1, int z1) {
116dcfda673SGerd Hoffmann     if (xyrel) {
117dcfda673SGerd Hoffmann         e->xdx += x1;
118dcfda673SGerd Hoffmann         e->ydy += y1;
119dcfda673SGerd Hoffmann     } else {
120dcfda673SGerd Hoffmann         e->xdx = x1;
121dcfda673SGerd Hoffmann         e->ydy = y1;
122dcfda673SGerd Hoffmann         /* Windows drivers do not like the 0/0 position and ignore such
123dcfda673SGerd Hoffmann          * events. */
124dcfda673SGerd Hoffmann         if (!(x1 | y1)) {
12518f88f11SBlue Swirl             e->xdx = 1;
126dcfda673SGerd Hoffmann         }
127dcfda673SGerd Hoffmann     }
128dcfda673SGerd Hoffmann     e->dz += z1;
129dcfda673SGerd Hoffmann }
130dcfda673SGerd Hoffmann 
131dcfda673SGerd Hoffmann static void hid_pointer_event(void *opaque,
132dcfda673SGerd Hoffmann                               int x1, int y1, int z1, int buttons_state)
133dcfda673SGerd Hoffmann {
134dcfda673SGerd Hoffmann     HIDState *hs = opaque;
135dcfda673SGerd Hoffmann     unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK;
136dcfda673SGerd Hoffmann     unsigned previous_slot = (use_slot - 1) & QUEUE_MASK;
137dcfda673SGerd Hoffmann 
138dcfda673SGerd Hoffmann     /* We combine events where feasible to keep the queue small.  We shouldn't
139dcfda673SGerd Hoffmann      * combine anything with the first event of a particular button state, as
140dcfda673SGerd Hoffmann      * that would change the location of the button state change.  When the
141dcfda673SGerd Hoffmann      * queue is empty, a second event is needed because we don't know if
142dcfda673SGerd Hoffmann      * the first event changed the button state.  */
143dcfda673SGerd Hoffmann     if (hs->n == QUEUE_LENGTH) {
144dcfda673SGerd Hoffmann         /* Queue full.  Discard old button state, combine motion normally.  */
145dcfda673SGerd Hoffmann         hs->ptr.queue[use_slot].buttons_state = buttons_state;
146dcfda673SGerd Hoffmann     } else if (hs->n < 2 ||
147dcfda673SGerd Hoffmann                hs->ptr.queue[use_slot].buttons_state != buttons_state ||
148dcfda673SGerd Hoffmann                hs->ptr.queue[previous_slot].buttons_state !=
149dcfda673SGerd Hoffmann                hs->ptr.queue[use_slot].buttons_state) {
150dcfda673SGerd Hoffmann         /* Cannot or should not combine, so add an empty item to the queue.  */
151dcfda673SGerd Hoffmann         QUEUE_INCR(use_slot);
152dcfda673SGerd Hoffmann         hs->n++;
153dcfda673SGerd Hoffmann         hid_pointer_event_clear(&hs->ptr.queue[use_slot], buttons_state);
154dcfda673SGerd Hoffmann     }
155dcfda673SGerd Hoffmann     hid_pointer_event_combine(&hs->ptr.queue[use_slot],
156dcfda673SGerd Hoffmann                               hs->kind == HID_MOUSE,
157dcfda673SGerd Hoffmann                               x1, y1, z1);
158dcfda673SGerd Hoffmann     hs->event(hs);
159dcfda673SGerd Hoffmann }
160dcfda673SGerd Hoffmann 
161*1ff5eeddSGerd Hoffmann static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
162*1ff5eeddSGerd Hoffmann                                InputEvent *evt)
163dcfda673SGerd Hoffmann {
164*1ff5eeddSGerd Hoffmann     HIDState *hs = (HIDState *)dev;
165*1ff5eeddSGerd Hoffmann     int scancodes[3], i, count;
166dcfda673SGerd Hoffmann     int slot;
167dcfda673SGerd Hoffmann 
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) {
172dcfda673SGerd Hoffmann         fprintf(stderr, "usb-kbd: warning: key event queue full\n");
173dcfda673SGerd Hoffmann         return;
174dcfda673SGerd Hoffmann     }
175*1ff5eeddSGerd Hoffmann     for (i = 0; i < count; i++) {
176dcfda673SGerd Hoffmann         slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
177*1ff5eeddSGerd Hoffmann         hs->kbd.keycodes[slot] = scancodes[i];
178*1ff5eeddSGerd Hoffmann     }
179dcfda673SGerd Hoffmann     hs->event(hs);
180dcfda673SGerd Hoffmann }
181dcfda673SGerd Hoffmann 
182dcfda673SGerd Hoffmann static void hid_keyboard_process_keycode(HIDState *hs)
183dcfda673SGerd Hoffmann {
184dcfda673SGerd Hoffmann     uint8_t hid_code, key;
185dcfda673SGerd Hoffmann     int i, keycode, slot;
186dcfda673SGerd Hoffmann 
187dcfda673SGerd Hoffmann     if (hs->n == 0) {
188dcfda673SGerd Hoffmann         return;
189dcfda673SGerd Hoffmann     }
190dcfda673SGerd Hoffmann     slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
191dcfda673SGerd Hoffmann     keycode = hs->kbd.keycodes[slot];
192dcfda673SGerd Hoffmann 
193dcfda673SGerd Hoffmann     key = keycode & 0x7f;
194dcfda673SGerd Hoffmann     hid_code = hid_usage_keys[key | ((hs->kbd.modifiers >> 1) & (1 << 7))];
195dcfda673SGerd Hoffmann     hs->kbd.modifiers &= ~(1 << 8);
196dcfda673SGerd Hoffmann 
197dcfda673SGerd Hoffmann     switch (hid_code) {
198dcfda673SGerd Hoffmann     case 0x00:
199dcfda673SGerd Hoffmann         return;
200dcfda673SGerd Hoffmann 
201dcfda673SGerd Hoffmann     case 0xe0:
202dcfda673SGerd Hoffmann         if (hs->kbd.modifiers & (1 << 9)) {
203dcfda673SGerd Hoffmann             hs->kbd.modifiers ^= 3 << 8;
204dcfda673SGerd Hoffmann             return;
205dcfda673SGerd Hoffmann         }
206dcfda673SGerd Hoffmann     case 0xe1 ... 0xe7:
207dcfda673SGerd Hoffmann         if (keycode & (1 << 7)) {
208dcfda673SGerd Hoffmann             hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
209dcfda673SGerd Hoffmann             return;
210dcfda673SGerd Hoffmann         }
211dcfda673SGerd Hoffmann     case 0xe8 ... 0xef:
212dcfda673SGerd Hoffmann         hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
213dcfda673SGerd Hoffmann         return;
214dcfda673SGerd Hoffmann     }
215dcfda673SGerd Hoffmann 
216dcfda673SGerd Hoffmann     if (keycode & (1 << 7)) {
217dcfda673SGerd Hoffmann         for (i = hs->kbd.keys - 1; i >= 0; i--) {
218dcfda673SGerd Hoffmann             if (hs->kbd.key[i] == hid_code) {
219dcfda673SGerd Hoffmann                 hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
220dcfda673SGerd Hoffmann                 hs->kbd.key[hs->kbd.keys] = 0x00;
221dcfda673SGerd Hoffmann                 break;
222dcfda673SGerd Hoffmann             }
223dcfda673SGerd Hoffmann         }
224dcfda673SGerd Hoffmann         if (i < 0) {
225dcfda673SGerd Hoffmann             return;
226dcfda673SGerd Hoffmann         }
227dcfda673SGerd Hoffmann     } else {
228dcfda673SGerd Hoffmann         for (i = hs->kbd.keys - 1; i >= 0; i--) {
229dcfda673SGerd Hoffmann             if (hs->kbd.key[i] == hid_code) {
230dcfda673SGerd Hoffmann                 break;
231dcfda673SGerd Hoffmann             }
232dcfda673SGerd Hoffmann         }
233dcfda673SGerd Hoffmann         if (i < 0) {
234dcfda673SGerd Hoffmann             if (hs->kbd.keys < sizeof(hs->kbd.key)) {
235dcfda673SGerd Hoffmann                 hs->kbd.key[hs->kbd.keys++] = hid_code;
236dcfda673SGerd Hoffmann             }
237dcfda673SGerd Hoffmann         } else {
238dcfda673SGerd Hoffmann             return;
239dcfda673SGerd Hoffmann         }
240dcfda673SGerd Hoffmann     }
241dcfda673SGerd Hoffmann }
242dcfda673SGerd Hoffmann 
243dcfda673SGerd Hoffmann static inline int int_clamp(int val, int vmin, int vmax)
244dcfda673SGerd Hoffmann {
245dcfda673SGerd Hoffmann     if (val < vmin) {
246dcfda673SGerd Hoffmann         return vmin;
247dcfda673SGerd Hoffmann     } else if (val > vmax) {
248dcfda673SGerd Hoffmann         return vmax;
249dcfda673SGerd Hoffmann     } else {
250dcfda673SGerd Hoffmann         return val;
251dcfda673SGerd Hoffmann     }
252dcfda673SGerd Hoffmann }
253dcfda673SGerd Hoffmann 
25421635e12SGerd Hoffmann void hid_pointer_activate(HIDState *hs)
25521635e12SGerd Hoffmann {
25621635e12SGerd Hoffmann     if (!hs->ptr.mouse_grabbed) {
25721635e12SGerd Hoffmann         qemu_activate_mouse_event_handler(hs->ptr.eh_entry);
25821635e12SGerd Hoffmann         hs->ptr.mouse_grabbed = 1;
25921635e12SGerd Hoffmann     }
26021635e12SGerd Hoffmann }
26121635e12SGerd Hoffmann 
262dcfda673SGerd Hoffmann int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
263dcfda673SGerd Hoffmann {
264dcfda673SGerd Hoffmann     int dx, dy, dz, b, l;
265dcfda673SGerd Hoffmann     int index;
266dcfda673SGerd Hoffmann     HIDPointerEvent *e;
267dcfda673SGerd Hoffmann 
268027c03f7SHans de Goede     hs->idle_pending = false;
269027c03f7SHans de Goede 
27021635e12SGerd Hoffmann     hid_pointer_activate(hs);
271dcfda673SGerd Hoffmann 
272dcfda673SGerd Hoffmann     /* When the buffer is empty, return the last event.  Relative
273dcfda673SGerd Hoffmann        movements will all be zero.  */
274dcfda673SGerd Hoffmann     index = (hs->n ? hs->head : hs->head - 1);
275dcfda673SGerd Hoffmann     e = &hs->ptr.queue[index & QUEUE_MASK];
276dcfda673SGerd Hoffmann 
277dcfda673SGerd Hoffmann     if (hs->kind == HID_MOUSE) {
278dcfda673SGerd Hoffmann         dx = int_clamp(e->xdx, -127, 127);
279dcfda673SGerd Hoffmann         dy = int_clamp(e->ydy, -127, 127);
280dcfda673SGerd Hoffmann         e->xdx -= dx;
281dcfda673SGerd Hoffmann         e->ydy -= dy;
282dcfda673SGerd Hoffmann     } else {
283dcfda673SGerd Hoffmann         dx = e->xdx;
284dcfda673SGerd Hoffmann         dy = e->ydy;
285dcfda673SGerd Hoffmann     }
286dcfda673SGerd Hoffmann     dz = int_clamp(e->dz, -127, 127);
287dcfda673SGerd Hoffmann     e->dz -= dz;
288dcfda673SGerd Hoffmann 
289dcfda673SGerd Hoffmann     b = 0;
290dcfda673SGerd Hoffmann     if (e->buttons_state & MOUSE_EVENT_LBUTTON) {
291dcfda673SGerd Hoffmann         b |= 0x01;
292dcfda673SGerd Hoffmann     }
293dcfda673SGerd Hoffmann     if (e->buttons_state & MOUSE_EVENT_RBUTTON) {
294dcfda673SGerd Hoffmann         b |= 0x02;
295dcfda673SGerd Hoffmann     }
296dcfda673SGerd Hoffmann     if (e->buttons_state & MOUSE_EVENT_MBUTTON) {
297dcfda673SGerd Hoffmann         b |= 0x04;
298dcfda673SGerd Hoffmann     }
299dcfda673SGerd Hoffmann 
300dcfda673SGerd Hoffmann     if (hs->n &&
301dcfda673SGerd Hoffmann         !e->dz &&
302dcfda673SGerd Hoffmann         (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
303dcfda673SGerd Hoffmann         /* that deals with this event */
304dcfda673SGerd Hoffmann         QUEUE_INCR(hs->head);
305dcfda673SGerd Hoffmann         hs->n--;
306dcfda673SGerd Hoffmann     }
307dcfda673SGerd Hoffmann 
308dcfda673SGerd Hoffmann     /* Appears we have to invert the wheel direction */
309dcfda673SGerd Hoffmann     dz = 0 - dz;
310dcfda673SGerd Hoffmann     l = 0;
311dcfda673SGerd Hoffmann     switch (hs->kind) {
312dcfda673SGerd Hoffmann     case HID_MOUSE:
313dcfda673SGerd Hoffmann         if (len > l) {
314dcfda673SGerd Hoffmann             buf[l++] = b;
315dcfda673SGerd Hoffmann         }
316dcfda673SGerd Hoffmann         if (len > l) {
317dcfda673SGerd Hoffmann             buf[l++] = dx;
318dcfda673SGerd Hoffmann         }
319dcfda673SGerd Hoffmann         if (len > l) {
320dcfda673SGerd Hoffmann             buf[l++] = dy;
321dcfda673SGerd Hoffmann         }
322dcfda673SGerd Hoffmann         if (len > l) {
323dcfda673SGerd Hoffmann             buf[l++] = dz;
324dcfda673SGerd Hoffmann         }
325dcfda673SGerd Hoffmann         break;
326dcfda673SGerd Hoffmann 
327dcfda673SGerd Hoffmann     case HID_TABLET:
328dcfda673SGerd Hoffmann         if (len > l) {
329dcfda673SGerd Hoffmann             buf[l++] = b;
330dcfda673SGerd Hoffmann         }
331dcfda673SGerd Hoffmann         if (len > l) {
332dcfda673SGerd Hoffmann             buf[l++] = dx & 0xff;
333dcfda673SGerd Hoffmann         }
334dcfda673SGerd Hoffmann         if (len > l) {
335dcfda673SGerd Hoffmann             buf[l++] = dx >> 8;
336dcfda673SGerd Hoffmann         }
337dcfda673SGerd Hoffmann         if (len > l) {
338dcfda673SGerd Hoffmann             buf[l++] = dy & 0xff;
339dcfda673SGerd Hoffmann         }
340dcfda673SGerd Hoffmann         if (len > l) {
341dcfda673SGerd Hoffmann             buf[l++] = dy >> 8;
342dcfda673SGerd Hoffmann         }
343dcfda673SGerd Hoffmann         if (len > l) {
344dcfda673SGerd Hoffmann             buf[l++] = dz;
345dcfda673SGerd Hoffmann         }
346dcfda673SGerd Hoffmann         break;
347dcfda673SGerd Hoffmann 
348dcfda673SGerd Hoffmann     default:
349dcfda673SGerd Hoffmann         abort();
350dcfda673SGerd Hoffmann     }
351dcfda673SGerd Hoffmann 
352dcfda673SGerd Hoffmann     return l;
353dcfda673SGerd Hoffmann }
354dcfda673SGerd Hoffmann 
355dcfda673SGerd Hoffmann int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
356dcfda673SGerd Hoffmann {
357027c03f7SHans de Goede     hs->idle_pending = false;
358027c03f7SHans de Goede 
359dcfda673SGerd Hoffmann     if (len < 2) {
360dcfda673SGerd Hoffmann         return 0;
361dcfda673SGerd Hoffmann     }
362dcfda673SGerd Hoffmann 
363dcfda673SGerd Hoffmann     hid_keyboard_process_keycode(hs);
364dcfda673SGerd Hoffmann 
365dcfda673SGerd Hoffmann     buf[0] = hs->kbd.modifiers & 0xff;
366dcfda673SGerd Hoffmann     buf[1] = 0;
367dcfda673SGerd Hoffmann     if (hs->kbd.keys > 6) {
368dcfda673SGerd Hoffmann         memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
369dcfda673SGerd Hoffmann     } else {
370dcfda673SGerd Hoffmann         memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
371dcfda673SGerd Hoffmann     }
372dcfda673SGerd Hoffmann 
373dcfda673SGerd Hoffmann     return MIN(8, len);
374dcfda673SGerd Hoffmann }
375dcfda673SGerd Hoffmann 
376dcfda673SGerd Hoffmann int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
377dcfda673SGerd Hoffmann {
378dcfda673SGerd Hoffmann     if (len > 0) {
379dcfda673SGerd Hoffmann         int ledstate = 0;
380dcfda673SGerd Hoffmann         /* 0x01: Num Lock LED
381dcfda673SGerd Hoffmann          * 0x02: Caps Lock LED
382dcfda673SGerd Hoffmann          * 0x04: Scroll Lock LED
383dcfda673SGerd Hoffmann          * 0x08: Compose LED
384dcfda673SGerd Hoffmann          * 0x10: Kana LED */
385dcfda673SGerd Hoffmann         hs->kbd.leds = buf[0];
386dcfda673SGerd Hoffmann         if (hs->kbd.leds & 0x04) {
387dcfda673SGerd Hoffmann             ledstate |= QEMU_SCROLL_LOCK_LED;
388dcfda673SGerd Hoffmann         }
389dcfda673SGerd Hoffmann         if (hs->kbd.leds & 0x01) {
390dcfda673SGerd Hoffmann             ledstate |= QEMU_NUM_LOCK_LED;
391dcfda673SGerd Hoffmann         }
392dcfda673SGerd Hoffmann         if (hs->kbd.leds & 0x02) {
393dcfda673SGerd Hoffmann             ledstate |= QEMU_CAPS_LOCK_LED;
394dcfda673SGerd Hoffmann         }
395dcfda673SGerd Hoffmann         kbd_put_ledstate(ledstate);
396dcfda673SGerd Hoffmann     }
397dcfda673SGerd Hoffmann     return 0;
398dcfda673SGerd Hoffmann }
399dcfda673SGerd Hoffmann 
400dcfda673SGerd Hoffmann void hid_reset(HIDState *hs)
401dcfda673SGerd Hoffmann {
402dcfda673SGerd Hoffmann     switch (hs->kind) {
403dcfda673SGerd Hoffmann     case HID_KEYBOARD:
404dcfda673SGerd Hoffmann         memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
405dcfda673SGerd Hoffmann         memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
406dcfda673SGerd Hoffmann         hs->kbd.keys = 0;
407dcfda673SGerd Hoffmann         break;
408dcfda673SGerd Hoffmann     case HID_MOUSE:
409dcfda673SGerd Hoffmann     case HID_TABLET:
410dcfda673SGerd Hoffmann         memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
411dcfda673SGerd Hoffmann         break;
412dcfda673SGerd Hoffmann     }
413dcfda673SGerd Hoffmann     hs->head = 0;
414dcfda673SGerd Hoffmann     hs->n = 0;
415b069d348SGerd Hoffmann     hs->protocol = 1;
416b069d348SGerd Hoffmann     hs->idle = 0;
417027c03f7SHans de Goede     hs->idle_pending = false;
418027c03f7SHans de Goede     hid_del_idle_timer(hs);
419dcfda673SGerd Hoffmann }
420dcfda673SGerd Hoffmann 
421dcfda673SGerd Hoffmann void hid_free(HIDState *hs)
422dcfda673SGerd Hoffmann {
423dcfda673SGerd Hoffmann     switch (hs->kind) {
424dcfda673SGerd Hoffmann     case HID_KEYBOARD:
425*1ff5eeddSGerd Hoffmann         qemu_input_handler_unregister(hs->s);
426dcfda673SGerd Hoffmann         break;
427dcfda673SGerd Hoffmann     case HID_MOUSE:
428dcfda673SGerd Hoffmann     case HID_TABLET:
429dcfda673SGerd Hoffmann         qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
430dcfda673SGerd Hoffmann         break;
431dcfda673SGerd Hoffmann     }
432027c03f7SHans de Goede     hid_del_idle_timer(hs);
433dcfda673SGerd Hoffmann }
434dcfda673SGerd Hoffmann 
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 
441dcfda673SGerd Hoffmann void hid_init(HIDState *hs, int kind, HIDEventFunc event)
442dcfda673SGerd Hoffmann {
443dcfda673SGerd Hoffmann     hs->kind = kind;
444dcfda673SGerd Hoffmann     hs->event = event;
445dcfda673SGerd Hoffmann 
446bb0db527SMichael Walle     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);
450bb0db527SMichael Walle     } else if (hs->kind == HID_MOUSE) {
451dcfda673SGerd Hoffmann         hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
452dcfda673SGerd Hoffmann                                                         0, "QEMU HID Mouse");
453dcfda673SGerd Hoffmann     } else if (hs->kind == HID_TABLET) {
454dcfda673SGerd Hoffmann         hs->ptr.eh_entry = qemu_add_mouse_event_handler(hid_pointer_event, hs,
455dcfda673SGerd Hoffmann                                                         1, "QEMU HID Tablet");
456dcfda673SGerd Hoffmann     }
457dcfda673SGerd Hoffmann }
458ccd4ed06SMichael Walle 
459ccd4ed06SMichael Walle static int hid_post_load(void *opaque, int version_id)
460ccd4ed06SMichael Walle {
461ccd4ed06SMichael Walle     HIDState *s = opaque;
462ccd4ed06SMichael Walle 
463027c03f7SHans de Goede     hid_set_next_idle(s);
464ccd4ed06SMichael Walle     return 0;
465ccd4ed06SMichael Walle }
466ccd4ed06SMichael Walle 
467ccd4ed06SMichael Walle static const VMStateDescription vmstate_hid_ptr_queue = {
468ccd4ed06SMichael Walle     .name = "HIDPointerEventQueue",
469ccd4ed06SMichael Walle     .version_id = 1,
470ccd4ed06SMichael Walle     .minimum_version_id = 1,
471ccd4ed06SMichael Walle     .fields = (VMStateField[]) {
472ccd4ed06SMichael Walle         VMSTATE_INT32(xdx, HIDPointerEvent),
473ccd4ed06SMichael Walle         VMSTATE_INT32(ydy, HIDPointerEvent),
474ccd4ed06SMichael Walle         VMSTATE_INT32(dz, HIDPointerEvent),
475ccd4ed06SMichael Walle         VMSTATE_INT32(buttons_state, HIDPointerEvent),
476ccd4ed06SMichael Walle         VMSTATE_END_OF_LIST()
477ccd4ed06SMichael Walle     }
478ccd4ed06SMichael Walle };
479ccd4ed06SMichael Walle 
480ccd4ed06SMichael Walle const VMStateDescription vmstate_hid_ptr_device = {
481ccd4ed06SMichael Walle     .name = "HIDPointerDevice",
482ccd4ed06SMichael Walle     .version_id = 1,
483ccd4ed06SMichael Walle     .minimum_version_id = 1,
484ccd4ed06SMichael Walle     .post_load = hid_post_load,
485ccd4ed06SMichael Walle     .fields = (VMStateField[]) {
486ccd4ed06SMichael Walle         VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
487ccd4ed06SMichael Walle                              vmstate_hid_ptr_queue, HIDPointerEvent),
488ccd4ed06SMichael Walle         VMSTATE_UINT32(head, HIDState),
489ccd4ed06SMichael Walle         VMSTATE_UINT32(n, HIDState),
490ccd4ed06SMichael Walle         VMSTATE_INT32(protocol, HIDState),
491ccd4ed06SMichael Walle         VMSTATE_UINT8(idle, HIDState),
492ccd4ed06SMichael Walle         VMSTATE_END_OF_LIST(),
493ccd4ed06SMichael Walle     }
494ccd4ed06SMichael Walle };
495ccd4ed06SMichael Walle 
496ccd4ed06SMichael Walle const VMStateDescription vmstate_hid_keyboard_device = {
497ccd4ed06SMichael Walle     .name = "HIDKeyboardDevice",
498ccd4ed06SMichael Walle     .version_id = 1,
499ccd4ed06SMichael Walle     .minimum_version_id = 1,
500ccd4ed06SMichael Walle     .post_load = hid_post_load,
501ccd4ed06SMichael Walle     .fields = (VMStateField[]) {
502ccd4ed06SMichael Walle         VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
503ccd4ed06SMichael Walle         VMSTATE_UINT32(head, HIDState),
504ccd4ed06SMichael Walle         VMSTATE_UINT32(n, HIDState),
505ccd4ed06SMichael Walle         VMSTATE_UINT16(kbd.modifiers, HIDState),
506ccd4ed06SMichael Walle         VMSTATE_UINT8(kbd.leds, HIDState),
507ccd4ed06SMichael Walle         VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
508ccd4ed06SMichael Walle         VMSTATE_INT32(kbd.keys, HIDState),
509ccd4ed06SMichael Walle         VMSTATE_INT32(protocol, HIDState),
510ccd4ed06SMichael Walle         VMSTATE_UINT8(idle, HIDState),
511ccd4ed06SMichael Walle         VMSTATE_END_OF_LIST(),
512ccd4ed06SMichael Walle     }
513ccd4ed06SMichael Walle };
514