xref: /qemu/chardev/msmouse.c (revision 06252bf5)
16b10e573SMarc-André Lureau /*
26b10e573SMarc-André Lureau  * QEMU Microsoft serial mouse emulation
36b10e573SMarc-André Lureau  *
46b10e573SMarc-André Lureau  * Copyright (c) 2008 Lubomir Rintel
56b10e573SMarc-André Lureau  *
66b10e573SMarc-André Lureau  * Permission is hereby granted, free of charge, to any person obtaining a copy
76b10e573SMarc-André Lureau  * of this software and associated documentation files (the "Software"), to deal
86b10e573SMarc-André Lureau  * in the Software without restriction, including without limitation the rights
96b10e573SMarc-André Lureau  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
106b10e573SMarc-André Lureau  * copies of the Software, and to permit persons to whom the Software is
116b10e573SMarc-André Lureau  * furnished to do so, subject to the following conditions:
126b10e573SMarc-André Lureau  *
136b10e573SMarc-André Lureau  * The above copyright notice and this permission notice shall be included in
146b10e573SMarc-André Lureau  * all copies or substantial portions of the Software.
156b10e573SMarc-André Lureau  *
166b10e573SMarc-André Lureau  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
176b10e573SMarc-André Lureau  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186b10e573SMarc-André Lureau  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
196b10e573SMarc-André Lureau  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
206b10e573SMarc-André Lureau  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
216b10e573SMarc-André Lureau  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
226b10e573SMarc-André Lureau  * THE SOFTWARE.
236b10e573SMarc-André Lureau  */
240b8fa32fSMarkus Armbruster 
256b10e573SMarc-André Lureau #include "qemu/osdep.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
27e0cf7f23SArwed Meyer #include "qemu/fifo8.h"
286b10e573SMarc-André Lureau #include "chardev/char.h"
29a39fe105SArwed Meyer #include "chardev/char-serial.h"
306b10e573SMarc-André Lureau #include "ui/console.h"
316b10e573SMarc-André Lureau #include "ui/input.h"
32db1015e9SEduardo Habkost #include "qom/object.h"
336b10e573SMarc-André Lureau 
346b10e573SMarc-André Lureau #define MSMOUSE_LO6(n)  ((n) & 0x3f)
356b10e573SMarc-André Lureau #define MSMOUSE_HI2(n)  (((n) & 0xc0) >> 6)
36a39fe105SArwed Meyer #define MSMOUSE_PWR(cm) (cm & (CHR_TIOCM_RTS | CHR_TIOCM_DTR))
376b10e573SMarc-André Lureau 
3850d03d48SArwed Meyer /* Serial PnP for 6 bit devices/mice sends all ASCII chars - 0x20 */
3950d03d48SArwed Meyer #define M(c) (c - 0x20)
40e0cf7f23SArwed Meyer /* Serial fifo size. */
41e0cf7f23SArwed Meyer #define MSMOUSE_BUF_SZ 64
42e0cf7f23SArwed Meyer 
43e0cf7f23SArwed Meyer /* Mouse ID: Send "M3" cause we behave like a 3 button logitech mouse. */
44e0cf7f23SArwed Meyer const uint8_t mouse_id[] = {'M', '3'};
4550d03d48SArwed Meyer /*
4650d03d48SArwed Meyer  * PnP start "(", PnP version (1.0), vendor ID, product ID, '\\',
4750d03d48SArwed Meyer  * serial ID (omitted), '\\', MS class name, '\\', driver ID (omitted), '\\',
4850d03d48SArwed Meyer  * product description, checksum, ")"
4950d03d48SArwed Meyer  * Missing parts are inserted later.
5050d03d48SArwed Meyer  */
5150d03d48SArwed Meyer const uint8_t pnp_data[] = {M('('), 1, '$', M('Q'), M('M'), M('U'),
5250d03d48SArwed Meyer                          M('0'), M('0'), M('0'), M('1'),
5350d03d48SArwed Meyer                          M('\\'), M('\\'),
5450d03d48SArwed Meyer                          M('M'), M('O'), M('U'), M('S'), M('E'),
5550d03d48SArwed Meyer                          M('\\'), M('\\')};
56e0cf7f23SArwed Meyer 
57db1015e9SEduardo Habkost struct MouseChardev {
586b10e573SMarc-André Lureau     Chardev parent;
596b10e573SMarc-André Lureau 
606b10e573SMarc-André Lureau     QemuInputHandlerState *hs;
61a39fe105SArwed Meyer     int tiocm;
626b10e573SMarc-André Lureau     int axis[INPUT_AXIS__MAX];
636b10e573SMarc-André Lureau     bool btns[INPUT_BUTTON__MAX];
646b10e573SMarc-André Lureau     bool btnc[INPUT_BUTTON__MAX];
65e0cf7f23SArwed Meyer     Fifo8 outbuf;
66db1015e9SEduardo Habkost };
67db1015e9SEduardo Habkost typedef struct MouseChardev MouseChardev;
686b10e573SMarc-André Lureau 
696b10e573SMarc-André Lureau #define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
DECLARE_INSTANCE_CHECKER(MouseChardev,MOUSE_CHARDEV,TYPE_CHARDEV_MSMOUSE)708110fa1dSEduardo Habkost DECLARE_INSTANCE_CHECKER(MouseChardev, MOUSE_CHARDEV,
718110fa1dSEduardo Habkost                          TYPE_CHARDEV_MSMOUSE)
726b10e573SMarc-André Lureau 
736b10e573SMarc-André Lureau static void msmouse_chr_accept_input(Chardev *chr)
746b10e573SMarc-André Lureau {
756b10e573SMarc-André Lureau     MouseChardev *mouse = MOUSE_CHARDEV(chr);
76e0cf7f23SArwed Meyer     uint32_t len, avail;
776b10e573SMarc-André Lureau 
786b10e573SMarc-André Lureau     len = qemu_chr_be_can_write(chr);
79e0cf7f23SArwed Meyer     avail = fifo8_num_used(&mouse->outbuf);
80e0cf7f23SArwed Meyer     while (len > 0 && avail > 0) {
81e0cf7f23SArwed Meyer         const uint8_t *buf;
82e0cf7f23SArwed Meyer         uint32_t size;
836b10e573SMarc-André Lureau 
84*06252bf5SPhilippe Mathieu-Daudé         buf = fifo8_pop_bufptr(&mouse->outbuf, MIN(len, avail), &size);
85e0cf7f23SArwed Meyer         qemu_chr_be_write(chr, buf, size);
86e0cf7f23SArwed Meyer         len = qemu_chr_be_can_write(chr);
87e0cf7f23SArwed Meyer         avail -= size;
886b10e573SMarc-André Lureau     }
896b10e573SMarc-André Lureau }
906b10e573SMarc-André Lureau 
msmouse_queue_event(MouseChardev * mouse)916b10e573SMarc-André Lureau static void msmouse_queue_event(MouseChardev *mouse)
926b10e573SMarc-André Lureau {
936b10e573SMarc-André Lureau     unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 };
946b10e573SMarc-André Lureau     int dx, dy, count = 3;
956b10e573SMarc-André Lureau 
966b10e573SMarc-André Lureau     dx = mouse->axis[INPUT_AXIS_X];
976b10e573SMarc-André Lureau     mouse->axis[INPUT_AXIS_X] = 0;
986b10e573SMarc-André Lureau 
996b10e573SMarc-André Lureau     dy = mouse->axis[INPUT_AXIS_Y];
1006b10e573SMarc-André Lureau     mouse->axis[INPUT_AXIS_Y] = 0;
1016b10e573SMarc-André Lureau 
1026b10e573SMarc-André Lureau     /* Movement deltas */
1036b10e573SMarc-André Lureau     bytes[0] |= (MSMOUSE_HI2(dy) << 2) | MSMOUSE_HI2(dx);
1046b10e573SMarc-André Lureau     bytes[1] |= MSMOUSE_LO6(dx);
1056b10e573SMarc-André Lureau     bytes[2] |= MSMOUSE_LO6(dy);
1066b10e573SMarc-André Lureau 
1076b10e573SMarc-André Lureau     /* Buttons */
1086b10e573SMarc-André Lureau     bytes[0] |= (mouse->btns[INPUT_BUTTON_LEFT]   ? 0x20 : 0x00);
1096b10e573SMarc-André Lureau     bytes[0] |= (mouse->btns[INPUT_BUTTON_RIGHT]  ? 0x10 : 0x00);
1106b10e573SMarc-André Lureau     if (mouse->btns[INPUT_BUTTON_MIDDLE] ||
1116b10e573SMarc-André Lureau         mouse->btnc[INPUT_BUTTON_MIDDLE]) {
1126b10e573SMarc-André Lureau         bytes[3] |= (mouse->btns[INPUT_BUTTON_MIDDLE] ? 0x20 : 0x00);
1136b10e573SMarc-André Lureau         mouse->btnc[INPUT_BUTTON_MIDDLE] = false;
114e0cf7f23SArwed Meyer         count++;
1156b10e573SMarc-André Lureau     }
1166b10e573SMarc-André Lureau 
117e0cf7f23SArwed Meyer     if (fifo8_num_free(&mouse->outbuf) >= count) {
118e0cf7f23SArwed Meyer         fifo8_push_all(&mouse->outbuf, bytes, count);
1196b10e573SMarc-André Lureau     } else {
1206b10e573SMarc-André Lureau         /* queue full -> drop event */
1216b10e573SMarc-André Lureau     }
1226b10e573SMarc-André Lureau }
1236b10e573SMarc-André Lureau 
msmouse_input_event(DeviceState * dev,QemuConsole * src,InputEvent * evt)1246b10e573SMarc-André Lureau static void msmouse_input_event(DeviceState *dev, QemuConsole *src,
1256b10e573SMarc-André Lureau                                 InputEvent *evt)
1266b10e573SMarc-André Lureau {
1276b10e573SMarc-André Lureau     MouseChardev *mouse = MOUSE_CHARDEV(dev);
1286b10e573SMarc-André Lureau     InputMoveEvent *move;
1296b10e573SMarc-André Lureau     InputBtnEvent *btn;
1306b10e573SMarc-André Lureau 
131a39fe105SArwed Meyer     /* Ignore events if serial mouse powered down. */
132a39fe105SArwed Meyer     if (!MSMOUSE_PWR(mouse->tiocm)) {
133a39fe105SArwed Meyer         return;
134a39fe105SArwed Meyer     }
135a39fe105SArwed Meyer 
1366b10e573SMarc-André Lureau     switch (evt->type) {
1376b10e573SMarc-André Lureau     case INPUT_EVENT_KIND_REL:
1386b10e573SMarc-André Lureau         move = evt->u.rel.data;
1396b10e573SMarc-André Lureau         mouse->axis[move->axis] += move->value;
1406b10e573SMarc-André Lureau         break;
1416b10e573SMarc-André Lureau 
1426b10e573SMarc-André Lureau     case INPUT_EVENT_KIND_BTN:
1436b10e573SMarc-André Lureau         btn = evt->u.btn.data;
1446b10e573SMarc-André Lureau         mouse->btns[btn->button] = btn->down;
1456b10e573SMarc-André Lureau         mouse->btnc[btn->button] = true;
1466b10e573SMarc-André Lureau         break;
1476b10e573SMarc-André Lureau 
1486b10e573SMarc-André Lureau     default:
1496b10e573SMarc-André Lureau         /* keep gcc happy */
1506b10e573SMarc-André Lureau         break;
1516b10e573SMarc-André Lureau     }
1526b10e573SMarc-André Lureau }
1536b10e573SMarc-André Lureau 
msmouse_input_sync(DeviceState * dev)1546b10e573SMarc-André Lureau static void msmouse_input_sync(DeviceState *dev)
1556b10e573SMarc-André Lureau {
1566b10e573SMarc-André Lureau     MouseChardev *mouse = MOUSE_CHARDEV(dev);
1576b10e573SMarc-André Lureau     Chardev *chr = CHARDEV(dev);
1586b10e573SMarc-André Lureau 
159a39fe105SArwed Meyer     /* Ignore events if serial mouse powered down. */
160a39fe105SArwed Meyer     if (!MSMOUSE_PWR(mouse->tiocm)) {
161a39fe105SArwed Meyer         return;
162a39fe105SArwed Meyer     }
163a39fe105SArwed Meyer 
1646b10e573SMarc-André Lureau     msmouse_queue_event(mouse);
1656b10e573SMarc-André Lureau     msmouse_chr_accept_input(chr);
1666b10e573SMarc-André Lureau }
1676b10e573SMarc-André Lureau 
msmouse_chr_write(struct Chardev * s,const uint8_t * buf,int len)1686b10e573SMarc-André Lureau static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len)
1696b10e573SMarc-André Lureau {
1706b10e573SMarc-André Lureau     /* Ignore writes to mouse port */
1716b10e573SMarc-André Lureau     return len;
1726b10e573SMarc-André Lureau }
1736b10e573SMarc-André Lureau 
174b1be65f6SPhilippe Mathieu-Daudé static const QemuInputHandler msmouse_handler = {
17550d03d48SArwed Meyer     .name  = "QEMU Microsoft Mouse",
17650d03d48SArwed Meyer     .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
17750d03d48SArwed Meyer     .event = msmouse_input_event,
17850d03d48SArwed Meyer     .sync  = msmouse_input_sync,
17950d03d48SArwed Meyer };
18050d03d48SArwed Meyer 
msmouse_ioctl(Chardev * chr,int cmd,void * arg)181a39fe105SArwed Meyer static int msmouse_ioctl(Chardev *chr, int cmd, void *arg)
182a39fe105SArwed Meyer {
183a39fe105SArwed Meyer     MouseChardev *mouse = MOUSE_CHARDEV(chr);
18450d03d48SArwed Meyer     int c, i, j;
18550d03d48SArwed Meyer     uint8_t bytes[MSMOUSE_BUF_SZ / 2];
186a39fe105SArwed Meyer     int *targ = (int *)arg;
18750d03d48SArwed Meyer     const uint8_t hexchr[16] = {M('0'), M('1'), M('2'), M('3'), M('4'), M('5'),
18850d03d48SArwed Meyer                              M('6'), M('7'), M('8'), M('9'), M('A'), M('B'),
18950d03d48SArwed Meyer                              M('C'), M('D'), M('E'), M('F')};
190a39fe105SArwed Meyer 
191a39fe105SArwed Meyer     switch (cmd) {
192a39fe105SArwed Meyer     case CHR_IOCTL_SERIAL_SET_TIOCM:
193a39fe105SArwed Meyer         c = mouse->tiocm;
194a39fe105SArwed Meyer         mouse->tiocm = *(int *)arg;
195a39fe105SArwed Meyer         if (MSMOUSE_PWR(mouse->tiocm)) {
196a39fe105SArwed Meyer             if (!MSMOUSE_PWR(c)) {
197a39fe105SArwed Meyer                 /*
19850d03d48SArwed Meyer                  * Power on after reset: Send ID and PnP data
19950d03d48SArwed Meyer                  * No need to check fifo space as it is empty at this point.
200a39fe105SArwed Meyer                  */
201e0cf7f23SArwed Meyer                 fifo8_push_all(&mouse->outbuf, mouse_id, sizeof(mouse_id));
20250d03d48SArwed Meyer                 /* Add PnP data: */
20350d03d48SArwed Meyer                 fifo8_push_all(&mouse->outbuf, pnp_data, sizeof(pnp_data));
20450d03d48SArwed Meyer                 /*
20550d03d48SArwed Meyer                  * Add device description from qemu handler name.
20650d03d48SArwed Meyer                  * Make sure this all fits into the queue beforehand!
20750d03d48SArwed Meyer                  */
20850d03d48SArwed Meyer                 c = M(')');
20950d03d48SArwed Meyer                 for (i = 0; msmouse_handler.name[i]; i++) {
21050d03d48SArwed Meyer                     bytes[i] = M(msmouse_handler.name[i]);
21150d03d48SArwed Meyer                     c += bytes[i];
21250d03d48SArwed Meyer                 }
21350d03d48SArwed Meyer                 /* Calc more of checksum */
21450d03d48SArwed Meyer                 for (j = 0; j < sizeof(pnp_data); j++) {
21550d03d48SArwed Meyer                     c += pnp_data[j];
21650d03d48SArwed Meyer                 }
21750d03d48SArwed Meyer                 c &= 0xff;
21850d03d48SArwed Meyer                 bytes[i++] = hexchr[c >> 4];
21950d03d48SArwed Meyer                 bytes[i++] = hexchr[c & 0x0f];
22050d03d48SArwed Meyer                 bytes[i++] = M(')');
22150d03d48SArwed Meyer                 fifo8_push_all(&mouse->outbuf, bytes, i);
222a39fe105SArwed Meyer                 /* Start sending data to serial. */
223a39fe105SArwed Meyer                 msmouse_chr_accept_input(chr);
224a39fe105SArwed Meyer             }
225a39fe105SArwed Meyer             break;
226a39fe105SArwed Meyer         }
227a39fe105SArwed Meyer         /*
228a39fe105SArwed Meyer          * Reset mouse buffers on power down.
229a39fe105SArwed Meyer          * Mouse won't send anything without power.
230a39fe105SArwed Meyer          */
231e0cf7f23SArwed Meyer         fifo8_reset(&mouse->outbuf);
232a39fe105SArwed Meyer         memset(mouse->axis, 0, sizeof(mouse->axis));
233a39fe105SArwed Meyer         memset(mouse->btns, false, sizeof(mouse->btns));
234a39fe105SArwed Meyer         memset(mouse->btnc, false, sizeof(mouse->btns));
235a39fe105SArwed Meyer         break;
236a39fe105SArwed Meyer     case CHR_IOCTL_SERIAL_GET_TIOCM:
237a39fe105SArwed Meyer         /* Remember line control status. */
238a39fe105SArwed Meyer         *targ = mouse->tiocm;
239a39fe105SArwed Meyer         break;
240a39fe105SArwed Meyer     default:
241a39fe105SArwed Meyer         return -ENOTSUP;
242a39fe105SArwed Meyer     }
243a39fe105SArwed Meyer     return 0;
244a39fe105SArwed Meyer }
245a39fe105SArwed Meyer 
char_msmouse_finalize(Object * obj)2466b10e573SMarc-André Lureau static void char_msmouse_finalize(Object *obj)
2476b10e573SMarc-André Lureau {
2486b10e573SMarc-André Lureau     MouseChardev *mouse = MOUSE_CHARDEV(obj);
2496b10e573SMarc-André Lureau 
250fc0c1285SMaksim Davydov     if (mouse->hs) {
2516b10e573SMarc-André Lureau         qemu_input_handler_unregister(mouse->hs);
252fc0c1285SMaksim Davydov     }
253e0cf7f23SArwed Meyer     fifo8_destroy(&mouse->outbuf);
2546b10e573SMarc-André Lureau }
2556b10e573SMarc-André Lureau 
msmouse_chr_open(Chardev * chr,ChardevBackend * backend,bool * be_opened,Error ** errp)2566b10e573SMarc-André Lureau static void msmouse_chr_open(Chardev *chr,
2576b10e573SMarc-André Lureau                              ChardevBackend *backend,
2586b10e573SMarc-André Lureau                              bool *be_opened,
2596b10e573SMarc-André Lureau                              Error **errp)
2606b10e573SMarc-André Lureau {
2616b10e573SMarc-André Lureau     MouseChardev *mouse = MOUSE_CHARDEV(chr);
2626b10e573SMarc-André Lureau 
2636b10e573SMarc-André Lureau     *be_opened = false;
2646b10e573SMarc-André Lureau     mouse->hs = qemu_input_handler_register((DeviceState *)mouse,
2656b10e573SMarc-André Lureau                                             &msmouse_handler);
266a39fe105SArwed Meyer     mouse->tiocm = 0;
267e0cf7f23SArwed Meyer     fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ);
2686b10e573SMarc-André Lureau }
2696b10e573SMarc-André Lureau 
char_msmouse_class_init(ObjectClass * oc,void * data)2706b10e573SMarc-André Lureau static void char_msmouse_class_init(ObjectClass *oc, void *data)
2716b10e573SMarc-André Lureau {
2726b10e573SMarc-André Lureau     ChardevClass *cc = CHARDEV_CLASS(oc);
2736b10e573SMarc-André Lureau 
2746b10e573SMarc-André Lureau     cc->open = msmouse_chr_open;
2756b10e573SMarc-André Lureau     cc->chr_write = msmouse_chr_write;
2766b10e573SMarc-André Lureau     cc->chr_accept_input = msmouse_chr_accept_input;
277a39fe105SArwed Meyer     cc->chr_ioctl = msmouse_ioctl;
2786b10e573SMarc-André Lureau }
2796b10e573SMarc-André Lureau 
2806b10e573SMarc-André Lureau static const TypeInfo char_msmouse_type_info = {
2816b10e573SMarc-André Lureau     .name = TYPE_CHARDEV_MSMOUSE,
2826b10e573SMarc-André Lureau     .parent = TYPE_CHARDEV,
2836b10e573SMarc-André Lureau     .instance_size = sizeof(MouseChardev),
2846b10e573SMarc-André Lureau     .instance_finalize = char_msmouse_finalize,
2856b10e573SMarc-André Lureau     .class_init = char_msmouse_class_init,
2866b10e573SMarc-André Lureau };
2876b10e573SMarc-André Lureau 
register_types(void)2886b10e573SMarc-André Lureau static void register_types(void)
2896b10e573SMarc-André Lureau {
2906b10e573SMarc-André Lureau     type_register_static(&char_msmouse_type_info);
2916b10e573SMarc-André Lureau }
2926b10e573SMarc-André Lureau 
2936b10e573SMarc-André Lureau type_init(register_types);
294