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