|
|
1.1 ! root 1: /* ! 2: * Wacom PenPartner USB tablet emulation. ! 3: * ! 4: * Copyright (c) 2006 Openedhand Ltd. ! 5: * Author: Andrzej Zaborowski <[email protected]> ! 6: * ! 7: * Based on hw/usb-hid.c: ! 8: * Copyright (c) 2005 Fabrice Bellard ! 9: * ! 10: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 11: * of this software and associated documentation files (the "Software"), to deal ! 12: * in the Software without restriction, including without limitation the rights ! 13: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 14: * copies of the Software, and to permit persons to whom the Software is ! 15: * furnished to do so, subject to the following conditions: ! 16: * ! 17: * The above copyright notice and this permission notice shall be included in ! 18: * all copies or substantial portions of the Software. ! 19: * ! 20: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 21: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 22: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 23: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 24: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 25: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 26: * THE SOFTWARE. ! 27: */ ! 28: #include "hw.h" ! 29: #include "console.h" ! 30: #include "usb.h" ! 31: ! 32: /* Interface requests */ ! 33: #define WACOM_GET_REPORT 0x2101 ! 34: #define WACOM_SET_REPORT 0x2109 ! 35: ! 36: /* HID interface requests */ ! 37: #define HID_GET_REPORT 0xa101 ! 38: #define HID_GET_IDLE 0xa102 ! 39: #define HID_GET_PROTOCOL 0xa103 ! 40: #define HID_SET_IDLE 0x210a ! 41: #define HID_SET_PROTOCOL 0x210b ! 42: ! 43: typedef struct USBWacomState { ! 44: USBDevice dev; ! 45: QEMUPutMouseEntry *eh_entry; ! 46: int dx, dy, dz, buttons_state; ! 47: int x, y; ! 48: int mouse_grabbed; ! 49: enum { ! 50: WACOM_MODE_HID = 1, ! 51: WACOM_MODE_WACOM = 2, ! 52: } mode; ! 53: } USBWacomState; ! 54: ! 55: static const uint8_t qemu_wacom_dev_descriptor[] = { ! 56: 0x12, /* u8 bLength; */ ! 57: 0x01, /* u8 bDescriptorType; Device */ ! 58: 0x10, 0x10, /* u16 bcdUSB; v1.10 */ ! 59: ! 60: 0x00, /* u8 bDeviceClass; */ ! 61: 0x00, /* u8 bDeviceSubClass; */ ! 62: 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ ! 63: 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ ! 64: ! 65: 0x6a, 0x05, /* u16 idVendor; */ ! 66: 0x00, 0x00, /* u16 idProduct; */ ! 67: 0x10, 0x42, /* u16 bcdDevice */ ! 68: ! 69: 0x01, /* u8 iManufacturer; */ ! 70: 0x02, /* u8 iProduct; */ ! 71: 0x00, /* u8 iSerialNumber; */ ! 72: 0x01, /* u8 bNumConfigurations; */ ! 73: }; ! 74: ! 75: static const uint8_t qemu_wacom_config_descriptor[] = { ! 76: /* one configuration */ ! 77: 0x09, /* u8 bLength; */ ! 78: 0x02, /* u8 bDescriptorType; Configuration */ ! 79: 0x22, 0x00, /* u16 wTotalLength; */ ! 80: 0x01, /* u8 bNumInterfaces; (1) */ ! 81: 0x01, /* u8 bConfigurationValue; */ ! 82: 0x00, /* u8 iConfiguration; */ ! 83: 0x80, /* u8 bmAttributes; ! 84: Bit 7: must be set, ! 85: 6: Self-powered, ! 86: 5: Remote wakeup, ! 87: 4..0: resvd */ ! 88: 40, /* u8 MaxPower; */ ! 89: ! 90: /* one interface */ ! 91: 0x09, /* u8 if_bLength; */ ! 92: 0x04, /* u8 if_bDescriptorType; Interface */ ! 93: 0x00, /* u8 if_bInterfaceNumber; */ ! 94: 0x00, /* u8 if_bAlternateSetting; */ ! 95: 0x01, /* u8 if_bNumEndpoints; */ ! 96: 0x03, /* u8 if_bInterfaceClass; HID */ ! 97: 0x01, /* u8 if_bInterfaceSubClass; Boot */ ! 98: 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ ! 99: 0x00, /* u8 if_iInterface; */ ! 100: ! 101: /* HID descriptor */ ! 102: 0x09, /* u8 bLength; */ ! 103: 0x21, /* u8 bDescriptorType; */ ! 104: 0x01, 0x10, /* u16 HID_class */ ! 105: 0x00, /* u8 country_code */ ! 106: 0x01, /* u8 num_descriptors */ ! 107: 0x22, /* u8 type; Report */ ! 108: 0x6e, 0x00, /* u16 len */ ! 109: ! 110: /* one endpoint (status change endpoint) */ ! 111: 0x07, /* u8 ep_bLength; */ ! 112: 0x05, /* u8 ep_bDescriptorType; Endpoint */ ! 113: 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ ! 114: 0x03, /* u8 ep_bmAttributes; Interrupt */ ! 115: 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ ! 116: 0x0a, /* u8 ep_bInterval; */ ! 117: }; ! 118: ! 119: static void usb_mouse_event(void *opaque, ! 120: int dx1, int dy1, int dz1, int buttons_state) ! 121: { ! 122: USBWacomState *s = opaque; ! 123: ! 124: s->dx += dx1; ! 125: s->dy += dy1; ! 126: s->dz += dz1; ! 127: s->buttons_state = buttons_state; ! 128: } ! 129: ! 130: static void usb_wacom_event(void *opaque, ! 131: int x, int y, int dz, int buttons_state) ! 132: { ! 133: USBWacomState *s = opaque; ! 134: ! 135: s->x = x; ! 136: s->y = y; ! 137: s->dz += dz; ! 138: s->buttons_state = buttons_state; ! 139: } ! 140: ! 141: static inline int int_clamp(int val, int vmin, int vmax) ! 142: { ! 143: if (val < vmin) ! 144: return vmin; ! 145: else if (val > vmax) ! 146: return vmax; ! 147: else ! 148: return val; ! 149: } ! 150: ! 151: static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) ! 152: { ! 153: int dx, dy, dz, b, l; ! 154: ! 155: if (!s->mouse_grabbed) { ! 156: s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, ! 157: "QEMU PenPartner tablet"); ! 158: s->mouse_grabbed = 1; ! 159: } ! 160: ! 161: dx = int_clamp(s->dx, -128, 127); ! 162: dy = int_clamp(s->dy, -128, 127); ! 163: dz = int_clamp(s->dz, -128, 127); ! 164: ! 165: s->dx -= dx; ! 166: s->dy -= dy; ! 167: s->dz -= dz; ! 168: ! 169: b = 0; ! 170: if (s->buttons_state & MOUSE_EVENT_LBUTTON) ! 171: b |= 0x01; ! 172: if (s->buttons_state & MOUSE_EVENT_RBUTTON) ! 173: b |= 0x02; ! 174: if (s->buttons_state & MOUSE_EVENT_MBUTTON) ! 175: b |= 0x04; ! 176: ! 177: buf[0] = b; ! 178: buf[1] = dx; ! 179: buf[2] = dy; ! 180: l = 3; ! 181: if (len >= 4) { ! 182: buf[3] = dz; ! 183: l = 4; ! 184: } ! 185: return l; ! 186: } ! 187: ! 188: static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) ! 189: { ! 190: int b; ! 191: ! 192: if (!s->mouse_grabbed) { ! 193: s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, ! 194: "QEMU PenPartner tablet"); ! 195: s->mouse_grabbed = 1; ! 196: } ! 197: ! 198: b = 0; ! 199: if (s->buttons_state & MOUSE_EVENT_LBUTTON) ! 200: b |= 0x01; ! 201: if (s->buttons_state & MOUSE_EVENT_RBUTTON) ! 202: b |= 0x02; ! 203: if (s->buttons_state & MOUSE_EVENT_MBUTTON) ! 204: b |= 0x04; ! 205: ! 206: if (len < 7) ! 207: return 0; ! 208: ! 209: buf[0] = s->mode; ! 210: buf[5] = 0x00; ! 211: if (b) { ! 212: buf[1] = s->x & 0xff; ! 213: buf[2] = s->x >> 8; ! 214: buf[3] = s->y & 0xff; ! 215: buf[4] = s->y >> 8; ! 216: buf[6] = 0; ! 217: } else { ! 218: buf[1] = 0; ! 219: buf[2] = 0; ! 220: buf[3] = 0; ! 221: buf[4] = 0; ! 222: buf[6] = (unsigned char) -127; ! 223: } ! 224: ! 225: return 7; ! 226: } ! 227: ! 228: static void usb_wacom_handle_reset(USBDevice *dev) ! 229: { ! 230: USBWacomState *s = (USBWacomState *) dev; ! 231: ! 232: s->dx = 0; ! 233: s->dy = 0; ! 234: s->dz = 0; ! 235: s->x = 0; ! 236: s->y = 0; ! 237: s->buttons_state = 0; ! 238: s->mode = WACOM_MODE_HID; ! 239: } ! 240: ! 241: static int usb_wacom_handle_control(USBDevice *dev, int request, int value, ! 242: int index, int length, uint8_t *data) ! 243: { ! 244: USBWacomState *s = (USBWacomState *) dev; ! 245: int ret = 0; ! 246: ! 247: switch (request) { ! 248: case DeviceRequest | USB_REQ_GET_STATUS: ! 249: data[0] = (1 << USB_DEVICE_SELF_POWERED) | ! 250: (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); ! 251: data[1] = 0x00; ! 252: ret = 2; ! 253: break; ! 254: case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: ! 255: if (value == USB_DEVICE_REMOTE_WAKEUP) { ! 256: dev->remote_wakeup = 0; ! 257: } else { ! 258: goto fail; ! 259: } ! 260: ret = 0; ! 261: break; ! 262: case DeviceOutRequest | USB_REQ_SET_FEATURE: ! 263: if (value == USB_DEVICE_REMOTE_WAKEUP) { ! 264: dev->remote_wakeup = 1; ! 265: } else { ! 266: goto fail; ! 267: } ! 268: ret = 0; ! 269: break; ! 270: case DeviceOutRequest | USB_REQ_SET_ADDRESS: ! 271: dev->addr = value; ! 272: ret = 0; ! 273: break; ! 274: case DeviceRequest | USB_REQ_GET_DESCRIPTOR: ! 275: switch (value >> 8) { ! 276: case USB_DT_DEVICE: ! 277: memcpy(data, qemu_wacom_dev_descriptor, ! 278: sizeof(qemu_wacom_dev_descriptor)); ! 279: ret = sizeof(qemu_wacom_dev_descriptor); ! 280: break; ! 281: case USB_DT_CONFIG: ! 282: memcpy(data, qemu_wacom_config_descriptor, ! 283: sizeof(qemu_wacom_config_descriptor)); ! 284: ret = sizeof(qemu_wacom_config_descriptor); ! 285: break; ! 286: case USB_DT_STRING: ! 287: switch (value & 0xff) { ! 288: case 0: ! 289: /* language ids */ ! 290: data[0] = 4; ! 291: data[1] = 3; ! 292: data[2] = 0x09; ! 293: data[3] = 0x04; ! 294: ret = 4; ! 295: break; ! 296: case 1: ! 297: /* serial number */ ! 298: ret = set_usb_string(data, "1"); ! 299: break; ! 300: case 2: ! 301: ret = set_usb_string(data, "Wacom PenPartner"); ! 302: break; ! 303: case 3: ! 304: /* vendor description */ ! 305: ret = set_usb_string(data, "QEMU " QEMU_VERSION); ! 306: break; ! 307: case 4: ! 308: ret = set_usb_string(data, "Wacom Tablet"); ! 309: break; ! 310: case 5: ! 311: ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); ! 312: break; ! 313: default: ! 314: goto fail; ! 315: } ! 316: break; ! 317: default: ! 318: goto fail; ! 319: } ! 320: break; ! 321: case DeviceRequest | USB_REQ_GET_CONFIGURATION: ! 322: data[0] = 1; ! 323: ret = 1; ! 324: break; ! 325: case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: ! 326: ret = 0; ! 327: break; ! 328: case DeviceRequest | USB_REQ_GET_INTERFACE: ! 329: data[0] = 0; ! 330: ret = 1; ! 331: break; ! 332: case DeviceOutRequest | USB_REQ_SET_INTERFACE: ! 333: ret = 0; ! 334: break; ! 335: case WACOM_SET_REPORT: ! 336: qemu_remove_mouse_event_handler(s->eh_entry); ! 337: s->mouse_grabbed = 0; ! 338: s->mode = data[0]; ! 339: ret = 0; ! 340: break; ! 341: case WACOM_GET_REPORT: ! 342: data[0] = 0; ! 343: data[1] = s->mode; ! 344: ret = 2; ! 345: break; ! 346: /* USB HID requests */ ! 347: case HID_GET_REPORT: ! 348: if (s->mode == WACOM_MODE_HID) ! 349: ret = usb_mouse_poll(s, data, length); ! 350: else if (s->mode == WACOM_MODE_WACOM) ! 351: ret = usb_wacom_poll(s, data, length); ! 352: break; ! 353: case HID_SET_IDLE: ! 354: ret = 0; ! 355: break; ! 356: default: ! 357: fail: ! 358: ret = USB_RET_STALL; ! 359: break; ! 360: } ! 361: return ret; ! 362: } ! 363: ! 364: static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) ! 365: { ! 366: USBWacomState *s = (USBWacomState *) dev; ! 367: int ret = 0; ! 368: ! 369: switch (p->pid) { ! 370: case USB_TOKEN_IN: ! 371: if (p->devep == 1) { ! 372: if (s->mode == WACOM_MODE_HID) ! 373: ret = usb_mouse_poll(s, p->data, p->len); ! 374: else if (s->mode == WACOM_MODE_WACOM) ! 375: ret = usb_wacom_poll(s, p->data, p->len); ! 376: break; ! 377: } ! 378: /* Fall through. */ ! 379: case USB_TOKEN_OUT: ! 380: default: ! 381: ret = USB_RET_STALL; ! 382: break; ! 383: } ! 384: return ret; ! 385: } ! 386: ! 387: static void usb_wacom_handle_destroy(USBDevice *dev) ! 388: { ! 389: USBWacomState *s = (USBWacomState *) dev; ! 390: ! 391: qemu_remove_mouse_event_handler(s->eh_entry); ! 392: qemu_free(s); ! 393: } ! 394: ! 395: USBDevice *usb_wacom_init(void) ! 396: { ! 397: USBWacomState *s; ! 398: ! 399: s = qemu_mallocz(sizeof(USBWacomState)); ! 400: if (!s) ! 401: return NULL; ! 402: s->dev.speed = USB_SPEED_FULL; ! 403: s->dev.handle_packet = usb_generic_handle_packet; ! 404: ! 405: s->dev.handle_reset = usb_wacom_handle_reset; ! 406: s->dev.handle_control = usb_wacom_handle_control; ! 407: s->dev.handle_data = usb_wacom_handle_data; ! 408: s->dev.handle_destroy = usb_wacom_handle_destroy; ! 409: ! 410: pstrcpy(s->dev.devname, sizeof(s->dev.devname), ! 411: "QEMU PenPartner Tablet"); ! 412: ! 413: return (USBDevice *) s; ! 414: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.