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