2 * QEMU Microsoft serial mouse emulation
4 * Copyright (c) 2008 Lubomir Rintel
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
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"
32 #include "qom/object.h"
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))
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
43 /* Mouse ID: Send "M3" cause we behave like a 3 button logitech mouse. */
44 const uint8_t mouse_id
[] = {'M', '3'};
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.
51 const uint8_t pnp_data
[] = {M('('), 1, '$', M('Q'), M('M'), M('U'),
52 M('0'), M('0'), M('0'), M('1'),
54 M('M'), M('O'), M('U'), M('S'), M('E'),
60 QemuInputHandlerState
*hs
;
62 int axis
[INPUT_AXIS__MAX
];
63 bool btns
[INPUT_BUTTON__MAX
];
64 bool btnc
[INPUT_BUTTON__MAX
];
67 typedef struct MouseChardev MouseChardev
;
69 #define TYPE_CHARDEV_MSMOUSE "chardev-msmouse"
70 DECLARE_INSTANCE_CHECKER(MouseChardev
, MOUSE_CHARDEV
,
73 static void msmouse_chr_accept_input(Chardev
*chr
)
75 MouseChardev
*mouse
= MOUSE_CHARDEV(chr
);
78 len
= qemu_chr_be_can_write(chr
);
79 avail
= fifo8_num_used(&mouse
->outbuf
);
80 while (len
> 0 && avail
> 0) {
84 buf
= fifo8_pop_bufptr(&mouse
->outbuf
, MIN(len
, avail
), &size
);
85 qemu_chr_be_write(chr
, buf
, size
);
86 len
= qemu_chr_be_can_write(chr
);
91 static void msmouse_queue_event(MouseChardev
*mouse
)
93 unsigned char bytes
[4] = { 0x40, 0x00, 0x00, 0x00 };
94 int dx
, dy
, count
= 3;
96 dx
= mouse
->axis
[INPUT_AXIS_X
];
97 mouse
->axis
[INPUT_AXIS_X
] = 0;
99 dy
= mouse
->axis
[INPUT_AXIS_Y
];
100 mouse
->axis
[INPUT_AXIS_Y
] = 0;
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
);
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;
117 if (fifo8_num_free(&mouse
->outbuf
) >= count
) {
118 fifo8_push_all(&mouse
->outbuf
, bytes
, count
);
120 /* queue full -> drop event */
124 static void msmouse_input_event(DeviceState
*dev
, QemuConsole
*src
,
127 MouseChardev
*mouse
= MOUSE_CHARDEV(dev
);
128 InputMoveEvent
*move
;
131 /* Ignore events if serial mouse powered down. */
132 if (!MSMOUSE_PWR(mouse
->tiocm
)) {
137 case INPUT_EVENT_KIND_REL
:
138 move
= evt
->u
.rel
.data
;
139 mouse
->axis
[move
->axis
] += move
->value
;
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;
154 static void msmouse_input_sync(DeviceState
*dev
)
156 MouseChardev
*mouse
= MOUSE_CHARDEV(dev
);
157 Chardev
*chr
= CHARDEV(dev
);
159 /* Ignore events if serial mouse powered down. */
160 if (!MSMOUSE_PWR(mouse
->tiocm
)) {
164 msmouse_queue_event(mouse
);
165 msmouse_chr_accept_input(chr
);
168 static int msmouse_chr_write(struct Chardev
*s
, const uint8_t *buf
, int len
)
170 /* Ignore writes to mouse port */
174 static const 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
,
181 static int msmouse_ioctl(Chardev
*chr
, int cmd
, void *arg
)
183 MouseChardev
*mouse
= MOUSE_CHARDEV(chr
);
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')};
192 case CHR_IOCTL_SERIAL_SET_TIOCM
:
194 mouse
->tiocm
= *(int *)arg
;
195 if (MSMOUSE_PWR(mouse
->tiocm
)) {
196 if (!MSMOUSE_PWR(c
)) {
198 * Power on after reset: Send ID and PnP data
199 * No need to check fifo space as it is empty at this point.
201 fifo8_push_all(&mouse
->outbuf
, mouse_id
, sizeof(mouse_id
));
203 fifo8_push_all(&mouse
->outbuf
, pnp_data
, sizeof(pnp_data
));
205 * Add device description from qemu handler name.
206 * Make sure this all fits into the queue beforehand!
209 for (i
= 0; msmouse_handler
.name
[i
]; i
++) {
210 bytes
[i
] = M(msmouse_handler
.name
[i
]);
213 /* Calc more of checksum */
214 for (j
= 0; j
< sizeof(pnp_data
); j
++) {
218 bytes
[i
++] = hexchr
[c
>> 4];
219 bytes
[i
++] = hexchr
[c
& 0x0f];
221 fifo8_push_all(&mouse
->outbuf
, bytes
, i
);
222 /* Start sending data to serial. */
223 msmouse_chr_accept_input(chr
);
228 * Reset mouse buffers on power down.
229 * Mouse won't send anything without power.
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
));
236 case CHR_IOCTL_SERIAL_GET_TIOCM
:
237 /* Remember line control status. */
238 *targ
= mouse
->tiocm
;
246 static void char_msmouse_finalize(Object
*obj
)
248 MouseChardev
*mouse
= MOUSE_CHARDEV(obj
);
251 qemu_input_handler_unregister(mouse
->hs
);
253 fifo8_destroy(&mouse
->outbuf
);
256 static void msmouse_chr_open(Chardev
*chr
,
257 ChardevBackend
*backend
,
261 MouseChardev
*mouse
= MOUSE_CHARDEV(chr
);
264 mouse
->hs
= qemu_input_handler_register((DeviceState
*)mouse
,
267 fifo8_create(&mouse
->outbuf
, MSMOUSE_BUF_SZ
);
270 static void char_msmouse_class_init(ObjectClass
*oc
, void *data
)
272 ChardevClass
*cc
= CHARDEV_CLASS(oc
);
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
;
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
,
288 static void register_types(void)
290 type_register_static(&char_msmouse_type_info
);
293 type_init(register_types
);