]> git.proxmox.com Git - qemu.git/blame - hw/usb-hid.c
Malta CBUS UART support.
[qemu.git] / hw / usb-hid.c
CommitLineData
59ae540c
FB
1/*
2 * QEMU USB HID devices
3 *
4 * Copyright (c) 2005 Fabrice Bellard
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#include "vl.h"
25
26/* HID interface requests */
27#define GET_REPORT 0xa101
28#define GET_IDLE 0xa102
29#define GET_PROTOCOL 0xa103
30#define SET_IDLE 0x210a
31#define SET_PROTOCOL 0x210b
32
09b26c5e
FB
33#define USB_MOUSE 1
34#define USB_TABLET 2
35
59ae540c
FB
36typedef struct USBMouseState {
37 USBDevice dev;
38 int dx, dy, dz, buttons_state;
09b26c5e
FB
39 int x, y;
40 int kind;
41 int mouse_grabbed;
455204eb 42 QEMUPutMouseEntry *eh_entry;
59ae540c
FB
43} USBMouseState;
44
45/* mostly the same values as the Bochs USB Mouse device */
46static const uint8_t qemu_mouse_dev_descriptor[] = {
47 0x12, /* u8 bLength; */
48 0x01, /* u8 bDescriptorType; Device */
49 0x10, 0x00, /* u16 bcdUSB; v1.0 */
50
51 0x00, /* u8 bDeviceClass; */
52 0x00, /* u8 bDeviceSubClass; */
53 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
54 0x08, /* u8 bMaxPacketSize0; 8 Bytes */
55
56 0x27, 0x06, /* u16 idVendor; */
57 0x01, 0x00, /* u16 idProduct; */
58 0x00, 0x00, /* u16 bcdDevice */
59
60 0x03, /* u8 iManufacturer; */
61 0x02, /* u8 iProduct; */
62 0x01, /* u8 iSerialNumber; */
63 0x01 /* u8 bNumConfigurations; */
64};
65
66static const uint8_t qemu_mouse_config_descriptor[] = {
67 /* one configuration */
68 0x09, /* u8 bLength; */
69 0x02, /* u8 bDescriptorType; Configuration */
70 0x22, 0x00, /* u16 wTotalLength; */
71 0x01, /* u8 bNumInterfaces; (1) */
72 0x01, /* u8 bConfigurationValue; */
73 0x04, /* u8 iConfiguration; */
74 0xa0, /* u8 bmAttributes;
75 Bit 7: must be set,
76 6: Self-powered,
77 5: Remote wakeup,
78 4..0: resvd */
79 50, /* u8 MaxPower; */
80
81 /* USB 1.1:
82 * USB 2.0, single TT organization (mandatory):
83 * one interface, protocol 0
84 *
85 * USB 2.0, multiple TT organization (optional):
86 * two interfaces, protocols 1 (like single TT)
87 * and 2 (multiple TT mode) ... config is
88 * sometimes settable
89 * NOT IMPLEMENTED
90 */
91
92 /* one interface */
93 0x09, /* u8 if_bLength; */
94 0x04, /* u8 if_bDescriptorType; Interface */
95 0x00, /* u8 if_bInterfaceNumber; */
96 0x00, /* u8 if_bAlternateSetting; */
97 0x01, /* u8 if_bNumEndpoints; */
98 0x03, /* u8 if_bInterfaceClass; */
99 0x01, /* u8 if_bInterfaceSubClass; */
100 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
101 0x05, /* u8 if_iInterface; */
102
09b26c5e
FB
103 /* HID descriptor */
104 0x09, /* u8 bLength; */
105 0x21, /* u8 bDescriptorType; */
106 0x01, 0x00, /* u16 HID_class */
107 0x00, /* u8 country_code */
108 0x01, /* u8 num_descriptors */
109 0x22, /* u8 type; Report */
110 50, 0, /* u16 len */
111
59ae540c
FB
112 /* one endpoint (status change endpoint) */
113 0x07, /* u8 ep_bLength; */
114 0x05, /* u8 ep_bDescriptorType; Endpoint */
115 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
116 0x03, /* u8 ep_bmAttributes; Interrupt */
117 0x03, 0x00, /* u16 ep_wMaxPacketSize; */
118 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
09b26c5e
FB
119};
120
121static const uint8_t qemu_tablet_config_descriptor[] = {
122 /* one configuration */
123 0x09, /* u8 bLength; */
124 0x02, /* u8 bDescriptorType; Configuration */
125 0x22, 0x00, /* u16 wTotalLength; */
126 0x01, /* u8 bNumInterfaces; (1) */
127 0x01, /* u8 bConfigurationValue; */
128 0x04, /* u8 iConfiguration; */
129 0xa0, /* u8 bmAttributes;
130 Bit 7: must be set,
131 6: Self-powered,
132 5: Remote wakeup,
133 4..0: resvd */
134 50, /* u8 MaxPower; */
135
136 /* USB 1.1:
137 * USB 2.0, single TT organization (mandatory):
138 * one interface, protocol 0
139 *
140 * USB 2.0, multiple TT organization (optional):
141 * two interfaces, protocols 1 (like single TT)
142 * and 2 (multiple TT mode) ... config is
143 * sometimes settable
144 * NOT IMPLEMENTED
145 */
146
147 /* one interface */
148 0x09, /* u8 if_bLength; */
149 0x04, /* u8 if_bDescriptorType; Interface */
150 0x00, /* u8 if_bInterfaceNumber; */
151 0x00, /* u8 if_bAlternateSetting; */
152 0x01, /* u8 if_bNumEndpoints; */
153 0x03, /* u8 if_bInterfaceClass; */
154 0x01, /* u8 if_bInterfaceSubClass; */
155 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
156 0x05, /* u8 if_iInterface; */
59ae540c
FB
157
158 /* HID descriptor */
159 0x09, /* u8 bLength; */
160 0x21, /* u8 bDescriptorType; */
161 0x01, 0x00, /* u16 HID_class */
162 0x00, /* u8 country_code */
163 0x01, /* u8 num_descriptors */
164 0x22, /* u8 type; Report */
09b26c5e
FB
165 74, 0, /* u16 len */
166
167 /* one endpoint (status change endpoint) */
168 0x07, /* u8 ep_bLength; */
169 0x05, /* u8 ep_bDescriptorType; Endpoint */
170 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
171 0x03, /* u8 ep_bmAttributes; Interrupt */
172 0x08, 0x00, /* u16 ep_wMaxPacketSize; */
f2f1ac82 173 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
59ae540c
FB
174};
175
176static const uint8_t qemu_mouse_hid_report_descriptor[] = {
177 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01,
178 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03,
179 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01,
180 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
181 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81,
182 0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06,
183 0xC0, 0xC0,
184};
185
09b26c5e
FB
186static const uint8_t qemu_tablet_hid_report_descriptor[] = {
187 0x05, 0x01, /* Usage Page Generic Desktop */
188 0x09, 0x01, /* Usage Mouse */
189 0xA1, 0x01, /* Collection Application */
190 0x09, 0x01, /* Usage Pointer */
191 0xA1, 0x00, /* Collection Physical */
192 0x05, 0x09, /* Usage Page Button */
193 0x19, 0x01, /* Usage Minimum Button 1 */
194 0x29, 0x03, /* Usage Maximum Button 3 */
195 0x15, 0x00, /* Logical Minimum 0 */
196 0x25, 0x01, /* Logical Maximum 1 */
197 0x95, 0x03, /* Report Count 3 */
198 0x75, 0x01, /* Report Size 1 */
199 0x81, 0x02, /* Input (Data, Var, Abs) */
200 0x95, 0x01, /* Report Count 1 */
201 0x75, 0x05, /* Report Size 5 */
202 0x81, 0x01, /* Input (Cnst, Var, Abs) */
203 0x05, 0x01, /* Usage Page Generic Desktop */
204 0x09, 0x30, /* Usage X */
205 0x09, 0x31, /* Usage Y */
206 0x15, 0x00, /* Logical Minimum 0 */
207 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */
208 0x35, 0x00, /* Physical Minimum 0 */
209 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */
210 0x75, 0x10, /* Report Size 16 */
211 0x95, 0x02, /* Report Count 2 */
212 0x81, 0x02, /* Input (Data, Var, Abs) */
213 0x05, 0x01, /* Usage Page Generic Desktop */
214 0x09, 0x38, /* Usage Wheel */
215 0x15, 0x81, /* Logical Minimum -127 */
216 0x25, 0x7F, /* Logical Maximum 127 */
217 0x35, 0x00, /* Physical Minimum 0 (same as logical) */
218 0x45, 0x00, /* Physical Maximum 0 (same as logical) */
219 0x75, 0x08, /* Report Size 8 */
220 0x95, 0x01, /* Report Count 1 */
221 0x81, 0x02, /* Input (Data, Var, Rel) */
222 0xC0, /* End Collection */
223 0xC0, /* End Collection */
224};
225
59ae540c
FB
226static void usb_mouse_event(void *opaque,
227 int dx1, int dy1, int dz1, int buttons_state)
228{
229 USBMouseState *s = opaque;
230
231 s->dx += dx1;
232 s->dy += dy1;
233 s->dz += dz1;
234 s->buttons_state = buttons_state;
235}
236
09b26c5e
FB
237static void usb_tablet_event(void *opaque,
238 int x, int y, int dz, int buttons_state)
239{
240 USBMouseState *s = opaque;
241
242 s->x = x;
243 s->y = y;
244 s->dz += dz;
245 s->buttons_state = buttons_state;
246}
247
59ae540c
FB
248static inline int int_clamp(int val, int vmin, int vmax)
249{
250 if (val < vmin)
251 return vmin;
252 else if (val > vmax)
253 return vmax;
254 else
255 return val;
256}
257
258static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len)
259{
260 int dx, dy, dz, b, l;
261
09b26c5e 262 if (!s->mouse_grabbed) {
455204eb
TS
263 s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s,
264 0, "QEMU USB Mouse");
09b26c5e
FB
265 s->mouse_grabbed = 1;
266 }
267
59ae540c
FB
268 dx = int_clamp(s->dx, -128, 127);
269 dy = int_clamp(s->dy, -128, 127);
270 dz = int_clamp(s->dz, -128, 127);
271
272 s->dx -= dx;
273 s->dy -= dy;
274 s->dz -= dz;
275
276 b = 0;
277 if (s->buttons_state & MOUSE_EVENT_LBUTTON)
278 b |= 0x01;
279 if (s->buttons_state & MOUSE_EVENT_RBUTTON)
280 b |= 0x02;
281 if (s->buttons_state & MOUSE_EVENT_MBUTTON)
282 b |= 0x04;
283
284 buf[0] = b;
285 buf[1] = dx;
286 buf[2] = dy;
287 l = 3;
288 if (len >= 4) {
289 buf[3] = dz;
290 l = 4;
291 }
292 return l;
293}
294
09b26c5e
FB
295static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len)
296{
297 int dz, b, l;
298
299 if (!s->mouse_grabbed) {
455204eb
TS
300 s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, s,
301 1, "QEMU USB Tablet");
09b26c5e
FB
302 s->mouse_grabbed = 1;
303 }
304
305 dz = int_clamp(s->dz, -128, 127);
306 s->dz -= dz;
307
308 /* Appears we have to invert the wheel direction */
309 dz = 0 - dz;
310 b = 0;
311 if (s->buttons_state & MOUSE_EVENT_LBUTTON)
312 b |= 0x01;
313 if (s->buttons_state & MOUSE_EVENT_RBUTTON)
314 b |= 0x02;
315 if (s->buttons_state & MOUSE_EVENT_MBUTTON)
316 b |= 0x04;
317
318 buf[0] = b;
319 buf[1] = s->x & 0xff;
320 buf[2] = s->x >> 8;
321 buf[3] = s->y & 0xff;
322 buf[4] = s->y >> 8;
323 buf[5] = dz;
324 l = 6;
325
326 return l;
327}
328
059809e4 329static void usb_mouse_handle_reset(USBDevice *dev)
59ae540c
FB
330{
331 USBMouseState *s = (USBMouseState *)dev;
332
333 s->dx = 0;
334 s->dy = 0;
335 s->dz = 0;
09b26c5e
FB
336 s->x = 0;
337 s->y = 0;
59ae540c
FB
338 s->buttons_state = 0;
339}
340
341static int usb_mouse_handle_control(USBDevice *dev, int request, int value,
342 int index, int length, uint8_t *data)
343{
344 USBMouseState *s = (USBMouseState *)dev;
09b26c5e 345 int ret = 0;
59ae540c
FB
346
347 switch(request) {
348 case DeviceRequest | USB_REQ_GET_STATUS:
349 data[0] = (1 << USB_DEVICE_SELF_POWERED) |
350 (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
351 data[1] = 0x00;
352 ret = 2;
353 break;
354 case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
355 if (value == USB_DEVICE_REMOTE_WAKEUP) {
356 dev->remote_wakeup = 0;
357 } else {
358 goto fail;
359 }
360 ret = 0;
361 break;
362 case DeviceOutRequest | USB_REQ_SET_FEATURE:
363 if (value == USB_DEVICE_REMOTE_WAKEUP) {
364 dev->remote_wakeup = 1;
365 } else {
366 goto fail;
367 }
368 ret = 0;
369 break;
370 case DeviceOutRequest | USB_REQ_SET_ADDRESS:
371 dev->addr = value;
372 ret = 0;
373 break;
374 case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
375 switch(value >> 8) {
376 case USB_DT_DEVICE:
377 memcpy(data, qemu_mouse_dev_descriptor,
378 sizeof(qemu_mouse_dev_descriptor));
379 ret = sizeof(qemu_mouse_dev_descriptor);
380 break;
381 case USB_DT_CONFIG:
09b26c5e
FB
382 if (s->kind == USB_MOUSE) {
383 memcpy(data, qemu_mouse_config_descriptor,
384 sizeof(qemu_mouse_config_descriptor));
385 ret = sizeof(qemu_mouse_config_descriptor);
386 } else if (s->kind == USB_TABLET) {
387 memcpy(data, qemu_tablet_config_descriptor,
388 sizeof(qemu_tablet_config_descriptor));
389 ret = sizeof(qemu_tablet_config_descriptor);
390 }
59ae540c
FB
391 break;
392 case USB_DT_STRING:
393 switch(value & 0xff) {
394 case 0:
395 /* language ids */
396 data[0] = 4;
397 data[1] = 3;
398 data[2] = 0x09;
399 data[3] = 0x04;
400 ret = 4;
401 break;
402 case 1:
403 /* serial number */
404 ret = set_usb_string(data, "1");
405 break;
406 case 2:
407 /* product description */
09b26c5e
FB
408 if (s->kind == USB_MOUSE)
409 ret = set_usb_string(data, "QEMU USB Mouse");
410 else if (s->kind == USB_TABLET)
411 ret = set_usb_string(data, "QEMU USB Tablet");
59ae540c
FB
412 break;
413 case 3:
414 /* vendor description */
415 ret = set_usb_string(data, "QEMU " QEMU_VERSION);
416 break;
417 case 4:
418 ret = set_usb_string(data, "HID Mouse");
419 break;
420 case 5:
421 ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
422 break;
423 default:
424 goto fail;
425 }
426 break;
427 default:
428 goto fail;
429 }
430 break;
431 case DeviceRequest | USB_REQ_GET_CONFIGURATION:
432 data[0] = 1;
433 ret = 1;
434 break;
435 case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
436 ret = 0;
437 break;
438 case DeviceRequest | USB_REQ_GET_INTERFACE:
439 data[0] = 0;
440 ret = 1;
441 break;
442 case DeviceOutRequest | USB_REQ_SET_INTERFACE:
443 ret = 0;
444 break;
445 /* hid specific requests */
446 case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
447 switch(value >> 8) {
448 case 0x22:
09b26c5e
FB
449 if (s->kind == USB_MOUSE) {
450 memcpy(data, qemu_mouse_hid_report_descriptor,
451 sizeof(qemu_mouse_hid_report_descriptor));
452 ret = sizeof(qemu_mouse_hid_report_descriptor);
453 } else if (s->kind == USB_TABLET) {
454 memcpy(data, qemu_tablet_hid_report_descriptor,
455 sizeof(qemu_tablet_hid_report_descriptor));
456 ret = sizeof(qemu_tablet_hid_report_descriptor);
457 }
458 break;
59ae540c
FB
459 default:
460 goto fail;
461 }
462 break;
463 case GET_REPORT:
09b26c5e
FB
464 if (s->kind == USB_MOUSE)
465 ret = usb_mouse_poll(s, data, length);
466 else if (s->kind == USB_TABLET)
467 ret = usb_tablet_poll(s, data, length);
59ae540c
FB
468 break;
469 case SET_IDLE:
470 ret = 0;
471 break;
472 default:
473 fail:
474 ret = USB_RET_STALL;
475 break;
476 }
477 return ret;
478}
479
4d611c9a 480static int usb_mouse_handle_data(USBDevice *dev, USBPacket *p)
59ae540c
FB
481{
482 USBMouseState *s = (USBMouseState *)dev;
09b26c5e 483 int ret = 0;
59ae540c 484
4d611c9a 485 switch(p->pid) {
59ae540c 486 case USB_TOKEN_IN:
4d611c9a 487 if (p->devep == 1) {
09b26c5e 488 if (s->kind == USB_MOUSE)
4d611c9a 489 ret = usb_mouse_poll(s, p->data, p->len);
09b26c5e 490 else if (s->kind == USB_TABLET)
4d611c9a 491 ret = usb_tablet_poll(s, p->data, p->len);
59ae540c
FB
492 } else {
493 goto fail;
494 }
495 break;
496 case USB_TOKEN_OUT:
497 default:
498 fail:
499 ret = USB_RET_STALL;
500 break;
501 }
502 return ret;
503}
504
059809e4
FB
505static void usb_mouse_handle_destroy(USBDevice *dev)
506{
507 USBMouseState *s = (USBMouseState *)dev;
508
455204eb 509 qemu_remove_mouse_event_handler(s->eh_entry);
059809e4
FB
510 qemu_free(s);
511}
512
09b26c5e
FB
513USBDevice *usb_tablet_init(void)
514{
515 USBMouseState *s;
516
517 s = qemu_mallocz(sizeof(USBMouseState));
518 if (!s)
519 return NULL;
520 s->dev.speed = USB_SPEED_FULL;
521 s->dev.handle_packet = usb_generic_handle_packet;
522
523 s->dev.handle_reset = usb_mouse_handle_reset;
524 s->dev.handle_control = usb_mouse_handle_control;
525 s->dev.handle_data = usb_mouse_handle_data;
059809e4 526 s->dev.handle_destroy = usb_mouse_handle_destroy;
09b26c5e
FB
527 s->kind = USB_TABLET;
528
1f6e24e7
FB
529 pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
530
09b26c5e
FB
531 return (USBDevice *)s;
532}
533
59ae540c
FB
534USBDevice *usb_mouse_init(void)
535{
536 USBMouseState *s;
537
538 s = qemu_mallocz(sizeof(USBMouseState));
539 if (!s)
540 return NULL;
541 s->dev.speed = USB_SPEED_FULL;
542 s->dev.handle_packet = usb_generic_handle_packet;
543
544 s->dev.handle_reset = usb_mouse_handle_reset;
545 s->dev.handle_control = usb_mouse_handle_control;
546 s->dev.handle_data = usb_mouse_handle_data;
059809e4 547 s->dev.handle_destroy = usb_mouse_handle_destroy;
09b26c5e 548 s->kind = USB_MOUSE;
59ae540c 549
1f6e24e7
FB
550 pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
551
59ae540c
FB
552 return (USBDevice *)s;
553}