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