]> git.proxmox.com Git - mirror_qemu.git/blame - hw/input/hid.c
Replaced get_tick_per_sec() by NANOSECONDS_PER_SECOND
[mirror_qemu.git] / hw / input / hid.c
CommitLineData
dcfda673
GH
1/*
2 * QEMU HID devices
3 *
4 * Copyright (c) 2005 Fabrice Bellard
5 * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com)
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
0430891c 25#include "qemu/osdep.h"
83c9f4ca 26#include "hw/hw.h"
28ecbaee 27#include "ui/console.h"
1de7afc9 28#include "qemu/timer.h"
0d09e41a 29#include "hw/input/hid.h"
dcfda673
GH
30
31#define HID_USAGE_ERROR_ROLLOVER 0x01
32#define HID_USAGE_POSTFAIL 0x02
33#define HID_USAGE_ERROR_UNDEFINED 0x03
34
35/* Indices are QEMU keycodes, values are from HID Usage Table. Indices
36 * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */
37static const uint8_t hid_usage_keys[0x100] = {
38 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
39 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
40 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
41 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
42 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
43 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
44 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
0ee4de58 45 0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
dcfda673
GH
46 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
47 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
91dbeeda 48 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44,
dcfda673
GH
49 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
50 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
51 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
54
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
62 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
64 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
65 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71};
72
73bool hid_has_events(HIDState *hs)
74{
027c03f7 75 return hs->n > 0 || hs->idle_pending;
dcfda673
GH
76}
77
027c03f7 78static void hid_idle_timer(void *opaque)
b069d348 79{
027c03f7
HG
80 HIDState *hs = opaque;
81
82 hs->idle_pending = true;
83 hs->event(hs);
84}
85
86static void hid_del_idle_timer(HIDState *hs)
87{
88 if (hs->idle_timer) {
bc72ad67
AB
89 timer_del(hs->idle_timer);
90 timer_free(hs->idle_timer);
027c03f7
HG
91 hs->idle_timer = NULL;
92 }
93}
94
95void hid_set_next_idle(HIDState *hs)
96{
97 if (hs->idle) {
bc72ad67 98 uint64_t expire_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
73bcb24d 99 NANOSECONDS_PER_SECOND * hs->idle * 4 / 1000;
027c03f7 100 if (!hs->idle_timer) {
bc72ad67 101 hs->idle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hid_idle_timer, hs);
027c03f7 102 }
bc72ad67 103 timer_mod_ns(hs->idle_timer, expire_time);
027c03f7
HG
104 } else {
105 hid_del_idle_timer(hs);
106 }
b069d348
GH
107}
108
8b84286f
GH
109static void hid_pointer_event(DeviceState *dev, QemuConsole *src,
110 InputEvent *evt)
dcfda673 111{
7fb1cf16 112 static const int bmap[INPUT_BUTTON__MAX] = {
8b84286f
GH
113 [INPUT_BUTTON_LEFT] = 0x01,
114 [INPUT_BUTTON_RIGHT] = 0x02,
115 [INPUT_BUTTON_MIDDLE] = 0x04,
116 };
117 HIDState *hs = (HIDState *)dev;
118 HIDPointerEvent *e;
b5a1b443
EB
119 InputMoveEvent *move;
120 InputBtnEvent *btn;
dcfda673 121
8b84286f
GH
122 assert(hs->n < QUEUE_LENGTH);
123 e = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
124
568c73a4 125 switch (evt->type) {
8b84286f 126 case INPUT_EVENT_KIND_REL:
32bafa8f 127 move = evt->u.rel.data;
b5a1b443
EB
128 if (move->axis == INPUT_AXIS_X) {
129 e->xdx += move->value;
130 } else if (move->axis == INPUT_AXIS_Y) {
131 e->ydy += move->value;
8b84286f
GH
132 }
133 break;
134
135 case INPUT_EVENT_KIND_ABS:
32bafa8f 136 move = evt->u.abs.data;
b5a1b443
EB
137 if (move->axis == INPUT_AXIS_X) {
138 e->xdx = move->value;
139 } else if (move->axis == INPUT_AXIS_Y) {
140 e->ydy = move->value;
8b84286f
GH
141 }
142 break;
143
144 case INPUT_EVENT_KIND_BTN:
32bafa8f 145 btn = evt->u.btn.data;
b5a1b443
EB
146 if (btn->down) {
147 e->buttons_state |= bmap[btn->button];
148 if (btn->button == INPUT_BUTTON_WHEEL_UP) {
8b84286f 149 e->dz--;
b5a1b443 150 } else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
8b84286f
GH
151 e->dz++;
152 }
153 } else {
b5a1b443 154 e->buttons_state &= ~bmap[btn->button];
dcfda673 155 }
8b84286f
GH
156 break;
157
158 default:
159 /* keep gcc happy */
160 break;
dcfda673 161 }
8b84286f 162
dcfda673
GH
163}
164
8b84286f 165static void hid_pointer_sync(DeviceState *dev)
dcfda673 166{
8b84286f
GH
167 HIDState *hs = (HIDState *)dev;
168 HIDPointerEvent *prev, *curr, *next;
169 bool event_compression = false;
170
171 if (hs->n == QUEUE_LENGTH-1) {
172 /*
5d831be2 173 * Queue full. We are losing information, but we at least
8b84286f
GH
174 * keep track of most recent button state.
175 */
176 return;
177 }
178
179 prev = &hs->ptr.queue[(hs->head + hs->n - 1) & QUEUE_MASK];
180 curr = &hs->ptr.queue[(hs->head + hs->n) & QUEUE_MASK];
181 next = &hs->ptr.queue[(hs->head + hs->n + 1) & QUEUE_MASK];
182
183 if (hs->n > 0) {
184 /*
185 * No button state change between previous and current event
186 * (and previous wasn't seen by the guest yet), so there is
187 * motion information only and we can combine the two event
188 * into one.
189 */
190 if (curr->buttons_state == prev->buttons_state) {
191 event_compression = true;
192 }
193 }
194
195 if (event_compression) {
196 /* add current motion to previous, clear current */
197 if (hs->kind == HID_MOUSE) {
198 prev->xdx += curr->xdx;
199 curr->xdx = 0;
35e83d10 200 prev->ydy += curr->ydy;
8b84286f
GH
201 curr->ydy = 0;
202 } else {
203 prev->xdx = curr->xdx;
204 prev->ydy = curr->ydy;
205 }
206 prev->dz += curr->dz;
207 curr->dz = 0;
208 } else {
209 /* prepate next (clear rel, copy abs + btns) */
210 if (hs->kind == HID_MOUSE) {
211 next->xdx = 0;
212 next->ydy = 0;
213 } else {
214 next->xdx = curr->xdx;
215 next->ydy = curr->ydy;
216 }
217 next->dz = 0;
218 next->buttons_state = curr->buttons_state;
219 /* make current guest visible, notify guest */
dcfda673 220 hs->n++;
8b84286f 221 hs->event(hs);
dcfda673 222 }
dcfda673
GH
223}
224
1ff5eedd
GH
225static void hid_keyboard_event(DeviceState *dev, QemuConsole *src,
226 InputEvent *evt)
dcfda673 227{
1ff5eedd
GH
228 HIDState *hs = (HIDState *)dev;
229 int scancodes[3], i, count;
dcfda673 230 int slot;
32bafa8f 231 InputKeyEvent *key = evt->u.key.data;
dcfda673 232
b5a1b443
EB
233 count = qemu_input_key_value_to_scancode(key->key,
234 key->down,
1ff5eedd
GH
235 scancodes);
236 if (hs->n + count > QUEUE_LENGTH) {
dcfda673
GH
237 fprintf(stderr, "usb-kbd: warning: key event queue full\n");
238 return;
239 }
1ff5eedd
GH
240 for (i = 0; i < count; i++) {
241 slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++;
242 hs->kbd.keycodes[slot] = scancodes[i];
243 }
dcfda673
GH
244 hs->event(hs);
245}
246
247static void hid_keyboard_process_keycode(HIDState *hs)
248{
562f9375 249 uint8_t hid_code, index, key;
dcfda673
GH
250 int i, keycode, slot;
251
252 if (hs->n == 0) {
253 return;
254 }
255 slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--;
256 keycode = hs->kbd.keycodes[slot];
257
258 key = keycode & 0x7f;
562f9375
PB
259 index = key | ((hs->kbd.modifiers & (1 << 8)) >> 1);
260 hid_code = hid_usage_keys[index];
dcfda673
GH
261 hs->kbd.modifiers &= ~(1 << 8);
262
263 switch (hid_code) {
264 case 0x00:
265 return;
266
267 case 0xe0:
562f9375 268 assert(key == 0x1d);
dcfda673 269 if (hs->kbd.modifiers & (1 << 9)) {
562f9375
PB
270 /* The hid_codes for the 0xe1/0x1d scancode sequence are 0xe9/0xe0.
271 * Here we're processing the second hid_code. By dropping bit 9
272 * and setting bit 8, the scancode after 0x1d will access the
273 * second half of the table.
274 */
275 hs->kbd.modifiers ^= (1 << 8) | (1 << 9);
dcfda673
GH
276 return;
277 }
562f9375 278 /* fall through to process Ctrl_L */
dcfda673 279 case 0xe1 ... 0xe7:
562f9375
PB
280 /* Ctrl_L/Ctrl_R, Shift_L/Shift_R, Alt_L/Alt_R, Win_L/Win_R.
281 * Handle releases here, or fall through to process presses.
282 */
dcfda673
GH
283 if (keycode & (1 << 7)) {
284 hs->kbd.modifiers &= ~(1 << (hid_code & 0x0f));
285 return;
286 }
562f9375
PB
287 /* fall through */
288 case 0xe8 ... 0xe9:
289 /* USB modifiers are just 1 byte long. Bits 8 and 9 of
290 * hs->kbd.modifiers implement a state machine that detects the
291 * 0xe0 and 0xe1/0x1d sequences. These bits do not follow the
292 * usual rules where bit 7 marks released keys; they are cleared
293 * elsewhere in the function as the state machine dictates.
294 */
dcfda673
GH
295 hs->kbd.modifiers |= 1 << (hid_code & 0x0f);
296 return;
562f9375
PB
297
298 case 0xea ... 0xef:
299 abort();
300
301 default:
302 break;
dcfda673
GH
303 }
304
305 if (keycode & (1 << 7)) {
306 for (i = hs->kbd.keys - 1; i >= 0; i--) {
307 if (hs->kbd.key[i] == hid_code) {
308 hs->kbd.key[i] = hs->kbd.key[-- hs->kbd.keys];
309 hs->kbd.key[hs->kbd.keys] = 0x00;
310 break;
311 }
312 }
313 if (i < 0) {
314 return;
315 }
316 } else {
317 for (i = hs->kbd.keys - 1; i >= 0; i--) {
318 if (hs->kbd.key[i] == hid_code) {
319 break;
320 }
321 }
322 if (i < 0) {
323 if (hs->kbd.keys < sizeof(hs->kbd.key)) {
324 hs->kbd.key[hs->kbd.keys++] = hid_code;
325 }
326 } else {
327 return;
328 }
329 }
330}
331
332static inline int int_clamp(int val, int vmin, int vmax)
333{
334 if (val < vmin) {
335 return vmin;
336 } else if (val > vmax) {
337 return vmax;
338 } else {
339 return val;
340 }
341}
342
21635e12
GH
343void hid_pointer_activate(HIDState *hs)
344{
345 if (!hs->ptr.mouse_grabbed) {
8b84286f 346 qemu_input_handler_activate(hs->s);
21635e12
GH
347 hs->ptr.mouse_grabbed = 1;
348 }
349}
350
dcfda673
GH
351int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
352{
8b84286f 353 int dx, dy, dz, l;
dcfda673
GH
354 int index;
355 HIDPointerEvent *e;
356
027c03f7
HG
357 hs->idle_pending = false;
358
21635e12 359 hid_pointer_activate(hs);
dcfda673
GH
360
361 /* When the buffer is empty, return the last event. Relative
362 movements will all be zero. */
363 index = (hs->n ? hs->head : hs->head - 1);
364 e = &hs->ptr.queue[index & QUEUE_MASK];
365
366 if (hs->kind == HID_MOUSE) {
367 dx = int_clamp(e->xdx, -127, 127);
368 dy = int_clamp(e->ydy, -127, 127);
369 e->xdx -= dx;
370 e->ydy -= dy;
371 } else {
372 dx = e->xdx;
373 dy = e->ydy;
374 }
375 dz = int_clamp(e->dz, -127, 127);
376 e->dz -= dz;
377
dcfda673
GH
378 if (hs->n &&
379 !e->dz &&
380 (hs->kind == HID_TABLET || (!e->xdx && !e->ydy))) {
381 /* that deals with this event */
382 QUEUE_INCR(hs->head);
383 hs->n--;
384 }
385
386 /* Appears we have to invert the wheel direction */
387 dz = 0 - dz;
388 l = 0;
389 switch (hs->kind) {
390 case HID_MOUSE:
391 if (len > l) {
8b84286f 392 buf[l++] = e->buttons_state;
dcfda673
GH
393 }
394 if (len > l) {
395 buf[l++] = dx;
396 }
397 if (len > l) {
398 buf[l++] = dy;
399 }
400 if (len > l) {
401 buf[l++] = dz;
402 }
403 break;
404
405 case HID_TABLET:
406 if (len > l) {
8b84286f 407 buf[l++] = e->buttons_state;
dcfda673
GH
408 }
409 if (len > l) {
410 buf[l++] = dx & 0xff;
411 }
412 if (len > l) {
413 buf[l++] = dx >> 8;
414 }
415 if (len > l) {
416 buf[l++] = dy & 0xff;
417 }
418 if (len > l) {
419 buf[l++] = dy >> 8;
420 }
421 if (len > l) {
422 buf[l++] = dz;
423 }
424 break;
425
426 default:
427 abort();
428 }
429
430 return l;
431}
432
433int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
434{
027c03f7
HG
435 hs->idle_pending = false;
436
dcfda673
GH
437 if (len < 2) {
438 return 0;
439 }
440
441 hid_keyboard_process_keycode(hs);
442
443 buf[0] = hs->kbd.modifiers & 0xff;
444 buf[1] = 0;
445 if (hs->kbd.keys > 6) {
446 memset(buf + 2, HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
447 } else {
448 memcpy(buf + 2, hs->kbd.key, MIN(8, len) - 2);
449 }
450
451 return MIN(8, len);
452}
453
454int hid_keyboard_write(HIDState *hs, uint8_t *buf, int len)
455{
456 if (len > 0) {
457 int ledstate = 0;
458 /* 0x01: Num Lock LED
459 * 0x02: Caps Lock LED
460 * 0x04: Scroll Lock LED
461 * 0x08: Compose LED
462 * 0x10: Kana LED */
463 hs->kbd.leds = buf[0];
464 if (hs->kbd.leds & 0x04) {
465 ledstate |= QEMU_SCROLL_LOCK_LED;
466 }
467 if (hs->kbd.leds & 0x01) {
468 ledstate |= QEMU_NUM_LOCK_LED;
469 }
470 if (hs->kbd.leds & 0x02) {
471 ledstate |= QEMU_CAPS_LOCK_LED;
472 }
473 kbd_put_ledstate(ledstate);
474 }
475 return 0;
476}
477
478void hid_reset(HIDState *hs)
479{
480 switch (hs->kind) {
481 case HID_KEYBOARD:
dcfda673
GH
482 memset(hs->kbd.keycodes, 0, sizeof(hs->kbd.keycodes));
483 memset(hs->kbd.key, 0, sizeof(hs->kbd.key));
484 hs->kbd.keys = 0;
485 break;
486 case HID_MOUSE:
487 case HID_TABLET:
488 memset(hs->ptr.queue, 0, sizeof(hs->ptr.queue));
489 break;
490 }
491 hs->head = 0;
492 hs->n = 0;
b069d348
GH
493 hs->protocol = 1;
494 hs->idle = 0;
027c03f7
HG
495 hs->idle_pending = false;
496 hid_del_idle_timer(hs);
dcfda673
GH
497}
498
499void hid_free(HIDState *hs)
500{
8b84286f 501 qemu_input_handler_unregister(hs->s);
027c03f7 502 hid_del_idle_timer(hs);
dcfda673
GH
503}
504
1ff5eedd
GH
505static QemuInputHandler hid_keyboard_handler = {
506 .name = "QEMU HID Keyboard",
507 .mask = INPUT_EVENT_MASK_KEY,
508 .event = hid_keyboard_event,
509};
510
8b84286f
GH
511static QemuInputHandler hid_mouse_handler = {
512 .name = "QEMU HID Mouse",
513 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
514 .event = hid_pointer_event,
515 .sync = hid_pointer_sync,
516};
517
518static QemuInputHandler hid_tablet_handler = {
519 .name = "QEMU HID Tablet",
520 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
521 .event = hid_pointer_event,
522 .sync = hid_pointer_sync,
523};
524
dcfda673
GH
525void hid_init(HIDState *hs, int kind, HIDEventFunc event)
526{
527 hs->kind = kind;
528 hs->event = event;
529
bb0db527 530 if (hs->kind == HID_KEYBOARD) {
1ff5eedd
GH
531 hs->s = qemu_input_handler_register((DeviceState *)hs,
532 &hid_keyboard_handler);
533 qemu_input_handler_activate(hs->s);
bb0db527 534 } else if (hs->kind == HID_MOUSE) {
8b84286f
GH
535 hs->s = qemu_input_handler_register((DeviceState *)hs,
536 &hid_mouse_handler);
dcfda673 537 } else if (hs->kind == HID_TABLET) {
8b84286f
GH
538 hs->s = qemu_input_handler_register((DeviceState *)hs,
539 &hid_tablet_handler);
dcfda673
GH
540 }
541}
ccd4ed06
MW
542
543static int hid_post_load(void *opaque, int version_id)
544{
545 HIDState *s = opaque;
546
027c03f7 547 hid_set_next_idle(s);
ba4d2606
GH
548
549 if (s->n == QUEUE_LENGTH && (s->kind == HID_TABLET ||
550 s->kind == HID_MOUSE)) {
551 /*
552 * Handle ptr device migration from old qemu with full queue.
553 *
554 * Throw away everything but the last event, so we propagate
555 * at least the current button state to the guest. Also keep
556 * current position for the tablet, signal "no motion" for the
557 * mouse.
558 */
559 HIDPointerEvent evt;
560 evt = s->ptr.queue[(s->head+s->n) & QUEUE_MASK];
561 if (s->kind == HID_MOUSE) {
562 evt.xdx = 0;
563 evt.ydy = 0;
564 }
565 s->ptr.queue[0] = evt;
566 s->head = 0;
567 s->n = 1;
568 }
ccd4ed06
MW
569 return 0;
570}
571
572static const VMStateDescription vmstate_hid_ptr_queue = {
573 .name = "HIDPointerEventQueue",
574 .version_id = 1,
575 .minimum_version_id = 1,
576 .fields = (VMStateField[]) {
577 VMSTATE_INT32(xdx, HIDPointerEvent),
578 VMSTATE_INT32(ydy, HIDPointerEvent),
579 VMSTATE_INT32(dz, HIDPointerEvent),
580 VMSTATE_INT32(buttons_state, HIDPointerEvent),
581 VMSTATE_END_OF_LIST()
582 }
583};
584
585const VMStateDescription vmstate_hid_ptr_device = {
586 .name = "HIDPointerDevice",
587 .version_id = 1,
588 .minimum_version_id = 1,
589 .post_load = hid_post_load,
590 .fields = (VMStateField[]) {
591 VMSTATE_STRUCT_ARRAY(ptr.queue, HIDState, QUEUE_LENGTH, 0,
592 vmstate_hid_ptr_queue, HIDPointerEvent),
593 VMSTATE_UINT32(head, HIDState),
594 VMSTATE_UINT32(n, HIDState),
595 VMSTATE_INT32(protocol, HIDState),
596 VMSTATE_UINT8(idle, HIDState),
597 VMSTATE_END_OF_LIST(),
598 }
599};
600
601const VMStateDescription vmstate_hid_keyboard_device = {
602 .name = "HIDKeyboardDevice",
603 .version_id = 1,
604 .minimum_version_id = 1,
605 .post_load = hid_post_load,
606 .fields = (VMStateField[]) {
607 VMSTATE_UINT32_ARRAY(kbd.keycodes, HIDState, QUEUE_LENGTH),
608 VMSTATE_UINT32(head, HIDState),
609 VMSTATE_UINT32(n, HIDState),
610 VMSTATE_UINT16(kbd.modifiers, HIDState),
611 VMSTATE_UINT8(kbd.leds, HIDState),
612 VMSTATE_UINT8_ARRAY(kbd.key, HIDState, 16),
613 VMSTATE_INT32(kbd.keys, HIDState),
614 VMSTATE_INT32(protocol, HIDState),
615 VMSTATE_UINT8(idle, HIDState),
616 VMSTATE_END_OF_LIST(),
617 }
618};