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 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 static int usb_wacom_initfn(USBDevice *dev) 396 { 397 USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev); 398 s->dev.speed = USB_SPEED_FULL; 399 return 0; 400 } 401 402 static struct USBDeviceInfo wacom_info = { 403 .qdev.name = "QEMU PenPartner Tablet", 404 .qdev.size = sizeof(USBWacomState), 405 .init = usb_wacom_initfn, 406 .handle_packet = usb_generic_handle_packet, 407 .handle_reset = usb_wacom_handle_reset, 408 .handle_control = usb_wacom_handle_control, 409 .handle_data = usb_wacom_handle_data, 410 .handle_destroy = usb_wacom_handle_destroy, 411 }; 412 413 static void usb_wacom_register_devices(void) 414 { 415 usb_qdev_register(&wacom_info); 416 } 417 device_init(usb_wacom_register_devices) 418