1378af961SAnatoli Huseu1 /* 2378af961SAnatoli Huseu1 * QEMU Wacom Penpartner serial tablet emulation 3378af961SAnatoli Huseu1 * 4378af961SAnatoli Huseu1 * some protocol details: 5378af961SAnatoli Huseu1 * http://linuxwacom.sourceforge.net/wiki/index.php/Serial_Protocol_IV 6378af961SAnatoli Huseu1 * 7378af961SAnatoli Huseu1 * Copyright (c) 2016 Anatoli Huseu1 8378af961SAnatoli Huseu1 * Copyright (c) 2016,17 Gerd Hoffmann 9378af961SAnatoli Huseu1 * 10378af961SAnatoli Huseu1 * Permission is hereby granted, free of charge, to any person obtaining a copy 11378af961SAnatoli Huseu1 * of this software and associated documentation files (the "Software"), to 12378af961SAnatoli Huseu1 * deal in the Software without restriction, including without limitation 13378af961SAnatoli Huseu1 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 14378af961SAnatoli Huseu1 * and/or sell copies of the Software, and to permit persons to whom the 15378af961SAnatoli Huseu1 * Software is furnished to do so, subject to the following conditions: 16378af961SAnatoli Huseu1 * 17378af961SAnatoli Huseu1 * The above copyright notice and this permission notice shall be included in 18378af961SAnatoli Huseu1 * all copies or substantial portions of the Software. 19378af961SAnatoli Huseu1 * 20378af961SAnatoli Huseu1 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21378af961SAnatoli Huseu1 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22378af961SAnatoli Huseu1 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23378af961SAnatoli Huseu1 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24378af961SAnatoli Huseu1 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM 25378af961SAnatoli Huseu1 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26378af961SAnatoli Huseu1 * THE SOFTWARE. 27378af961SAnatoli Huseu1 */ 28378af961SAnatoli Huseu1 29378af961SAnatoli Huseu1 #include "qemu/osdep.h" 30*0b8fa32fSMarkus Armbruster #include "qemu/module.h" 317566c6efSMarc-André Lureau #include "chardev/char-serial.h" 32378af961SAnatoli Huseu1 #include "ui/console.h" 33378af961SAnatoli Huseu1 #include "ui/input.h" 34378af961SAnatoli Huseu1 #include "trace.h" 35378af961SAnatoli Huseu1 36378af961SAnatoli Huseu1 37378af961SAnatoli Huseu1 #define WC_OUTPUT_BUF_MAX_LEN 512 38378af961SAnatoli Huseu1 #define WC_COMMAND_MAX_LEN 60 39378af961SAnatoli Huseu1 40378af961SAnatoli Huseu1 #define WC_L7(n) ((n) & 127) 41378af961SAnatoli Huseu1 #define WC_M7(n) (((n) >> 7) & 127) 42378af961SAnatoli Huseu1 #define WC_H2(n) ((n) >> 14) 43378af961SAnatoli Huseu1 44378af961SAnatoli Huseu1 #define WC_L4(n) ((n) & 15) 45378af961SAnatoli Huseu1 #define WC_H4(n) (((n) >> 4) & 15) 46378af961SAnatoli Huseu1 47378af961SAnatoli Huseu1 /* Model string and config string */ 48378af961SAnatoli Huseu1 #define WC_MODEL_STRING_LENGTH 18 49378af961SAnatoli Huseu1 uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,"; 50378af961SAnatoli Huseu1 51378af961SAnatoli Huseu1 #define WC_CONFIG_STRING_LENGTH 8 52378af961SAnatoli Huseu1 uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0"; 53378af961SAnatoli Huseu1 54378af961SAnatoli Huseu1 #define WC_FULL_CONFIG_STRING_LENGTH 61 55378af961SAnatoli Huseu1 uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = { 56378af961SAnatoli Huseu1 0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c, 57378af961SAnatoli Huseu1 0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30, 58378af961SAnatoli Huseu1 0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c, 59378af961SAnatoli Huseu1 0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c, 60378af961SAnatoli Huseu1 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 61378af961SAnatoli Huseu1 0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52, 62378af961SAnatoli Huseu1 0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d, 63378af961SAnatoli Huseu1 0x0a, 0x45, 0x37, 0x29 64378af961SAnatoli Huseu1 }; 65378af961SAnatoli Huseu1 66378af961SAnatoli Huseu1 /* This structure is used to save private info for Wacom Tablet. */ 67378af961SAnatoli Huseu1 typedef struct { 68378af961SAnatoli Huseu1 Chardev parent; 69378af961SAnatoli Huseu1 QemuInputHandlerState *hs; 70378af961SAnatoli Huseu1 71378af961SAnatoli Huseu1 /* Query string from serial */ 72378af961SAnatoli Huseu1 uint8_t query[100]; 73378af961SAnatoli Huseu1 int query_index; 74378af961SAnatoli Huseu1 75378af961SAnatoli Huseu1 /* Command to be sent to serial port */ 76378af961SAnatoli Huseu1 uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN]; 77378af961SAnatoli Huseu1 int outlen; 78378af961SAnatoli Huseu1 79378af961SAnatoli Huseu1 int line_speed; 80378af961SAnatoli Huseu1 bool send_events; 81378af961SAnatoli Huseu1 int axis[INPUT_AXIS__MAX]; 82378af961SAnatoli Huseu1 bool btns[INPUT_BUTTON__MAX]; 83378af961SAnatoli Huseu1 84378af961SAnatoli Huseu1 } TabletChardev; 85378af961SAnatoli Huseu1 86378af961SAnatoli Huseu1 #define TYPE_CHARDEV_WCTABLET "chardev-wctablet" 87378af961SAnatoli Huseu1 #define WCTABLET_CHARDEV(obj) \ 88378af961SAnatoli Huseu1 OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET) 89378af961SAnatoli Huseu1 90378af961SAnatoli Huseu1 91378af961SAnatoli Huseu1 static void wctablet_chr_accept_input(Chardev *chr); 92378af961SAnatoli Huseu1 93378af961SAnatoli Huseu1 static void wctablet_shift_input(TabletChardev *tablet, int count) 94378af961SAnatoli Huseu1 { 95378af961SAnatoli Huseu1 tablet->query_index -= count; 96378af961SAnatoli Huseu1 memmove(tablet->query, tablet->query + count, tablet->query_index); 97378af961SAnatoli Huseu1 tablet->query[tablet->query_index] = 0; 98378af961SAnatoli Huseu1 } 99378af961SAnatoli Huseu1 100378af961SAnatoli Huseu1 static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count) 101378af961SAnatoli Huseu1 { 102378af961SAnatoli Huseu1 if (tablet->outlen + count > sizeof(tablet->outbuf)) { 103378af961SAnatoli Huseu1 return; 104378af961SAnatoli Huseu1 } 105378af961SAnatoli Huseu1 106378af961SAnatoli Huseu1 memcpy(tablet->outbuf + tablet->outlen, buf, count); 107378af961SAnatoli Huseu1 tablet->outlen += count; 108378af961SAnatoli Huseu1 wctablet_chr_accept_input(CHARDEV(tablet)); 109378af961SAnatoli Huseu1 } 110378af961SAnatoli Huseu1 111378af961SAnatoli Huseu1 static void wctablet_reset(TabletChardev *tablet) 112378af961SAnatoli Huseu1 { 113378af961SAnatoli Huseu1 /* clear buffers */ 114378af961SAnatoli Huseu1 tablet->query_index = 0; 115378af961SAnatoli Huseu1 tablet->outlen = 0; 116378af961SAnatoli Huseu1 /* reset state */ 117378af961SAnatoli Huseu1 tablet->send_events = false; 118378af961SAnatoli Huseu1 } 119378af961SAnatoli Huseu1 120378af961SAnatoli Huseu1 static void wctablet_queue_event(TabletChardev *tablet) 121378af961SAnatoli Huseu1 { 122378af961SAnatoli Huseu1 uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 }; 123378af961SAnatoli Huseu1 124378af961SAnatoli Huseu1 if (tablet->line_speed != 9600) { 125378af961SAnatoli Huseu1 return; 126378af961SAnatoli Huseu1 } 127378af961SAnatoli Huseu1 128378af961SAnatoli Huseu1 int newX = tablet->axis[INPUT_AXIS_X] * 0.1537; 129378af961SAnatoli Huseu1 int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152; 130378af961SAnatoli Huseu1 131378af961SAnatoli Huseu1 codes[0] = codes[0] | WC_H2(newX); 132378af961SAnatoli Huseu1 codes[1] = codes[1] | WC_M7(newX); 133378af961SAnatoli Huseu1 codes[2] = codes[2] | WC_L7(newX); 134378af961SAnatoli Huseu1 135378af961SAnatoli Huseu1 codes[3] = codes[3] | WC_H2(nexY); 136378af961SAnatoli Huseu1 codes[4] = codes[4] | WC_M7(nexY); 137378af961SAnatoli Huseu1 codes[5] = codes[5] | WC_L7(nexY); 138378af961SAnatoli Huseu1 139378af961SAnatoli Huseu1 if (tablet->btns[INPUT_BUTTON_LEFT]) { 140378af961SAnatoli Huseu1 codes[0] = 0xa0; 141378af961SAnatoli Huseu1 } 142378af961SAnatoli Huseu1 143378af961SAnatoli Huseu1 wctablet_queue_output(tablet, codes, 7); 144378af961SAnatoli Huseu1 } 145378af961SAnatoli Huseu1 146378af961SAnatoli Huseu1 static void wctablet_input_event(DeviceState *dev, QemuConsole *src, 147378af961SAnatoli Huseu1 InputEvent *evt) 148378af961SAnatoli Huseu1 { 149378af961SAnatoli Huseu1 TabletChardev *tablet = (TabletChardev *)dev; 150378af961SAnatoli Huseu1 InputMoveEvent *move; 151378af961SAnatoli Huseu1 InputBtnEvent *btn; 152378af961SAnatoli Huseu1 153378af961SAnatoli Huseu1 switch (evt->type) { 154378af961SAnatoli Huseu1 case INPUT_EVENT_KIND_ABS: 155378af961SAnatoli Huseu1 move = evt->u.abs.data; 156378af961SAnatoli Huseu1 tablet->axis[move->axis] = move->value; 157378af961SAnatoli Huseu1 break; 158378af961SAnatoli Huseu1 159378af961SAnatoli Huseu1 case INPUT_EVENT_KIND_BTN: 160378af961SAnatoli Huseu1 btn = evt->u.btn.data; 161378af961SAnatoli Huseu1 tablet->btns[btn->button] = btn->down; 162378af961SAnatoli Huseu1 break; 163378af961SAnatoli Huseu1 164378af961SAnatoli Huseu1 default: 165378af961SAnatoli Huseu1 /* keep gcc happy */ 166378af961SAnatoli Huseu1 break; 167378af961SAnatoli Huseu1 } 168378af961SAnatoli Huseu1 } 169378af961SAnatoli Huseu1 170378af961SAnatoli Huseu1 static void wctablet_input_sync(DeviceState *dev) 171378af961SAnatoli Huseu1 { 172378af961SAnatoli Huseu1 TabletChardev *tablet = (TabletChardev *)dev; 173378af961SAnatoli Huseu1 174378af961SAnatoli Huseu1 if (tablet->send_events) { 175378af961SAnatoli Huseu1 wctablet_queue_event(tablet); 176378af961SAnatoli Huseu1 } 177378af961SAnatoli Huseu1 } 178378af961SAnatoli Huseu1 179378af961SAnatoli Huseu1 static QemuInputHandler wctablet_handler = { 180129263c6SPhilippe Mathieu-Daudé .name = "QEMU Wacom Pen Tablet", 181378af961SAnatoli Huseu1 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, 182378af961SAnatoli Huseu1 .event = wctablet_input_event, 183378af961SAnatoli Huseu1 .sync = wctablet_input_sync, 184378af961SAnatoli Huseu1 }; 185378af961SAnatoli Huseu1 186378af961SAnatoli Huseu1 static void wctablet_chr_accept_input(Chardev *chr) 187378af961SAnatoli Huseu1 { 188378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 189378af961SAnatoli Huseu1 int len, canWrite; 190378af961SAnatoli Huseu1 191378af961SAnatoli Huseu1 canWrite = qemu_chr_be_can_write(chr); 192378af961SAnatoli Huseu1 len = canWrite; 193378af961SAnatoli Huseu1 if (len > tablet->outlen) { 194378af961SAnatoli Huseu1 len = tablet->outlen; 195378af961SAnatoli Huseu1 } 196378af961SAnatoli Huseu1 197378af961SAnatoli Huseu1 if (len) { 198378af961SAnatoli Huseu1 qemu_chr_be_write(chr, tablet->outbuf, len); 199378af961SAnatoli Huseu1 tablet->outlen -= len; 200378af961SAnatoli Huseu1 if (tablet->outlen) { 201378af961SAnatoli Huseu1 memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen); 202378af961SAnatoli Huseu1 } 203378af961SAnatoli Huseu1 } 204378af961SAnatoli Huseu1 } 205378af961SAnatoli Huseu1 206378af961SAnatoli Huseu1 static int wctablet_chr_write(struct Chardev *chr, 207378af961SAnatoli Huseu1 const uint8_t *buf, int len) 208378af961SAnatoli Huseu1 { 209378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 210378af961SAnatoli Huseu1 unsigned int i, clen; 211378af961SAnatoli Huseu1 char *pos; 212378af961SAnatoli Huseu1 213378af961SAnatoli Huseu1 if (tablet->line_speed != 9600) { 214378af961SAnatoli Huseu1 return len; 215378af961SAnatoli Huseu1 } 216378af961SAnatoli Huseu1 for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) { 217378af961SAnatoli Huseu1 tablet->query[tablet->query_index++] = buf[i]; 218378af961SAnatoli Huseu1 } 219378af961SAnatoli Huseu1 tablet->query[tablet->query_index] = 0; 220378af961SAnatoli Huseu1 221378af961SAnatoli Huseu1 while (tablet->query_index > 0 && (tablet->query[0] == '@' || 222378af961SAnatoli Huseu1 tablet->query[0] == '\r' || 223378af961SAnatoli Huseu1 tablet->query[0] == '\n')) { 224378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 1); 225378af961SAnatoli Huseu1 } 226378af961SAnatoli Huseu1 if (!tablet->query_index) { 227378af961SAnatoli Huseu1 return len; 228378af961SAnatoli Huseu1 } 229378af961SAnatoli Huseu1 230378af961SAnatoli Huseu1 if (strncmp((char *)tablet->query, "~#", 2) == 0) { 231378af961SAnatoli Huseu1 /* init / detect sequence */ 232378af961SAnatoli Huseu1 trace_wct_init(); 233378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 2); 234378af961SAnatoli Huseu1 wctablet_queue_output(tablet, WC_MODEL_STRING, 235378af961SAnatoli Huseu1 WC_MODEL_STRING_LENGTH); 236378af961SAnatoli Huseu1 return len; 237378af961SAnatoli Huseu1 } 238378af961SAnatoli Huseu1 239378af961SAnatoli Huseu1 /* detect line */ 240378af961SAnatoli Huseu1 pos = strchr((char *)tablet->query, '\r'); 241378af961SAnatoli Huseu1 if (!pos) { 242378af961SAnatoli Huseu1 pos = strchr((char *)tablet->query, '\n'); 243378af961SAnatoli Huseu1 } 244378af961SAnatoli Huseu1 if (!pos) { 245378af961SAnatoli Huseu1 return len; 246378af961SAnatoli Huseu1 } 247378af961SAnatoli Huseu1 clen = pos - (char *)tablet->query; 248378af961SAnatoli Huseu1 249378af961SAnatoli Huseu1 /* process commands */ 250378af961SAnatoli Huseu1 if (strncmp((char *)tablet->query, "RE", 2) == 0 && 251378af961SAnatoli Huseu1 clen == 2) { 252378af961SAnatoli Huseu1 trace_wct_cmd_re(); 253378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 3); 254378af961SAnatoli Huseu1 wctablet_queue_output(tablet, WC_CONFIG_STRING, 255378af961SAnatoli Huseu1 WC_CONFIG_STRING_LENGTH); 256378af961SAnatoli Huseu1 257378af961SAnatoli Huseu1 } else if (strncmp((char *)tablet->query, "ST", 2) == 0 && 258378af961SAnatoli Huseu1 clen == 2) { 259378af961SAnatoli Huseu1 trace_wct_cmd_st(); 260378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 3); 261378af961SAnatoli Huseu1 tablet->send_events = true; 262378af961SAnatoli Huseu1 wctablet_queue_event(tablet); 263378af961SAnatoli Huseu1 264378af961SAnatoli Huseu1 } else if (strncmp((char *)tablet->query, "SP", 2) == 0 && 265378af961SAnatoli Huseu1 clen == 2) { 266378af961SAnatoli Huseu1 trace_wct_cmd_sp(); 267378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 3); 268378af961SAnatoli Huseu1 tablet->send_events = false; 269378af961SAnatoli Huseu1 270378af961SAnatoli Huseu1 } else if (strncmp((char *)tablet->query, "TS", 2) == 0 && 271378af961SAnatoli Huseu1 clen == 3) { 272378af961SAnatoli Huseu1 unsigned int input = tablet->query[2]; 273378af961SAnatoli Huseu1 uint8_t codes[7] = { 274378af961SAnatoli Huseu1 0xa3, 275378af961SAnatoli Huseu1 ((input & 0x80) == 0) ? 0x7e : 0x7f, 276378af961SAnatoli Huseu1 (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7), 277378af961SAnatoli Huseu1 0x03, 278378af961SAnatoli Huseu1 0x7f, 279378af961SAnatoli Huseu1 0x7f, 280378af961SAnatoli Huseu1 0x00, 281378af961SAnatoli Huseu1 }; 282378af961SAnatoli Huseu1 trace_wct_cmd_ts(input); 283378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 4); 284378af961SAnatoli Huseu1 wctablet_queue_output(tablet, codes, 7); 285378af961SAnatoli Huseu1 286378af961SAnatoli Huseu1 } else { 287378af961SAnatoli Huseu1 tablet->query[clen] = 0; /* terminate line for printing */ 288378af961SAnatoli Huseu1 trace_wct_cmd_other((char *)tablet->query); 289378af961SAnatoli Huseu1 wctablet_shift_input(tablet, clen + 1); 290378af961SAnatoli Huseu1 291378af961SAnatoli Huseu1 } 292378af961SAnatoli Huseu1 293378af961SAnatoli Huseu1 return len; 294378af961SAnatoli Huseu1 } 295378af961SAnatoli Huseu1 296378af961SAnatoli Huseu1 static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg) 297378af961SAnatoli Huseu1 { 298378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 299378af961SAnatoli Huseu1 QEMUSerialSetParams *ssp; 300378af961SAnatoli Huseu1 301378af961SAnatoli Huseu1 switch (cmd) { 302378af961SAnatoli Huseu1 case CHR_IOCTL_SERIAL_SET_PARAMS: 303378af961SAnatoli Huseu1 ssp = arg; 304378af961SAnatoli Huseu1 if (tablet->line_speed != ssp->speed) { 305378af961SAnatoli Huseu1 trace_wct_speed(ssp->speed); 306378af961SAnatoli Huseu1 wctablet_reset(tablet); 307378af961SAnatoli Huseu1 tablet->line_speed = ssp->speed; 308378af961SAnatoli Huseu1 } 309378af961SAnatoli Huseu1 break; 310378af961SAnatoli Huseu1 default: 311378af961SAnatoli Huseu1 return -ENOTSUP; 312378af961SAnatoli Huseu1 } 313378af961SAnatoli Huseu1 return 0; 314378af961SAnatoli Huseu1 } 315378af961SAnatoli Huseu1 316378af961SAnatoli Huseu1 static void wctablet_chr_finalize(Object *obj) 317378af961SAnatoli Huseu1 { 318378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(obj); 319378af961SAnatoli Huseu1 320378af961SAnatoli Huseu1 qemu_input_handler_unregister(tablet->hs); 321378af961SAnatoli Huseu1 g_free(tablet); 322378af961SAnatoli Huseu1 } 323378af961SAnatoli Huseu1 324378af961SAnatoli Huseu1 static void wctablet_chr_open(Chardev *chr, 325378af961SAnatoli Huseu1 ChardevBackend *backend, 326378af961SAnatoli Huseu1 bool *be_opened, 327378af961SAnatoli Huseu1 Error **errp) 328378af961SAnatoli Huseu1 { 329378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 330378af961SAnatoli Huseu1 331378af961SAnatoli Huseu1 *be_opened = true; 332378af961SAnatoli Huseu1 333378af961SAnatoli Huseu1 /* init state machine */ 334378af961SAnatoli Huseu1 memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH); 335378af961SAnatoli Huseu1 tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH; 336378af961SAnatoli Huseu1 tablet->query_index = 0; 337378af961SAnatoli Huseu1 338378af961SAnatoli Huseu1 tablet->hs = qemu_input_handler_register((DeviceState *)tablet, 339378af961SAnatoli Huseu1 &wctablet_handler); 340378af961SAnatoli Huseu1 } 341378af961SAnatoli Huseu1 342378af961SAnatoli Huseu1 static void wctablet_chr_class_init(ObjectClass *oc, void *data) 343378af961SAnatoli Huseu1 { 344378af961SAnatoli Huseu1 ChardevClass *cc = CHARDEV_CLASS(oc); 345378af961SAnatoli Huseu1 346378af961SAnatoli Huseu1 cc->open = wctablet_chr_open; 347378af961SAnatoli Huseu1 cc->chr_write = wctablet_chr_write; 348378af961SAnatoli Huseu1 cc->chr_ioctl = wctablet_chr_ioctl; 349378af961SAnatoli Huseu1 cc->chr_accept_input = wctablet_chr_accept_input; 350378af961SAnatoli Huseu1 } 351378af961SAnatoli Huseu1 352378af961SAnatoli Huseu1 static const TypeInfo wctablet_type_info = { 353378af961SAnatoli Huseu1 .name = TYPE_CHARDEV_WCTABLET, 354378af961SAnatoli Huseu1 .parent = TYPE_CHARDEV, 355378af961SAnatoli Huseu1 .instance_size = sizeof(TabletChardev), 356378af961SAnatoli Huseu1 .instance_finalize = wctablet_chr_finalize, 357378af961SAnatoli Huseu1 .class_init = wctablet_chr_class_init, 358378af961SAnatoli Huseu1 }; 359378af961SAnatoli Huseu1 360378af961SAnatoli Huseu1 static void register_types(void) 361378af961SAnatoli Huseu1 { 362378af961SAnatoli Huseu1 type_register_static(&wctablet_type_info); 363378af961SAnatoli Huseu1 } 364378af961SAnatoli Huseu1 365378af961SAnatoli Huseu1 type_init(register_types); 366