Commit 47b2d338d982c9e26b2af6b51c1cc0da7cbd2b60
1 parent
07cf0ba0
Add USB HID keyboard.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2996 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
4 changed files
with
350 additions
and
46 deletions
hw/usb-hid.c
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | * QEMU USB HID devices |
| 3 | 3 | * |
| 4 | 4 | * Copyright (c) 2005 Fabrice Bellard |
| 5 | + * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) | |
| 5 | 6 | * |
| 6 | 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | 8 | * of this software and associated documentation files (the "Software"), to deal |
| ... | ... | @@ -27,21 +28,44 @@ |
| 27 | 28 | #define GET_REPORT 0xa101 |
| 28 | 29 | #define GET_IDLE 0xa102 |
| 29 | 30 | #define GET_PROTOCOL 0xa103 |
| 31 | +#define SET_REPORT 0x2109 | |
| 30 | 32 | #define SET_IDLE 0x210a |
| 31 | 33 | #define SET_PROTOCOL 0x210b |
| 32 | 34 | |
| 33 | -#define USB_MOUSE 1 | |
| 34 | -#define USB_TABLET 2 | |
| 35 | +/* HID descriptor types */ | |
| 36 | +#define USB_DT_HID 0x21 | |
| 37 | +#define USB_DT_REPORT 0x22 | |
| 38 | +#define USB_DT_PHY 0x23 | |
| 39 | + | |
| 40 | +#define USB_MOUSE 1 | |
| 41 | +#define USB_TABLET 2 | |
| 42 | +#define USB_KEYBOARD 3 | |
| 35 | 43 | |
| 36 | 44 | typedef struct USBMouseState { |
| 37 | - USBDevice dev; | |
| 38 | 45 | int dx, dy, dz, buttons_state; |
| 39 | 46 | int x, y; |
| 40 | - int kind; | |
| 41 | 47 | int mouse_grabbed; |
| 42 | 48 | QEMUPutMouseEntry *eh_entry; |
| 43 | 49 | } USBMouseState; |
| 44 | 50 | |
| 51 | +typedef struct USBKeyboardState { | |
| 52 | + uint16_t modifiers; | |
| 53 | + uint8_t leds; | |
| 54 | + uint8_t key[16]; | |
| 55 | + int keys; | |
| 56 | +} USBKeyboardState; | |
| 57 | + | |
| 58 | +typedef struct USBHIDState { | |
| 59 | + USBDevice dev; | |
| 60 | + union { | |
| 61 | + USBMouseState ptr; | |
| 62 | + USBKeyboardState kbd; | |
| 63 | + }; | |
| 64 | + int kind; | |
| 65 | + int protocol; | |
| 66 | + int idle; | |
| 67 | +} USBHIDState; | |
| 68 | + | |
| 45 | 69 | /* mostly the same values as the Bochs USB Mouse device */ |
| 46 | 70 | static const uint8_t qemu_mouse_dev_descriptor[] = { |
| 47 | 71 | 0x12, /* u8 bLength; */ |
| ... | ... | @@ -98,7 +122,7 @@ static const uint8_t qemu_mouse_config_descriptor[] = { |
| 98 | 122 | 0x03, /* u8 if_bInterfaceClass; */ |
| 99 | 123 | 0x01, /* u8 if_bInterfaceSubClass; */ |
| 100 | 124 | 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ |
| 101 | - 0x05, /* u8 if_iInterface; */ | |
| 125 | + 0x07, /* u8 if_iInterface; */ | |
| 102 | 126 | |
| 103 | 127 | /* HID descriptor */ |
| 104 | 128 | 0x09, /* u8 bLength; */ |
| ... | ... | @@ -125,7 +149,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = { |
| 125 | 149 | 0x22, 0x00, /* u16 wTotalLength; */ |
| 126 | 150 | 0x01, /* u8 bNumInterfaces; (1) */ |
| 127 | 151 | 0x01, /* u8 bConfigurationValue; */ |
| 128 | - 0x04, /* u8 iConfiguration; */ | |
| 152 | + 0x05, /* u8 iConfiguration; */ | |
| 129 | 153 | 0xa0, /* u8 bmAttributes; |
| 130 | 154 | Bit 7: must be set, |
| 131 | 155 | 6: Self-powered, |
| ... | ... | @@ -153,7 +177,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = { |
| 153 | 177 | 0x03, /* u8 if_bInterfaceClass; */ |
| 154 | 178 | 0x01, /* u8 if_bInterfaceSubClass; */ |
| 155 | 179 | 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ |
| 156 | - 0x05, /* u8 if_iInterface; */ | |
| 180 | + 0x07, /* u8 if_iInterface; */ | |
| 157 | 181 | |
| 158 | 182 | /* HID descriptor */ |
| 159 | 183 | 0x09, /* u8 bLength; */ |
| ... | ... | @@ -173,6 +197,61 @@ static const uint8_t qemu_tablet_config_descriptor[] = { |
| 173 | 197 | 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ |
| 174 | 198 | }; |
| 175 | 199 | |
| 200 | +static const uint8_t qemu_keyboard_config_descriptor[] = { | |
| 201 | + /* one configuration */ | |
| 202 | + 0x09, /* u8 bLength; */ | |
| 203 | + USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */ | |
| 204 | + 0x22, 0x00, /* u16 wTotalLength; */ | |
| 205 | + 0x01, /* u8 bNumInterfaces; (1) */ | |
| 206 | + 0x01, /* u8 bConfigurationValue; */ | |
| 207 | + 0x06, /* u8 iConfiguration; */ | |
| 208 | + 0xa0, /* u8 bmAttributes; | |
| 209 | + Bit 7: must be set, | |
| 210 | + 6: Self-powered, | |
| 211 | + 5: Remote wakeup, | |
| 212 | + 4..0: resvd */ | |
| 213 | + 0x32, /* u8 MaxPower; */ | |
| 214 | + | |
| 215 | + /* USB 1.1: | |
| 216 | + * USB 2.0, single TT organization (mandatory): | |
| 217 | + * one interface, protocol 0 | |
| 218 | + * | |
| 219 | + * USB 2.0, multiple TT organization (optional): | |
| 220 | + * two interfaces, protocols 1 (like single TT) | |
| 221 | + * and 2 (multiple TT mode) ... config is | |
| 222 | + * sometimes settable | |
| 223 | + * NOT IMPLEMENTED | |
| 224 | + */ | |
| 225 | + | |
| 226 | + /* one interface */ | |
| 227 | + 0x09, /* u8 if_bLength; */ | |
| 228 | + USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */ | |
| 229 | + 0x00, /* u8 if_bInterfaceNumber; */ | |
| 230 | + 0x00, /* u8 if_bAlternateSetting; */ | |
| 231 | + 0x01, /* u8 if_bNumEndpoints; */ | |
| 232 | + 0x03, /* u8 if_bInterfaceClass; HID */ | |
| 233 | + 0x01, /* u8 if_bInterfaceSubClass; Boot */ | |
| 234 | + 0x01, /* u8 if_bInterfaceProtocol; Keyboard */ | |
| 235 | + 0x07, /* u8 if_iInterface; */ | |
| 236 | + | |
| 237 | + /* HID descriptor */ | |
| 238 | + 0x09, /* u8 bLength; */ | |
| 239 | + USB_DT_HID, /* u8 bDescriptorType; */ | |
| 240 | + 0x11, 0x01, /* u16 HID_class */ | |
| 241 | + 0x00, /* u8 country_code */ | |
| 242 | + 0x01, /* u8 num_descriptors */ | |
| 243 | + USB_DT_REPORT, /* u8 type; Report */ | |
| 244 | + 0x3f, 0x00, /* u16 len */ | |
| 245 | + | |
| 246 | + /* one endpoint (status change endpoint) */ | |
| 247 | + 0x07, /* u8 ep_bLength; */ | |
| 248 | + USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; Endpoint */ | |
| 249 | + USB_DIR_IN | 0x01, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ | |
| 250 | + 0x03, /* u8 ep_bmAttributes; Interrupt */ | |
| 251 | + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ | |
| 252 | + 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ | |
| 253 | +}; | |
| 254 | + | |
| 176 | 255 | static const uint8_t qemu_mouse_hid_report_descriptor[] = { |
| 177 | 256 | 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, |
| 178 | 257 | 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, |
| ... | ... | @@ -223,6 +302,83 @@ static const uint8_t qemu_tablet_hid_report_descriptor[] = { |
| 223 | 302 | 0xC0, /* End Collection */ |
| 224 | 303 | }; |
| 225 | 304 | |
| 305 | +static const uint8_t qemu_keyboard_hid_report_descriptor[] = { | |
| 306 | + 0x05, 0x01, /* Usage Page (Generic Desktop) */ | |
| 307 | + 0x09, 0x06, /* Usage (Keyboard) */ | |
| 308 | + 0xa1, 0x01, /* Collection (Application) */ | |
| 309 | + 0x75, 0x01, /* Report Size (1) */ | |
| 310 | + 0x95, 0x08, /* Report Count (8) */ | |
| 311 | + 0x05, 0x07, /* Usage Page (Key Codes) */ | |
| 312 | + 0x19, 0xe0, /* Usage Minimum (224) */ | |
| 313 | + 0x29, 0xe7, /* Usage Maximum (231) */ | |
| 314 | + 0x15, 0x00, /* Logical Minimum (0) */ | |
| 315 | + 0x25, 0x01, /* Logical Maximum (1) */ | |
| 316 | + 0x81, 0x02, /* Input (Data, Variable, Absolute) */ | |
| 317 | + 0x95, 0x01, /* Report Count (1) */ | |
| 318 | + 0x75, 0x08, /* Report Size (8) */ | |
| 319 | + 0x81, 0x01, /* Input (Constant) */ | |
| 320 | + 0x95, 0x05, /* Report Count (5) */ | |
| 321 | + 0x75, 0x01, /* Report Size (1) */ | |
| 322 | + 0x05, 0x08, /* Usage Page (LEDs) */ | |
| 323 | + 0x19, 0x01, /* Usage Minimum (1) */ | |
| 324 | + 0x29, 0x05, /* Usage Maximum (5) */ | |
| 325 | + 0x91, 0x02, /* Output (Data, Variable, Absolute) */ | |
| 326 | + 0x95, 0x01, /* Report Count (1) */ | |
| 327 | + 0x75, 0x03, /* Report Size (3) */ | |
| 328 | + 0x91, 0x01, /* Output (Constant) */ | |
| 329 | + 0x95, 0x06, /* Report Count (6) */ | |
| 330 | + 0x75, 0x08, /* Report Size (8) */ | |
| 331 | + 0x15, 0x00, /* Logical Minimum (0) */ | |
| 332 | + 0x25, 0xff, /* Logical Maximum (255) */ | |
| 333 | + 0x05, 0x07, /* Usage Page (Key Codes) */ | |
| 334 | + 0x19, 0x00, /* Usage Minimum (0) */ | |
| 335 | + 0x29, 0xff, /* Usage Maximum (255) */ | |
| 336 | + 0x81, 0x00, /* Input (Data, Array) */ | |
| 337 | + 0xc0, /* End Collection */ | |
| 338 | +}; | |
| 339 | + | |
| 340 | +#define USB_HID_USAGE_ERROR_ROLLOVER 0x01 | |
| 341 | +#define USB_HID_USAGE_POSTFAIL 0x02 | |
| 342 | +#define USB_HID_USAGE_ERROR_UNDEFINED 0x03 | |
| 343 | + | |
| 344 | +/* Indices are QEMU keycodes, values are from HID Usage Table. Indices | |
| 345 | + * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d. */ | |
| 346 | +static const uint8_t usb_hid_usage_keys[0x100] = { | |
| 347 | + 0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, | |
| 348 | + 0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b, | |
| 349 | + 0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c, | |
| 350 | + 0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16, | |
| 351 | + 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33, | |
| 352 | + 0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19, | |
| 353 | + 0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55, | |
| 354 | + 0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, | |
| 355 | + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f, | |
| 356 | + 0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59, | |
| 357 | + 0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44, | |
| 358 | + 0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, | |
| 359 | + 0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00, | |
| 360 | + 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, | |
| 361 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 362 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, | |
| 363 | + | |
| 364 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 365 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 366 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 367 | + 0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00, | |
| 368 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 369 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 370 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46, | |
| 371 | + 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 372 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, | |
| 373 | + 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, | |
| 374 | + 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, | |
| 375 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 376 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 377 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 378 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 379 | + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
| 380 | +}; | |
| 381 | + | |
| 226 | 382 | static void usb_mouse_event(void *opaque, |
| 227 | 383 | int dx1, int dy1, int dz1, int buttons_state) |
| 228 | 384 | { |
| ... | ... | @@ -245,6 +401,51 @@ static void usb_tablet_event(void *opaque, |
| 245 | 401 | s->buttons_state = buttons_state; |
| 246 | 402 | } |
| 247 | 403 | |
| 404 | +static void usb_keyboard_event(void *opaque, int keycode) | |
| 405 | +{ | |
| 406 | + USBKeyboardState *s = opaque; | |
| 407 | + uint8_t hid_code, key; | |
| 408 | + int i; | |
| 409 | + | |
| 410 | + key = keycode & 0x7f; | |
| 411 | + hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; | |
| 412 | + s->modifiers &= ~(1 << 8); | |
| 413 | + | |
| 414 | + switch (hid_code) { | |
| 415 | + case 0x00: | |
| 416 | + return; | |
| 417 | + | |
| 418 | + case 0xe0: | |
| 419 | + if (s->modifiers & (1 << 9)) { | |
| 420 | + s->modifiers ^= 3 << 8; | |
| 421 | + return; | |
| 422 | + } | |
| 423 | + case 0xe1 ... 0xe7: | |
| 424 | + if (keycode & (1 << 7)) { | |
| 425 | + s->modifiers &= ~(1 << (hid_code & 0x0f)); | |
| 426 | + return; | |
| 427 | + } | |
| 428 | + case 0xe8 ... 0xef: | |
| 429 | + s->modifiers |= 1 << (hid_code & 0x0f); | |
| 430 | + return; | |
| 431 | + } | |
| 432 | + | |
| 433 | + if (keycode & (1 << 7)) { | |
| 434 | + for (i = s->keys - 1; i >= 0; i --) | |
| 435 | + if (s->key[i] == hid_code) { | |
| 436 | + s->key[i] = s->key[-- s->keys]; | |
| 437 | + s->key[s->keys] = 0x00; | |
| 438 | + return; | |
| 439 | + } | |
| 440 | + } else { | |
| 441 | + for (i = s->keys - 1; i >= 0; i --) | |
| 442 | + if (s->key[i] == hid_code) | |
| 443 | + return; | |
| 444 | + if (s->keys < sizeof(s->key)) | |
| 445 | + s->key[s->keys ++] = hid_code; | |
| 446 | + } | |
| 447 | +} | |
| 448 | + | |
| 248 | 449 | static inline int int_clamp(int val, int vmin, int vmax) |
| 249 | 450 | { |
| 250 | 451 | if (val < vmin) |
| ... | ... | @@ -326,22 +527,59 @@ static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) |
| 326 | 527 | return l; |
| 327 | 528 | } |
| 328 | 529 | |
| 530 | +static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len) | |
| 531 | +{ | |
| 532 | + if (len < 2) | |
| 533 | + return 0; | |
| 534 | + | |
| 535 | + buf[0] = s->modifiers & 0xff; | |
| 536 | + buf[1] = 0; | |
| 537 | + if (s->keys > 6) | |
| 538 | + memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2); | |
| 539 | + else | |
| 540 | + memcpy(buf + 2, s->key, MIN(8, len) - 2); | |
| 541 | + | |
| 542 | + return MIN(8, len); | |
| 543 | +} | |
| 544 | + | |
| 545 | +static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) | |
| 546 | +{ | |
| 547 | + if (len > 0) { | |
| 548 | + /* 0x01: Num Lock LED | |
| 549 | + * 0x02: Caps Lock LED | |
| 550 | + * 0x04: Scroll Lock LED | |
| 551 | + * 0x08: Compose LED | |
| 552 | + * 0x10: Kana LED */ | |
| 553 | + s->leds = buf[0]; | |
| 554 | + } | |
| 555 | + return 0; | |
| 556 | +} | |
| 557 | + | |
| 329 | 558 | static void usb_mouse_handle_reset(USBDevice *dev) |
| 330 | 559 | { |
| 331 | - USBMouseState *s = (USBMouseState *)dev; | |
| 332 | - | |
| 333 | - s->dx = 0; | |
| 334 | - s->dy = 0; | |
| 335 | - s->dz = 0; | |
| 336 | - s->x = 0; | |
| 337 | - s->y = 0; | |
| 338 | - s->buttons_state = 0; | |
| 560 | + USBHIDState *s = (USBHIDState *)dev; | |
| 561 | + | |
| 562 | + s->ptr.dx = 0; | |
| 563 | + s->ptr.dy = 0; | |
| 564 | + s->ptr.dz = 0; | |
| 565 | + s->ptr.x = 0; | |
| 566 | + s->ptr.y = 0; | |
| 567 | + s->ptr.buttons_state = 0; | |
| 568 | + s->protocol = 1; | |
| 569 | +} | |
| 570 | + | |
| 571 | +static void usb_keyboard_handle_reset(USBDevice *dev) | |
| 572 | +{ | |
| 573 | + USBHIDState *s = (USBHIDState *)dev; | |
| 574 | + | |
| 575 | + qemu_add_kbd_event_handler(usb_keyboard_event, &s->kbd); | |
| 576 | + s->protocol = 1; | |
| 339 | 577 | } |
| 340 | 578 | |
| 341 | -static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | |
| 579 | +static int usb_hid_handle_control(USBDevice *dev, int request, int value, | |
| 342 | 580 | int index, int length, uint8_t *data) |
| 343 | 581 | { |
| 344 | - USBMouseState *s = (USBMouseState *)dev; | |
| 582 | + USBHIDState *s = (USBHIDState *)dev; | |
| 345 | 583 | int ret = 0; |
| 346 | 584 | |
| 347 | 585 | switch(request) { |
| ... | ... | @@ -387,7 +625,11 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, |
| 387 | 625 | memcpy(data, qemu_tablet_config_descriptor, |
| 388 | 626 | sizeof(qemu_tablet_config_descriptor)); |
| 389 | 627 | ret = sizeof(qemu_tablet_config_descriptor); |
| 390 | - } | |
| 628 | + } else if (s->kind == USB_KEYBOARD) { | |
| 629 | + memcpy(data, qemu_keyboard_config_descriptor, | |
| 630 | + sizeof(qemu_keyboard_config_descriptor)); | |
| 631 | + ret = sizeof(qemu_keyboard_config_descriptor); | |
| 632 | + } | |
| 391 | 633 | break; |
| 392 | 634 | case USB_DT_STRING: |
| 393 | 635 | switch(value & 0xff) { |
| ... | ... | @@ -405,10 +647,7 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, |
| 405 | 647 | break; |
| 406 | 648 | case 2: |
| 407 | 649 | /* product description */ |
| 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"); | |
| 650 | + ret = set_usb_string(data, s->dev.devname); | |
| 412 | 651 | break; |
| 413 | 652 | case 3: |
| 414 | 653 | /* vendor description */ |
| ... | ... | @@ -418,6 +657,12 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, |
| 418 | 657 | ret = set_usb_string(data, "HID Mouse"); |
| 419 | 658 | break; |
| 420 | 659 | case 5: |
| 660 | + ret = set_usb_string(data, "HID Tablet"); | |
| 661 | + break; | |
| 662 | + case 6: | |
| 663 | + ret = set_usb_string(data, "HID Keyboard"); | |
| 664 | + break; | |
| 665 | + case 7: | |
| 421 | 666 | ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); |
| 422 | 667 | break; |
| 423 | 668 | default: |
| ... | ... | @@ -454,19 +699,48 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, |
| 454 | 699 | memcpy(data, qemu_tablet_hid_report_descriptor, |
| 455 | 700 | sizeof(qemu_tablet_hid_report_descriptor)); |
| 456 | 701 | ret = sizeof(qemu_tablet_hid_report_descriptor); |
| 457 | - } | |
| 458 | - break; | |
| 702 | + } else if (s->kind == USB_KEYBOARD) { | |
| 703 | + memcpy(data, qemu_keyboard_hid_report_descriptor, | |
| 704 | + sizeof(qemu_keyboard_hid_report_descriptor)); | |
| 705 | + ret = sizeof(qemu_keyboard_hid_report_descriptor); | |
| 706 | + } | |
| 707 | + break; | |
| 459 | 708 | default: |
| 460 | 709 | goto fail; |
| 461 | 710 | } |
| 462 | 711 | break; |
| 463 | 712 | case GET_REPORT: |
| 464 | 713 | if (s->kind == USB_MOUSE) |
| 465 | - ret = usb_mouse_poll(s, data, length); | |
| 714 | + ret = usb_mouse_poll(&s->ptr, data, length); | |
| 466 | 715 | else if (s->kind == USB_TABLET) |
| 467 | - ret = usb_tablet_poll(s, data, length); | |
| 716 | + ret = usb_tablet_poll(&s->ptr, data, length); | |
| 717 | + else if (s->kind == USB_KEYBOARD) | |
| 718 | + ret = usb_keyboard_poll(&s->kbd, data, length); | |
| 719 | + break; | |
| 720 | + case SET_REPORT: | |
| 721 | + if (s->kind == USB_KEYBOARD) | |
| 722 | + ret = usb_keyboard_write(&s->kbd, data, length); | |
| 723 | + else | |
| 724 | + goto fail; | |
| 725 | + break; | |
| 726 | + case GET_PROTOCOL: | |
| 727 | + if (s->kind != USB_KEYBOARD) | |
| 728 | + goto fail; | |
| 729 | + ret = 1; | |
| 730 | + data[0] = s->protocol; | |
| 731 | + break; | |
| 732 | + case SET_PROTOCOL: | |
| 733 | + if (s->kind != USB_KEYBOARD) | |
| 734 | + goto fail; | |
| 735 | + ret = 0; | |
| 736 | + s->protocol = value; | |
| 737 | + break; | |
| 738 | + case GET_IDLE: | |
| 739 | + ret = 1; | |
| 740 | + data[0] = s->idle; | |
| 468 | 741 | break; |
| 469 | 742 | case SET_IDLE: |
| 743 | + s->idle = value; | |
| 470 | 744 | ret = 0; |
| 471 | 745 | break; |
| 472 | 746 | default: |
| ... | ... | @@ -477,18 +751,20 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, |
| 477 | 751 | return ret; |
| 478 | 752 | } |
| 479 | 753 | |
| 480 | -static int usb_mouse_handle_data(USBDevice *dev, USBPacket *p) | |
| 754 | +static int usb_hid_handle_data(USBDevice *dev, USBPacket *p) | |
| 481 | 755 | { |
| 482 | - USBMouseState *s = (USBMouseState *)dev; | |
| 756 | + USBHIDState *s = (USBHIDState *)dev; | |
| 483 | 757 | int ret = 0; |
| 484 | 758 | |
| 485 | 759 | switch(p->pid) { |
| 486 | 760 | case USB_TOKEN_IN: |
| 487 | 761 | if (p->devep == 1) { |
| 488 | - if (s->kind == USB_MOUSE) | |
| 489 | - ret = usb_mouse_poll(s, p->data, p->len); | |
| 490 | - else if (s->kind == USB_TABLET) | |
| 491 | - ret = usb_tablet_poll(s, p->data, p->len); | |
| 762 | + if (s->kind == USB_MOUSE) | |
| 763 | + ret = usb_mouse_poll(&s->ptr, p->data, p->len); | |
| 764 | + else if (s->kind == USB_TABLET) | |
| 765 | + ret = usb_tablet_poll(&s->ptr, p->data, p->len); | |
| 766 | + else if (s->kind == USB_KEYBOARD) | |
| 767 | + ret = usb_keyboard_poll(&s->kbd, p->data, p->len); | |
| 492 | 768 | } else { |
| 493 | 769 | goto fail; |
| 494 | 770 | } |
| ... | ... | @@ -502,28 +778,30 @@ static int usb_mouse_handle_data(USBDevice *dev, USBPacket *p) |
| 502 | 778 | return ret; |
| 503 | 779 | } |
| 504 | 780 | |
| 505 | -static void usb_mouse_handle_destroy(USBDevice *dev) | |
| 781 | +static void usb_hid_handle_destroy(USBDevice *dev) | |
| 506 | 782 | { |
| 507 | - USBMouseState *s = (USBMouseState *)dev; | |
| 783 | + USBHIDState *s = (USBHIDState *)dev; | |
| 508 | 784 | |
| 509 | - qemu_remove_mouse_event_handler(s->eh_entry); | |
| 785 | + if (s->kind != USB_KEYBOARD) | |
| 786 | + qemu_remove_mouse_event_handler(s->ptr.eh_entry); | |
| 787 | + /* TODO: else */ | |
| 510 | 788 | qemu_free(s); |
| 511 | 789 | } |
| 512 | 790 | |
| 513 | 791 | USBDevice *usb_tablet_init(void) |
| 514 | 792 | { |
| 515 | - USBMouseState *s; | |
| 793 | + USBHIDState *s; | |
| 516 | 794 | |
| 517 | - s = qemu_mallocz(sizeof(USBMouseState)); | |
| 795 | + s = qemu_mallocz(sizeof(USBHIDState)); | |
| 518 | 796 | if (!s) |
| 519 | 797 | return NULL; |
| 520 | 798 | s->dev.speed = USB_SPEED_FULL; |
| 521 | 799 | s->dev.handle_packet = usb_generic_handle_packet; |
| 522 | 800 | |
| 523 | 801 | 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; | |
| 526 | - s->dev.handle_destroy = usb_mouse_handle_destroy; | |
| 802 | + s->dev.handle_control = usb_hid_handle_control; | |
| 803 | + s->dev.handle_data = usb_hid_handle_data; | |
| 804 | + s->dev.handle_destroy = usb_hid_handle_destroy; | |
| 527 | 805 | s->kind = USB_TABLET; |
| 528 | 806 | |
| 529 | 807 | pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); |
| ... | ... | @@ -533,21 +811,42 @@ USBDevice *usb_tablet_init(void) |
| 533 | 811 | |
| 534 | 812 | USBDevice *usb_mouse_init(void) |
| 535 | 813 | { |
| 536 | - USBMouseState *s; | |
| 814 | + USBHIDState *s; | |
| 537 | 815 | |
| 538 | - s = qemu_mallocz(sizeof(USBMouseState)); | |
| 816 | + s = qemu_mallocz(sizeof(USBHIDState)); | |
| 539 | 817 | if (!s) |
| 540 | 818 | return NULL; |
| 541 | 819 | s->dev.speed = USB_SPEED_FULL; |
| 542 | 820 | s->dev.handle_packet = usb_generic_handle_packet; |
| 543 | 821 | |
| 544 | 822 | 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; | |
| 547 | - s->dev.handle_destroy = usb_mouse_handle_destroy; | |
| 823 | + s->dev.handle_control = usb_hid_handle_control; | |
| 824 | + s->dev.handle_data = usb_hid_handle_data; | |
| 825 | + s->dev.handle_destroy = usb_hid_handle_destroy; | |
| 548 | 826 | s->kind = USB_MOUSE; |
| 549 | 827 | |
| 550 | 828 | pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); |
| 551 | 829 | |
| 552 | 830 | return (USBDevice *)s; |
| 553 | 831 | } |
| 832 | + | |
| 833 | +USBDevice *usb_keyboard_init(void) | |
| 834 | +{ | |
| 835 | + USBHIDState *s; | |
| 836 | + | |
| 837 | + s = qemu_mallocz(sizeof(USBHIDState)); | |
| 838 | + if (!s) | |
| 839 | + return NULL; | |
| 840 | + s->dev.speed = USB_SPEED_FULL; | |
| 841 | + s->dev.handle_packet = usb_generic_handle_packet; | |
| 842 | + | |
| 843 | + s->dev.handle_reset = usb_keyboard_handle_reset; | |
| 844 | + s->dev.handle_control = usb_hid_handle_control; | |
| 845 | + s->dev.handle_data = usb_hid_handle_data; | |
| 846 | + s->dev.handle_destroy = usb_hid_handle_destroy; | |
| 847 | + s->kind = USB_KEYBOARD; | |
| 848 | + | |
| 849 | + pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard"); | |
| 850 | + | |
| 851 | + return (USBDevice *) s; | |
| 852 | +} | ... | ... |
hw/usb.h
qemu-doc.texi
| ... | ... | @@ -1362,6 +1362,8 @@ Pass through the host device identified by @var{vendor_id:product_id} |
| 1362 | 1362 | Virtual Wacom PenPartner tablet. This device is similar to the @code{tablet} |
| 1363 | 1363 | above but it can be used with the tslib library because in addition to touch |
| 1364 | 1364 | coordinates it reports touch pressure. |
| 1365 | +@item @code{keyboard} | |
| 1366 | +Standard USB keyboard. Will override the PS/2 keyboard (if present). | |
| 1365 | 1367 | @end table |
| 1366 | 1368 | |
| 1367 | 1369 | @node host_usb_devices | ... | ... |
vl.c
| ... | ... | @@ -4319,7 +4319,9 @@ static int usb_device_add(const char *devname) |
| 4319 | 4319 | } else if (!strcmp(devname, "mouse")) { |
| 4320 | 4320 | dev = usb_mouse_init(); |
| 4321 | 4321 | } else if (!strcmp(devname, "tablet")) { |
| 4322 | - dev = usb_tablet_init(); | |
| 4322 | + dev = usb_tablet_init(); | |
| 4323 | + } else if (!strcmp(devname, "keyboard")) { | |
| 4324 | + dev = usb_keyboard_init(); | |
| 4323 | 4325 | } else if (strstart(devname, "disk:", &p)) { |
| 4324 | 4326 | dev = usb_msd_init(p); |
| 4325 | 4327 | } else if (!strcmp(devname, "wacom-tablet")) { | ... | ... |