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. */ 44 const 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 */ 51 const 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 57 struct 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 }; 67 typedef struct MouseChardev MouseChardev; 68 69 #define TYPE_CHARDEV_MSMOUSE "chardev-msmouse" 70 DECLARE_INSTANCE_CHECKER(MouseChardev, MOUSE_CHARDEV, 71 TYPE_CHARDEV_MSMOUSE) 72 73 static 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 91 static 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 124 static 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 154 static 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 168 static 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 174 static 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 181 static 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 246 static 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 256 static 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 270 static 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 280 static 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 288 static void register_types(void) 289 { 290 type_register_static(&char_msmouse_type_info); 291 } 292 293 type_init(register_types); 294