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