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,6 +2,7 @@ | ||
| 2 | * QEMU USB HID devices | 2 | * QEMU USB HID devices |
| 3 | * | 3 | * |
| 4 | * Copyright (c) 2005 Fabrice Bellard | 4 | * Copyright (c) 2005 Fabrice Bellard |
| 5 | + * Copyright (c) 2007 OpenMoko, Inc. (andrew@openedhand.com) | ||
| 5 | * | 6 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | 7 | * 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 | * of this software and associated documentation files (the "Software"), to deal |
| @@ -27,21 +28,44 @@ | @@ -27,21 +28,44 @@ | ||
| 27 | #define GET_REPORT 0xa101 | 28 | #define GET_REPORT 0xa101 |
| 28 | #define GET_IDLE 0xa102 | 29 | #define GET_IDLE 0xa102 |
| 29 | #define GET_PROTOCOL 0xa103 | 30 | #define GET_PROTOCOL 0xa103 |
| 31 | +#define SET_REPORT 0x2109 | ||
| 30 | #define SET_IDLE 0x210a | 32 | #define SET_IDLE 0x210a |
| 31 | #define SET_PROTOCOL 0x210b | 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 | typedef struct USBMouseState { | 44 | typedef struct USBMouseState { |
| 37 | - USBDevice dev; | ||
| 38 | int dx, dy, dz, buttons_state; | 45 | int dx, dy, dz, buttons_state; |
| 39 | int x, y; | 46 | int x, y; |
| 40 | - int kind; | ||
| 41 | int mouse_grabbed; | 47 | int mouse_grabbed; |
| 42 | QEMUPutMouseEntry *eh_entry; | 48 | QEMUPutMouseEntry *eh_entry; |
| 43 | } USBMouseState; | 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 | /* mostly the same values as the Bochs USB Mouse device */ | 69 | /* mostly the same values as the Bochs USB Mouse device */ |
| 46 | static const uint8_t qemu_mouse_dev_descriptor[] = { | 70 | static const uint8_t qemu_mouse_dev_descriptor[] = { |
| 47 | 0x12, /* u8 bLength; */ | 71 | 0x12, /* u8 bLength; */ |
| @@ -98,7 +122,7 @@ static const uint8_t qemu_mouse_config_descriptor[] = { | @@ -98,7 +122,7 @@ static const uint8_t qemu_mouse_config_descriptor[] = { | ||
| 98 | 0x03, /* u8 if_bInterfaceClass; */ | 122 | 0x03, /* u8 if_bInterfaceClass; */ |
| 99 | 0x01, /* u8 if_bInterfaceSubClass; */ | 123 | 0x01, /* u8 if_bInterfaceSubClass; */ |
| 100 | 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ | 124 | 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ |
| 101 | - 0x05, /* u8 if_iInterface; */ | 125 | + 0x07, /* u8 if_iInterface; */ |
| 102 | 126 | ||
| 103 | /* HID descriptor */ | 127 | /* HID descriptor */ |
| 104 | 0x09, /* u8 bLength; */ | 128 | 0x09, /* u8 bLength; */ |
| @@ -125,7 +149,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = { | @@ -125,7 +149,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = { | ||
| 125 | 0x22, 0x00, /* u16 wTotalLength; */ | 149 | 0x22, 0x00, /* u16 wTotalLength; */ |
| 126 | 0x01, /* u8 bNumInterfaces; (1) */ | 150 | 0x01, /* u8 bNumInterfaces; (1) */ |
| 127 | 0x01, /* u8 bConfigurationValue; */ | 151 | 0x01, /* u8 bConfigurationValue; */ |
| 128 | - 0x04, /* u8 iConfiguration; */ | 152 | + 0x05, /* u8 iConfiguration; */ |
| 129 | 0xa0, /* u8 bmAttributes; | 153 | 0xa0, /* u8 bmAttributes; |
| 130 | Bit 7: must be set, | 154 | Bit 7: must be set, |
| 131 | 6: Self-powered, | 155 | 6: Self-powered, |
| @@ -153,7 +177,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = { | @@ -153,7 +177,7 @@ static const uint8_t qemu_tablet_config_descriptor[] = { | ||
| 153 | 0x03, /* u8 if_bInterfaceClass; */ | 177 | 0x03, /* u8 if_bInterfaceClass; */ |
| 154 | 0x01, /* u8 if_bInterfaceSubClass; */ | 178 | 0x01, /* u8 if_bInterfaceSubClass; */ |
| 155 | 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ | 179 | 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ |
| 156 | - 0x05, /* u8 if_iInterface; */ | 180 | + 0x07, /* u8 if_iInterface; */ |
| 157 | 181 | ||
| 158 | /* HID descriptor */ | 182 | /* HID descriptor */ |
| 159 | 0x09, /* u8 bLength; */ | 183 | 0x09, /* u8 bLength; */ |
| @@ -173,6 +197,61 @@ static const uint8_t qemu_tablet_config_descriptor[] = { | @@ -173,6 +197,61 @@ static const uint8_t qemu_tablet_config_descriptor[] = { | ||
| 173 | 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ | 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 | static const uint8_t qemu_mouse_hid_report_descriptor[] = { | 255 | static const uint8_t qemu_mouse_hid_report_descriptor[] = { |
| 177 | 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, | 256 | 0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, |
| 178 | 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, | 257 | 0xA1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, |
| @@ -223,6 +302,83 @@ static const uint8_t qemu_tablet_hid_report_descriptor[] = { | @@ -223,6 +302,83 @@ static const uint8_t qemu_tablet_hid_report_descriptor[] = { | ||
| 223 | 0xC0, /* End Collection */ | 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 | static void usb_mouse_event(void *opaque, | 382 | static void usb_mouse_event(void *opaque, |
| 227 | int dx1, int dy1, int dz1, int buttons_state) | 383 | int dx1, int dy1, int dz1, int buttons_state) |
| 228 | { | 384 | { |
| @@ -245,6 +401,51 @@ static void usb_tablet_event(void *opaque, | @@ -245,6 +401,51 @@ static void usb_tablet_event(void *opaque, | ||
| 245 | s->buttons_state = buttons_state; | 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 | static inline int int_clamp(int val, int vmin, int vmax) | 449 | static inline int int_clamp(int val, int vmin, int vmax) |
| 249 | { | 450 | { |
| 250 | if (val < vmin) | 451 | if (val < vmin) |
| @@ -326,22 +527,59 @@ static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) | @@ -326,22 +527,59 @@ static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) | ||
| 326 | return l; | 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 | static void usb_mouse_handle_reset(USBDevice *dev) | 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 | int index, int length, uint8_t *data) | 580 | int index, int length, uint8_t *data) |
| 343 | { | 581 | { |
| 344 | - USBMouseState *s = (USBMouseState *)dev; | 582 | + USBHIDState *s = (USBHIDState *)dev; |
| 345 | int ret = 0; | 583 | int ret = 0; |
| 346 | 584 | ||
| 347 | switch(request) { | 585 | switch(request) { |
| @@ -387,7 +625,11 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | @@ -387,7 +625,11 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | ||
| 387 | memcpy(data, qemu_tablet_config_descriptor, | 625 | memcpy(data, qemu_tablet_config_descriptor, |
| 388 | sizeof(qemu_tablet_config_descriptor)); | 626 | sizeof(qemu_tablet_config_descriptor)); |
| 389 | ret = sizeof(qemu_tablet_config_descriptor); | 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 | break; | 633 | break; |
| 392 | case USB_DT_STRING: | 634 | case USB_DT_STRING: |
| 393 | switch(value & 0xff) { | 635 | switch(value & 0xff) { |
| @@ -405,10 +647,7 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | @@ -405,10 +647,7 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | ||
| 405 | break; | 647 | break; |
| 406 | case 2: | 648 | case 2: |
| 407 | /* product description */ | 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 | break; | 651 | break; |
| 413 | case 3: | 652 | case 3: |
| 414 | /* vendor description */ | 653 | /* vendor description */ |
| @@ -418,6 +657,12 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | @@ -418,6 +657,12 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | ||
| 418 | ret = set_usb_string(data, "HID Mouse"); | 657 | ret = set_usb_string(data, "HID Mouse"); |
| 419 | break; | 658 | break; |
| 420 | case 5: | 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 | ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); | 666 | ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); |
| 422 | break; | 667 | break; |
| 423 | default: | 668 | default: |
| @@ -454,19 +699,48 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | @@ -454,19 +699,48 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | ||
| 454 | memcpy(data, qemu_tablet_hid_report_descriptor, | 699 | memcpy(data, qemu_tablet_hid_report_descriptor, |
| 455 | sizeof(qemu_tablet_hid_report_descriptor)); | 700 | sizeof(qemu_tablet_hid_report_descriptor)); |
| 456 | ret = sizeof(qemu_tablet_hid_report_descriptor); | 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 | default: | 708 | default: |
| 460 | goto fail; | 709 | goto fail; |
| 461 | } | 710 | } |
| 462 | break; | 711 | break; |
| 463 | case GET_REPORT: | 712 | case GET_REPORT: |
| 464 | if (s->kind == USB_MOUSE) | 713 | if (s->kind == USB_MOUSE) |
| 465 | - ret = usb_mouse_poll(s, data, length); | 714 | + ret = usb_mouse_poll(&s->ptr, data, length); |
| 466 | else if (s->kind == USB_TABLET) | 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 | break; | 741 | break; |
| 469 | case SET_IDLE: | 742 | case SET_IDLE: |
| 743 | + s->idle = value; | ||
| 470 | ret = 0; | 744 | ret = 0; |
| 471 | break; | 745 | break; |
| 472 | default: | 746 | default: |
| @@ -477,18 +751,20 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | @@ -477,18 +751,20 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, | ||
| 477 | return ret; | 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 | int ret = 0; | 757 | int ret = 0; |
| 484 | 758 | ||
| 485 | switch(p->pid) { | 759 | switch(p->pid) { |
| 486 | case USB_TOKEN_IN: | 760 | case USB_TOKEN_IN: |
| 487 | if (p->devep == 1) { | 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 | } else { | 768 | } else { |
| 493 | goto fail; | 769 | goto fail; |
| 494 | } | 770 | } |
| @@ -502,28 +778,30 @@ static int usb_mouse_handle_data(USBDevice *dev, USBPacket *p) | @@ -502,28 +778,30 @@ static int usb_mouse_handle_data(USBDevice *dev, USBPacket *p) | ||
| 502 | return ret; | 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 | qemu_free(s); | 788 | qemu_free(s); |
| 511 | } | 789 | } |
| 512 | 790 | ||
| 513 | USBDevice *usb_tablet_init(void) | 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 | if (!s) | 796 | if (!s) |
| 519 | return NULL; | 797 | return NULL; |
| 520 | s->dev.speed = USB_SPEED_FULL; | 798 | s->dev.speed = USB_SPEED_FULL; |
| 521 | s->dev.handle_packet = usb_generic_handle_packet; | 799 | s->dev.handle_packet = usb_generic_handle_packet; |
| 522 | 800 | ||
| 523 | s->dev.handle_reset = usb_mouse_handle_reset; | 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 | s->kind = USB_TABLET; | 805 | s->kind = USB_TABLET; |
| 528 | 806 | ||
| 529 | pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); | 807 | pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet"); |
| @@ -533,21 +811,42 @@ USBDevice *usb_tablet_init(void) | @@ -533,21 +811,42 @@ USBDevice *usb_tablet_init(void) | ||
| 533 | 811 | ||
| 534 | USBDevice *usb_mouse_init(void) | 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 | if (!s) | 817 | if (!s) |
| 540 | return NULL; | 818 | return NULL; |
| 541 | s->dev.speed = USB_SPEED_FULL; | 819 | s->dev.speed = USB_SPEED_FULL; |
| 542 | s->dev.handle_packet = usb_generic_handle_packet; | 820 | s->dev.handle_packet = usb_generic_handle_packet; |
| 543 | 821 | ||
| 544 | s->dev.handle_reset = usb_mouse_handle_reset; | 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 | s->kind = USB_MOUSE; | 826 | s->kind = USB_MOUSE; |
| 549 | 827 | ||
| 550 | pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); | 828 | pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse"); |
| 551 | 829 | ||
| 552 | return (USBDevice *)s; | 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
| @@ -218,6 +218,7 @@ void usb_host_info(void); | @@ -218,6 +218,7 @@ void usb_host_info(void); | ||
| 218 | /* usb-hid.c */ | 218 | /* usb-hid.c */ |
| 219 | USBDevice *usb_mouse_init(void); | 219 | USBDevice *usb_mouse_init(void); |
| 220 | USBDevice *usb_tablet_init(void); | 220 | USBDevice *usb_tablet_init(void); |
| 221 | +USBDevice *usb_keyboard_init(void); | ||
| 221 | 222 | ||
| 222 | /* usb-msd.c */ | 223 | /* usb-msd.c */ |
| 223 | USBDevice *usb_msd_init(const char *filename); | 224 | USBDevice *usb_msd_init(const char *filename); |
qemu-doc.texi
| @@ -1362,6 +1362,8 @@ Pass through the host device identified by @var{vendor_id:product_id} | @@ -1362,6 +1362,8 @@ Pass through the host device identified by @var{vendor_id:product_id} | ||
| 1362 | Virtual Wacom PenPartner tablet. This device is similar to the @code{tablet} | 1362 | Virtual Wacom PenPartner tablet. This device is similar to the @code{tablet} |
| 1363 | above but it can be used with the tslib library because in addition to touch | 1363 | above but it can be used with the tslib library because in addition to touch |
| 1364 | coordinates it reports touch pressure. | 1364 | coordinates it reports touch pressure. |
| 1365 | +@item @code{keyboard} | ||
| 1366 | +Standard USB keyboard. Will override the PS/2 keyboard (if present). | ||
| 1365 | @end table | 1367 | @end table |
| 1366 | 1368 | ||
| 1367 | @node host_usb_devices | 1369 | @node host_usb_devices |
vl.c
| @@ -4319,7 +4319,9 @@ static int usb_device_add(const char *devname) | @@ -4319,7 +4319,9 @@ static int usb_device_add(const char *devname) | ||
| 4319 | } else if (!strcmp(devname, "mouse")) { | 4319 | } else if (!strcmp(devname, "mouse")) { |
| 4320 | dev = usb_mouse_init(); | 4320 | dev = usb_mouse_init(); |
| 4321 | } else if (!strcmp(devname, "tablet")) { | 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 | } else if (strstart(devname, "disk:", &p)) { | 4325 | } else if (strstart(devname, "disk:", &p)) { |
| 4324 | dev = usb_msd_init(p); | 4326 | dev = usb_msd_init(p); |
| 4325 | } else if (!strcmp(devname, "wacom-tablet")) { | 4327 | } else if (!strcmp(devname, "wacom-tablet")) { |