diff options
Diffstat (limited to 'hw/usb-hid.c')
-rw-r--r-- | hw/usb-hid.c | 220 |
1 files changed, 205 insertions, 15 deletions
diff --git a/hw/usb-hid.c b/hw/usb-hid.c index 6f9ee827d8..17160ebe32 100644 --- a/hw/usb-hid.c +++ b/hw/usb-hid.c @@ -30,9 +30,15 @@ #define SET_IDLE 0x210a #define SET_PROTOCOL 0x210b +#define USB_MOUSE 1 +#define USB_TABLET 2 + typedef struct USBMouseState { USBDevice dev; int dx, dy, dz, buttons_state; + int x, y; + int kind; + int mouse_grabbed; } USBMouseState; /* mostly the same values as the Bochs USB Mouse device */ @@ -93,6 +99,15 @@ static const uint8_t qemu_mouse_config_descriptor[] = { 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ 0x05, /* u8 if_iInterface; */ + /* HID descriptor */ + 0x09, /* u8 bLength; */ + 0x21, /* u8 bDescriptorType; */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type; Report */ + 50, 0, /* u16 len */ + /* one endpoint (status change endpoint) */ 0x07, /* u8 ep_bLength; */ 0x05, /* u8 ep_bDescriptorType; Endpoint */ @@ -100,6 +115,44 @@ static const uint8_t qemu_mouse_config_descriptor[] = { 0x03, /* u8 ep_bmAttributes; Interrupt */ 0x03, 0x00, /* u16 ep_wMaxPacketSize; */ 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +}; + +static const uint8_t qemu_tablet_config_descriptor[] = { + /* one configuration */ + 0x09, /* u8 bLength; */ + 0x02, /* u8 bDescriptorType; Configuration */ + 0x22, 0x00, /* u16 wTotalLength; */ + 0x01, /* u8 bNumInterfaces; (1) */ + 0x01, /* u8 bConfigurationValue; */ + 0x04, /* u8 iConfiguration; */ + 0xa0, /* u8 bmAttributes; + Bit 7: must be set, + 6: Self-powered, + 5: Remote wakeup, + 4..0: resvd */ + 50, /* u8 MaxPower; */ + + /* USB 1.1: + * USB 2.0, single TT organization (mandatory): + * one interface, protocol 0 + * + * USB 2.0, multiple TT organization (optional): + * two interfaces, protocols 1 (like single TT) + * and 2 (multiple TT mode) ... config is + * sometimes settable + * NOT IMPLEMENTED + */ + + /* one interface */ + 0x09, /* u8 if_bLength; */ + 0x04, /* u8 if_bDescriptorType; Interface */ + 0x00, /* u8 if_bInterfaceNumber; */ + 0x00, /* u8 if_bAlternateSetting; */ + 0x01, /* u8 if_bNumEndpoints; */ + 0x03, /* u8 if_bInterfaceClass; */ + 0x01, /* u8 if_bInterfaceSubClass; */ + 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ + 0x05, /* u8 if_iInterface; */ /* HID descriptor */ 0x09, /* u8 bLength; */ @@ -108,7 +161,15 @@ static const uint8_t qemu_mouse_config_descriptor[] = { 0x00, /* u8 country_code */ 0x01, /* u8 num_descriptors */ 0x22, /* u8 type; Report */ - 50, 0, /* u16 len */ + 74, 0, /* u16 len */ + + /* one endpoint (status change endpoint) */ + 0x07, /* u8 ep_bLength; */ + 0x05, /* u8 ep_bDescriptorType; Endpoint */ + 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* u8 ep_bmAttributes; Interrupt */ + 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ + 0x03, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ }; static const uint8_t qemu_mouse_hid_report_descriptor[] = { @@ -121,6 +182,46 @@ static const uint8_t qemu_mouse_hid_report_descriptor[] = { 0xC0, 0xC0, }; +static const uint8_t qemu_tablet_hid_report_descriptor[] = { + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x01, /* Usage Mouse */ + 0xA1, 0x01, /* Collection Application */ + 0x09, 0x01, /* Usage Pointer */ + 0xA1, 0x00, /* Collection Physical */ + 0x05, 0x09, /* Usage Page Button */ + 0x19, 0x01, /* Usage Minimum Button 1 */ + 0x29, 0x03, /* Usage Maximum Button 3 */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x25, 0x01, /* Logical Maximum 1 */ + 0x95, 0x03, /* Report Count 3 */ + 0x75, 0x01, /* Report Size 1 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x95, 0x01, /* Report Count 1 */ + 0x75, 0x05, /* Report Size 5 */ + 0x81, 0x01, /* Input (Cnst, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x30, /* Usage X */ + 0x09, 0x31, /* Usage Y */ + 0x15, 0x00, /* Logical Minimum 0 */ + 0x26, 0xFF, 0x7F, /* Logical Maximum 0x7fff */ + 0x35, 0x00, /* Physical Minimum 0 */ + 0x46, 0xFE, 0x7F, /* Physical Maximum 0x7fff */ + 0x75, 0x10, /* Report Size 16 */ + 0x95, 0x02, /* Report Count 2 */ + 0x81, 0x02, /* Input (Data, Var, Abs) */ + 0x05, 0x01, /* Usage Page Generic Desktop */ + 0x09, 0x38, /* Usage Wheel */ + 0x15, 0x81, /* Logical Minimum -127 */ + 0x25, 0x7F, /* Logical Maximum 127 */ + 0x35, 0x00, /* Physical Minimum 0 (same as logical) */ + 0x45, 0x00, /* Physical Maximum 0 (same as logical) */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x01, /* Report Count 1 */ + 0x81, 0x02, /* Input (Data, Var, Rel) */ + 0xC0, /* End Collection */ + 0xC0, /* End Collection */ +}; + static void usb_mouse_event(void *opaque, int dx1, int dy1, int dz1, int buttons_state) { @@ -132,6 +233,17 @@ static void usb_mouse_event(void *opaque, s->buttons_state = buttons_state; } +static void usb_tablet_event(void *opaque, + int x, int y, int dz, int buttons_state) +{ + USBMouseState *s = opaque; + + s->x = x; + s->y = y; + s->dz += dz; + s->buttons_state = buttons_state; +} + static inline int int_clamp(int val, int vmin, int vmax) { if (val < vmin) @@ -146,6 +258,11 @@ static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) { int dx, dy, dz, b, l; + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_mouse_event, s, 0); + s->mouse_grabbed = 1; + } + dx = int_clamp(s->dx, -128, 127); dy = int_clamp(s->dy, -128, 127); dz = int_clamp(s->dz, -128, 127); @@ -173,6 +290,39 @@ static int usb_mouse_poll(USBMouseState *s, uint8_t *buf, int len) return l; } +static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len) +{ + int dz, b, l; + + if (!s->mouse_grabbed) { + qemu_add_mouse_event_handler(usb_tablet_event, s, 1); + s->mouse_grabbed = 1; + } + + dz = int_clamp(s->dz, -128, 127); + s->dz -= dz; + + /* Appears we have to invert the wheel direction */ + dz = 0 - dz; + b = 0; + if (s->buttons_state & MOUSE_EVENT_LBUTTON) + b |= 0x01; + if (s->buttons_state & MOUSE_EVENT_RBUTTON) + b |= 0x02; + if (s->buttons_state & MOUSE_EVENT_MBUTTON) + b |= 0x04; + + buf[0] = b; + buf[1] = s->x & 0xff; + buf[2] = s->x >> 8; + buf[3] = s->y & 0xff; + buf[4] = s->y >> 8; + buf[5] = dz; + l = 6; + + return l; +} + static void usb_mouse_handle_reset(USBDevice *dev) { USBMouseState *s = (USBMouseState *)dev; @@ -180,6 +330,8 @@ static void usb_mouse_handle_reset(USBDevice *dev) s->dx = 0; s->dy = 0; s->dz = 0; + s->x = 0; + s->y = 0; s->buttons_state = 0; } @@ -187,7 +339,7 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBMouseState *s = (USBMouseState *)dev; - int ret; + int ret = 0; switch(request) { case DeviceRequest | USB_REQ_GET_STATUS: @@ -224,9 +376,15 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, ret = sizeof(qemu_mouse_dev_descriptor); break; case USB_DT_CONFIG: - memcpy(data, qemu_mouse_config_descriptor, - sizeof(qemu_mouse_config_descriptor)); - ret = sizeof(qemu_mouse_config_descriptor); + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_config_descriptor, + sizeof(qemu_mouse_config_descriptor)); + ret = sizeof(qemu_mouse_config_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_config_descriptor, + sizeof(qemu_tablet_config_descriptor)); + ret = sizeof(qemu_tablet_config_descriptor); + } break; case USB_DT_STRING: switch(value & 0xff) { @@ -244,7 +402,10 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, break; case 2: /* product description */ - ret = set_usb_string(data, "QEMU USB Mouse"); + if (s->kind == USB_MOUSE) + ret = set_usb_string(data, "QEMU USB Mouse"); + else if (s->kind == USB_TABLET) + ret = set_usb_string(data, "QEMU USB Tablet"); break; case 3: /* vendor description */ @@ -282,16 +443,25 @@ static int usb_mouse_handle_control(USBDevice *dev, int request, int value, case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: switch(value >> 8) { case 0x22: - memcpy(data, qemu_mouse_hid_report_descriptor, - sizeof(qemu_mouse_hid_report_descriptor)); - ret = sizeof(qemu_mouse_hid_report_descriptor); - break; + if (s->kind == USB_MOUSE) { + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); + ret = sizeof(qemu_mouse_hid_report_descriptor); + } else if (s->kind == USB_TABLET) { + memcpy(data, qemu_tablet_hid_report_descriptor, + sizeof(qemu_tablet_hid_report_descriptor)); + ret = sizeof(qemu_tablet_hid_report_descriptor); + } + break; default: goto fail; } break; case GET_REPORT: - ret = usb_mouse_poll(s, data, length); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, length); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, length); break; case SET_IDLE: ret = 0; @@ -308,12 +478,15 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, uint8_t devep, uint8_t *data, int len) { USBMouseState *s = (USBMouseState *)dev; - int ret; + int ret = 0; switch(pid) { case USB_TOKEN_IN: if (devep == 1) { - ret = usb_mouse_poll(s, data, len); + if (s->kind == USB_MOUSE) + ret = usb_mouse_poll(s, data, len); + else if (s->kind == USB_TABLET) + ret = usb_tablet_poll(s, data, len); } else { goto fail; } @@ -327,6 +500,24 @@ static int usb_mouse_handle_data(USBDevice *dev, int pid, return ret; } +USBDevice *usb_tablet_init(void) +{ + USBMouseState *s; + + s = qemu_mallocz(sizeof(USBMouseState)); + if (!s) + return NULL; + s->dev.speed = USB_SPEED_FULL; + s->dev.handle_packet = usb_generic_handle_packet; + + s->dev.handle_reset = usb_mouse_handle_reset; + s->dev.handle_control = usb_mouse_handle_control; + s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_TABLET; + + return (USBDevice *)s; +} + USBDevice *usb_mouse_init(void) { USBMouseState *s; @@ -340,8 +531,7 @@ USBDevice *usb_mouse_init(void) s->dev.handle_reset = usb_mouse_handle_reset; s->dev.handle_control = usb_mouse_handle_control; s->dev.handle_data = usb_mouse_handle_data; + s->kind = USB_MOUSE; - qemu_add_mouse_event_handler(usb_mouse_event, s); - return (USBDevice *)s; } |