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" 300b8fa32fSMarkus 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" 35*db1015e9SEduardo Habkost #include "qom/object.h" 366b10e573SMarc-André Lureau 376b10e573SMarc-André Lureau 386b10e573SMarc-André Lureau #define WC_OUTPUT_BUF_MAX_LEN 512 396b10e573SMarc-André Lureau #define WC_COMMAND_MAX_LEN 60 406b10e573SMarc-André Lureau 416b10e573SMarc-André Lureau #define WC_L7(n) ((n) & 127) 426b10e573SMarc-André Lureau #define WC_M7(n) (((n) >> 7) & 127) 436b10e573SMarc-André Lureau #define WC_H2(n) ((n) >> 14) 446b10e573SMarc-André Lureau 456b10e573SMarc-André Lureau #define WC_L4(n) ((n) & 15) 466b10e573SMarc-André Lureau #define WC_H4(n) (((n) >> 4) & 15) 476b10e573SMarc-André Lureau 486b10e573SMarc-André Lureau /* Model string and config string */ 496b10e573SMarc-André Lureau #define WC_MODEL_STRING_LENGTH 18 506b10e573SMarc-André Lureau uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,"; 516b10e573SMarc-André Lureau 526b10e573SMarc-André Lureau #define WC_CONFIG_STRING_LENGTH 8 536b10e573SMarc-André Lureau uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0"; 546b10e573SMarc-André Lureau 556b10e573SMarc-André Lureau #define WC_FULL_CONFIG_STRING_LENGTH 61 566b10e573SMarc-André Lureau uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = { 576b10e573SMarc-André Lureau 0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c, 586b10e573SMarc-André Lureau 0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30, 596b10e573SMarc-André Lureau 0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c, 606b10e573SMarc-André Lureau 0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c, 616b10e573SMarc-André Lureau 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 626b10e573SMarc-André Lureau 0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52, 636b10e573SMarc-André Lureau 0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d, 646b10e573SMarc-André Lureau 0x0a, 0x45, 0x37, 0x29 656b10e573SMarc-André Lureau }; 666b10e573SMarc-André Lureau 676b10e573SMarc-André Lureau /* This structure is used to save private info for Wacom Tablet. */ 68*db1015e9SEduardo Habkost struct TabletChardev { 696b10e573SMarc-André Lureau Chardev parent; 706b10e573SMarc-André Lureau QemuInputHandlerState *hs; 716b10e573SMarc-André Lureau 726b10e573SMarc-André Lureau /* Query string from serial */ 736b10e573SMarc-André Lureau uint8_t query[100]; 746b10e573SMarc-André Lureau int query_index; 756b10e573SMarc-André Lureau 766b10e573SMarc-André Lureau /* Command to be sent to serial port */ 776b10e573SMarc-André Lureau uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN]; 786b10e573SMarc-André Lureau int outlen; 796b10e573SMarc-André Lureau 806b10e573SMarc-André Lureau int line_speed; 816b10e573SMarc-André Lureau bool send_events; 826b10e573SMarc-André Lureau int axis[INPUT_AXIS__MAX]; 836b10e573SMarc-André Lureau bool btns[INPUT_BUTTON__MAX]; 846b10e573SMarc-André Lureau 85*db1015e9SEduardo Habkost }; 86*db1015e9SEduardo Habkost typedef struct TabletChardev TabletChardev; 876b10e573SMarc-André Lureau 886b10e573SMarc-André Lureau #define TYPE_CHARDEV_WCTABLET "chardev-wctablet" 896b10e573SMarc-André Lureau #define WCTABLET_CHARDEV(obj) \ 906b10e573SMarc-André Lureau OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET) 916b10e573SMarc-André Lureau 926b10e573SMarc-André Lureau 936b10e573SMarc-André Lureau static void wctablet_chr_accept_input(Chardev *chr); 946b10e573SMarc-André Lureau 956b10e573SMarc-André Lureau static void wctablet_shift_input(TabletChardev *tablet, int count) 966b10e573SMarc-André Lureau { 976b10e573SMarc-André Lureau tablet->query_index -= count; 986b10e573SMarc-André Lureau memmove(tablet->query, tablet->query + count, tablet->query_index); 996b10e573SMarc-André Lureau tablet->query[tablet->query_index] = 0; 1006b10e573SMarc-André Lureau } 1016b10e573SMarc-André Lureau 1026b10e573SMarc-André Lureau static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count) 1036b10e573SMarc-André Lureau { 1046b10e573SMarc-André Lureau if (tablet->outlen + count > sizeof(tablet->outbuf)) { 1056b10e573SMarc-André Lureau return; 1066b10e573SMarc-André Lureau } 1076b10e573SMarc-André Lureau 1086b10e573SMarc-André Lureau memcpy(tablet->outbuf + tablet->outlen, buf, count); 1096b10e573SMarc-André Lureau tablet->outlen += count; 1106b10e573SMarc-André Lureau wctablet_chr_accept_input(CHARDEV(tablet)); 1116b10e573SMarc-André Lureau } 1126b10e573SMarc-André Lureau 1136b10e573SMarc-André Lureau static void wctablet_reset(TabletChardev *tablet) 1146b10e573SMarc-André Lureau { 1156b10e573SMarc-André Lureau /* clear buffers */ 1166b10e573SMarc-André Lureau tablet->query_index = 0; 1176b10e573SMarc-André Lureau tablet->outlen = 0; 1186b10e573SMarc-André Lureau /* reset state */ 1196b10e573SMarc-André Lureau tablet->send_events = false; 1206b10e573SMarc-André Lureau } 1216b10e573SMarc-André Lureau 1226b10e573SMarc-André Lureau static void wctablet_queue_event(TabletChardev *tablet) 1236b10e573SMarc-André Lureau { 1246b10e573SMarc-André Lureau uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 }; 1256b10e573SMarc-André Lureau 1266b10e573SMarc-André Lureau if (tablet->line_speed != 9600) { 1276b10e573SMarc-André Lureau return; 1286b10e573SMarc-André Lureau } 1296b10e573SMarc-André Lureau 1306b10e573SMarc-André Lureau int newX = tablet->axis[INPUT_AXIS_X] * 0.1537; 1316b10e573SMarc-André Lureau int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152; 1326b10e573SMarc-André Lureau 1336b10e573SMarc-André Lureau codes[0] = codes[0] | WC_H2(newX); 1346b10e573SMarc-André Lureau codes[1] = codes[1] | WC_M7(newX); 1356b10e573SMarc-André Lureau codes[2] = codes[2] | WC_L7(newX); 1366b10e573SMarc-André Lureau 1376b10e573SMarc-André Lureau codes[3] = codes[3] | WC_H2(nexY); 1386b10e573SMarc-André Lureau codes[4] = codes[4] | WC_M7(nexY); 1396b10e573SMarc-André Lureau codes[5] = codes[5] | WC_L7(nexY); 1406b10e573SMarc-André Lureau 1416b10e573SMarc-André Lureau if (tablet->btns[INPUT_BUTTON_LEFT]) { 1426b10e573SMarc-André Lureau codes[0] = 0xa0; 1436b10e573SMarc-André Lureau } 1446b10e573SMarc-André Lureau 1456b10e573SMarc-André Lureau wctablet_queue_output(tablet, codes, 7); 1466b10e573SMarc-André Lureau } 1476b10e573SMarc-André Lureau 1486b10e573SMarc-André Lureau static void wctablet_input_event(DeviceState *dev, QemuConsole *src, 1496b10e573SMarc-André Lureau InputEvent *evt) 1506b10e573SMarc-André Lureau { 1516b10e573SMarc-André Lureau TabletChardev *tablet = (TabletChardev *)dev; 1526b10e573SMarc-André Lureau InputMoveEvent *move; 1536b10e573SMarc-André Lureau InputBtnEvent *btn; 1546b10e573SMarc-André Lureau 1556b10e573SMarc-André Lureau switch (evt->type) { 1566b10e573SMarc-André Lureau case INPUT_EVENT_KIND_ABS: 1576b10e573SMarc-André Lureau move = evt->u.abs.data; 1586b10e573SMarc-André Lureau tablet->axis[move->axis] = move->value; 1596b10e573SMarc-André Lureau break; 1606b10e573SMarc-André Lureau 1616b10e573SMarc-André Lureau case INPUT_EVENT_KIND_BTN: 1626b10e573SMarc-André Lureau btn = evt->u.btn.data; 1636b10e573SMarc-André Lureau tablet->btns[btn->button] = btn->down; 1646b10e573SMarc-André Lureau break; 1656b10e573SMarc-André Lureau 1666b10e573SMarc-André Lureau default: 1676b10e573SMarc-André Lureau /* keep gcc happy */ 1686b10e573SMarc-André Lureau break; 1696b10e573SMarc-André Lureau } 1706b10e573SMarc-André Lureau } 1716b10e573SMarc-André Lureau 1726b10e573SMarc-André Lureau static void wctablet_input_sync(DeviceState *dev) 1736b10e573SMarc-André Lureau { 1746b10e573SMarc-André Lureau TabletChardev *tablet = (TabletChardev *)dev; 1756b10e573SMarc-André Lureau 1766b10e573SMarc-André Lureau if (tablet->send_events) { 1776b10e573SMarc-André Lureau wctablet_queue_event(tablet); 1786b10e573SMarc-André Lureau } 1796b10e573SMarc-André Lureau } 1806b10e573SMarc-André Lureau 1816b10e573SMarc-André Lureau static QemuInputHandler wctablet_handler = { 182129263c6SPhilippe Mathieu-Daudé .name = "QEMU Wacom Pen Tablet", 1836b10e573SMarc-André Lureau .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, 1846b10e573SMarc-André Lureau .event = wctablet_input_event, 1856b10e573SMarc-André Lureau .sync = wctablet_input_sync, 1866b10e573SMarc-André Lureau }; 1876b10e573SMarc-André Lureau 1886b10e573SMarc-André Lureau static void wctablet_chr_accept_input(Chardev *chr) 1896b10e573SMarc-André Lureau { 1906b10e573SMarc-André Lureau TabletChardev *tablet = WCTABLET_CHARDEV(chr); 1916b10e573SMarc-André Lureau int len, canWrite; 1926b10e573SMarc-André Lureau 1936b10e573SMarc-André Lureau canWrite = qemu_chr_be_can_write(chr); 1946b10e573SMarc-André Lureau len = canWrite; 1956b10e573SMarc-André Lureau if (len > tablet->outlen) { 1966b10e573SMarc-André Lureau len = tablet->outlen; 1976b10e573SMarc-André Lureau } 1986b10e573SMarc-André Lureau 1996b10e573SMarc-André Lureau if (len) { 2006b10e573SMarc-André Lureau qemu_chr_be_write(chr, tablet->outbuf, len); 2016b10e573SMarc-André Lureau tablet->outlen -= len; 2026b10e573SMarc-André Lureau if (tablet->outlen) { 2036b10e573SMarc-André Lureau memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen); 2046b10e573SMarc-André Lureau } 2056b10e573SMarc-André Lureau } 2066b10e573SMarc-André Lureau } 2076b10e573SMarc-André Lureau 2086b10e573SMarc-André Lureau static int wctablet_chr_write(struct Chardev *chr, 2096b10e573SMarc-André Lureau const uint8_t *buf, int len) 2106b10e573SMarc-André Lureau { 2116b10e573SMarc-André Lureau TabletChardev *tablet = WCTABLET_CHARDEV(chr); 2126b10e573SMarc-André Lureau unsigned int i, clen; 2136b10e573SMarc-André Lureau char *pos; 2146b10e573SMarc-André Lureau 2156b10e573SMarc-André Lureau if (tablet->line_speed != 9600) { 2166b10e573SMarc-André Lureau return len; 2176b10e573SMarc-André Lureau } 2186b10e573SMarc-André Lureau for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) { 2196b10e573SMarc-André Lureau tablet->query[tablet->query_index++] = buf[i]; 2206b10e573SMarc-André Lureau } 2216b10e573SMarc-André Lureau tablet->query[tablet->query_index] = 0; 2226b10e573SMarc-André Lureau 2236b10e573SMarc-André Lureau while (tablet->query_index > 0 && (tablet->query[0] == '@' || 2246b10e573SMarc-André Lureau tablet->query[0] == '\r' || 2256b10e573SMarc-André Lureau tablet->query[0] == '\n')) { 2266b10e573SMarc-André Lureau wctablet_shift_input(tablet, 1); 2276b10e573SMarc-André Lureau } 2286b10e573SMarc-André Lureau if (!tablet->query_index) { 2296b10e573SMarc-André Lureau return len; 2306b10e573SMarc-André Lureau } 2316b10e573SMarc-André Lureau 2326b10e573SMarc-André Lureau if (strncmp((char *)tablet->query, "~#", 2) == 0) { 2336b10e573SMarc-André Lureau /* init / detect sequence */ 2346b10e573SMarc-André Lureau trace_wct_init(); 2356b10e573SMarc-André Lureau wctablet_shift_input(tablet, 2); 2366b10e573SMarc-André Lureau wctablet_queue_output(tablet, WC_MODEL_STRING, 2376b10e573SMarc-André Lureau WC_MODEL_STRING_LENGTH); 2386b10e573SMarc-André Lureau return len; 2396b10e573SMarc-André Lureau } 2406b10e573SMarc-André Lureau 2416b10e573SMarc-André Lureau /* detect line */ 2426b10e573SMarc-André Lureau pos = strchr((char *)tablet->query, '\r'); 2436b10e573SMarc-André Lureau if (!pos) { 2446b10e573SMarc-André Lureau pos = strchr((char *)tablet->query, '\n'); 2456b10e573SMarc-André Lureau } 2466b10e573SMarc-André Lureau if (!pos) { 2476b10e573SMarc-André Lureau return len; 2486b10e573SMarc-André Lureau } 2496b10e573SMarc-André Lureau clen = pos - (char *)tablet->query; 2506b10e573SMarc-André Lureau 2516b10e573SMarc-André Lureau /* process commands */ 2526b10e573SMarc-André Lureau if (strncmp((char *)tablet->query, "RE", 2) == 0 && 2536b10e573SMarc-André Lureau clen == 2) { 2546b10e573SMarc-André Lureau trace_wct_cmd_re(); 2556b10e573SMarc-André Lureau wctablet_shift_input(tablet, 3); 2566b10e573SMarc-André Lureau wctablet_queue_output(tablet, WC_CONFIG_STRING, 2576b10e573SMarc-André Lureau WC_CONFIG_STRING_LENGTH); 2586b10e573SMarc-André Lureau 2596b10e573SMarc-André Lureau } else if (strncmp((char *)tablet->query, "ST", 2) == 0 && 2606b10e573SMarc-André Lureau clen == 2) { 2616b10e573SMarc-André Lureau trace_wct_cmd_st(); 2626b10e573SMarc-André Lureau wctablet_shift_input(tablet, 3); 2636b10e573SMarc-André Lureau tablet->send_events = true; 2646b10e573SMarc-André Lureau wctablet_queue_event(tablet); 2656b10e573SMarc-André Lureau 2666b10e573SMarc-André Lureau } else if (strncmp((char *)tablet->query, "SP", 2) == 0 && 2676b10e573SMarc-André Lureau clen == 2) { 2686b10e573SMarc-André Lureau trace_wct_cmd_sp(); 2696b10e573SMarc-André Lureau wctablet_shift_input(tablet, 3); 2706b10e573SMarc-André Lureau tablet->send_events = false; 2716b10e573SMarc-André Lureau 2726b10e573SMarc-André Lureau } else if (strncmp((char *)tablet->query, "TS", 2) == 0 && 2736b10e573SMarc-André Lureau clen == 3) { 2746b10e573SMarc-André Lureau unsigned int input = tablet->query[2]; 2756b10e573SMarc-André Lureau uint8_t codes[7] = { 2766b10e573SMarc-André Lureau 0xa3, 2776b10e573SMarc-André Lureau ((input & 0x80) == 0) ? 0x7e : 0x7f, 2786b10e573SMarc-André Lureau (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7), 2796b10e573SMarc-André Lureau 0x03, 2806b10e573SMarc-André Lureau 0x7f, 2816b10e573SMarc-André Lureau 0x7f, 2826b10e573SMarc-André Lureau 0x00, 2836b10e573SMarc-André Lureau }; 2846b10e573SMarc-André Lureau trace_wct_cmd_ts(input); 2856b10e573SMarc-André Lureau wctablet_shift_input(tablet, 4); 2866b10e573SMarc-André Lureau wctablet_queue_output(tablet, codes, 7); 2876b10e573SMarc-André Lureau 2886b10e573SMarc-André Lureau } else { 2896b10e573SMarc-André Lureau tablet->query[clen] = 0; /* terminate line for printing */ 2906b10e573SMarc-André Lureau trace_wct_cmd_other((char *)tablet->query); 2916b10e573SMarc-André Lureau wctablet_shift_input(tablet, clen + 1); 2926b10e573SMarc-André Lureau 2936b10e573SMarc-André Lureau } 2946b10e573SMarc-André Lureau 2956b10e573SMarc-André Lureau return len; 2966b10e573SMarc-André Lureau } 2976b10e573SMarc-André Lureau 2986b10e573SMarc-André Lureau static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg) 2996b10e573SMarc-André Lureau { 3006b10e573SMarc-André Lureau TabletChardev *tablet = WCTABLET_CHARDEV(chr); 3016b10e573SMarc-André Lureau QEMUSerialSetParams *ssp; 3026b10e573SMarc-André Lureau 3036b10e573SMarc-André Lureau switch (cmd) { 3046b10e573SMarc-André Lureau case CHR_IOCTL_SERIAL_SET_PARAMS: 3056b10e573SMarc-André Lureau ssp = arg; 3066b10e573SMarc-André Lureau if (tablet->line_speed != ssp->speed) { 3076b10e573SMarc-André Lureau trace_wct_speed(ssp->speed); 3086b10e573SMarc-André Lureau wctablet_reset(tablet); 3096b10e573SMarc-André Lureau tablet->line_speed = ssp->speed; 3106b10e573SMarc-André Lureau } 3116b10e573SMarc-André Lureau break; 3126b10e573SMarc-André Lureau default: 3136b10e573SMarc-André Lureau return -ENOTSUP; 3146b10e573SMarc-André Lureau } 3156b10e573SMarc-André Lureau return 0; 3166b10e573SMarc-André Lureau } 3176b10e573SMarc-André Lureau 3186b10e573SMarc-André Lureau static void wctablet_chr_finalize(Object *obj) 3196b10e573SMarc-André Lureau { 3206b10e573SMarc-André Lureau TabletChardev *tablet = WCTABLET_CHARDEV(obj); 3216b10e573SMarc-André Lureau 3226b10e573SMarc-André Lureau qemu_input_handler_unregister(tablet->hs); 3236b10e573SMarc-André Lureau g_free(tablet); 3246b10e573SMarc-André Lureau } 3256b10e573SMarc-André Lureau 3266b10e573SMarc-André Lureau static void wctablet_chr_open(Chardev *chr, 3276b10e573SMarc-André Lureau ChardevBackend *backend, 3286b10e573SMarc-André Lureau bool *be_opened, 3296b10e573SMarc-André Lureau Error **errp) 3306b10e573SMarc-André Lureau { 3316b10e573SMarc-André Lureau TabletChardev *tablet = WCTABLET_CHARDEV(chr); 3326b10e573SMarc-André Lureau 3336b10e573SMarc-André Lureau *be_opened = true; 3346b10e573SMarc-André Lureau 3356b10e573SMarc-André Lureau /* init state machine */ 3366b10e573SMarc-André Lureau memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH); 3376b10e573SMarc-André Lureau tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH; 3386b10e573SMarc-André Lureau tablet->query_index = 0; 3396b10e573SMarc-André Lureau 3406b10e573SMarc-André Lureau tablet->hs = qemu_input_handler_register((DeviceState *)tablet, 3416b10e573SMarc-André Lureau &wctablet_handler); 3426b10e573SMarc-André Lureau } 3436b10e573SMarc-André Lureau 3446b10e573SMarc-André Lureau static void wctablet_chr_class_init(ObjectClass *oc, void *data) 3456b10e573SMarc-André Lureau { 3466b10e573SMarc-André Lureau ChardevClass *cc = CHARDEV_CLASS(oc); 3476b10e573SMarc-André Lureau 3486b10e573SMarc-André Lureau cc->open = wctablet_chr_open; 3496b10e573SMarc-André Lureau cc->chr_write = wctablet_chr_write; 3506b10e573SMarc-André Lureau cc->chr_ioctl = wctablet_chr_ioctl; 3516b10e573SMarc-André Lureau cc->chr_accept_input = wctablet_chr_accept_input; 3526b10e573SMarc-André Lureau } 3536b10e573SMarc-André Lureau 3546b10e573SMarc-André Lureau static const TypeInfo wctablet_type_info = { 3556b10e573SMarc-André Lureau .name = TYPE_CHARDEV_WCTABLET, 3566b10e573SMarc-André Lureau .parent = TYPE_CHARDEV, 3576b10e573SMarc-André Lureau .instance_size = sizeof(TabletChardev), 3586b10e573SMarc-André Lureau .instance_finalize = wctablet_chr_finalize, 3596b10e573SMarc-André Lureau .class_init = wctablet_chr_class_init, 3606b10e573SMarc-André Lureau }; 3616b10e573SMarc-André Lureau 3626b10e573SMarc-André Lureau static void register_types(void) 3636b10e573SMarc-André Lureau { 3646b10e573SMarc-André Lureau type_register_static(&wctablet_type_info); 3656b10e573SMarc-André Lureau } 3666b10e573SMarc-André Lureau 3676b10e573SMarc-André Lureau type_init(register_types); 368