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 "vl.h" 29 30 /* Interface requests */ 31 #define WACOM_GET_REPORT 0x2101 32 #define WACOM_SET_REPORT 0x2109 33 34 /* HID interface requests */ 35 #define HID_GET_REPORT 0xa101 36 #define HID_GET_IDLE 0xa102 37 #define HID_GET_PROTOCOL 0xa103 38 #define HID_SET_IDLE 0x210a 39 #define HID_SET_PROTOCOL 0x210b 40 41 typedef struct USBWacomState { 42 USBDevice dev; 43 QEMUPutMouseEntry *eh_entry; 44 int dx, dy, dz, buttons_state; 45 int x, y; 46 int mouse_grabbed; 47 enum { 48 WACOM_MODE_HID = 1, 49 WACOM_MODE_WACOM = 2, 50 } mode; 51 } USBWacomState; 52 53 static const uint8_t qemu_wacom_dev_descriptor[] = { 54 0x12, /* u8 bLength; */ 55 0x01, /* u8 bDescriptorType; Device */ 56 0x10, 0x10, /* u16 bcdUSB; v1.10 */ 57 58 0x00, /* u8 bDeviceClass; */ 59 0x00, /* u8 bDeviceSubClass; */ 60 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ 61 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ 62 63 0x6a, 0x05, /* u16 idVendor; */ 64 0x00, 0x00, /* u16 idProduct; */ 65 0x10, 0x42, /* u16 bcdDevice */ 66 67 0x01, /* u8 iManufacturer; */ 68 0x02, /* u8 iProduct; */ 69 0x00, /* u8 iSerialNumber; */ 70 0x01, /* u8 bNumConfigurations; */ 71 }; 72 73 static const uint8_t qemu_wacom_config_descriptor[] = { 74 /* one configuration */ 75 0x09, /* u8 bLength; */ 76 0x02, /* u8 bDescriptorType; Configuration */ 77 0x22, 0x00, /* u16 wTotalLength; */ 78 0x01, /* u8 bNumInterfaces; (1) */ 79 0x01, /* u8 bConfigurationValue; */ 80 0x00, /* u8 iConfiguration; */ 81 0x80, /* u8 bmAttributes; 82 Bit 7: must be set, 83 6: Self-powered, 84 5: Remote wakeup, 85 4..0: resvd */ 86 40, /* u8 MaxPower; */ 87 88 /* one interface */ 89 0x09, /* u8 if_bLength; */ 90 0x04, /* u8 if_bDescriptorType; Interface */ 91 0x00, /* u8 if_bInterfaceNumber; */ 92 0x00, /* u8 if_bAlternateSetting; */ 93 0x01, /* u8 if_bNumEndpoints; */ 94 0x03, /* u8 if_bInterfaceClass; HID */ 95 0x01, /* u8 if_bInterfaceSubClass; Boot */ 96 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ 97 0x00, /* u8 if_iInterface; */ 98 99 /* HID descriptor */ 100 0x09, /* u8 bLength; */ 101 0x21, /* u8 bDescriptorType; */ 102 0x01, 0x10, /* u16 HID_class */ 103 0x00, /* u8 country_code */ 104 0x01, /* u8 num_descriptors */ 105 0x22, /* u8 type; Report */ 106 0x6e, 0x00, /* u16 len */ 107 108 /* one endpoint (status change endpoint) */ 109 0x07, /* u8 ep_bLength; */ 110 0x05, /* u8 ep_bDescriptorType; Endpoint */ 111 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ 112 0x03, /* u8 ep_bmAttributes; Interrupt */ 113 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ 114 0x0a, /* u8 ep_bInterval; */ 115 }; 116 117 static void usb_mouse_event(void *opaque, 118 int dx1, int dy1, int dz1, int buttons_state) 119 { 120 USBWacomState *s = opaque; 121 122 s->dx += dx1; 123 s->dy += dy1; 124 s->dz += dz1; 125 s->buttons_state = buttons_state; 126 } 127 128 static void usb_wacom_event(void *opaque, 129 int x, int y, int dz, int buttons_state) 130 { 131 USBWacomState *s = opaque; 132 133 s->x = x; 134 s->y = y; 135 s->dz += dz; 136 s->buttons_state = buttons_state; 137 } 138 139 static inline int int_clamp(int val, int vmin, int vmax) 140 { 141 if (val < vmin) 142 return vmin; 143 else if (val > vmax) 144 return vmax; 145 else 146 return val; 147 } 148 149 static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len) 150 { 151 int dx, dy, dz, b, l; 152 153 if (!s->mouse_grabbed) { 154 s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, 155 "QEMU PenPartner tablet"); 156 s->mouse_grabbed = 1; 157 } 158 159 dx = int_clamp(s->dx, -128, 127); 160 dy = int_clamp(s->dy, -128, 127); 161 dz = int_clamp(s->dz, -128, 127); 162 163 s->dx -= dx; 164 s->dy -= dy; 165 s->dz -= dz; 166 167 b = 0; 168 if (s->buttons_state & MOUSE_EVENT_LBUTTON) 169 b |= 0x01; 170 if (s->buttons_state & MOUSE_EVENT_RBUTTON) 171 b |= 0x02; 172 if (s->buttons_state & MOUSE_EVENT_MBUTTON) 173 b |= 0x04; 174 175 buf[0] = b; 176 buf[1] = dx; 177 buf[2] = dy; 178 l = 3; 179 if (len >= 4) { 180 buf[3] = dz; 181 l = 4; 182 } 183 return l; 184 } 185 186 static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len) 187 { 188 int b; 189 190 if (!s->mouse_grabbed) { 191 s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, 192 "QEMU PenPartner tablet"); 193 s->mouse_grabbed = 1; 194 } 195 196 b = 0; 197 if (s->buttons_state & MOUSE_EVENT_LBUTTON) 198 b |= 0x01; 199 if (s->buttons_state & MOUSE_EVENT_RBUTTON) 200 b |= 0x02; 201 if (s->buttons_state & MOUSE_EVENT_MBUTTON) 202 b |= 0x04; 203 204 if (len < 7) 205 return 0; 206 207 buf[0] = s->mode; 208 buf[5] = 0x00; 209 if (b) { 210 buf[1] = s->x & 0xff; 211 buf[2] = s->x >> 8; 212 buf[3] = s->y & 0xff; 213 buf[4] = s->y >> 8; 214 buf[6] = 0; 215 } else { 216 buf[1] = 0; 217 buf[2] = 0; 218 buf[3] = 0; 219 buf[4] = 0; 220 buf[6] = (unsigned char) -127; 221 } 222 223 return 7; 224 } 225 226 static void usb_wacom_handle_reset(USBDevice *dev) 227 { 228 USBWacomState *s = (USBWacomState *) dev; 229 230 s->dx = 0; 231 s->dy = 0; 232 s->dz = 0; 233 s->x = 0; 234 s->y = 0; 235 s->buttons_state = 0; 236 s->mode = WACOM_MODE_HID; 237 } 238 239 static int usb_wacom_handle_control(USBDevice *dev, int request, int value, 240 int index, int length, uint8_t *data) 241 { 242 USBWacomState *s = (USBWacomState *) dev; 243 int ret = 0; 244 245 switch (request) { 246 case DeviceRequest | USB_REQ_GET_STATUS: 247 data[0] = (1 << USB_DEVICE_SELF_POWERED) | 248 (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); 249 data[1] = 0x00; 250 ret = 2; 251 break; 252 case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: 253 if (value == USB_DEVICE_REMOTE_WAKEUP) { 254 dev->remote_wakeup = 0; 255 } else { 256 goto fail; 257 } 258 ret = 0; 259 break; 260 case DeviceOutRequest | USB_REQ_SET_FEATURE: 261 if (value == USB_DEVICE_REMOTE_WAKEUP) { 262 dev->remote_wakeup = 1; 263 } else { 264 goto fail; 265 } 266 ret = 0; 267 break; 268 case DeviceOutRequest | USB_REQ_SET_ADDRESS: 269 dev->addr = value; 270 ret = 0; 271 break; 272 case DeviceRequest | USB_REQ_GET_DESCRIPTOR: 273 switch (value >> 8) { 274 case USB_DT_DEVICE: 275 memcpy(data, qemu_wacom_dev_descriptor, 276 sizeof(qemu_wacom_dev_descriptor)); 277 ret = sizeof(qemu_wacom_dev_descriptor); 278 break; 279 case USB_DT_CONFIG: 280 memcpy(data, qemu_wacom_config_descriptor, 281 sizeof(qemu_wacom_config_descriptor)); 282 ret = sizeof(qemu_wacom_config_descriptor); 283 break; 284 case USB_DT_STRING: 285 switch (value & 0xff) { 286 case 0: 287 /* language ids */ 288 data[0] = 4; 289 data[1] = 3; 290 data[2] = 0x09; 291 data[3] = 0x04; 292 ret = 4; 293 break; 294 case 1: 295 /* serial number */ 296 ret = set_usb_string(data, "1"); 297 break; 298 case 2: 299 ret = set_usb_string(data, "Wacom PenPartner"); 300 break; 301 case 3: 302 /* vendor description */ 303 ret = set_usb_string(data, "QEMU " QEMU_VERSION); 304 break; 305 case 4: 306 ret = set_usb_string(data, "Wacom Tablet"); 307 break; 308 case 5: 309 ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); 310 break; 311 default: 312 goto fail; 313 } 314 break; 315 default: 316 goto fail; 317 } 318 break; 319 case DeviceRequest | USB_REQ_GET_CONFIGURATION: 320 data[0] = 1; 321 ret = 1; 322 break; 323 case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: 324 ret = 0; 325 break; 326 case DeviceRequest | USB_REQ_GET_INTERFACE: 327 data[0] = 0; 328 ret = 1; 329 break; 330 case DeviceOutRequest | USB_REQ_SET_INTERFACE: 331 ret = 0; 332 break; 333 case WACOM_SET_REPORT: 334 qemu_remove_mouse_event_handler(s->eh_entry); 335 s->mouse_grabbed = 0; 336 s->mode = data[0]; 337 ret = 0; 338 break; 339 case WACOM_GET_REPORT: 340 data[0] = 0; 341 data[1] = s->mode; 342 ret = 2; 343 break; 344 /* USB HID requests */ 345 case HID_GET_REPORT: 346 if (s->mode == WACOM_MODE_HID) 347 ret = usb_mouse_poll(s, data, length); 348 else if (s->mode == WACOM_MODE_WACOM) 349 ret = usb_wacom_poll(s, data, length); 350 break; 351 case HID_SET_IDLE: 352 ret = 0; 353 break; 354 default: 355 fail: 356 ret = USB_RET_STALL; 357 break; 358 } 359 return ret; 360 } 361 362 static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p) 363 { 364 USBWacomState *s = (USBWacomState *) dev; 365 int ret = 0; 366 367 switch (p->pid) { 368 case USB_TOKEN_IN: 369 if (p->devep == 1) { 370 if (s->mode == WACOM_MODE_HID) 371 ret = usb_mouse_poll(s, p->data, p->len); 372 else if (s->mode == WACOM_MODE_WACOM) 373 ret = usb_wacom_poll(s, p->data, p->len); 374 break; 375 } 376 /* Fall through. */ 377 case USB_TOKEN_OUT: 378 default: 379 ret = USB_RET_STALL; 380 break; 381 } 382 return ret; 383 } 384 385 static void usb_wacom_handle_destroy(USBDevice *dev) 386 { 387 USBWacomState *s = (USBWacomState *) dev; 388 389 qemu_remove_mouse_event_handler(s->eh_entry); 390 qemu_free(s); 391 } 392 393 USBDevice *usb_wacom_init(void) 394 { 395 USBWacomState *s; 396 397 s = qemu_mallocz(sizeof(USBWacomState)); 398 if (!s) 399 return NULL; 400 s->dev.speed = USB_SPEED_FULL; 401 s->dev.handle_packet = usb_generic_handle_packet; 402 403 s->dev.handle_reset = usb_wacom_handle_reset; 404 s->dev.handle_control = usb_wacom_handle_control; 405 s->dev.handle_data = usb_wacom_handle_data; 406 s->dev.handle_destroy = usb_wacom_handle_destroy; 407 408 pstrcpy(s->dev.devname, sizeof(s->dev.devname), 409 "QEMU PenPartner Tablet"); 410 411 return (USBDevice *) s; 412 } 413