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