master
c 490 lines 14.5 KB
Raw
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/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 QemuInputHandlerState *hs;
46 int axis[INPUT_AXIS__MAX];
47 int dz;
48 bool btns[INPUT_BUTTON__MAX];
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_wacom_input_event(DeviceState *dev, QemuConsole *src,
192 QemuInputEvent *evt)
193 {
194 USBWacomState *s = USB_WACOM(dev);
195
196 switch (evt->type) {
197 case INPUT_EVENT_KIND_BTN:
198 if (evt->btn.down) {
199 if (evt->btn.button == INPUT_BUTTON_WHEEL_UP) {
200 s->dz--;
201 } else if (evt->btn.button == INPUT_BUTTON_WHEEL_DOWN) {
202 s->dz++;
203 }
204 }
205 s->btns[evt->btn.button] = evt->btn.down;
206 break;
207 case INPUT_EVENT_KIND_ABS:
208 s->axis[evt->abs.axis] = evt->abs.value;
209 break;
210 case INPUT_EVENT_KIND_REL:
211 s->axis[evt->rel.axis] += evt->rel.value;
212 break;
213 default:
214 break;
215 }
216 }
217
218 static void usb_wacom_input_sync(DeviceState *dev)
219 {
220 USBWacomState *s = USB_WACOM(dev);
221
222 s->changed = 1;
223 usb_wakeup(s->intr, 0);
224 }
225
226 static inline int int_clamp(int val, int vmin, int vmax)
227 {
228 if (val < vmin)
229 return vmin;
230 else if (val > vmax)
231 return vmax;
232 else
233 return val;
234 }
235
236 static void usb_wacom_register_input_handler(USBWacomState *s, bool absolute)
237 {
238 static const QemuInputHandler usb_wacom_abs_handler = {
239 .name = "QEMU PenPartner tablet",
240 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
241 .event = usb_wacom_input_event,
242 .sync = usb_wacom_input_sync,
243 };
244
245 static const QemuInputHandler usb_wacom_rel_handler = {
246 .name = "QEMU PenPartner tablet",
247 .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
248 .event = usb_wacom_input_event,
249 .sync = usb_wacom_input_sync,
250 };
251
252 const QemuInputHandler *h = absolute ?
253 &usb_wacom_abs_handler : &usb_wacom_rel_handler;
254
255 g_clear_pointer(&s->hs, qemu_input_handler_unregister);
256
257 s->hs = qemu_input_handler_register(DEVICE(s), h);
258 qemu_input_handler_activate(s->hs);
259 }
260
261 static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
262 {
263 int dx, dy, dz, b, l;
264
265 if (!s->hs) {
266 usb_wacom_register_input_handler(s, false);
267 }
268
269 dx = int_clamp(s->axis[INPUT_AXIS_X], -128, 127);
270 dy = int_clamp(s->axis[INPUT_AXIS_Y], -128, 127);
271 dz = int_clamp(s->dz, -128, 127);
272
273 s->axis[INPUT_AXIS_X] -= dx;
274 s->axis[INPUT_AXIS_Y] -= dy;
275 s->dz -= dz;
276
277 b = 0;
278 if (s->btns[INPUT_BUTTON_LEFT]) {
279 b |= 0x01;
280 }
281 if (s->btns[INPUT_BUTTON_RIGHT]) {
282 b |= 0x02;
283 }
284 if (s->btns[INPUT_BUTTON_MIDDLE]) {
285 b |= 0x04;
286 }
287
288 if (len < 3) {
289 return 0;
290 }
291
292 buf[0] = b;
293 buf[1] = dx;
294 buf[2] = dy;
295 l = 3;
296 if (len >= 4) {
297 buf[3] = dz;
298 l = 4;
299 }
300 return l;
301 }
302
303 static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
304 {
305 int b, x, y;
306
307 if (!s->hs) {
308 usb_wacom_register_input_handler(s, true);
309 }
310
311 b = 0;
312 if (s->btns[INPUT_BUTTON_LEFT]) {
313 b |= 0x01;
314 }
315 if (s->btns[INPUT_BUTTON_RIGHT]) {
316 b |= 0x40;
317 }
318 if (s->btns[INPUT_BUTTON_MIDDLE]) {
319 b |= 0x20; /* eraser */
320 }
321
322 if (len < 7) {
323 return 0;
324 }
325
326 x = qemu_input_scale_axis(s->axis[INPUT_AXIS_X],
327 INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX,
328 0, 5040);
329 y = qemu_input_scale_axis(s->axis[INPUT_AXIS_Y],
330 INPUT_EVENT_ABS_MIN, INPUT_EVENT_ABS_MAX,
331 0, 3780);
332
333 buf[0] = s->mode;
334 buf[5] = 0x00 | (b & 0xf0);
335 buf[1] = x & 0xff;
336 buf[2] = x >> 8;
337 buf[3] = y & 0xff;
338 buf[4] = y >> 8;
339 if (b & 0x3f) {
340 buf[6] = 0;
341 } else {
342 buf[6] = (unsigned char) -127;
343 }
344
345 return 7;
346 }
347
348 static void usb_wacom_handle_reset(USBDevice *dev)
349 {
350 USBWacomState *s = USB_WACOM(dev);
351
352 memset(s->axis, 0, sizeof(s->axis));
353 memset(s->btns, 0, sizeof(s->btns));
354 s->dz = 0;
355 s->mode = WACOM_MODE_HID;
356 g_clear_pointer(&s->hs, qemu_input_handler_unregister);
357 }
358
359 static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
360 int request, int value, int index, int length, uint8_t *data)
361 {
362 USBWacomState *s = (USBWacomState *) dev;
363 int ret;
364
365 ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
366 if (ret >= 0) {
367 return;
368 }
369
370 switch (request) {
371 case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
372 switch (value >> 8) {
373 case 0x22:
374 memcpy(data, qemu_wacom_hid_report_descriptor,
375 sizeof(qemu_wacom_hid_report_descriptor));
376 p->actual_length = sizeof(qemu_wacom_hid_report_descriptor);
377 break;
378 default:
379 return;
380 }
381 break;
382 case WACOM_SET_REPORT:
383 g_clear_pointer(&s->hs, qemu_input_handler_unregister);
384 s->mode = data[0];
385 break;
386 case WACOM_GET_REPORT:
387 data[0] = 0;
388 data[1] = s->mode;
389 p->actual_length = 2;
390 break;
391 /* USB HID requests */
392 case HID_GET_REPORT:
393 if (s->mode == WACOM_MODE_HID)
394 p->actual_length = usb_mouse_poll(s, data, length);
395 else if (s->mode == WACOM_MODE_WACOM)
396 p->actual_length = usb_wacom_poll(s, data, length);
397 break;
398 case HID_GET_IDLE:
399 data[0] = s->idle;
400 p->actual_length = 1;
401 break;
402 case HID_SET_IDLE:
403 s->idle = (uint8_t) (value >> 8);
404 break;
405 default:
406 p->status = USB_RET_STALL;
407 break;
408 }
409 }
410
411 static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
412 {
413 USBWacomState *s = (USBWacomState *) dev;
414 g_autofree uint8_t *buf = g_malloc(p->iov.size);
415 int len = 0;
416
417 switch (p->pid) {
418 case USB_TOKEN_IN:
419 if (p->ep->nr == 1) {
420 if (!(s->changed || s->idle)) {
421 p->status = USB_RET_NAK;
422 return;
423 }
424 s->changed = 0;
425 if (s->mode == WACOM_MODE_HID)
426 len = usb_mouse_poll(s, buf, p->iov.size);
427 else if (s->mode == WACOM_MODE_WACOM)
428 len = usb_wacom_poll(s, buf, p->iov.size);
429 usb_packet_copy(p, buf, len);
430 break;
431 }
432 /* Fall through. */
433 case USB_TOKEN_OUT:
434 default:
435 p->status = USB_RET_STALL;
436 }
437 }
438
439 static void usb_wacom_unrealize(USBDevice *dev)
440 {
441 USBWacomState *s = (USBWacomState *) dev;
442
443 g_clear_pointer(&s->hs, qemu_input_handler_unregister);
444 }
445
446 static void usb_wacom_realize(USBDevice *dev, Error **errp)
447 {
448 USBWacomState *s = USB_WACOM(dev);
449 usb_desc_create_serial(dev);
450 usb_desc_init(dev);
451 s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
452 s->changed = 1;
453 }
454
455 static const VMStateDescription vmstate_usb_wacom = {
456 .name = "usb-wacom",
457 .unmigratable = 1,
458 };
459
460 static void usb_wacom_class_init(ObjectClass *klass, const void *data)
461 {
462 DeviceClass *dc = DEVICE_CLASS(klass);
463 USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
464
465 uc->product_desc = "QEMU PenPartner Tablet";
466 uc->usb_desc = &desc_wacom;
467 uc->realize = usb_wacom_realize;
468 uc->handle_reset = usb_wacom_handle_reset;
469 uc->handle_control = usb_wacom_handle_control;
470 uc->handle_data = usb_wacom_handle_data;
471 uc->unrealize = usb_wacom_unrealize;
472 set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
473 dc->desc = "QEMU PenPartner Tablet";
474 dc->vmsd = &vmstate_usb_wacom;
475 }
476
477 static const TypeInfo wacom_info = {
478 .name = TYPE_USB_WACOM,
479 .parent = TYPE_USB_DEVICE,
480 .instance_size = sizeof(USBWacomState),
481 .class_init = usb_wacom_class_init,
482 };
483
484 static void usb_wacom_register_types(void)
485 {
486 type_register_static(&wacom_info);
487 usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL);
488 }
489
490 type_init(usb_wacom_register_types)