vga: change tabs to spaces
[qemu] / hw / usb-wacom.c
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 USBDevice *usb_wacom_init(void)
396 {
397     USBWacomState *s;
398
399     s = qemu_mallocz(sizeof(USBWacomState));
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 }