1 /* 2 * Wacom PenPartner USB tablet emulation. 3 * 4 * Copyright (c) 2006 Openedhand Ltd. 5 * Author: Andrzej Zaborowski <balrog@zabor.org> 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 29 #include "qemu/osdep.h" 30 #include "ui/console.h" 31 #include "hw/usb.h" 32 #include "hw/usb/hid.h" 33 #include "migration/vmstate.h" 34 #include "qemu/module.h" 35 #include "desc.h" 36 #include "qom/object.h" 37 38 /* Interface requests */ 39 #define WACOM_GET_REPORT 0x2101 40 #define WACOM_SET_REPORT 0x2109 41 42 struct USBWacomState { 43 USBDevice dev; 44 USBEndpoint *intr; 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 uint8_t idle; 54 int changed; 55 }; 56 57 #define TYPE_USB_WACOM "usb-wacom-tablet" 58 OBJECT_DECLARE_SIMPLE_TYPE(USBWacomState, USB_WACOM) 59 60 enum { 61 STR_MANUFACTURER = 1, 62 STR_PRODUCT, 63 STR_SERIALNUMBER, 64 }; 65 66 static const USBDescStrings desc_strings = { 67 [STR_MANUFACTURER] = "QEMU", 68 [STR_PRODUCT] = "Wacom PenPartner", 69 [STR_SERIALNUMBER] = "1", 70 }; 71 72 static const uint8_t qemu_wacom_hid_report_descriptor[] = { 73 0x05, 0x01, /* Usage Page (Desktop) */ 74 0x09, 0x02, /* Usage (Mouse) */ 75 0xa1, 0x01, /* Collection (Application) */ 76 0x85, 0x01, /* Report ID (1) */ 77 0x09, 0x01, /* Usage (Pointer) */ 78 0xa1, 0x00, /* Collection (Physical) */ 79 0x05, 0x09, /* Usage Page (Button) */ 80 0x19, 0x01, /* Usage Minimum (01h) */ 81 0x29, 0x03, /* Usage Maximum (03h) */ 82 0x15, 0x00, /* Logical Minimum (0) */ 83 0x25, 0x01, /* Logical Maximum (1) */ 84 0x95, 0x03, /* Report Count (3) */ 85 0x75, 0x01, /* Report Size (1) */ 86 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 87 0x95, 0x01, /* Report Count (1) */ 88 0x75, 0x05, /* Report Size (5) */ 89 0x81, 0x01, /* Input (Constant) */ 90 0x05, 0x01, /* Usage Page (Desktop) */ 91 0x09, 0x30, /* Usage (X) */ 92 0x09, 0x31, /* Usage (Y) */ 93 0x09, 0x38, /* Usage (Wheel) */ 94 0x15, 0x81, /* Logical Minimum (-127) */ 95 0x25, 0x7f, /* Logical Maximum (127) */ 96 0x75, 0x08, /* Report Size (8) */ 97 0x95, 0x03, /* Report Count (3) */ 98 0x81, 0x06, /* Input (Data, Variable, Relative) */ 99 0x95, 0x03, /* Report Count (3) */ 100 0x81, 0x01, /* Input (Constant) */ 101 0xc0, /* End Collection */ 102 0xc0, /* End Collection */ 103 0x05, 0x0d, /* Usage Page (Digitizer) */ 104 0x09, 0x01, /* Usage (Digitizer) */ 105 0xa1, 0x01, /* Collection (Application) */ 106 0x85, 0x02, /* Report ID (2) */ 107 0xa1, 0x00, /* Collection (Physical) */ 108 0x06, 0x00, 0xff,/* Usage Page (ff00h), vendor-defined */ 109 0x09, 0x01, /* Usage (01h) */ 110 0x15, 0x00, /* Logical Minimum (0) */ 111 0x26, 0xff, 0x00,/* Logical Maximum (255) */ 112 0x75, 0x08, /* Report Size (8) */ 113 0x95, 0x07, /* Report Count (7) */ 114 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 115 0xc0, /* End Collection */ 116 0x09, 0x01, /* Usage (01h) */ 117 0x85, 0x63, /* Report ID (99) */ 118 0x95, 0x07, /* Report Count (7) */ 119 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 120 0x09, 0x01, /* Usage (01h) */ 121 0x85, 0x02, /* Report ID (2) */ 122 0x95, 0x01, /* Report Count (1) */ 123 0xb1, 0x02, /* Feature (Variable) */ 124 0x09, 0x01, /* Usage (01h) */ 125 0x85, 0x03, /* Report ID (3) */ 126 0x95, 0x01, /* Report Count (1) */ 127 0xb1, 0x02, /* Feature (Variable) */ 128 0xc0 /* End Collection */ 129 }; 130 131 static const USBDescIface desc_iface_wacom = { 132 .bInterfaceNumber = 0, 133 .bNumEndpoints = 1, 134 .bInterfaceClass = USB_CLASS_HID, 135 .bInterfaceSubClass = 0x01, /* boot */ 136 .bInterfaceProtocol = 0x02, 137 .ndesc = 1, 138 .descs = (USBDescOther[]) { 139 { 140 /* HID descriptor */ 141 .data = (uint8_t[]) { 142 0x09, /* u8 bLength */ 143 USB_DT_HID, /* u8 bDescriptorType */ 144 0x01, 0x10, /* u16 HID_class */ 145 0x00, /* u8 country_code */ 146 0x01, /* u8 num_descriptors */ 147 USB_DT_REPORT, /* u8 type: Report */ 148 sizeof(qemu_wacom_hid_report_descriptor), 0, /* u16 len */ 149 }, 150 }, 151 }, 152 .eps = (USBDescEndpoint[]) { 153 { 154 .bEndpointAddress = USB_DIR_IN | 0x01, 155 .bmAttributes = USB_ENDPOINT_XFER_INT, 156 .wMaxPacketSize = 8, 157 .bInterval = 0x0a, 158 }, 159 }, 160 }; 161 162 static const USBDescDevice desc_device_wacom = { 163 .bcdUSB = 0x0110, 164 .bMaxPacketSize0 = 8, 165 .bNumConfigurations = 1, 166 .confs = (USBDescConfig[]) { 167 { 168 .bNumInterfaces = 1, 169 .bConfigurationValue = 1, 170 .bmAttributes = USB_CFG_ATT_ONE, 171 .bMaxPower = 40, 172 .nif = 1, 173 .ifs = &desc_iface_wacom, 174 }, 175 }, 176 }; 177 178 static const USBDesc desc_wacom = { 179 .id = { 180 .idVendor = 0x056a, 181 .idProduct = 0x0000, 182 .bcdDevice = 0x4210, 183 .iManufacturer = STR_MANUFACTURER, 184 .iProduct = STR_PRODUCT, 185 .iSerialNumber = STR_SERIALNUMBER, 186 }, 187 .full = &desc_device_wacom, 188 .str = desc_strings, 189 }; 190 191 static void usb_mouse_event(void *opaque, 192 int dx1, int dy1, int dz1, int buttons_state) 193 { 194 USBWacomState *s = opaque; 195 196 s->dx += dx1; 197 s->dy += dy1; 198 s->dz += dz1; 199 s->buttons_state = buttons_state; 200 s->changed = 1; 201 usb_wakeup(s->intr, 0); 202 } 203 204 static void usb_wacom_event(void *opaque, 205 int x, int y, int dz, int buttons_state) 206 { 207 USBWacomState *s = opaque; 208 209 /* scale to Penpartner resolution */ 210 s->x = (x * 5040 / 0x7FFF); 211 s->y = (y * 3780 / 0x7FFF); 212 s->dz += dz; 213 s->buttons_state = buttons_state; 214 s->changed = 1; 215 usb_wakeup(s->intr, 0); 216 } 217 218 static inline int int_clamp(int val, int vmin, int vmax) 219 { 220 if (val < vmin) 221 return vmin; 222 else if (val > vmax) 223 return vmax; 224 else 225 return val; 226 } 227 228 static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) 229 { 230 int dx, dy, dz, b, l; 231 232 if (!s->mouse_grabbed) { 233 s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, 234 "QEMU PenPartner tablet"); 235 qemu_activate_mouse_event_handler(s->eh_entry); 236 s->mouse_grabbed = 1; 237 } 238 239 dx = int_clamp(s->dx, -128, 127); 240 dy = int_clamp(s->dy, -128, 127); 241 dz = int_clamp(s->dz, -128, 127); 242 243 s->dx -= dx; 244 s->dy -= dy; 245 s->dz -= dz; 246 247 b = 0; 248 if (s->buttons_state & MOUSE_EVENT_LBUTTON) 249 b |= 0x01; 250 if (s->buttons_state & MOUSE_EVENT_RBUTTON) 251 b |= 0x02; 252 if (s->buttons_state & MOUSE_EVENT_MBUTTON) 253 b |= 0x04; 254 255 buf[0] = b; 256 buf[1] = dx; 257 buf[2] = dy; 258 l = 3; 259 if (len >= 4) { 260 buf[3] = dz; 261 l = 4; 262 } 263 return l; 264 } 265 266 static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) 267 { 268 int b; 269 270 if (!s->mouse_grabbed) { 271 s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, 272 "QEMU PenPartner tablet"); 273 qemu_activate_mouse_event_handler(s->eh_entry); 274 s->mouse_grabbed = 1; 275 } 276 277 b = 0; 278 if (s->buttons_state & MOUSE_EVENT_LBUTTON) 279 b |= 0x01; 280 if (s->buttons_state & MOUSE_EVENT_RBUTTON) 281 b |= 0x40; 282 if (s->buttons_state & MOUSE_EVENT_MBUTTON) 283 b |= 0x20; /* eraser */ 284 285 if (len < 7) 286 return 0; 287 288 buf[0] = s->mode; 289 buf[5] = 0x00 | (b & 0xf0); 290 buf[1] = s->x & 0xff; 291 buf[2] = s->x >> 8; 292 buf[3] = s->y & 0xff; 293 buf[4] = s->y >> 8; 294 if (b & 0x3f) { 295 buf[6] = 0; 296 } else { 297 buf[6] = (unsigned char) -127; 298 } 299 300 return 7; 301 } 302 303 static void usb_wacom_handle_reset(USBDevice *dev) 304 { 305 USBWacomState *s = (USBWacomState *) dev; 306 307 s->dx = 0; 308 s->dy = 0; 309 s->dz = 0; 310 s->x = 0; 311 s->y = 0; 312 s->buttons_state = 0; 313 s->mode = WACOM_MODE_HID; 314 } 315 316 static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p, 317 int request, int value, int index, int length, uint8_t *data) 318 { 319 USBWacomState *s = (USBWacomState *) dev; 320 int ret; 321 322 ret = usb_desc_handle_control(dev, p, request, value, index, length, data); 323 if (ret >= 0) { 324 return; 325 } 326 327 switch (request) { 328 case InterfaceRequest | USB_REQ_GET_DESCRIPTOR: 329 switch (value >> 8) { 330 case 0x22: 331 memcpy(data, qemu_wacom_hid_report_descriptor, 332 sizeof(qemu_wacom_hid_report_descriptor)); 333 p->actual_length = sizeof(qemu_wacom_hid_report_descriptor); 334 break; 335 default: 336 return; 337 } 338 break; 339 case WACOM_SET_REPORT: 340 if (s->mouse_grabbed) { 341 qemu_remove_mouse_event_handler(s->eh_entry); 342 s->mouse_grabbed = 0; 343 } 344 s->mode = data[0]; 345 break; 346 case WACOM_GET_REPORT: 347 data[0] = 0; 348 data[1] = s->mode; 349 p->actual_length = 2; 350 break; 351 /* USB HID requests */ 352 case HID_GET_REPORT: 353 if (s->mode == WACOM_MODE_HID) 354 p->actual_length = usb_mouse_poll(s, data, length); 355 else if (s->mode == WACOM_MODE_WACOM) 356 p->actual_length = usb_wacom_poll(s, data, length); 357 break; 358 case HID_GET_IDLE: 359 data[0] = s->idle; 360 p->actual_length = 1; 361 break; 362 case HID_SET_IDLE: 363 s->idle = (uint8_t) (value >> 8); 364 break; 365 default: 366 p->status = USB_RET_STALL; 367 break; 368 } 369 } 370 371 static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p) 372 { 373 USBWacomState *s = (USBWacomState *) dev; 374 g_autofree uint8_t *buf = g_malloc(p->iov.size); 375 int len = 0; 376 377 switch (p->pid) { 378 case USB_TOKEN_IN: 379 if (p->ep->nr == 1) { 380 if (!(s->changed || s->idle)) { 381 p->status = USB_RET_NAK; 382 return; 383 } 384 s->changed = 0; 385 if (s->mode == WACOM_MODE_HID) 386 len = usb_mouse_poll(s, buf, p->iov.size); 387 else if (s->mode == WACOM_MODE_WACOM) 388 len = usb_wacom_poll(s, buf, p->iov.size); 389 usb_packet_copy(p, buf, len); 390 break; 391 } 392 /* Fall through. */ 393 case USB_TOKEN_OUT: 394 default: 395 p->status = USB_RET_STALL; 396 } 397 } 398 399 static void usb_wacom_unrealize(USBDevice *dev) 400 { 401 USBWacomState *s = (USBWacomState *) dev; 402 403 if (s->mouse_grabbed) { 404 qemu_remove_mouse_event_handler(s->eh_entry); 405 s->mouse_grabbed = 0; 406 } 407 } 408 409 static void usb_wacom_realize(USBDevice *dev, Error **errp) 410 { 411 USBWacomState *s = USB_WACOM(dev); 412 usb_desc_create_serial(dev); 413 usb_desc_init(dev); 414 s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); 415 s->changed = 1; 416 } 417 418 static const VMStateDescription vmstate_usb_wacom = { 419 .name = "usb-wacom", 420 .unmigratable = 1, 421 }; 422 423 static void usb_wacom_class_init(ObjectClass *klass, void *data) 424 { 425 DeviceClass *dc = DEVICE_CLASS(klass); 426 USBDeviceClass *uc = USB_DEVICE_CLASS(klass); 427 428 uc->product_desc = "QEMU PenPartner Tablet"; 429 uc->usb_desc = &desc_wacom; 430 uc->realize = usb_wacom_realize; 431 uc->handle_reset = usb_wacom_handle_reset; 432 uc->handle_control = usb_wacom_handle_control; 433 uc->handle_data = usb_wacom_handle_data; 434 uc->unrealize = usb_wacom_unrealize; 435 set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 436 dc->desc = "QEMU PenPartner Tablet"; 437 dc->vmsd = &vmstate_usb_wacom; 438 } 439 440 static const TypeInfo wacom_info = { 441 .name = TYPE_USB_WACOM, 442 .parent = TYPE_USB_DEVICE, 443 .instance_size = sizeof(USBWacomState), 444 .class_init = usb_wacom_class_init, 445 }; 446 447 static void usb_wacom_register_types(void) 448 { 449 type_register_static(&wacom_info); 450 usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL); 451 } 452 453 type_init(usb_wacom_register_types) 454