qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

wctablet.c (10580B)


      1 /*
      2  * QEMU Wacom Penpartner serial tablet emulation
      3  *
      4  * some protocol details:
      5  *   http://linuxwacom.sourceforge.net/wiki/index.php/Serial_Protocol_IV
      6  *
      7  * Copyright (c) 2016 Anatoli Huseu1
      8  * Copyright (c) 2016,17 Gerd Hoffmann
      9  *
     10  * Permission is hereby granted, free of charge, to any person obtaining a copy
     11  * of this software and associated documentation files (the "Software"), to
     12  * deal in the Software without restriction, including without limitation
     13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     14  * and/or sell copies of the Software, and to permit persons to whom the
     15  * Software is furnished to do so, subject to the following conditions:
     16  *
     17  * The above copyright notice and this permission notice shall be included in
     18  * all copies or substantial portions of the Software.
     19  *
     20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
     25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     26  * THE SOFTWARE.
     27  */
     28 
     29 #include "qemu/osdep.h"
     30 #include "qemu/module.h"
     31 #include "chardev/char-serial.h"
     32 #include "ui/console.h"
     33 #include "ui/input.h"
     34 #include "trace.h"
     35 #include "qom/object.h"
     36 
     37 
     38 #define WC_OUTPUT_BUF_MAX_LEN 512
     39 #define WC_COMMAND_MAX_LEN 60
     40 
     41 #define WC_L7(n) ((n) & 127)
     42 #define WC_M7(n) (((n) >> 7) & 127)
     43 #define WC_H2(n) ((n) >> 14)
     44 
     45 #define WC_L4(n) ((n) & 15)
     46 #define WC_H4(n) (((n) >> 4) & 15)
     47 
     48 /* Model string and config string */
     49 #define WC_MODEL_STRING_LENGTH 18
     50 uint8_t WC_MODEL_STRING[WC_MODEL_STRING_LENGTH + 1] = "~#CT-0045R,V1.3-5,";
     51 
     52 #define WC_CONFIG_STRING_LENGTH 8
     53 uint8_t WC_CONFIG_STRING[WC_CONFIG_STRING_LENGTH + 1] = "96,N,8,0";
     54 
     55 #define WC_FULL_CONFIG_STRING_LENGTH 61
     56 uint8_t WC_FULL_CONFIG_STRING[WC_FULL_CONFIG_STRING_LENGTH + 1] = {
     57     0x5c, 0x39, 0x36, 0x2c, 0x4e, 0x2c, 0x38, 0x2c,
     58     0x31, 0x28, 0x01, 0x24, 0x57, 0x41, 0x43, 0x30,
     59     0x30, 0x34, 0x35, 0x5c, 0x5c, 0x50, 0x45, 0x4e, 0x5c,
     60     0x57, 0x41, 0x43, 0x30, 0x30, 0x30, 0x30, 0x5c,
     61     0x54, 0x61, 0x62, 0x6c, 0x65, 0x74, 0x0d, 0x0a,
     62     0x43, 0x54, 0x2d, 0x30, 0x30, 0x34, 0x35, 0x52,
     63     0x2c, 0x56, 0x31, 0x2e, 0x33, 0x2d, 0x35, 0x0d,
     64     0x0a, 0x45, 0x37, 0x29
     65 };
     66 
     67 /* This structure is used to save private info for Wacom Tablet. */
     68 struct TabletChardev {
     69     Chardev parent;
     70     QemuInputHandlerState *hs;
     71 
     72     /* Query string from serial */
     73     uint8_t query[100];
     74     int query_index;
     75 
     76     /* Command to be sent to serial port */
     77     uint8_t outbuf[WC_OUTPUT_BUF_MAX_LEN];
     78     int outlen;
     79 
     80     int line_speed;
     81     bool send_events;
     82     int axis[INPUT_AXIS__MAX];
     83     bool btns[INPUT_BUTTON__MAX];
     84 
     85 };
     86 typedef struct TabletChardev TabletChardev;
     87 
     88 #define TYPE_CHARDEV_WCTABLET "chardev-wctablet"
     89 DECLARE_INSTANCE_CHECKER(TabletChardev, WCTABLET_CHARDEV,
     90                          TYPE_CHARDEV_WCTABLET)
     91 
     92 
     93 static void wctablet_chr_accept_input(Chardev *chr);
     94 
     95 static void wctablet_shift_input(TabletChardev *tablet, int count)
     96 {
     97     tablet->query_index -= count;
     98     memmove(tablet->query, tablet->query + count, tablet->query_index);
     99     tablet->query[tablet->query_index] = 0;
    100 }
    101 
    102 static void wctablet_queue_output(TabletChardev *tablet, uint8_t *buf, int count)
    103 {
    104     if (tablet->outlen + count > sizeof(tablet->outbuf)) {
    105         return;
    106     }
    107 
    108     memcpy(tablet->outbuf + tablet->outlen, buf, count);
    109     tablet->outlen += count;
    110     wctablet_chr_accept_input(CHARDEV(tablet));
    111 }
    112 
    113 static void wctablet_reset(TabletChardev *tablet)
    114 {
    115     /* clear buffers */
    116     tablet->query_index = 0;
    117     tablet->outlen = 0;
    118     /* reset state */
    119     tablet->send_events = false;
    120 }
    121 
    122 static void wctablet_queue_event(TabletChardev *tablet)
    123 {
    124     uint8_t codes[8] = { 0xe0, 0, 0, 0, 0, 0, 0 };
    125 
    126     if (tablet->line_speed != 9600) {
    127         return;
    128     }
    129 
    130     int newX = tablet->axis[INPUT_AXIS_X] * 0.1537;
    131     int nexY = tablet->axis[INPUT_AXIS_Y] * 0.1152;
    132 
    133     codes[0] = codes[0] | WC_H2(newX);
    134     codes[1] = codes[1] | WC_M7(newX);
    135     codes[2] = codes[2] | WC_L7(newX);
    136 
    137     codes[3] = codes[3] | WC_H2(nexY);
    138     codes[4] = codes[4] | WC_M7(nexY);
    139     codes[5] = codes[5] | WC_L7(nexY);
    140 
    141     if (tablet->btns[INPUT_BUTTON_LEFT]) {
    142         codes[0] = 0xa0;
    143     }
    144 
    145     wctablet_queue_output(tablet, codes, 7);
    146 }
    147 
    148 static void wctablet_input_event(DeviceState *dev, QemuConsole *src,
    149                                 InputEvent *evt)
    150 {
    151     TabletChardev *tablet = (TabletChardev *)dev;
    152     InputMoveEvent *move;
    153     InputBtnEvent *btn;
    154 
    155     switch (evt->type) {
    156     case INPUT_EVENT_KIND_ABS:
    157         move = evt->u.abs.data;
    158         tablet->axis[move->axis] = move->value;
    159         break;
    160 
    161     case INPUT_EVENT_KIND_BTN:
    162         btn = evt->u.btn.data;
    163         tablet->btns[btn->button] = btn->down;
    164         break;
    165 
    166     default:
    167         /* keep gcc happy */
    168         break;
    169     }
    170 }
    171 
    172 static void wctablet_input_sync(DeviceState *dev)
    173 {
    174     TabletChardev *tablet = (TabletChardev *)dev;
    175 
    176     if (tablet->send_events) {
    177         wctablet_queue_event(tablet);
    178     }
    179 }
    180 
    181 static QemuInputHandler wctablet_handler = {
    182     .name  = "QEMU Wacom Pen Tablet",
    183     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
    184     .event = wctablet_input_event,
    185     .sync  = wctablet_input_sync,
    186 };
    187 
    188 static void wctablet_chr_accept_input(Chardev *chr)
    189 {
    190     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
    191     int len, canWrite;
    192 
    193     canWrite = qemu_chr_be_can_write(chr);
    194     len = canWrite;
    195     if (len > tablet->outlen) {
    196         len = tablet->outlen;
    197     }
    198 
    199     if (len) {
    200         qemu_chr_be_write(chr, tablet->outbuf, len);
    201         tablet->outlen -= len;
    202         if (tablet->outlen) {
    203             memmove(tablet->outbuf, tablet->outbuf + len, tablet->outlen);
    204         }
    205     }
    206 }
    207 
    208 static int wctablet_chr_write(struct Chardev *chr,
    209                               const uint8_t *buf, int len)
    210 {
    211     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
    212     unsigned int i, clen;
    213     char *pos;
    214 
    215     if (tablet->line_speed != 9600) {
    216         return len;
    217     }
    218     for (i = 0; i < len && tablet->query_index < sizeof(tablet->query) - 1; i++) {
    219         tablet->query[tablet->query_index++] = buf[i];
    220     }
    221     tablet->query[tablet->query_index] = 0;
    222 
    223     while (tablet->query_index > 0 && (tablet->query[0] == '@'  ||
    224                                        tablet->query[0] == '\r' ||
    225                                        tablet->query[0] == '\n')) {
    226         wctablet_shift_input(tablet, 1);
    227     }
    228     if (!tablet->query_index) {
    229         return len;
    230     }
    231 
    232     if (strncmp((char *)tablet->query, "~#", 2) == 0) {
    233         /* init / detect sequence */
    234         trace_wct_init();
    235         wctablet_shift_input(tablet, 2);
    236         wctablet_queue_output(tablet, WC_MODEL_STRING,
    237                               WC_MODEL_STRING_LENGTH);
    238         return len;
    239     }
    240 
    241     /* detect line */
    242     pos = strchr((char *)tablet->query, '\r');
    243     if (!pos) {
    244         pos = strchr((char *)tablet->query, '\n');
    245     }
    246     if (!pos) {
    247         return len;
    248     }
    249     clen = pos - (char *)tablet->query;
    250 
    251     /* process commands */
    252     if (strncmp((char *)tablet->query, "RE", 2) == 0 &&
    253         clen == 2) {
    254         trace_wct_cmd_re();
    255         wctablet_shift_input(tablet, 3);
    256         wctablet_queue_output(tablet, WC_CONFIG_STRING,
    257                               WC_CONFIG_STRING_LENGTH);
    258 
    259     } else if (strncmp((char *)tablet->query, "ST", 2) == 0 &&
    260                clen == 2) {
    261         trace_wct_cmd_st();
    262         wctablet_shift_input(tablet, 3);
    263         tablet->send_events = true;
    264         wctablet_queue_event(tablet);
    265 
    266     } else if (strncmp((char *)tablet->query, "SP", 2) == 0 &&
    267                clen == 2) {
    268         trace_wct_cmd_sp();
    269         wctablet_shift_input(tablet, 3);
    270         tablet->send_events = false;
    271 
    272     } else if (strncmp((char *)tablet->query, "TS", 2) == 0 &&
    273                clen == 3) {
    274         unsigned int input = tablet->query[2];
    275         uint8_t codes[7] = {
    276             0xa3,
    277             ((input & 0x80) == 0) ? 0x7e : 0x7f,
    278             (((WC_H4(input) & 0x7) ^ 0x5) << 4) | (WC_L4(input) ^ 0x7),
    279             0x03,
    280             0x7f,
    281             0x7f,
    282             0x00,
    283         };
    284         trace_wct_cmd_ts(input);
    285         wctablet_shift_input(tablet, 4);
    286         wctablet_queue_output(tablet, codes, 7);
    287 
    288     } else {
    289         tablet->query[clen] = 0; /* terminate line for printing */
    290         trace_wct_cmd_other((char *)tablet->query);
    291         wctablet_shift_input(tablet, clen + 1);
    292 
    293     }
    294 
    295     return len;
    296 }
    297 
    298 static int wctablet_chr_ioctl(Chardev *chr, int cmd, void *arg)
    299 {
    300     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
    301     QEMUSerialSetParams *ssp;
    302 
    303     switch (cmd) {
    304     case CHR_IOCTL_SERIAL_SET_PARAMS:
    305         ssp = arg;
    306         if (tablet->line_speed != ssp->speed) {
    307             trace_wct_speed(ssp->speed);
    308             wctablet_reset(tablet);
    309             tablet->line_speed = ssp->speed;
    310         }
    311         break;
    312     default:
    313         return -ENOTSUP;
    314     }
    315     return 0;
    316 }
    317 
    318 static void wctablet_chr_finalize(Object *obj)
    319 {
    320     TabletChardev *tablet = WCTABLET_CHARDEV(obj);
    321 
    322     if (tablet->hs) {
    323         qemu_input_handler_unregister(tablet->hs);
    324     }
    325 }
    326 
    327 static void wctablet_chr_open(Chardev *chr,
    328                               ChardevBackend *backend,
    329                               bool *be_opened,
    330                               Error **errp)
    331 {
    332     TabletChardev *tablet = WCTABLET_CHARDEV(chr);
    333 
    334     *be_opened = true;
    335 
    336     /* init state machine */
    337     memcpy(tablet->outbuf, WC_FULL_CONFIG_STRING, WC_FULL_CONFIG_STRING_LENGTH);
    338     tablet->outlen = WC_FULL_CONFIG_STRING_LENGTH;
    339     tablet->query_index = 0;
    340 
    341     tablet->hs = qemu_input_handler_register((DeviceState *)tablet,
    342                                              &wctablet_handler);
    343 }
    344 
    345 static void wctablet_chr_class_init(ObjectClass *oc, void *data)
    346 {
    347     ChardevClass *cc = CHARDEV_CLASS(oc);
    348 
    349     cc->open = wctablet_chr_open;
    350     cc->chr_write = wctablet_chr_write;
    351     cc->chr_ioctl = wctablet_chr_ioctl;
    352     cc->chr_accept_input = wctablet_chr_accept_input;
    353 }
    354 
    355 static const TypeInfo wctablet_type_info = {
    356     .name = TYPE_CHARDEV_WCTABLET,
    357     .parent = TYPE_CHARDEV,
    358     .instance_size = sizeof(TabletChardev),
    359     .instance_finalize = wctablet_chr_finalize,
    360     .class_init = wctablet_chr_class_init,
    361 };
    362 
    363 static void register_types(void)
    364 {
    365      type_register_static(&wctablet_type_info);
    366 }
    367 
    368 type_init(register_types);