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