qemu/chardev/msmouse.c
<<
>>
Prefs
   1/*
   2 * QEMU Microsoft serial mouse emulation
   3 *
   4 * Copyright (c) 2008 Lubomir Rintel
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu/osdep.h"
  26#include "qemu/module.h"
  27#include "qemu/fifo8.h"
  28#include "chardev/char.h"
  29#include "chardev/char-serial.h"
  30#include "ui/console.h"
  31#include "ui/input.h"
  32#include "qom/object.h"
  33
  34#define MSMOUSE_LO6(n)  ((n) & 0x3f)
  35#define MSMOUSE_HI2(n)  (((n) & 0xc0) >> 6)
  36#define MSMOUSE_PWR(cm) (cm & (CHR_TIOCM_RTS | CHR_TIOCM_DTR))
  37
  38/* Serial PnP for 6 bit devices/mice sends all ASCII chars - 0x20 */
  39#define M(c) (c - 0x20)
  40/* Serial fifo size. */
  41#define MSMOUSE_BUF_SZ 64
  42
  43/* Mouse ID: Send "M3" cause we behave like a 3 button logitech mouse. */
  44const uint8_t mouse_id[] = {'M', '3'};
  45/*
  46 * PnP start "(", PnP version (1.0), vendor ID, product ID, '\\',
  47 * serial ID (omitted), '\\', MS class name, '\\', driver ID (omitted), '\\',
  48 * product description, checksum, ")"
  49 * Missing parts are inserted later.
  50 */
  51const uint8_t pnp_data[] = {M('('), 1, '$', M('Q'), M('M'), M('U'),
  52                         M('0'), M('0'), M('0'), M('1'),
  53                         M('\\'), M('\\'),
  54                         M('M'), M('O'), M('U'), M('S'), M('E'),
  55                         M('\\'), M('\\')};
  56
  57struct MouseChardev {
  58    Chardev parent;
  59
  60    QemuInputHandlerState *hs;
  61    int tiocm;
  62    int axis[INPUT_AXIS__MAX];
  63    bool btns[INPUT_BUTTON__MAX];
  64    bool btnc[INPUT_BUTTON__MAX];
  65    Fifo8 outbuf;
  66};
  67typedef struct MouseChardev MouseChardev;
  68
  69#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
  70DECLARE_INSTANCE_CHECKER(MouseChardev, MOUSE_CHARDEV,
  71                         TYPE_CHARDEV_MSMOUSE)
  72
  73static void msmouse_chr_accept_input(Chardev *chr)
  74{
  75    MouseChardev *mouse = MOUSE_CHARDEV(chr);
  76    uint32_t len, avail;
  77
  78    len = qemu_chr_be_can_write(chr);
  79    avail = fifo8_num_used(&mouse->outbuf);
  80    while (len > 0 && avail > 0) {
  81        const uint8_t *buf;
  82        uint32_t size;
  83
  84        buf = fifo8_pop_buf(&mouse->outbuf, MIN(len, avail), &size);
  85        qemu_chr_be_write(chr, buf, size);
  86        len = qemu_chr_be_can_write(chr);
  87        avail -= size;
  88    }
  89}
  90
  91static void msmouse_queue_event(MouseChardev *mouse)
  92{
  93    unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
  94    int dx, dy, count = 3;
  95
  96    dx = mouse->axis[INPUT_AXIS_X];
  97    mouse->axis[INPUT_AXIS_X] = 0;
  98
  99    dy = mouse->axis[INPUT_AXIS_Y];
 100    mouse->axis[INPUT_AXIS_Y] = 0;
 101
 102    /* Movement deltas */
 103    bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx);
 104    bytes[1] |= MSMOUSE_LO6(dx);
 105    bytes[2] |= MSMOUSE_LO6(dy);
 106
 107    /* Buttons */
 108    bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT]   ? 0x20 : 0x00);
 109    bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT]  ? 0x10 : 0x00);
 110    if (mouse->btns[INPUT_BUTTON_MIDDLE] ||
 111        mouse->btnc[INPUT_BUTTON_MIDDLE]) {
 112        bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00);
 113        mouse->btnc[INPUT_BUTTON_MIDDLE] = false;
 114        count++;
 115    }
 116
 117    if (fifo8_num_free(&mouse->outbuf) >= count) {
 118        fifo8_push_all(&mouse->outbuf, bytes, count);
 119    } else {
 120        /* queue full -> drop event */
 121    }
 122}
 123
 124static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
 125                                InputEvent *evt)
 126{
 127    MouseChardev *mouse = MOUSE_CHARDEV(dev);
 128    InputMoveEvent *move;
 129    InputBtnEvent *btn;
 130
 131    /* Ignore events if serial mouse powered down. */
 132    if (!MSMOUSE_PWR(mouse->tiocm)) {
 133        return;
 134    }
 135
 136    switch (evt->type) {
 137    case INPUT_EVENT_KIND_REL:
 138        move = evt->u.rel.data;
 139        mouse->axis[move->axis] += move->value;
 140        break;
 141
 142    case INPUT_EVENT_KIND_BTN:
 143        btn = evt->u.btn.data;
 144        mouse->btns[btn->button] = btn->down;
 145        mouse->btnc[btn->button] = true;
 146        break;
 147
 148    default:
 149        /* keep gcc happy */
 150        break;
 151    }
 152}
 153
 154static void msmouse_input_sync(DeviceState *dev)
 155{
 156    MouseChardev *mouse = MOUSE_CHARDEV(dev);
 157    Chardev *chr = CHARDEV(dev);
 158
 159    /* Ignore events if serial mouse powered down. */
 160    if (!MSMOUSE_PWR(mouse->tiocm)) {
 161        return;
 162    }
 163
 164    msmouse_queue_event(mouse);
 165    msmouse_chr_accept_input(chr);
 166}
 167
 168static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
 169{
 170    /* Ignore writes to mouse port */
 171    return len;
 172}
 173
 174static QemuInputHandler msmouse_handler = {
 175    .name  = "QEMU Microsoft Mouse",
 176    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
 177    .event = msmouse_input_event,
 178    .sync  = msmouse_input_sync,
 179};
 180
 181static int msmouse_ioctl(Chardev *chr, int cmd, void *arg)
 182{
 183    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 184    int c, i, j;
 185    uint8_t bytes[MSMOUSE_BUF_SZ / 2];
 186    int *targ = (int *)arg;
 187    const uint8_t hexchr[16] = {M('0'), M('1'), M('2'), M('3'), M('4'), M('5'),
 188                             M('6'), M('7'), M('8'), M('9'), M('A'), M('B'),
 189                             M('C'), M('D'), M('E'), M('F')};
 190
 191    switch (cmd) {
 192    case CHR_IOCTL_SERIAL_SET_TIOCM:
 193        c = mouse->tiocm;
 194        mouse->tiocm = *(int *)arg;
 195        if (MSMOUSE_PWR(mouse->tiocm)) {
 196            if (!MSMOUSE_PWR(c)) {
 197                /*
 198                 * Power on after reset: Send ID and PnP data
 199                 * No need to check fifo space as it is empty at this point.
 200                 */
 201                fifo8_push_all(&mouse->outbuf, mouse_id, sizeof(mouse_id));
 202                /* Add PnP data: */
 203                fifo8_push_all(&mouse->outbuf, pnp_data, sizeof(pnp_data));
 204                /*
 205                 * Add device description from qemu handler name.
 206                 * Make sure this all fits into the queue beforehand!
 207                 */
 208                c = M(')');
 209                for (i = 0; msmouse_handler.name[i]; i++) {
 210                    bytes[i] = M(msmouse_handler.name[i]);
 211                    c += bytes[i];
 212                }
 213                /* Calc more of checksum */
 214                for (j = 0; j < sizeof(pnp_data); j++) {
 215                    c += pnp_data[j];
 216                }
 217                c &= 0xff;
 218                bytes[i++] = hexchr[c >> 4];
 219                bytes[i++] = hexchr[c & 0x0f];
 220                bytes[i++] = M(')');
 221                fifo8_push_all(&mouse->outbuf, bytes, i);
 222                /* Start sending data to serial. */
 223                msmouse_chr_accept_input(chr);
 224            }
 225            break;
 226        }
 227        /*
 228         * Reset mouse buffers on power down.
 229         * Mouse won't send anything without power.
 230         */
 231        fifo8_reset(&mouse->outbuf);
 232        memset(mouse->axis, 0, sizeof(mouse->axis));
 233        memset(mouse->btns, false, sizeof(mouse->btns));
 234        memset(mouse->btnc, false, sizeof(mouse->btns));
 235        break;
 236    case CHR_IOCTL_SERIAL_GET_TIOCM:
 237        /* Remember line control status. */
 238        *targ = mouse->tiocm;
 239        break;
 240    default:
 241        return -ENOTSUP;
 242    }
 243    return 0;
 244}
 245
 246static void char_msmouse_finalize(Object *obj)
 247{
 248    MouseChardev *mouse = MOUSE_CHARDEV(obj);
 249
 250    if (mouse->hs) {
 251        qemu_input_handler_unregister(mouse->hs);
 252    }
 253    fifo8_destroy(&mouse->outbuf);
 254}
 255
 256static void msmouse_chr_open(Chardev *chr,
 257                             ChardevBackend *backend,
 258                             bool *be_opened,
 259                             Error **errp)
 260{
 261    MouseChardev *mouse = MOUSE_CHARDEV(chr);
 262
 263    *be_opened = false;
 264    mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
 265                                            &msmouse_handler);
 266    mouse->tiocm = 0;
 267    fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ);
 268}
 269
 270static void char_msmouse_class_init(ObjectClass *oc, void *data)
 271{
 272    ChardevClass *cc = CHARDEV_CLASS(oc);
 273
 274    cc->open = msmouse_chr_open;
 275    cc->chr_write = msmouse_chr_write;
 276    cc->chr_accept_input = msmouse_chr_accept_input;
 277    cc->chr_ioctl = msmouse_ioctl;
 278}
 279
 280static const TypeInfo char_msmouse_type_info = {
 281    .name = TYPE_CHARDEV_MSMOUSE,
 282    .parent = TYPE_CHARDEV,
 283    .instance_size = sizeof(MouseChardev),
 284    .instance_finalize = char_msmouse_finalize,
 285    .class_init = char_msmouse_class_init,
 286};
 287
 288static void register_types(void)
 289{
 290    type_register_static(&char_msmouse_type_info);
 291}
 292
 293type_init(register_types);
 294