xref: /openbmc/qemu/chardev/wctablet.c (revision 0b8fa32f551e863bb548a11394239239270dd3dc)
16b10e573SMarc-André Lureau /*
26b10e573SMarc-André Lureau  * QEMU Wacom Penpartner serial tablet emulation
36b10e573SMarc-André Lureau  *
46b10e573SMarc-André Lureau  * some protocol details:
56b10e573SMarc-André Lureau  *   http://linuxwacom.sourceforge.net/wiki/index.php/Serial_Protocol_IV
66b10e573SMarc-André Lureau  *
76b10e573SMarc-André Lureau  * Copyright (c) 2016 Anatoli Huseu1
86b10e573SMarc-André Lureau  * Copyright (c) 2016,17 Gerd Hoffmann
96b10e573SMarc-André Lureau  *
106b10e573SMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
116b10e573SMarc-André Lureau  * of this software and associated documentation files (the "Software"), to
126b10e573SMarc-André Lureau  * deal in the Software without restriction, including without limitation
136b10e573SMarc-André Lureau  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
146b10e573SMarc-André Lureau  * and/or sell copies of the Software, and to permit persons to whom the
156b10e573SMarc-André Lureau  * Software is furnished to do so, subject to the following conditions:
166b10e573SMarc-André Lureau  *
176b10e573SMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
186b10e573SMarc-André Lureau  * all copies or substantial portions of the Software.
196b10e573SMarc-André Lureau  *
206b10e573SMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
216b10e573SMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
226b10e573SMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
236b10e573SMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
246b10e573SMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
256b10e573SMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
266b10e573SMarc-André Lureau  * THE SOFTWARE.
276b10e573SMarc-André Lureau  */
286b10e573SMarc-André Lureau 
296b10e573SMarc-André Lureau #include "qemu/osdep.h"
30*0b8fa32fSMarkus Armbruster #include "qemu/module.h"
316b10e573SMarc-André Lureau #include "chardev/char-serial.h"
326b10e573SMarc-André Lureau #include "ui/console.h"
336b10e573SMarc-André Lureau #include "ui/input.h"
346b10e573SMarc-André Lureau #include "trace.h"
356b10e573SMarc-André Lureau 
366b10e573SMarc-André Lureau 
376b10e573SMarc-André Lureau #define WC_OUTPUT_BUF_MAX_LEN 512
386b10e573SMarc-André Lureau #define WC_COMMAND_MAX_LEN 60
396b10e573SMarc-André Lureau 
406b10e573SMarc-André Lureau #define WC_L7(n) ((n) & 127)
416b10e573SMarc-André Lureau #define WC_M7(n) (((n) >> 7) & 127)
426b10e573SMarc-André Lureau #define WC_H2(n) ((n) >> 14)
436b10e573SMarc-André Lureau 
446b10e573SMarc-André Lureau #define WC_L4(n) ((n) & 15)
456b10e573SMarc-André Lureau #define WC_H4(n) (((n) >> 4) & 15)
466b10e573SMarc-André Lureau 
476b10e573SMarc-André Lureau /* Model string and config string */
486b10e573SMarc-André Lureau #define WC_MODEL_STRING_LENGTH 18
496b10e573SMarc-André Lureau uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,";
506b10e573SMarc-André Lureau 
516b10e573SMarc-André Lureau #define WC_CONFIG_STRING_LENGTH 8
526b10e573SMarc-André Lureau uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0";
536b10e573SMarc-André Lureau 
546b10e573SMarc-André Lureau #define WC_FULL_CONFIG_STRING_LENGTH 61
556b10e573SMarc-André Lureau uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = {
566b10e573SMarc-André Lureau     0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c,
576b10e573SMarc-André Lureau     0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30,
586b10e573SMarc-André Lureau     0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c,
596b10e573SMarc-André Lureau     0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c,
606b10e573SMarc-André Lureau     0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a,
616b10e573SMarc-André Lureau     0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52,
626b10e573SMarc-André Lureau     0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d,
636b10e573SMarc-André Lureau     0x0a, 0x45, 0x37, 0x29
646b10e573SMarc-André Lureau };
656b10e573SMarc-André Lureau 
666b10e573SMarc-André Lureau /* This structure is used to save private info for Wacom Tablet. */
676b10e573SMarc-André Lureau typedef struct {
686b10e573SMarc-André Lureau     Chardev parent;
696b10e573SMarc-André Lureau     QemuInputHandlerState *hs;
706b10e573SMarc-André Lureau 
716b10e573SMarc-André Lureau     /* Query string from serial */
726b10e573SMarc-André Lureau     uint8_t query[100];
736b10e573SMarc-André Lureau     int query_index;
746b10e573SMarc-André Lureau 
756b10e573SMarc-André Lureau     /* Command to be sent to serial port */
766b10e573SMarc-André Lureau     uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN];
776b10e573SMarc-André Lureau     int outlen;
786b10e573SMarc-André Lureau 
796b10e573SMarc-André Lureau     int line_speed;
806b10e573SMarc-André Lureau     bool send_events;
816b10e573SMarc-André Lureau     int axis[INPUT_AXIS__MAX];
826b10e573SMarc-André Lureau     bool btns[INPUT_BUTTON__MAX];
836b10e573SMarc-André Lureau 
846b10e573SMarc-André Lureau } TabletChardev;
856b10e573SMarc-André Lureau 
866b10e573SMarc-André Lureau #define TYPE_CHARDEV_WCTABLET "chardev-wctablet"
876b10e573SMarc-André Lureau #define WCTABLET_CHARDEV(obj)                                      \
886b10e573SMarc-André Lureau     OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET)
896b10e573SMarc-André Lureau 
906b10e573SMarc-André Lureau 
916b10e573SMarc-André Lureau static void wctablet_chr_accept_input(Chardev *chr);
926b10e573SMarc-André Lureau 
936b10e573SMarc-André Lureau static void wctablet_shift_input(TabletChardev *tablet, int count)
946b10e573SMarc-André Lureau {
956b10e573SMarc-André Lureau     tablet->query_index -= count;
966b10e573SMarc-André Lureau     memmove(tablet->query, tablet->query + count, tablet->query_index);
976b10e573SMarc-André Lureau     tablet->query[tablet->query_index] = 0;
986b10e573SMarc-André Lureau }
996b10e573SMarc-André Lureau 
1006b10e573SMarc-André Lureau static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count)
1016b10e573SMarc-André Lureau {
1026b10e573SMarc-André Lureau     if (tablet->outlen + count > sizeof(tablet->outbuf)) {
1036b10e573SMarc-André Lureau         return;
1046b10e573SMarc-André Lureau     }
1056b10e573SMarc-André Lureau 
1066b10e573SMarc-André Lureau     memcpy(tablet->outbuf + tablet->outlen, buf, count);
1076b10e573SMarc-André Lureau     tablet->outlen += count;
1086b10e573SMarc-André Lureau     wctablet_chr_accept_input(CHARDEV(tablet));
1096b10e573SMarc-André Lureau }
1106b10e573SMarc-André Lureau 
1116b10e573SMarc-André Lureau static void wctablet_reset(TabletChardev *tablet)
1126b10e573SMarc-André Lureau {
1136b10e573SMarc-André Lureau     /* clear buffers */
1146b10e573SMarc-André Lureau     tablet->query_index = 0;
1156b10e573SMarc-André Lureau     tablet->outlen = 0;
1166b10e573SMarc-André Lureau     /* reset state */
1176b10e573SMarc-André Lureau     tablet->send_events = false;
1186b10e573SMarc-André Lureau }
1196b10e573SMarc-André Lureau 
1206b10e573SMarc-André Lureau static void wctablet_queue_event(TabletChardev *tablet)
1216b10e573SMarc-André Lureau {
1226b10e573SMarc-André Lureau     uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 };
1236b10e573SMarc-André Lureau 
1246b10e573SMarc-André Lureau     if (tablet->line_speed != 9600) {
1256b10e573SMarc-André Lureau         return;
1266b10e573SMarc-André Lureau     }
1276b10e573SMarc-André Lureau 
1286b10e573SMarc-André Lureau     int newX = tablet->axis[INPUT_AXIS_X] * 0.1537;
1296b10e573SMarc-André Lureau     int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152;
1306b10e573SMarc-André Lureau 
1316b10e573SMarc-André Lureau     codes[0] = codes[0] | WC_H2(newX);
1326b10e573SMarc-André Lureau     codes[1] = codes[1] | WC_M7(newX);
1336b10e573SMarc-André Lureau     codes[2] = codes[2] | WC_L7(newX);
1346b10e573SMarc-André Lureau 
1356b10e573SMarc-André Lureau     codes[3] = codes[3] | WC_H2(nexY);
1366b10e573SMarc-André Lureau     codes[4] = codes[4] | WC_M7(nexY);
1376b10e573SMarc-André Lureau     codes[5] = codes[5] | WC_L7(nexY);
1386b10e573SMarc-André Lureau 
1396b10e573SMarc-André Lureau     if (tablet->btns[INPUT_BUTTON_LEFT]) {
1406b10e573SMarc-André Lureau         codes[0] = 0xa0;
1416b10e573SMarc-André Lureau     }
1426b10e573SMarc-André Lureau 
1436b10e573SMarc-André Lureau     wctablet_queue_output(tablet, codes, 7);
1446b10e573SMarc-André Lureau }
1456b10e573SMarc-André Lureau 
1466b10e573SMarc-André Lureau static void wctablet_input_event(DeviceState *dev, QemuConsole *src,
1476b10e573SMarc-André Lureau                                 InputEvent *evt)
1486b10e573SMarc-André Lureau {
1496b10e573SMarc-André Lureau     TabletChardev *tablet = (TabletChardev *)dev;
1506b10e573SMarc-André Lureau     InputMoveEvent *move;
1516b10e573SMarc-André Lureau     InputBtnEvent *btn;
1526b10e573SMarc-André Lureau 
1536b10e573SMarc-André Lureau     switch (evt->type) {
1546b10e573SMarc-André Lureau     case INPUT_EVENT_KIND_ABS:
1556b10e573SMarc-André Lureau         move = evt->u.abs.data;
1566b10e573SMarc-André Lureau         tablet->axis[move->axis] = move->value;
1576b10e573SMarc-André Lureau         break;
1586b10e573SMarc-André Lureau 
1596b10e573SMarc-André Lureau     case INPUT_EVENT_KIND_BTN:
1606b10e573SMarc-André Lureau         btn = evt->u.btn.data;
1616b10e573SMarc-André Lureau         tablet->btns[btn->button] = btn->down;
1626b10e573SMarc-André Lureau         break;
1636b10e573SMarc-André Lureau 
1646b10e573SMarc-André Lureau     default:
1656b10e573SMarc-André Lureau         /* keep gcc happy */
1666b10e573SMarc-André Lureau         break;
1676b10e573SMarc-André Lureau     }
1686b10e573SMarc-André Lureau }
1696b10e573SMarc-André Lureau 
1706b10e573SMarc-André Lureau static void wctablet_input_sync(DeviceState *dev)
1716b10e573SMarc-André Lureau {
1726b10e573SMarc-André Lureau     TabletChardev *tablet = (TabletChardev *)dev;
1736b10e573SMarc-André Lureau 
1746b10e573SMarc-André Lureau     if (tablet->send_events) {
1756b10e573SMarc-André Lureau         wctablet_queue_event(tablet);
1766b10e573SMarc-André Lureau     }
1776b10e573SMarc-André Lureau }
1786b10e573SMarc-André Lureau 
1796b10e573SMarc-André Lureau static QemuInputHandler wctablet_handler = {
180129263c6SPhilippe Mathieu-Daudé     .name  = "QEMU Wacom Pen Tablet",
1816b10e573SMarc-André Lureau     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
1826b10e573SMarc-André Lureau     .event = wctablet_input_event,
1836b10e573SMarc-André Lureau     .sync  = wctablet_input_sync,
1846b10e573SMarc-André Lureau };
1856b10e573SMarc-André Lureau 
1866b10e573SMarc-André Lureau static void wctablet_chr_accept_input(Chardev *chr)
1876b10e573SMarc-André Lureau {
1886b10e573SMarc-André Lureau     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
1896b10e573SMarc-André Lureau     int len, canWrite;
1906b10e573SMarc-André Lureau 
1916b10e573SMarc-André Lureau     canWrite = qemu_chr_be_can_write(chr);
1926b10e573SMarc-André Lureau     len = canWrite;
1936b10e573SMarc-André Lureau     if (len > tablet->outlen) {
1946b10e573SMarc-André Lureau         len = tablet->outlen;
1956b10e573SMarc-André Lureau     }
1966b10e573SMarc-André Lureau 
1976b10e573SMarc-André Lureau     if (len) {
1986b10e573SMarc-André Lureau         qemu_chr_be_write(chr, tablet->outbuf, len);
1996b10e573SMarc-André Lureau         tablet->outlen -= len;
2006b10e573SMarc-André Lureau         if (tablet->outlen) {
2016b10e573SMarc-André Lureau             memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen);
2026b10e573SMarc-André Lureau         }
2036b10e573SMarc-André Lureau     }
2046b10e573SMarc-André Lureau }
2056b10e573SMarc-André Lureau 
2066b10e573SMarc-André Lureau static int wctablet_chr_write(struct Chardev *chr,
2076b10e573SMarc-André Lureau                               const uint8_t *buf, int len)
2086b10e573SMarc-André Lureau {
2096b10e573SMarc-André Lureau     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
2106b10e573SMarc-André Lureau     unsigned int i, clen;
2116b10e573SMarc-André Lureau     char *pos;
2126b10e573SMarc-André Lureau 
2136b10e573SMarc-André Lureau     if (tablet->line_speed != 9600) {
2146b10e573SMarc-André Lureau         return len;
2156b10e573SMarc-André Lureau     }
2166b10e573SMarc-André Lureau     for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) {
2176b10e573SMarc-André Lureau         tablet->query[tablet->query_index++] = buf[i];
2186b10e573SMarc-André Lureau     }
2196b10e573SMarc-André Lureau     tablet->query[tablet->query_index] = 0;
2206b10e573SMarc-André Lureau 
2216b10e573SMarc-André Lureau     while (tablet->query_index > 0 && (tablet->query[0] == '@'  ||
2226b10e573SMarc-André Lureau                                        tablet->query[0] == '\r' ||
2236b10e573SMarc-André Lureau                                        tablet->query[0] == '\n')) {
2246b10e573SMarc-André Lureau         wctablet_shift_input(tablet, 1);
2256b10e573SMarc-André Lureau     }
2266b10e573SMarc-André Lureau     if (!tablet->query_index) {
2276b10e573SMarc-André Lureau         return len;
2286b10e573SMarc-André Lureau     }
2296b10e573SMarc-André Lureau 
2306b10e573SMarc-André Lureau     if (strncmp((char *)tablet->query, "~#", 2) == 0) {
2316b10e573SMarc-André Lureau         /* init / detect sequence */
2326b10e573SMarc-André Lureau         trace_wct_init();
2336b10e573SMarc-André Lureau         wctablet_shift_input(tablet, 2);
2346b10e573SMarc-André Lureau         wctablet_queue_output(tablet, WC_MODEL_STRING,
2356b10e573SMarc-André Lureau                               WC_MODEL_STRING_LENGTH);
2366b10e573SMarc-André Lureau         return len;
2376b10e573SMarc-André Lureau     }
2386b10e573SMarc-André Lureau 
2396b10e573SMarc-André Lureau     /* detect line */
2406b10e573SMarc-André Lureau     pos = strchr((char *)tablet->query, '\r');
2416b10e573SMarc-André Lureau     if (!pos) {
2426b10e573SMarc-André Lureau         pos = strchr((char *)tablet->query, '\n');
2436b10e573SMarc-André Lureau     }
2446b10e573SMarc-André Lureau     if (!pos) {
2456b10e573SMarc-André Lureau         return len;
2466b10e573SMarc-André Lureau     }
2476b10e573SMarc-André Lureau     clen = pos - (char *)tablet->query;
2486b10e573SMarc-André Lureau 
2496b10e573SMarc-André Lureau     /* process commands */
2506b10e573SMarc-André Lureau     if (strncmp((char *)tablet->query, "RE", 2) == 0 &&
2516b10e573SMarc-André Lureau         clen == 2) {
2526b10e573SMarc-André Lureau         trace_wct_cmd_re();
2536b10e573SMarc-André Lureau         wctablet_shift_input(tablet, 3);
2546b10e573SMarc-André Lureau         wctablet_queue_output(tablet, WC_CONFIG_STRING,
2556b10e573SMarc-André Lureau                               WC_CONFIG_STRING_LENGTH);
2566b10e573SMarc-André Lureau 
2576b10e573SMarc-André Lureau     } else if (strncmp((char *)tablet->query, "ST", 2) == 0 &&
2586b10e573SMarc-André Lureau                clen == 2) {
2596b10e573SMarc-André Lureau         trace_wct_cmd_st();
2606b10e573SMarc-André Lureau         wctablet_shift_input(tablet, 3);
2616b10e573SMarc-André Lureau         tablet->send_events = true;
2626b10e573SMarc-André Lureau         wctablet_queue_event(tablet);
2636b10e573SMarc-André Lureau 
2646b10e573SMarc-André Lureau     } else if (strncmp((char *)tablet->query, "SP", 2) == 0 &&
2656b10e573SMarc-André Lureau                clen == 2) {
2666b10e573SMarc-André Lureau         trace_wct_cmd_sp();
2676b10e573SMarc-André Lureau         wctablet_shift_input(tablet, 3);
2686b10e573SMarc-André Lureau         tablet->send_events = false;
2696b10e573SMarc-André Lureau 
2706b10e573SMarc-André Lureau     } else if (strncmp((char *)tablet->query, "TS", 2) == 0 &&
2716b10e573SMarc-André Lureau                clen == 3) {
2726b10e573SMarc-André Lureau         unsigned int input = tablet->query[2];
2736b10e573SMarc-André Lureau         uint8_t codes[7] = {
2746b10e573SMarc-André Lureau             0xa3,
2756b10e573SMarc-André Lureau             ((input & 0x80) == 0) ? 0x7e : 0x7f,
2766b10e573SMarc-André Lureau             (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7),
2776b10e573SMarc-André Lureau             0x03,
2786b10e573SMarc-André Lureau             0x7f,
2796b10e573SMarc-André Lureau             0x7f,
2806b10e573SMarc-André Lureau             0x00,
2816b10e573SMarc-André Lureau         };
2826b10e573SMarc-André Lureau         trace_wct_cmd_ts(input);
2836b10e573SMarc-André Lureau         wctablet_shift_input(tablet, 4);
2846b10e573SMarc-André Lureau         wctablet_queue_output(tablet, codes, 7);
2856b10e573SMarc-André Lureau 
2866b10e573SMarc-André Lureau     } else {
2876b10e573SMarc-André Lureau         tablet->query[clen] = 0; /* terminate line for printing */
2886b10e573SMarc-André Lureau         trace_wct_cmd_other((char *)tablet->query);
2896b10e573SMarc-André Lureau         wctablet_shift_input(tablet, clen + 1);
2906b10e573SMarc-André Lureau 
2916b10e573SMarc-André Lureau     }
2926b10e573SMarc-André Lureau 
2936b10e573SMarc-André Lureau     return len;
2946b10e573SMarc-André Lureau }
2956b10e573SMarc-André Lureau 
2966b10e573SMarc-André Lureau static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg)
2976b10e573SMarc-André Lureau {
2986b10e573SMarc-André Lureau     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
2996b10e573SMarc-André Lureau     QEMUSerialSetParams *ssp;
3006b10e573SMarc-André Lureau 
3016b10e573SMarc-André Lureau     switch (cmd) {
3026b10e573SMarc-André Lureau     case CHR_IOCTL_SERIAL_SET_PARAMS:
3036b10e573SMarc-André Lureau         ssp = arg;
3046b10e573SMarc-André Lureau         if (tablet->line_speed != ssp->speed) {
3056b10e573SMarc-André Lureau             trace_wct_speed(ssp->speed);
3066b10e573SMarc-André Lureau             wctablet_reset(tablet);
3076b10e573SMarc-André Lureau             tablet->line_speed = ssp->speed;
3086b10e573SMarc-André Lureau         }
3096b10e573SMarc-André Lureau         break;
3106b10e573SMarc-André Lureau     default:
3116b10e573SMarc-André Lureau         return -ENOTSUP;
3126b10e573SMarc-André Lureau     }
3136b10e573SMarc-André Lureau     return 0;
3146b10e573SMarc-André Lureau }
3156b10e573SMarc-André Lureau 
3166b10e573SMarc-André Lureau static void wctablet_chr_finalize(Object *obj)
3176b10e573SMarc-André Lureau {
3186b10e573SMarc-André Lureau     TabletChardev *tablet = WCTABLET_CHARDEV(obj);
3196b10e573SMarc-André Lureau 
3206b10e573SMarc-André Lureau     qemu_input_handler_unregister(tablet->hs);
3216b10e573SMarc-André Lureau     g_free(tablet);
3226b10e573SMarc-André Lureau }
3236b10e573SMarc-André Lureau 
3246b10e573SMarc-André Lureau static void wctablet_chr_open(Chardev *chr,
3256b10e573SMarc-André Lureau                               ChardevBackend *backend,
3266b10e573SMarc-André Lureau                               bool *be_opened,
3276b10e573SMarc-André Lureau                               Error **errp)
3286b10e573SMarc-André Lureau {
3296b10e573SMarc-André Lureau     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
3306b10e573SMarc-André Lureau 
3316b10e573SMarc-André Lureau     *be_opened = true;
3326b10e573SMarc-André Lureau 
3336b10e573SMarc-André Lureau     /* init state machine */
3346b10e573SMarc-André Lureau     memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH);
3356b10e573SMarc-André Lureau     tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH;
3366b10e573SMarc-André Lureau     tablet->query_index = 0;
3376b10e573SMarc-André Lureau 
3386b10e573SMarc-André Lureau     tablet->hs = qemu_input_handler_register((DeviceState *)tablet,
3396b10e573SMarc-André Lureau                                              &wctablet_handler);
3406b10e573SMarc-André Lureau }
3416b10e573SMarc-André Lureau 
3426b10e573SMarc-André Lureau static void wctablet_chr_class_init(ObjectClass *oc, void *data)
3436b10e573SMarc-André Lureau {
3446b10e573SMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
3456b10e573SMarc-André Lureau 
3466b10e573SMarc-André Lureau     cc->open = wctablet_chr_open;
3476b10e573SMarc-André Lureau     cc->chr_write = wctablet_chr_write;
3486b10e573SMarc-André Lureau     cc->chr_ioctl = wctablet_chr_ioctl;
3496b10e573SMarc-André Lureau     cc->chr_accept_input = wctablet_chr_accept_input;
3506b10e573SMarc-André Lureau }
3516b10e573SMarc-André Lureau 
3526b10e573SMarc-André Lureau static const TypeInfo wctablet_type_info = {
3536b10e573SMarc-André Lureau     .name = TYPE_CHARDEV_WCTABLET,
3546b10e573SMarc-André Lureau     .parent = TYPE_CHARDEV,
3556b10e573SMarc-André Lureau     .instance_size = sizeof(TabletChardev),
3566b10e573SMarc-André Lureau     .instance_finalize = wctablet_chr_finalize,
3576b10e573SMarc-André Lureau     .class_init = wctablet_chr_class_init,
3586b10e573SMarc-André Lureau };
3596b10e573SMarc-André Lureau 
3606b10e573SMarc-André Lureau static void register_types(void)
3616b10e573SMarc-André Lureau {
3626b10e573SMarc-André Lureau      type_register_static(&wctablet_type_info);
3636b10e573SMarc-André Lureau }
3646b10e573SMarc-André Lureau 
3656b10e573SMarc-André Lureau type_init(register_types);
366