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 #include <stdlib.h> 29378af961SAnatoli Huseu1 #include <string.h> 30378af961SAnatoli Huseu1 #include <sys/time.h> 31378af961SAnatoli Huseu1 #include <time.h> 32378af961SAnatoli Huseu1 33378af961SAnatoli Huseu1 #include "qemu/osdep.h" 34378af961SAnatoli Huseu1 #include "qemu-common.h" 35*7566c6efSMarc-André Lureau #include "chardev/char-serial.h" 36378af961SAnatoli Huseu1 #include "ui/console.h" 37378af961SAnatoli Huseu1 #include "ui/input.h" 38378af961SAnatoli Huseu1 #include "trace.h" 39378af961SAnatoli Huseu1 40378af961SAnatoli Huseu1 41378af961SAnatoli Huseu1 #define WC_OUTPUT_BUF_MAX_LEN 512 42378af961SAnatoli Huseu1 #define WC_COMMAND_MAX_LEN 60 43378af961SAnatoli Huseu1 44378af961SAnatoli Huseu1 #define WC_L7(n) ((n) & 127) 45378af961SAnatoli Huseu1 #define WC_M7(n) (((n) >> 7) & 127) 46378af961SAnatoli Huseu1 #define WC_H2(n) ((n) >> 14) 47378af961SAnatoli Huseu1 48378af961SAnatoli Huseu1 #define WC_L4(n) ((n) & 15) 49378af961SAnatoli Huseu1 #define WC_H4(n) (((n) >> 4) & 15) 50378af961SAnatoli Huseu1 51378af961SAnatoli Huseu1 /* Model string and config string */ 52378af961SAnatoli Huseu1 #define WC_MODEL_STRING_LENGTH 18 53378af961SAnatoli Huseu1 uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,"; 54378af961SAnatoli Huseu1 55378af961SAnatoli Huseu1 #define WC_CONFIG_STRING_LENGTH 8 56378af961SAnatoli Huseu1 uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0"; 57378af961SAnatoli Huseu1 58378af961SAnatoli Huseu1 #define WC_FULL_CONFIG_STRING_LENGTH 61 59378af961SAnatoli Huseu1 uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = { 60378af961SAnatoli Huseu1 0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c, 61378af961SAnatoli Huseu1 0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30, 62378af961SAnatoli Huseu1 0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c, 63378af961SAnatoli Huseu1 0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c, 64378af961SAnatoli Huseu1 0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a, 65378af961SAnatoli Huseu1 0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52, 66378af961SAnatoli Huseu1 0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d, 67378af961SAnatoli Huseu1 0x0a, 0x45, 0x37, 0x29 68378af961SAnatoli Huseu1 }; 69378af961SAnatoli Huseu1 70378af961SAnatoli Huseu1 /* This structure is used to save private info for Wacom Tablet. */ 71378af961SAnatoli Huseu1 typedef struct { 72378af961SAnatoli Huseu1 Chardev parent; 73378af961SAnatoli Huseu1 QemuInputHandlerState *hs; 74378af961SAnatoli Huseu1 75378af961SAnatoli Huseu1 /* Query string from serial */ 76378af961SAnatoli Huseu1 uint8_t query[100]; 77378af961SAnatoli Huseu1 int query_index; 78378af961SAnatoli Huseu1 79378af961SAnatoli Huseu1 /* Command to be sent to serial port */ 80378af961SAnatoli Huseu1 uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN]; 81378af961SAnatoli Huseu1 int outlen; 82378af961SAnatoli Huseu1 83378af961SAnatoli Huseu1 int line_speed; 84378af961SAnatoli Huseu1 bool send_events; 85378af961SAnatoli Huseu1 int axis[INPUT_AXIS__MAX]; 86378af961SAnatoli Huseu1 bool btns[INPUT_BUTTON__MAX]; 87378af961SAnatoli Huseu1 88378af961SAnatoli Huseu1 } TabletChardev; 89378af961SAnatoli Huseu1 90378af961SAnatoli Huseu1 #define TYPE_CHARDEV_WCTABLET "chardev-wctablet" 91378af961SAnatoli Huseu1 #define WCTABLET_CHARDEV(obj) \ 92378af961SAnatoli Huseu1 OBJECT_CHECK(TabletChardev, (obj), TYPE_CHARDEV_WCTABLET) 93378af961SAnatoli Huseu1 94378af961SAnatoli Huseu1 95378af961SAnatoli Huseu1 static void wctablet_chr_accept_input(Chardev *chr); 96378af961SAnatoli Huseu1 97378af961SAnatoli Huseu1 static void wctablet_shift_input(TabletChardev *tablet, int count) 98378af961SAnatoli Huseu1 { 99378af961SAnatoli Huseu1 tablet->query_index -= count; 100378af961SAnatoli Huseu1 memmove(tablet->query, tablet->query + count, tablet->query_index); 101378af961SAnatoli Huseu1 tablet->query[tablet->query_index] = 0; 102378af961SAnatoli Huseu1 } 103378af961SAnatoli Huseu1 104378af961SAnatoli Huseu1 static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count) 105378af961SAnatoli Huseu1 { 106378af961SAnatoli Huseu1 if (tablet->outlen + count > sizeof(tablet->outbuf)) { 107378af961SAnatoli Huseu1 return; 108378af961SAnatoli Huseu1 } 109378af961SAnatoli Huseu1 110378af961SAnatoli Huseu1 memcpy(tablet->outbuf + tablet->outlen, buf, count); 111378af961SAnatoli Huseu1 tablet->outlen += count; 112378af961SAnatoli Huseu1 wctablet_chr_accept_input(CHARDEV(tablet)); 113378af961SAnatoli Huseu1 } 114378af961SAnatoli Huseu1 115378af961SAnatoli Huseu1 static void wctablet_reset(TabletChardev *tablet) 116378af961SAnatoli Huseu1 { 117378af961SAnatoli Huseu1 /* clear buffers */ 118378af961SAnatoli Huseu1 tablet->query_index = 0; 119378af961SAnatoli Huseu1 tablet->outlen = 0; 120378af961SAnatoli Huseu1 /* reset state */ 121378af961SAnatoli Huseu1 tablet->send_events = false; 122378af961SAnatoli Huseu1 } 123378af961SAnatoli Huseu1 124378af961SAnatoli Huseu1 static void wctablet_queue_event(TabletChardev *tablet) 125378af961SAnatoli Huseu1 { 126378af961SAnatoli Huseu1 uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 }; 127378af961SAnatoli Huseu1 128378af961SAnatoli Huseu1 if (tablet->line_speed != 9600) { 129378af961SAnatoli Huseu1 return; 130378af961SAnatoli Huseu1 } 131378af961SAnatoli Huseu1 132378af961SAnatoli Huseu1 int newX = tablet->axis[INPUT_AXIS_X] * 0.1537; 133378af961SAnatoli Huseu1 int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152; 134378af961SAnatoli Huseu1 135378af961SAnatoli Huseu1 codes[0] = codes[0] | WC_H2(newX); 136378af961SAnatoli Huseu1 codes[1] = codes[1] | WC_M7(newX); 137378af961SAnatoli Huseu1 codes[2] = codes[2] | WC_L7(newX); 138378af961SAnatoli Huseu1 139378af961SAnatoli Huseu1 codes[3] = codes[3] | WC_H2(nexY); 140378af961SAnatoli Huseu1 codes[4] = codes[4] | WC_M7(nexY); 141378af961SAnatoli Huseu1 codes[5] = codes[5] | WC_L7(nexY); 142378af961SAnatoli Huseu1 143378af961SAnatoli Huseu1 if (tablet->btns[INPUT_BUTTON_LEFT]) { 144378af961SAnatoli Huseu1 codes[0] = 0xa0; 145378af961SAnatoli Huseu1 } 146378af961SAnatoli Huseu1 147378af961SAnatoli Huseu1 wctablet_queue_output(tablet, codes, 7); 148378af961SAnatoli Huseu1 } 149378af961SAnatoli Huseu1 150378af961SAnatoli Huseu1 static void wctablet_input_event(DeviceState *dev, QemuConsole *src, 151378af961SAnatoli Huseu1 InputEvent *evt) 152378af961SAnatoli Huseu1 { 153378af961SAnatoli Huseu1 TabletChardev *tablet = (TabletChardev *)dev; 154378af961SAnatoli Huseu1 InputMoveEvent *move; 155378af961SAnatoli Huseu1 InputBtnEvent *btn; 156378af961SAnatoli Huseu1 157378af961SAnatoli Huseu1 switch (evt->type) { 158378af961SAnatoli Huseu1 case INPUT_EVENT_KIND_ABS: 159378af961SAnatoli Huseu1 move = evt->u.abs.data; 160378af961SAnatoli Huseu1 tablet->axis[move->axis] = move->value; 161378af961SAnatoli Huseu1 break; 162378af961SAnatoli Huseu1 163378af961SAnatoli Huseu1 case INPUT_EVENT_KIND_BTN: 164378af961SAnatoli Huseu1 btn = evt->u.btn.data; 165378af961SAnatoli Huseu1 tablet->btns[btn->button] = btn->down; 166378af961SAnatoli Huseu1 break; 167378af961SAnatoli Huseu1 168378af961SAnatoli Huseu1 default: 169378af961SAnatoli Huseu1 /* keep gcc happy */ 170378af961SAnatoli Huseu1 break; 171378af961SAnatoli Huseu1 } 172378af961SAnatoli Huseu1 } 173378af961SAnatoli Huseu1 174378af961SAnatoli Huseu1 static void wctablet_input_sync(DeviceState *dev) 175378af961SAnatoli Huseu1 { 176378af961SAnatoli Huseu1 TabletChardev *tablet = (TabletChardev *)dev; 177378af961SAnatoli Huseu1 178378af961SAnatoli Huseu1 if (tablet->send_events) { 179378af961SAnatoli Huseu1 wctablet_queue_event(tablet); 180378af961SAnatoli Huseu1 } 181378af961SAnatoli Huseu1 } 182378af961SAnatoli Huseu1 183378af961SAnatoli Huseu1 static QemuInputHandler wctablet_handler = { 184378af961SAnatoli Huseu1 .name = "QEMU Wacome Pen Tablet", 185378af961SAnatoli Huseu1 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, 186378af961SAnatoli Huseu1 .event = wctablet_input_event, 187378af961SAnatoli Huseu1 .sync = wctablet_input_sync, 188378af961SAnatoli Huseu1 }; 189378af961SAnatoli Huseu1 190378af961SAnatoli Huseu1 static void wctablet_chr_accept_input(Chardev *chr) 191378af961SAnatoli Huseu1 { 192378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 193378af961SAnatoli Huseu1 int len, canWrite; 194378af961SAnatoli Huseu1 195378af961SAnatoli Huseu1 canWrite = qemu_chr_be_can_write(chr); 196378af961SAnatoli Huseu1 len = canWrite; 197378af961SAnatoli Huseu1 if (len > tablet->outlen) { 198378af961SAnatoli Huseu1 len = tablet->outlen; 199378af961SAnatoli Huseu1 } 200378af961SAnatoli Huseu1 201378af961SAnatoli Huseu1 if (len) { 202378af961SAnatoli Huseu1 qemu_chr_be_write(chr, tablet->outbuf, len); 203378af961SAnatoli Huseu1 tablet->outlen -= len; 204378af961SAnatoli Huseu1 if (tablet->outlen) { 205378af961SAnatoli Huseu1 memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen); 206378af961SAnatoli Huseu1 } 207378af961SAnatoli Huseu1 } 208378af961SAnatoli Huseu1 } 209378af961SAnatoli Huseu1 210378af961SAnatoli Huseu1 static int wctablet_chr_write(struct Chardev *chr, 211378af961SAnatoli Huseu1 const uint8_t *buf, int len) 212378af961SAnatoli Huseu1 { 213378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 214378af961SAnatoli Huseu1 unsigned int i, clen; 215378af961SAnatoli Huseu1 char *pos; 216378af961SAnatoli Huseu1 217378af961SAnatoli Huseu1 if (tablet->line_speed != 9600) { 218378af961SAnatoli Huseu1 return len; 219378af961SAnatoli Huseu1 } 220378af961SAnatoli Huseu1 for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) { 221378af961SAnatoli Huseu1 tablet->query[tablet->query_index++] = buf[i]; 222378af961SAnatoli Huseu1 } 223378af961SAnatoli Huseu1 tablet->query[tablet->query_index] = 0; 224378af961SAnatoli Huseu1 225378af961SAnatoli Huseu1 while (tablet->query_index > 0 && (tablet->query[0] == '@' || 226378af961SAnatoli Huseu1 tablet->query[0] == '\r' || 227378af961SAnatoli Huseu1 tablet->query[0] == '\n')) { 228378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 1); 229378af961SAnatoli Huseu1 } 230378af961SAnatoli Huseu1 if (!tablet->query_index) { 231378af961SAnatoli Huseu1 return len; 232378af961SAnatoli Huseu1 } 233378af961SAnatoli Huseu1 234378af961SAnatoli Huseu1 if (strncmp((char *)tablet->query, "~#", 2) == 0) { 235378af961SAnatoli Huseu1 /* init / detect sequence */ 236378af961SAnatoli Huseu1 trace_wct_init(); 237378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 2); 238378af961SAnatoli Huseu1 wctablet_queue_output(tablet, WC_MODEL_STRING, 239378af961SAnatoli Huseu1 WC_MODEL_STRING_LENGTH); 240378af961SAnatoli Huseu1 return len; 241378af961SAnatoli Huseu1 } 242378af961SAnatoli Huseu1 243378af961SAnatoli Huseu1 /* detect line */ 244378af961SAnatoli Huseu1 pos = strchr((char *)tablet->query, '\r'); 245378af961SAnatoli Huseu1 if (!pos) { 246378af961SAnatoli Huseu1 pos = strchr((char *)tablet->query, '\n'); 247378af961SAnatoli Huseu1 } 248378af961SAnatoli Huseu1 if (!pos) { 249378af961SAnatoli Huseu1 return len; 250378af961SAnatoli Huseu1 } 251378af961SAnatoli Huseu1 clen = pos - (char *)tablet->query; 252378af961SAnatoli Huseu1 253378af961SAnatoli Huseu1 /* process commands */ 254378af961SAnatoli Huseu1 if (strncmp((char *)tablet->query, "RE", 2) == 0 && 255378af961SAnatoli Huseu1 clen == 2) { 256378af961SAnatoli Huseu1 trace_wct_cmd_re(); 257378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 3); 258378af961SAnatoli Huseu1 wctablet_queue_output(tablet, WC_CONFIG_STRING, 259378af961SAnatoli Huseu1 WC_CONFIG_STRING_LENGTH); 260378af961SAnatoli Huseu1 261378af961SAnatoli Huseu1 } else if (strncmp((char *)tablet->query, "ST", 2) == 0 && 262378af961SAnatoli Huseu1 clen == 2) { 263378af961SAnatoli Huseu1 trace_wct_cmd_st(); 264378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 3); 265378af961SAnatoli Huseu1 tablet->send_events = true; 266378af961SAnatoli Huseu1 wctablet_queue_event(tablet); 267378af961SAnatoli Huseu1 268378af961SAnatoli Huseu1 } else if (strncmp((char *)tablet->query, "SP", 2) == 0 && 269378af961SAnatoli Huseu1 clen == 2) { 270378af961SAnatoli Huseu1 trace_wct_cmd_sp(); 271378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 3); 272378af961SAnatoli Huseu1 tablet->send_events = false; 273378af961SAnatoli Huseu1 274378af961SAnatoli Huseu1 } else if (strncmp((char *)tablet->query, "TS", 2) == 0 && 275378af961SAnatoli Huseu1 clen == 3) { 276378af961SAnatoli Huseu1 unsigned int input = tablet->query[2]; 277378af961SAnatoli Huseu1 uint8_t codes[7] = { 278378af961SAnatoli Huseu1 0xa3, 279378af961SAnatoli Huseu1 ((input & 0x80) == 0) ? 0x7e : 0x7f, 280378af961SAnatoli Huseu1 (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7), 281378af961SAnatoli Huseu1 0x03, 282378af961SAnatoli Huseu1 0x7f, 283378af961SAnatoli Huseu1 0x7f, 284378af961SAnatoli Huseu1 0x00, 285378af961SAnatoli Huseu1 }; 286378af961SAnatoli Huseu1 trace_wct_cmd_ts(input); 287378af961SAnatoli Huseu1 wctablet_shift_input(tablet, 4); 288378af961SAnatoli Huseu1 wctablet_queue_output(tablet, codes, 7); 289378af961SAnatoli Huseu1 290378af961SAnatoli Huseu1 } else { 291378af961SAnatoli Huseu1 tablet->query[clen] = 0; /* terminate line for printing */ 292378af961SAnatoli Huseu1 trace_wct_cmd_other((char *)tablet->query); 293378af961SAnatoli Huseu1 wctablet_shift_input(tablet, clen + 1); 294378af961SAnatoli Huseu1 295378af961SAnatoli Huseu1 } 296378af961SAnatoli Huseu1 297378af961SAnatoli Huseu1 return len; 298378af961SAnatoli Huseu1 } 299378af961SAnatoli Huseu1 300378af961SAnatoli Huseu1 static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg) 301378af961SAnatoli Huseu1 { 302378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 303378af961SAnatoli Huseu1 QEMUSerialSetParams *ssp; 304378af961SAnatoli Huseu1 305378af961SAnatoli Huseu1 switch (cmd) { 306378af961SAnatoli Huseu1 case CHR_IOCTL_SERIAL_SET_PARAMS: 307378af961SAnatoli Huseu1 ssp = arg; 308378af961SAnatoli Huseu1 if (tablet->line_speed != ssp->speed) { 309378af961SAnatoli Huseu1 trace_wct_speed(ssp->speed); 310378af961SAnatoli Huseu1 wctablet_reset(tablet); 311378af961SAnatoli Huseu1 tablet->line_speed = ssp->speed; 312378af961SAnatoli Huseu1 } 313378af961SAnatoli Huseu1 break; 314378af961SAnatoli Huseu1 default: 315378af961SAnatoli Huseu1 return -ENOTSUP; 316378af961SAnatoli Huseu1 } 317378af961SAnatoli Huseu1 return 0; 318378af961SAnatoli Huseu1 } 319378af961SAnatoli Huseu1 320378af961SAnatoli Huseu1 static void wctablet_chr_finalize(Object *obj) 321378af961SAnatoli Huseu1 { 322378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(obj); 323378af961SAnatoli Huseu1 324378af961SAnatoli Huseu1 qemu_input_handler_unregister(tablet->hs); 325378af961SAnatoli Huseu1 g_free(tablet); 326378af961SAnatoli Huseu1 } 327378af961SAnatoli Huseu1 328378af961SAnatoli Huseu1 static void wctablet_chr_open(Chardev *chr, 329378af961SAnatoli Huseu1 ChardevBackend *backend, 330378af961SAnatoli Huseu1 bool *be_opened, 331378af961SAnatoli Huseu1 Error **errp) 332378af961SAnatoli Huseu1 { 333378af961SAnatoli Huseu1 TabletChardev *tablet = WCTABLET_CHARDEV(chr); 334378af961SAnatoli Huseu1 335378af961SAnatoli Huseu1 *be_opened = true; 336378af961SAnatoli Huseu1 337378af961SAnatoli Huseu1 /* init state machine */ 338378af961SAnatoli Huseu1 memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH); 339378af961SAnatoli Huseu1 tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH; 340378af961SAnatoli Huseu1 tablet->query_index = 0; 341378af961SAnatoli Huseu1 342378af961SAnatoli Huseu1 tablet->hs = qemu_input_handler_register((DeviceState *)tablet, 343378af961SAnatoli Huseu1 &wctablet_handler); 344378af961SAnatoli Huseu1 } 345378af961SAnatoli Huseu1 346378af961SAnatoli Huseu1 static void wctablet_chr_class_init(ObjectClass *oc, void *data) 347378af961SAnatoli Huseu1 { 348378af961SAnatoli Huseu1 ChardevClass *cc = CHARDEV_CLASS(oc); 349378af961SAnatoli Huseu1 350378af961SAnatoli Huseu1 cc->open = wctablet_chr_open; 351378af961SAnatoli Huseu1 cc->chr_write = wctablet_chr_write; 352378af961SAnatoli Huseu1 cc->chr_ioctl = wctablet_chr_ioctl; 353378af961SAnatoli Huseu1 cc->chr_accept_input = wctablet_chr_accept_input; 354378af961SAnatoli Huseu1 } 355378af961SAnatoli Huseu1 356378af961SAnatoli Huseu1 static const TypeInfo wctablet_type_info = { 357378af961SAnatoli Huseu1 .name = TYPE_CHARDEV_WCTABLET, 358378af961SAnatoli Huseu1 .parent = TYPE_CHARDEV, 359378af961SAnatoli Huseu1 .instance_size = sizeof(TabletChardev), 360378af961SAnatoli Huseu1 .instance_finalize = wctablet_chr_finalize, 361378af961SAnatoli Huseu1 .class_init = wctablet_chr_class_init, 362378af961SAnatoli Huseu1 }; 363378af961SAnatoli Huseu1 364378af961SAnatoli Huseu1 static void register_types(void) 365378af961SAnatoli Huseu1 { 366378af961SAnatoli Huseu1 type_register_static(&wctablet_type_info); 367378af961SAnatoli Huseu1 } 368378af961SAnatoli Huseu1 369378af961SAnatoli Huseu1 type_init(register_types); 370