xref: /openbmc/qemu/hw/input/virtio-input-hid.c (revision 06831001)
1 /*
2  * This work is licensed under the terms of the GNU GPL, version 2 or
3  * (at your option) any later version.  See the COPYING file in the
4  * top-level directory.
5  */
6 
7 #include "qemu/osdep.h"
8 #include "qemu/iov.h"
9 #include "qemu/module.h"
10 
11 #include "hw/virtio/virtio.h"
12 #include "hw/qdev-properties.h"
13 #include "hw/virtio/virtio-input.h"
14 
15 #include "ui/console.h"
16 
17 #include "standard-headers/linux/input.h"
18 
19 #define VIRTIO_ID_NAME_KEYBOARD     "QEMU Virtio Keyboard"
20 #define VIRTIO_ID_NAME_MOUSE        "QEMU Virtio Mouse"
21 #define VIRTIO_ID_NAME_TABLET       "QEMU Virtio Tablet"
22 #define VIRTIO_ID_NAME_MULTITOUCH   "QEMU Virtio MultiTouch"
23 
24 /* ----------------------------------------------------------------- */
25 
26 static const unsigned short keymap_button[INPUT_BUTTON__MAX] = {
27     [INPUT_BUTTON_LEFT]              = BTN_LEFT,
28     [INPUT_BUTTON_RIGHT]             = BTN_RIGHT,
29     [INPUT_BUTTON_MIDDLE]            = BTN_MIDDLE,
30     [INPUT_BUTTON_WHEEL_UP]          = BTN_GEAR_UP,
31     [INPUT_BUTTON_WHEEL_DOWN]        = BTN_GEAR_DOWN,
32     [INPUT_BUTTON_SIDE]              = BTN_SIDE,
33     [INPUT_BUTTON_EXTRA]             = BTN_EXTRA,
34     [INPUT_BUTTON_TOUCH]             = BTN_TOUCH,
35 };
36 
37 static const unsigned short axismap_rel[INPUT_AXIS__MAX] = {
38     [INPUT_AXIS_X]                   = REL_X,
39     [INPUT_AXIS_Y]                   = REL_Y,
40 };
41 
42 static const unsigned short axismap_abs[INPUT_AXIS__MAX] = {
43     [INPUT_AXIS_X]                   = ABS_X,
44     [INPUT_AXIS_Y]                   = ABS_Y,
45 };
46 
47 static const unsigned short axismap_tch[INPUT_AXIS__MAX] = {
48     [INPUT_AXIS_X]                   = ABS_MT_POSITION_X,
49     [INPUT_AXIS_Y]                   = ABS_MT_POSITION_Y,
50 };
51 
52 /* ----------------------------------------------------------------- */
53 
54 static void virtio_input_extend_config(VirtIOInput *vinput,
55                                        const unsigned short *map,
56                                        size_t mapsize,
57                                        uint8_t select, uint8_t subsel)
58 {
59     virtio_input_config ext;
60     int i, bit, byte, bmax = 0;
61 
62     memset(&ext, 0, sizeof(ext));
63     for (i = 0; i < mapsize; i++) {
64         bit = map[i];
65         if (!bit) {
66             continue;
67         }
68         byte = bit / 8;
69         bit  = bit % 8;
70         ext.u.bitmap[byte] |= (1 << bit);
71         if (bmax < byte+1) {
72             bmax = byte+1;
73         }
74     }
75     ext.select = select;
76     ext.subsel = subsel;
77     ext.size   = bmax;
78     virtio_input_add_config(vinput, &ext);
79 }
80 
81 static void virtio_input_handle_event(DeviceState *dev, QemuConsole *src,
82                                       InputEvent *evt)
83 {
84     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
85     VirtIOInput *vinput = VIRTIO_INPUT(dev);
86     virtio_input_event event;
87     int qcode;
88     InputKeyEvent *key;
89     InputMoveEvent *move;
90     InputBtnEvent *btn;
91     InputMultiTouchEvent *mtt;
92 
93     switch (evt->type) {
94     case INPUT_EVENT_KIND_KEY:
95         key = evt->u.key.data;
96         qcode = qemu_input_key_value_to_qcode(key->key);
97         if (qcode < qemu_input_map_qcode_to_linux_len &&
98             qemu_input_map_qcode_to_linux[qcode]) {
99             event.type  = cpu_to_le16(EV_KEY);
100             event.code  = cpu_to_le16(qemu_input_map_qcode_to_linux[qcode]);
101             event.value = cpu_to_le32(key->down ? 1 : 0);
102             virtio_input_send(vinput, &event);
103         } else {
104             if (key->down) {
105                 fprintf(stderr, "%s: unmapped key: %d [%s]\n", __func__,
106                         qcode, QKeyCode_str(qcode));
107             }
108         }
109         break;
110     case INPUT_EVENT_KIND_BTN:
111         btn = evt->u.btn.data;
112         if (vhid->wheel_axis &&
113             (btn->button == INPUT_BUTTON_WHEEL_UP ||
114              btn->button == INPUT_BUTTON_WHEEL_DOWN) &&
115             btn->down) {
116             event.type  = cpu_to_le16(EV_REL);
117             event.code  = cpu_to_le16(REL_WHEEL);
118             event.value = cpu_to_le32(btn->button == INPUT_BUTTON_WHEEL_UP
119                                       ? 1 : -1);
120             virtio_input_send(vinput, &event);
121         } else if (keymap_button[btn->button]) {
122             event.type  = cpu_to_le16(EV_KEY);
123             event.code  = cpu_to_le16(keymap_button[btn->button]);
124             event.value = cpu_to_le32(btn->down ? 1 : 0);
125             virtio_input_send(vinput, &event);
126         } else {
127             if (btn->down) {
128                 fprintf(stderr, "%s: unmapped button: %d [%s]\n", __func__,
129                         btn->button,
130                         InputButton_str(btn->button));
131             }
132         }
133         break;
134     case INPUT_EVENT_KIND_REL:
135         move = evt->u.rel.data;
136         event.type  = cpu_to_le16(EV_REL);
137         event.code  = cpu_to_le16(axismap_rel[move->axis]);
138         event.value = cpu_to_le32(move->value);
139         virtio_input_send(vinput, &event);
140         break;
141     case INPUT_EVENT_KIND_ABS:
142         move = evt->u.abs.data;
143         event.type  = cpu_to_le16(EV_ABS);
144         event.code  = cpu_to_le16(axismap_abs[move->axis]);
145         event.value = cpu_to_le32(move->value);
146         virtio_input_send(vinput, &event);
147         break;
148     case INPUT_EVENT_KIND_MTT:
149         mtt = evt->u.mtt.data;
150         if (mtt->type == INPUT_MULTI_TOUCH_TYPE_DATA) {
151             event.type  = cpu_to_le16(EV_ABS);
152             event.code  = cpu_to_le16(axismap_tch[mtt->axis]);
153             event.value = cpu_to_le32(mtt->value);
154             virtio_input_send(vinput, &event);
155         } else {
156             event.type  = cpu_to_le16(EV_ABS);
157             event.code  = cpu_to_le16(ABS_MT_SLOT);
158             event.value = cpu_to_le32(mtt->slot);
159             virtio_input_send(vinput, &event);
160             event.type  = cpu_to_le16(EV_ABS);
161             event.code  = cpu_to_le16(ABS_MT_TRACKING_ID);
162             event.value = cpu_to_le32(mtt->tracking_id);
163             virtio_input_send(vinput, &event);
164         }
165         break;
166     default:
167         /* keep gcc happy */
168         break;
169     }
170 }
171 
172 static void virtio_input_handle_sync(DeviceState *dev)
173 {
174     VirtIOInput *vinput = VIRTIO_INPUT(dev);
175     virtio_input_event event = {
176         .type  = cpu_to_le16(EV_SYN),
177         .code  = cpu_to_le16(SYN_REPORT),
178         .value = 0,
179     };
180 
181     virtio_input_send(vinput, &event);
182 }
183 
184 static void virtio_input_hid_realize(DeviceState *dev, Error **errp)
185 {
186     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
187 
188     vhid->hs = qemu_input_handler_register(dev, vhid->handler);
189     if (vhid->display && vhid->hs) {
190         qemu_input_handler_bind(vhid->hs, vhid->display, vhid->head, NULL);
191     }
192 }
193 
194 static void virtio_input_hid_unrealize(DeviceState *dev)
195 {
196     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(dev);
197     qemu_input_handler_unregister(vhid->hs);
198 }
199 
200 static void virtio_input_hid_change_active(VirtIOInput *vinput)
201 {
202     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput);
203 
204     if (vinput->active) {
205         qemu_input_handler_activate(vhid->hs);
206     } else {
207         qemu_input_handler_deactivate(vhid->hs);
208     }
209 }
210 
211 static void virtio_input_hid_handle_status(VirtIOInput *vinput,
212                                            virtio_input_event *event)
213 {
214     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(vinput);
215     int ledbit = 0;
216 
217     switch (le16_to_cpu(event->type)) {
218     case EV_LED:
219         if (event->code == LED_NUML) {
220             ledbit = QEMU_NUM_LOCK_LED;
221         } else if (event->code == LED_CAPSL) {
222             ledbit = QEMU_CAPS_LOCK_LED;
223         } else if (event->code == LED_SCROLLL) {
224             ledbit = QEMU_SCROLL_LOCK_LED;
225         }
226         if (event->value) {
227             vhid->ledstate |= ledbit;
228         } else {
229             vhid->ledstate &= ~ledbit;
230         }
231         kbd_put_ledstate(vhid->ledstate);
232         break;
233     default:
234         fprintf(stderr, "%s: unknown type %d\n", __func__,
235                 le16_to_cpu(event->type));
236         break;
237     }
238 }
239 
240 static Property virtio_input_hid_properties[] = {
241     DEFINE_PROP_STRING("display", VirtIOInputHID, display),
242     DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0),
243     DEFINE_PROP_END_OF_LIST(),
244 };
245 
246 static void virtio_input_hid_class_init(ObjectClass *klass, void *data)
247 {
248     DeviceClass *dc = DEVICE_CLASS(klass);
249     VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass);
250 
251     device_class_set_props(dc, virtio_input_hid_properties);
252     vic->realize       = virtio_input_hid_realize;
253     vic->unrealize     = virtio_input_hid_unrealize;
254     vic->change_active = virtio_input_hid_change_active;
255     vic->handle_status = virtio_input_hid_handle_status;
256 }
257 
258 static const TypeInfo virtio_input_hid_info = {
259     .name          = TYPE_VIRTIO_INPUT_HID,
260     .parent        = TYPE_VIRTIO_INPUT,
261     .instance_size = sizeof(VirtIOInputHID),
262     .class_init    = virtio_input_hid_class_init,
263     .abstract      = true,
264 };
265 
266 /* ----------------------------------------------------------------- */
267 
268 static QemuInputHandler virtio_keyboard_handler = {
269     .name  = VIRTIO_ID_NAME_KEYBOARD,
270     .mask  = INPUT_EVENT_MASK_KEY,
271     .event = virtio_input_handle_event,
272     .sync  = virtio_input_handle_sync,
273 };
274 
275 static struct virtio_input_config virtio_keyboard_config[] = {
276     {
277         .select    = VIRTIO_INPUT_CFG_ID_NAME,
278         .size      = sizeof(VIRTIO_ID_NAME_KEYBOARD),
279         .u.string  = VIRTIO_ID_NAME_KEYBOARD,
280     },{
281         .select    = VIRTIO_INPUT_CFG_ID_DEVIDS,
282         .size      = sizeof(struct virtio_input_devids),
283         .u.ids     = {
284             .bustype = const_le16(BUS_VIRTUAL),
285             .vendor  = const_le16(0x0627), /* same we use for usb hid devices */
286             .product = const_le16(0x0001),
287             .version = const_le16(0x0001),
288         },
289     },{
290         .select    = VIRTIO_INPUT_CFG_EV_BITS,
291         .subsel    = EV_REP,
292         .size      = 1,
293     },{
294         .select    = VIRTIO_INPUT_CFG_EV_BITS,
295         .subsel    = EV_LED,
296         .size      = 1,
297         .u.bitmap  = {
298             (1 << LED_NUML) | (1 << LED_CAPSL) | (1 << LED_SCROLLL),
299         },
300     },
301     { /* end of list */ },
302 };
303 
304 static void virtio_keyboard_init(Object *obj)
305 {
306     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
307     VirtIOInput *vinput = VIRTIO_INPUT(obj);
308 
309     vhid->handler = &virtio_keyboard_handler;
310     virtio_input_init_config(vinput, virtio_keyboard_config);
311     virtio_input_extend_config(vinput, qemu_input_map_qcode_to_linux,
312                                qemu_input_map_qcode_to_linux_len,
313                                VIRTIO_INPUT_CFG_EV_BITS, EV_KEY);
314 }
315 
316 static const TypeInfo virtio_keyboard_info = {
317     .name          = TYPE_VIRTIO_KEYBOARD,
318     .parent        = TYPE_VIRTIO_INPUT_HID,
319     .instance_size = sizeof(VirtIOInputHID),
320     .instance_init = virtio_keyboard_init,
321 };
322 
323 /* ----------------------------------------------------------------- */
324 
325 static QemuInputHandler virtio_mouse_handler = {
326     .name  = VIRTIO_ID_NAME_MOUSE,
327     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
328     .event = virtio_input_handle_event,
329     .sync  = virtio_input_handle_sync,
330 };
331 
332 static struct virtio_input_config virtio_mouse_config_v1[] = {
333     {
334         .select    = VIRTIO_INPUT_CFG_ID_NAME,
335         .size      = sizeof(VIRTIO_ID_NAME_MOUSE),
336         .u.string  = VIRTIO_ID_NAME_MOUSE,
337     },{
338         .select    = VIRTIO_INPUT_CFG_ID_DEVIDS,
339         .size      = sizeof(struct virtio_input_devids),
340         .u.ids     = {
341             .bustype = const_le16(BUS_VIRTUAL),
342             .vendor  = const_le16(0x0627), /* same we use for usb hid devices */
343             .product = const_le16(0x0002),
344             .version = const_le16(0x0001),
345         },
346     },{
347         .select    = VIRTIO_INPUT_CFG_EV_BITS,
348         .subsel    = EV_REL,
349         .size      = 1,
350         .u.bitmap  = {
351             (1 << REL_X) | (1 << REL_Y),
352         },
353     },
354     { /* end of list */ },
355 };
356 
357 static struct virtio_input_config virtio_mouse_config_v2[] = {
358     {
359         .select    = VIRTIO_INPUT_CFG_ID_NAME,
360         .size      = sizeof(VIRTIO_ID_NAME_MOUSE),
361         .u.string  = VIRTIO_ID_NAME_MOUSE,
362     },{
363         .select    = VIRTIO_INPUT_CFG_ID_DEVIDS,
364         .size      = sizeof(struct virtio_input_devids),
365         .u.ids     = {
366             .bustype = const_le16(BUS_VIRTUAL),
367             .vendor  = const_le16(0x0627), /* same we use for usb hid devices */
368             .product = const_le16(0x0002),
369             .version = const_le16(0x0002),
370         },
371     },{
372         .select    = VIRTIO_INPUT_CFG_EV_BITS,
373         .subsel    = EV_REL,
374         .size      = 2,
375         .u.bitmap  = {
376             (1 << REL_X) | (1 << REL_Y),
377             (1 << (REL_WHEEL - 8))
378         },
379     },
380     { /* end of list */ },
381 };
382 
383 static Property virtio_mouse_properties[] = {
384     DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true),
385     DEFINE_PROP_END_OF_LIST(),
386 };
387 
388 static void virtio_mouse_class_init(ObjectClass *klass, void *data)
389 {
390     DeviceClass *dc = DEVICE_CLASS(klass);
391 
392     device_class_set_props(dc, virtio_mouse_properties);
393 }
394 
395 static void virtio_mouse_init(Object *obj)
396 {
397     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
398     VirtIOInput *vinput = VIRTIO_INPUT(obj);
399 
400     vhid->handler = &virtio_mouse_handler;
401     virtio_input_init_config(vinput, vhid->wheel_axis
402                              ? virtio_mouse_config_v2
403                              : virtio_mouse_config_v1);
404     virtio_input_extend_config(vinput, keymap_button,
405                                ARRAY_SIZE(keymap_button),
406                                VIRTIO_INPUT_CFG_EV_BITS, EV_KEY);
407 }
408 
409 static const TypeInfo virtio_mouse_info = {
410     .name          = TYPE_VIRTIO_MOUSE,
411     .parent        = TYPE_VIRTIO_INPUT_HID,
412     .instance_size = sizeof(VirtIOInputHID),
413     .instance_init = virtio_mouse_init,
414     .class_init    = virtio_mouse_class_init,
415 };
416 
417 /* ----------------------------------------------------------------- */
418 
419 static QemuInputHandler virtio_tablet_handler = {
420     .name  = VIRTIO_ID_NAME_TABLET,
421     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
422     .event = virtio_input_handle_event,
423     .sync  = virtio_input_handle_sync,
424 };
425 
426 static struct virtio_input_config virtio_tablet_config_v1[] = {
427     {
428         .select    = VIRTIO_INPUT_CFG_ID_NAME,
429         .size      = sizeof(VIRTIO_ID_NAME_TABLET),
430         .u.string  = VIRTIO_ID_NAME_TABLET,
431     },{
432         .select    = VIRTIO_INPUT_CFG_ID_DEVIDS,
433         .size      = sizeof(struct virtio_input_devids),
434         .u.ids     = {
435             .bustype = const_le16(BUS_VIRTUAL),
436             .vendor  = const_le16(0x0627), /* same we use for usb hid devices */
437             .product = const_le16(0x0003),
438             .version = const_le16(0x0001),
439         },
440     },{
441         .select    = VIRTIO_INPUT_CFG_EV_BITS,
442         .subsel    = EV_ABS,
443         .size      = 1,
444         .u.bitmap  = {
445             (1 << ABS_X) | (1 << ABS_Y),
446         },
447     },{
448         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
449         .subsel    = ABS_X,
450         .size      = sizeof(virtio_input_absinfo),
451         .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
452         .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
453     },{
454         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
455         .subsel    = ABS_Y,
456         .size      = sizeof(virtio_input_absinfo),
457         .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
458         .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
459     },
460     { /* end of list */ },
461 };
462 
463 static struct virtio_input_config virtio_tablet_config_v2[] = {
464     {
465         .select    = VIRTIO_INPUT_CFG_ID_NAME,
466         .size      = sizeof(VIRTIO_ID_NAME_TABLET),
467         .u.string  = VIRTIO_ID_NAME_TABLET,
468     },{
469         .select    = VIRTIO_INPUT_CFG_ID_DEVIDS,
470         .size      = sizeof(struct virtio_input_devids),
471         .u.ids     = {
472             .bustype = const_le16(BUS_VIRTUAL),
473             .vendor  = const_le16(0x0627), /* same we use for usb hid devices */
474             .product = const_le16(0x0003),
475             .version = const_le16(0x0002),
476         },
477     },{
478         .select    = VIRTIO_INPUT_CFG_EV_BITS,
479         .subsel    = EV_ABS,
480         .size      = 1,
481         .u.bitmap  = {
482             (1 << ABS_X) | (1 << ABS_Y),
483         },
484     },{
485         .select    = VIRTIO_INPUT_CFG_EV_BITS,
486         .subsel    = EV_REL,
487         .size      = 2,
488         .u.bitmap  = {
489             0,
490             (1 << (REL_WHEEL - 8))
491         },
492     },{
493         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
494         .subsel    = ABS_X,
495         .size      = sizeof(virtio_input_absinfo),
496         .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
497         .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
498     },{
499         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
500         .subsel    = ABS_Y,
501         .size      = sizeof(virtio_input_absinfo),
502         .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
503         .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
504     },
505     { /* end of list */ },
506 };
507 
508 static Property virtio_tablet_properties[] = {
509     DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true),
510     DEFINE_PROP_END_OF_LIST(),
511 };
512 
513 static void virtio_tablet_class_init(ObjectClass *klass, void *data)
514 {
515     DeviceClass *dc = DEVICE_CLASS(klass);
516 
517     device_class_set_props(dc, virtio_tablet_properties);
518 }
519 
520 static void virtio_tablet_init(Object *obj)
521 {
522     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
523     VirtIOInput *vinput = VIRTIO_INPUT(obj);
524 
525     vhid->handler = &virtio_tablet_handler;
526     virtio_input_init_config(vinput, vhid->wheel_axis
527                              ? virtio_tablet_config_v2
528                              : virtio_tablet_config_v1);
529     virtio_input_extend_config(vinput, keymap_button,
530                                ARRAY_SIZE(keymap_button),
531                                VIRTIO_INPUT_CFG_EV_BITS, EV_KEY);
532 }
533 
534 static const TypeInfo virtio_tablet_info = {
535     .name          = TYPE_VIRTIO_TABLET,
536     .parent        = TYPE_VIRTIO_INPUT_HID,
537     .instance_size = sizeof(VirtIOInputHID),
538     .instance_init = virtio_tablet_init,
539     .class_init    = virtio_tablet_class_init,
540 };
541 
542 /* ----------------------------------------------------------------- */
543 
544 static QemuInputHandler virtio_multitouch_handler = {
545     .name  = VIRTIO_ID_NAME_MULTITOUCH,
546     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_MTT,
547     .event = virtio_input_handle_event,
548     .sync  = virtio_input_handle_sync,
549 };
550 
551 static struct virtio_input_config virtio_multitouch_config[] = {
552     {
553         .select    = VIRTIO_INPUT_CFG_ID_NAME,
554         .size      = sizeof(VIRTIO_ID_NAME_MULTITOUCH),
555         .u.string  = VIRTIO_ID_NAME_MULTITOUCH,
556     },{
557         .select    = VIRTIO_INPUT_CFG_ID_DEVIDS,
558         .size      = sizeof(struct virtio_input_devids),
559         .u.ids     = {
560             .bustype = const_le16(BUS_VIRTUAL),
561             .vendor  = const_le16(0x0627), /* same we use for usb hid devices */
562             .product = const_le16(0x0003),
563             .version = const_le16(0x0001),
564         },
565     },{
566         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
567         .subsel    = ABS_MT_SLOT,
568         .size      = sizeof(virtio_input_absinfo),
569         .u.abs.min = const_le32(INPUT_EVENT_SLOTS_MIN),
570         .u.abs.max = const_le32(INPUT_EVENT_SLOTS_MAX),
571     },{
572         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
573         .subsel    = ABS_MT_TRACKING_ID,
574         .size      = sizeof(virtio_input_absinfo),
575         .u.abs.min = const_le32(INPUT_EVENT_SLOTS_MIN),
576         .u.abs.max = const_le32(INPUT_EVENT_SLOTS_MAX),
577     },{
578         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
579         .subsel    = ABS_MT_POSITION_X,
580         .size      = sizeof(virtio_input_absinfo),
581         .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
582         .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
583     },{
584         .select    = VIRTIO_INPUT_CFG_ABS_INFO,
585         .subsel    = ABS_MT_POSITION_Y,
586         .size      = sizeof(virtio_input_absinfo),
587         .u.abs.min = const_le32(INPUT_EVENT_ABS_MIN),
588         .u.abs.max = const_le32(INPUT_EVENT_ABS_MAX),
589     },
590     { /* end of list */ },
591 };
592 
593 static void virtio_multitouch_init(Object *obj)
594 {
595     VirtIOInputHID *vhid = VIRTIO_INPUT_HID(obj);
596     VirtIOInput *vinput = VIRTIO_INPUT(obj);
597     unsigned short abs_props[] = {
598         INPUT_PROP_DIRECT,
599     };
600     unsigned short abs_bits[] = {
601         ABS_MT_SLOT,
602         ABS_MT_TRACKING_ID,
603         ABS_MT_POSITION_X,
604         ABS_MT_POSITION_Y,
605     };
606 
607     vhid->handler = &virtio_multitouch_handler;
608     virtio_input_init_config(vinput, virtio_multitouch_config);
609     virtio_input_extend_config(vinput, keymap_button,
610                                ARRAY_SIZE(keymap_button),
611                                VIRTIO_INPUT_CFG_EV_BITS, EV_KEY);
612     virtio_input_extend_config(vinput, abs_props,
613                                ARRAY_SIZE(abs_props),
614                                VIRTIO_INPUT_CFG_PROP_BITS, 0);
615     virtio_input_extend_config(vinput, abs_bits,
616                                ARRAY_SIZE(abs_bits),
617                                VIRTIO_INPUT_CFG_EV_BITS, EV_ABS);
618 }
619 
620 static const TypeInfo virtio_multitouch_info = {
621     .name          = TYPE_VIRTIO_MULTITOUCH,
622     .parent        = TYPE_VIRTIO_INPUT_HID,
623     .instance_size = sizeof(VirtIOInputHID),
624     .instance_init = virtio_multitouch_init,
625 };
626 
627 /* ----------------------------------------------------------------- */
628 
629 static void virtio_register_types(void)
630 {
631     type_register_static(&virtio_input_hid_info);
632     type_register_static(&virtio_keyboard_info);
633     type_register_static(&virtio_mouse_info);
634     type_register_static(&virtio_tablet_info);
635     type_register_static(&virtio_multitouch_info);
636 }
637 
638 type_init(virtio_register_types)
639