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