|
|
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/hw.h" ! 29: #include "console.h" ! 30: #include "hw/usb.h" ! 31: #include "hw/usb/desc.h" ! 32: ! 33: /* Interface requests */ ! 34: #define WACOM_GET_REPORT 0x2101 ! 35: #define WACOM_SET_REPORT 0x2109 ! 36: ! 37: /* HID interface requests */ ! 38: #define HID_GET_REPORT 0xa101 ! 39: #define HID_GET_IDLE 0xa102 ! 40: #define HID_GET_PROTOCOL 0xa103 ! 41: #define HID_SET_IDLE 0x210a ! 42: #define HID_SET_PROTOCOL 0x210b ! 43: ! 44: typedef struct USBWacomState { ! 45: USBDevice dev; ! 46: QEMUPutMouseEntry *eh_entry; ! 47: int dx, dy, dz, buttons_state; ! 48: int x, y; ! 49: int mouse_grabbed; ! 50: enum { ! 51: WACOM_MODE_HID = 1, ! 52: WACOM_MODE_WACOM = 2, ! 53: } mode; ! 54: uint8_t idle; ! 55: int changed; ! 56: } USBWacomState; ! 57: ! 58: enum { ! 59: STR_MANUFACTURER = 1, ! 60: STR_PRODUCT, ! 61: STR_SERIALNUMBER, ! 62: }; ! 63: ! 64: static const USBDescStrings desc_strings = { ! 65: [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, ! 66: [STR_PRODUCT] = "Wacom PenPartner", ! 67: [STR_SERIALNUMBER] = "1", ! 68: }; ! 69: ! 70: static const USBDescIface desc_iface_wacom = { ! 71: .bInterfaceNumber = 0, ! 72: .bNumEndpoints = 1, ! 73: .bInterfaceClass = USB_CLASS_HID, ! 74: .bInterfaceSubClass = 0x01, /* boot */ ! 75: .bInterfaceProtocol = 0x02, ! 76: .ndesc = 1, ! 77: .descs = (USBDescOther[]) { ! 78: { ! 79: /* HID descriptor */ ! 80: .data = (uint8_t[]) { ! 81: 0x09, /* u8 bLength */ ! 82: 0x21, /* u8 bDescriptorType */ ! 83: 0x01, 0x10, /* u16 HID_class */ ! 84: 0x00, /* u8 country_code */ ! 85: 0x01, /* u8 num_descriptors */ ! 86: 0x22, /* u8 type: Report */ ! 87: 0x6e, 0, /* u16 len */ ! 88: }, ! 89: }, ! 90: }, ! 91: .eps = (USBDescEndpoint[]) { ! 92: { ! 93: .bEndpointAddress = USB_DIR_IN | 0x01, ! 94: .bmAttributes = USB_ENDPOINT_XFER_INT, ! 95: .wMaxPacketSize = 8, ! 96: .bInterval = 0x0a, ! 97: }, ! 98: }, ! 99: }; ! 100: ! 101: static const USBDescDevice desc_device_wacom = { ! 102: .bcdUSB = 0x0110, ! 103: .bMaxPacketSize0 = 8, ! 104: .bNumConfigurations = 1, ! 105: .confs = (USBDescConfig[]) { ! 106: { ! 107: .bNumInterfaces = 1, ! 108: .bConfigurationValue = 1, ! 109: .bmAttributes = 0x80, ! 110: .bMaxPower = 40, ! 111: .nif = 1, ! 112: .ifs = &desc_iface_wacom, ! 113: }, ! 114: }, ! 115: }; ! 116: ! 117: static const USBDesc desc_wacom = { ! 118: .id = { ! 119: .idVendor = 0x056a, ! 120: .idProduct = 0x0000, ! 121: .bcdDevice = 0x4210, ! 122: .iManufacturer = STR_MANUFACTURER, ! 123: .iProduct = STR_PRODUCT, ! 124: .iSerialNumber = STR_SERIALNUMBER, ! 125: }, ! 126: .full = &desc_device_wacom, ! 127: .str = desc_strings, ! 128: }; ! 129: ! 130: static void usb_mouse_event(void *opaque, ! 131: int dx1, int dy1, int dz1, int buttons_state) ! 132: { ! 133: USBWacomState *s = opaque; ! 134: ! 135: s->dx += dx1; ! 136: s->dy += dy1; ! 137: s->dz += dz1; ! 138: s->buttons_state = buttons_state; ! 139: s->changed = 1; ! 140: } ! 141: ! 142: static void usb_wacom_event(void *opaque, ! 143: int x, int y, int dz, int buttons_state) ! 144: { ! 145: USBWacomState *s = opaque; ! 146: ! 147: /* scale to Penpartner resolution */ ! 148: s->x = (x * 5040 / 0x7FFF); ! 149: s->y = (y * 3780 / 0x7FFF); ! 150: s->dz += dz; ! 151: s->buttons_state = buttons_state; ! 152: s->changed = 1; ! 153: } ! 154: ! 155: static inline int int_clamp(int val, int vmin, int vmax) ! 156: { ! 157: if (val < vmin) ! 158: return vmin; ! 159: else if (val > vmax) ! 160: return vmax; ! 161: else ! 162: return val; ! 163: } ! 164: ! 165: static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) ! 166: { ! 167: int dx, dy, dz, b, l; ! 168: ! 169: if (!s->mouse_grabbed) { ! 170: s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, ! 171: "QEMU PenPartner tablet"); ! 172: qemu_activate_mouse_event_handler(s->eh_entry); ! 173: s->mouse_grabbed = 1; ! 174: } ! 175: ! 176: dx = int_clamp(s->dx, -128, 127); ! 177: dy = int_clamp(s->dy, -128, 127); ! 178: dz = int_clamp(s->dz, -128, 127); ! 179: ! 180: s->dx -= dx; ! 181: s->dy -= dy; ! 182: s->dz -= dz; ! 183: ! 184: b = 0; ! 185: if (s->buttons_state & MOUSE_EVENT_LBUTTON) ! 186: b |= 0x01; ! 187: if (s->buttons_state & MOUSE_EVENT_RBUTTON) ! 188: b |= 0x02; ! 189: if (s->buttons_state & MOUSE_EVENT_MBUTTON) ! 190: b |= 0x04; ! 191: ! 192: buf[0] = b; ! 193: buf[1] = dx; ! 194: buf[2] = dy; ! 195: l = 3; ! 196: if (len >= 4) { ! 197: buf[3] = dz; ! 198: l = 4; ! 199: } ! 200: return l; ! 201: } ! 202: ! 203: static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) ! 204: { ! 205: int b; ! 206: ! 207: if (!s->mouse_grabbed) { ! 208: s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, ! 209: "QEMU PenPartner tablet"); ! 210: qemu_activate_mouse_event_handler(s->eh_entry); ! 211: s->mouse_grabbed = 1; ! 212: } ! 213: ! 214: b = 0; ! 215: if (s->buttons_state & MOUSE_EVENT_LBUTTON) ! 216: b |= 0x01; ! 217: if (s->buttons_state & MOUSE_EVENT_RBUTTON) ! 218: b |= 0x40; ! 219: if (s->buttons_state & MOUSE_EVENT_MBUTTON) ! 220: b |= 0x20; /* eraser */ ! 221: ! 222: if (len < 7) ! 223: return 0; ! 224: ! 225: buf[0] = s->mode; ! 226: buf[5] = 0x00 | (b & 0xf0); ! 227: buf[1] = s->x & 0xff; ! 228: buf[2] = s->x >> 8; ! 229: buf[3] = s->y & 0xff; ! 230: buf[4] = s->y >> 8; ! 231: if (b & 0x3f) { ! 232: buf[6] = 0; ! 233: } else { ! 234: buf[6] = (unsigned char) -127; ! 235: } ! 236: ! 237: return 7; ! 238: } ! 239: ! 240: static void usb_wacom_handle_reset(USBDevice *dev) ! 241: { ! 242: USBWacomState *s = (USBWacomState *) dev; ! 243: ! 244: s->dx = 0; ! 245: s->dy = 0; ! 246: s->dz = 0; ! 247: s->x = 0; ! 248: s->y = 0; ! 249: s->buttons_state = 0; ! 250: s->mode = WACOM_MODE_HID; ! 251: } ! 252: ! 253: static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p, ! 254: int request, int value, int index, int length, uint8_t *data) ! 255: { ! 256: USBWacomState *s = (USBWacomState *) dev; ! 257: int ret; ! 258: ! 259: ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ! 260: if (ret >= 0) { ! 261: return ret; ! 262: } ! 263: ! 264: ret = 0; ! 265: switch (request) { ! 266: case WACOM_SET_REPORT: ! 267: if (s->mouse_grabbed) { ! 268: qemu_remove_mouse_event_handler(s->eh_entry); ! 269: s->mouse_grabbed = 0; ! 270: } ! 271: s->mode = data[0]; ! 272: ret = 0; ! 273: break; ! 274: case WACOM_GET_REPORT: ! 275: data[0] = 0; ! 276: data[1] = s->mode; ! 277: ret = 2; ! 278: break; ! 279: /* USB HID requests */ ! 280: case HID_GET_REPORT: ! 281: if (s->mode == WACOM_MODE_HID) ! 282: ret = usb_mouse_poll(s, data, length); ! 283: else if (s->mode == WACOM_MODE_WACOM) ! 284: ret = usb_wacom_poll(s, data, length); ! 285: break; ! 286: case HID_GET_IDLE: ! 287: ret = 1; ! 288: data[0] = s->idle; ! 289: break; ! 290: case HID_SET_IDLE: ! 291: s->idle = (uint8_t) (value >> 8); ! 292: ret = 0; ! 293: break; ! 294: default: ! 295: ret = USB_RET_STALL; ! 296: break; ! 297: } ! 298: return ret; ! 299: } ! 300: ! 301: static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) ! 302: { ! 303: USBWacomState *s = (USBWacomState *) dev; ! 304: uint8_t buf[p->iov.size]; ! 305: int ret = 0; ! 306: ! 307: switch (p->pid) { ! 308: case USB_TOKEN_IN: ! 309: if (p->ep->nr == 1) { ! 310: if (!(s->changed || s->idle)) ! 311: return USB_RET_NAK; ! 312: s->changed = 0; ! 313: if (s->mode == WACOM_MODE_HID) ! 314: ret = usb_mouse_poll(s, buf, p->iov.size); ! 315: else if (s->mode == WACOM_MODE_WACOM) ! 316: ret = usb_wacom_poll(s, buf, p->iov.size); ! 317: usb_packet_copy(p, buf, ret); ! 318: break; ! 319: } ! 320: /* Fall through. */ ! 321: case USB_TOKEN_OUT: ! 322: default: ! 323: ret = USB_RET_STALL; ! 324: break; ! 325: } ! 326: return ret; ! 327: } ! 328: ! 329: static void usb_wacom_handle_destroy(USBDevice *dev) ! 330: { ! 331: USBWacomState *s = (USBWacomState *) dev; ! 332: ! 333: if (s->mouse_grabbed) { ! 334: qemu_remove_mouse_event_handler(s->eh_entry); ! 335: s->mouse_grabbed = 0; ! 336: } ! 337: } ! 338: ! 339: static int usb_wacom_initfn(USBDevice *dev) ! 340: { ! 341: USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev); ! 342: usb_desc_create_serial(dev); ! 343: usb_desc_init(dev); ! 344: s->changed = 1; ! 345: return 0; ! 346: } ! 347: ! 348: static const VMStateDescription vmstate_usb_wacom = { ! 349: .name = "usb-wacom", ! 350: .unmigratable = 1, ! 351: }; ! 352: ! 353: static void usb_wacom_class_init(ObjectClass *klass, void *data) ! 354: { ! 355: DeviceClass *dc = DEVICE_CLASS(klass); ! 356: USBDeviceClass *uc = USB_DEVICE_CLASS(klass); ! 357: ! 358: uc->product_desc = "QEMU PenPartner Tablet"; ! 359: uc->usb_desc = &desc_wacom; ! 360: uc->init = usb_wacom_initfn; ! 361: uc->handle_reset = usb_wacom_handle_reset; ! 362: uc->handle_control = usb_wacom_handle_control; ! 363: uc->handle_data = usb_wacom_handle_data; ! 364: uc->handle_destroy = usb_wacom_handle_destroy; ! 365: dc->desc = "QEMU PenPartner Tablet"; ! 366: dc->vmsd = &vmstate_usb_wacom; ! 367: } ! 368: ! 369: static TypeInfo wacom_info = { ! 370: .name = "usb-wacom-tablet", ! 371: .parent = TYPE_USB_DEVICE, ! 372: .instance_size = sizeof(USBWacomState), ! 373: .class_init = usb_wacom_class_init, ! 374: }; ! 375: ! 376: static void usb_wacom_register_types(void) ! 377: { ! 378: type_register_static(&wacom_info); ! 379: usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL); ! 380: } ! 381: ! 382: type_init(usb_wacom_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.