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