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 #include "hw.h" 29 #include "console.h" 30 #include "usb.h" 31 #include "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 .ifs = &desc_iface_wacom, 112 }, 113 }, 114 }; 115 116 static const USBDesc desc_wacom = { 117 .id = { 118 .idVendor = 0x056a, 119 .idProduct = 0x0000, 120 .bcdDevice = 0x4210, 121 .iManufacturer = STR_MANUFACTURER, 122 .iProduct = STR_PRODUCT, 123 .iSerialNumber = STR_SERIALNUMBER, 124 }, 125 .full = &desc_device_wacom, 126 .str = desc_strings, 127 }; 128 129 static void usb_mouse_event(void *opaque, 130 int dx1, int dy1, int dz1, int buttons_state) 131 { 132 USBWacomState *s = opaque; 133 134 s->dx += dx1; 135 s->dy += dy1; 136 s->dz += dz1; 137 s->buttons_state = buttons_state; 138 s->changed = 1; 139 } 140 141 static void usb_wacom_event(void *opaque, 142 int x, int y, int dz, int buttons_state) 143 { 144 USBWacomState *s = opaque; 145 146 /* scale to Penpartner resolution */ 147 s->x = (x * 5040 / 0x7FFF); 148 s->y = (y * 3780 / 0x7FFF); 149 s->dz += dz; 150 s->buttons_state = buttons_state; 151 s->changed = 1; 152 } 153 154 static inline int int_clamp(int val, int vmin, int vmax) 155 { 156 if (val < vmin) 157 return vmin; 158 else if (val > vmax) 159 return vmax; 160 else 161 return val; 162 } 163 164 static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) 165 { 166 int dx, dy, dz, b, l; 167 168 if (!s->mouse_grabbed) { 169 s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, 170 "QEMU PenPartner tablet"); 171 qemu_activate_mouse_event_handler(s->eh_entry); 172 s->mouse_grabbed = 1; 173 } 174 175 dx = int_clamp(s->dx, -128, 127); 176 dy = int_clamp(s->dy, -128, 127); 177 dz = int_clamp(s->dz, -128, 127); 178 179 s->dx -= dx; 180 s->dy -= dy; 181 s->dz -= dz; 182 183 b = 0; 184 if (s->buttons_state & MOUSE_EVENT_LBUTTON) 185 b |= 0x01; 186 if (s->buttons_state & MOUSE_EVENT_RBUTTON) 187 b |= 0x02; 188 if (s->buttons_state & MOUSE_EVENT_MBUTTON) 189 b |= 0x04; 190 191 buf[0] = b; 192 buf[1] = dx; 193 buf[2] = dy; 194 l = 3; 195 if (len >= 4) { 196 buf[3] = dz; 197 l = 4; 198 } 199 return l; 200 } 201 202 static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) 203 { 204 int b; 205 206 if (!s->mouse_grabbed) { 207 s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, 208 "QEMU PenPartner tablet"); 209 qemu_activate_mouse_event_handler(s->eh_entry); 210 s->mouse_grabbed = 1; 211 } 212 213 b = 0; 214 if (s->buttons_state & MOUSE_EVENT_LBUTTON) 215 b |= 0x01; 216 if (s->buttons_state & MOUSE_EVENT_RBUTTON) 217 b |= 0x40; 218 if (s->buttons_state & MOUSE_EVENT_MBUTTON) 219 b |= 0x20; /* eraser */ 220 221 if (len < 7) 222 return 0; 223 224 buf[0] = s->mode; 225 buf[5] = 0x00 | (b & 0xf0); 226 buf[1] = s->x & 0xff; 227 buf[2] = s->x >> 8; 228 buf[3] = s->y & 0xff; 229 buf[4] = s->y >> 8; 230 if (b & 0x3f) { 231 buf[6] = 0; 232 } else { 233 buf[6] = (unsigned char) -127; 234 } 235 236 return 7; 237 } 238 239 static void usb_wacom_handle_reset(USBDevice *dev) 240 { 241 USBWacomState *s = (USBWacomState *) dev; 242 243 s->dx = 0; 244 s->dy = 0; 245 s->dz = 0; 246 s->x = 0; 247 s->y = 0; 248 s->buttons_state = 0; 249 s->mode = WACOM_MODE_HID; 250 } 251 252 static int usb_wacom_handle_control(USBDevice *dev, int request, int value, 253 int index, int length, uint8_t *data) 254 { 255 USBWacomState *s = (USBWacomState *) dev; 256 int ret; 257 258 ret = usb_desc_handle_control(dev, request, value, index, length, data); 259 if (ret >= 0) { 260 return ret; 261 } 262 263 ret = 0; 264 switch (request) { 265 case DeviceRequest | USB_REQ_GET_STATUS: 266 data[0] = (1 << USB_DEVICE_SELF_POWERED) | 267 (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); 268 data[1] = 0x00; 269 ret = 2; 270 break; 271 case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: 272 if (value == USB_DEVICE_REMOTE_WAKEUP) { 273 dev->remote_wakeup = 0; 274 } else { 275 goto fail; 276 } 277 ret = 0; 278 break; 279 case DeviceOutRequest | USB_REQ_SET_FEATURE: 280 if (value == USB_DEVICE_REMOTE_WAKEUP) { 281 dev->remote_wakeup = 1; 282 } else { 283 goto fail; 284 } 285 ret = 0; 286 break; 287 case DeviceRequest | USB_REQ_GET_CONFIGURATION: 288 data[0] = 1; 289 ret = 1; 290 break; 291 case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: 292 ret = 0; 293 break; 294 case DeviceRequest | USB_REQ_GET_INTERFACE: 295 data[0] = 0; 296 ret = 1; 297 break; 298 case DeviceOutRequest | USB_REQ_SET_INTERFACE: 299 ret = 0; 300 break; 301 case WACOM_SET_REPORT: 302 if (s->mouse_grabbed) { 303 qemu_remove_mouse_event_handler(s->eh_entry); 304 s->mouse_grabbed = 0; 305 } 306 s->mode = data[0]; 307 ret = 0; 308 break; 309 case WACOM_GET_REPORT: 310 data[0] = 0; 311 data[1] = s->mode; 312 ret = 2; 313 break; 314 /* USB HID requests */ 315 case HID_GET_REPORT: 316 if (s->mode == WACOM_MODE_HID) 317 ret = usb_mouse_poll(s, data, length); 318 else if (s->mode == WACOM_MODE_WACOM) 319 ret = usb_wacom_poll(s, data, length); 320 break; 321 case HID_GET_IDLE: 322 ret = 1; 323 data[0] = s->idle; 324 break; 325 case HID_SET_IDLE: 326 s->idle = (uint8_t) (value >> 8); 327 ret = 0; 328 break; 329 default: 330 fail: 331 ret = USB_RET_STALL; 332 break; 333 } 334 return ret; 335 } 336 337 static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) 338 { 339 USBWacomState *s = (USBWacomState *) dev; 340 int ret = 0; 341 342 switch (p->pid) { 343 case USB_TOKEN_IN: 344 if (p->devep == 1) { 345 if (!(s->changed || s->idle)) 346 return USB_RET_NAK; 347 s->changed = 0; 348 if (s->mode == WACOM_MODE_HID) 349 ret = usb_mouse_poll(s, p->data, p->len); 350 else if (s->mode == WACOM_MODE_WACOM) 351 ret = usb_wacom_poll(s, p->data, p->len); 352 break; 353 } 354 /* Fall through. */ 355 case USB_TOKEN_OUT: 356 default: 357 ret = USB_RET_STALL; 358 break; 359 } 360 return ret; 361 } 362 363 static void usb_wacom_handle_destroy(USBDevice *dev) 364 { 365 USBWacomState *s = (USBWacomState *) dev; 366 367 if (s->mouse_grabbed) { 368 qemu_remove_mouse_event_handler(s->eh_entry); 369 s->mouse_grabbed = 0; 370 } 371 } 372 373 static int usb_wacom_initfn(USBDevice *dev) 374 { 375 USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev); 376 s->dev.speed = USB_SPEED_FULL; 377 s->changed = 1; 378 return 0; 379 } 380 381 static struct USBDeviceInfo wacom_info = { 382 .product_desc = "QEMU PenPartner Tablet", 383 .qdev.name = "usb-wacom-tablet", 384 .qdev.desc = "QEMU PenPartner Tablet", 385 .usbdevice_name = "wacom-tablet", 386 .usb_desc = &desc_wacom, 387 .qdev.size = sizeof(USBWacomState), 388 .init = usb_wacom_initfn, 389 .handle_packet = usb_generic_handle_packet, 390 .handle_reset = usb_wacom_handle_reset, 391 .handle_control = usb_wacom_handle_control, 392 .handle_data = usb_wacom_handle_data, 393 .handle_destroy = usb_wacom_handle_destroy, 394 }; 395 396 static void usb_wacom_register_devices(void) 397 { 398 usb_qdev_register(&wacom_info); 399 } 400 device_init(usb_wacom_register_devices) 401