Add PowerPC power-management state check callback.
[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 "vl.h"
29
30 /* Interface requests */
31 #define WACOM_GET_REPORT        0x2101
32 #define WACOM_SET_REPORT        0x2109
33
34 /* HID interface requests */
35 #define HID_GET_REPORT          0xa101
36 #define HID_GET_IDLE            0xa102
37 #define HID_GET_PROTOCOL        0xa103
38 #define HID_SET_IDLE            0x210a
39 #define HID_SET_PROTOCOL        0x210b
40
41 typedef struct USBWacomState {
42     USBDevice dev;
43     QEMUPutMouseEntry *eh_entry;
44     int dx, dy, dz, buttons_state;
45     int x, y;
46     int mouse_grabbed;
47     enum {
48         WACOM_MODE_HID = 1,
49         WACOM_MODE_WACOM = 2,
50     } mode;
51 } USBWacomState;
52
53 static const uint8_t qemu_wacom_dev_descriptor[] = {
54     0x12,       /*  u8 bLength; */
55     0x01,       /*  u8 bDescriptorType; Device */
56     0x10, 0x10, /*  u16 bcdUSB; v1.10 */
57
58     0x00,       /*  u8  bDeviceClass; */
59     0x00,       /*  u8  bDeviceSubClass; */
60     0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
61     0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
62
63     0x6a, 0x05, /*  u16 idVendor; */
64     0x00, 0x00, /*  u16 idProduct; */
65     0x10, 0x42, /*  u16 bcdDevice */
66
67     0x01,       /*  u8  iManufacturer; */
68     0x02,       /*  u8  iProduct; */
69     0x00,       /*  u8  iSerialNumber; */
70     0x01,       /*  u8  bNumConfigurations; */
71 };
72
73 static const uint8_t qemu_wacom_config_descriptor[] = {
74     /* one configuration */
75     0x09,       /*  u8  bLength; */
76     0x02,       /*  u8  bDescriptorType; Configuration */
77     0x22, 0x00, /*  u16 wTotalLength; */
78     0x01,       /*  u8  bNumInterfaces; (1) */
79     0x01,       /*  u8  bConfigurationValue; */
80     0x00,       /*  u8  iConfiguration; */
81     0x80,       /*  u8  bmAttributes;
82                                  Bit 7: must be set,
83                                      6: Self-powered,
84                                      5: Remote wakeup,
85                                      4..0: resvd */
86     40,         /*  u8  MaxPower; */
87
88     /* one interface */
89     0x09,       /*  u8  if_bLength; */
90     0x04,       /*  u8  if_bDescriptorType; Interface */
91     0x00,       /*  u8  if_bInterfaceNumber; */
92     0x00,       /*  u8  if_bAlternateSetting; */
93     0x01,       /*  u8  if_bNumEndpoints; */
94     0x03,       /*  u8  if_bInterfaceClass; HID */
95     0x01,       /*  u8  if_bInterfaceSubClass; Boot */
96     0x02,       /*  u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
97     0x00,       /*  u8  if_iInterface; */
98
99     /* HID descriptor */
100     0x09,       /*  u8  bLength; */
101     0x21,       /*  u8  bDescriptorType; */
102     0x01, 0x10, /*  u16 HID_class */
103     0x00,       /*  u8  country_code */
104     0x01,       /*  u8  num_descriptors */
105     0x22,       /*  u8  type; Report */
106     0x6e, 0x00, /*  u16 len */
107
108     /* one endpoint (status change endpoint) */
109     0x07,       /*  u8  ep_bLength; */
110     0x05,       /*  u8  ep_bDescriptorType; Endpoint */
111     0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
112     0x03,       /*  u8  ep_bmAttributes; Interrupt */
113     0x08, 0x00, /*  u16 ep_wMaxPacketSize; */
114     0x0a,       /*  u8  ep_bInterval; */
115 };
116
117 static void usb_mouse_event(void *opaque,
118                             int dx1, int dy1, int dz1, int buttons_state)
119 {
120     USBWacomState *s = opaque;
121
122     s->dx += dx1;
123     s->dy += dy1;
124     s->dz += dz1;
125     s->buttons_state = buttons_state;
126 }
127
128 static void usb_wacom_event(void *opaque,
129                             int x, int y, int dz, int buttons_state)
130 {
131     USBWacomState *s = opaque;
132
133     s->x = x;
134     s->y = y;
135     s->dz += dz;
136     s->buttons_state = buttons_state;
137 }
138
139 static inline int int_clamp(int val, int vmin, int vmax)
140 {
141     if (val < vmin)
142         return vmin;
143     else if (val > vmax)
144         return vmax;
145     else
146         return val;
147 }
148
149 static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
150 {
151     int dx, dy, dz, b, l;
152
153     if (!s->mouse_grabbed) {
154         s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
155                         "QEMU PenPartner tablet");
156         s->mouse_grabbed = 1;
157     }
158
159     dx = int_clamp(s->dx, -128, 127);
160     dy = int_clamp(s->dy, -128, 127);
161     dz = int_clamp(s->dz, -128, 127);
162
163     s->dx -= dx;
164     s->dy -= dy;
165     s->dz -= dz;
166
167     b = 0;
168     if (s->buttons_state & MOUSE_EVENT_LBUTTON)
169         b |= 0x01;
170     if (s->buttons_state & MOUSE_EVENT_RBUTTON)
171         b |= 0x02;
172     if (s->buttons_state & MOUSE_EVENT_MBUTTON)
173         b |= 0x04;
174
175     buf[0] = b;
176     buf[1] = dx;
177     buf[2] = dy;
178     l = 3;
179     if (len >= 4) {
180         buf[3] = dz;
181         l = 4;
182     }
183     return l;
184 }
185
186 static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
187 {
188     int b;
189
190     if (!s->mouse_grabbed) {
191         s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
192                         "QEMU PenPartner tablet");
193         s->mouse_grabbed = 1;
194     }
195
196     b = 0;
197     if (s->buttons_state & MOUSE_EVENT_LBUTTON)
198         b |= 0x01;
199     if (s->buttons_state & MOUSE_EVENT_RBUTTON)
200         b |= 0x02;
201     if (s->buttons_state & MOUSE_EVENT_MBUTTON)
202         b |= 0x04;
203
204     if (len < 7)
205         return 0;
206
207     buf[0] = s->mode;
208     buf[5] = 0x00;
209     if (b) {
210         buf[1] = s->x & 0xff;
211         buf[2] = s->x >> 8;
212         buf[3] = s->y & 0xff;
213         buf[4] = s->y >> 8;
214         buf[6] = 0;
215     } else {
216         buf[1] = 0;
217         buf[2] = 0;
218         buf[3] = 0;
219         buf[4] = 0;
220         buf[6] = (unsigned char) -127;
221     }
222
223     return 7;
224 }
225
226 static void usb_wacom_handle_reset(USBDevice *dev)
227 {
228     USBWacomState *s = (USBWacomState *) dev;
229
230     s->dx = 0;
231     s->dy = 0;
232     s->dz = 0;
233     s->x = 0;
234     s->y = 0;
235     s->buttons_state = 0;
236     s->mode = WACOM_MODE_HID;
237 }
238
239 static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
240                                     int index, int length, uint8_t *data)
241 {
242     USBWacomState *s = (USBWacomState *) dev;
243     int ret = 0;
244
245     switch (request) {
246     case DeviceRequest | USB_REQ_GET_STATUS:
247         data[0] = (1 << USB_DEVICE_SELF_POWERED) |
248             (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
249         data[1] = 0x00;
250         ret = 2;
251         break;
252     case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
253         if (value == USB_DEVICE_REMOTE_WAKEUP) {
254             dev->remote_wakeup = 0;
255         } else {
256             goto fail;
257         }
258         ret = 0;
259         break;
260     case DeviceOutRequest | USB_REQ_SET_FEATURE:
261         if (value == USB_DEVICE_REMOTE_WAKEUP) {
262             dev->remote_wakeup = 1;
263         } else {
264             goto fail;
265         }
266         ret = 0;
267         break;
268     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
269         dev->addr = value;
270         ret = 0;
271         break;
272     case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
273         switch (value >> 8) {
274         case USB_DT_DEVICE:
275             memcpy(data, qemu_wacom_dev_descriptor,
276                    sizeof(qemu_wacom_dev_descriptor));
277             ret = sizeof(qemu_wacom_dev_descriptor);
278             break;
279         case USB_DT_CONFIG:
280             memcpy(data, qemu_wacom_config_descriptor,
281                    sizeof(qemu_wacom_config_descriptor));
282             ret = sizeof(qemu_wacom_config_descriptor);
283             break;
284         case USB_DT_STRING:
285             switch (value & 0xff) {
286             case 0:
287                 /* language ids */
288                 data[0] = 4;
289                 data[1] = 3;
290                 data[2] = 0x09;
291                 data[3] = 0x04;
292                 ret = 4;
293                 break;
294             case 1:
295                 /* serial number */
296                 ret = set_usb_string(data, "1");
297                 break;
298             case 2:
299                 ret = set_usb_string(data, "Wacom PenPartner");
300                 break;
301             case 3:
302                 /* vendor description */
303                 ret = set_usb_string(data, "QEMU " QEMU_VERSION);
304                 break;
305             case 4:
306                 ret = set_usb_string(data, "Wacom Tablet");
307                 break;
308             case 5:
309                 ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
310                 break;
311             default:
312                 goto fail;
313             }
314             break;
315         default:
316             goto fail;
317         }
318         break;
319     case DeviceRequest | USB_REQ_GET_CONFIGURATION:
320         data[0] = 1;
321         ret = 1;
322         break;
323     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
324         ret = 0;
325         break;
326     case DeviceRequest | USB_REQ_GET_INTERFACE:
327         data[0] = 0;
328         ret = 1;
329         break;
330     case DeviceOutRequest | USB_REQ_SET_INTERFACE:
331         ret = 0;
332         break;
333     case WACOM_SET_REPORT:
334         qemu_remove_mouse_event_handler(s->eh_entry);
335         s->mouse_grabbed = 0;
336         s->mode = data[0];
337         ret = 0;
338         break;
339     case WACOM_GET_REPORT:
340         data[0] = 0;
341         data[1] = s->mode;
342         ret = 2;
343         break;
344     /* USB HID requests */
345     case HID_GET_REPORT:
346         if (s->mode == WACOM_MODE_HID)
347             ret = usb_mouse_poll(s, data, length);
348         else if (s->mode == WACOM_MODE_WACOM)
349             ret = usb_wacom_poll(s, data, length);
350         break;
351     case HID_SET_IDLE:
352         ret = 0;
353         break;
354     default:
355     fail:
356         ret = USB_RET_STALL;
357         break;
358     }
359     return ret;
360 }
361
362 static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
363 {
364     USBWacomState *s = (USBWacomState *) dev;
365     int ret = 0;
366
367     switch (p->pid) {
368     case USB_TOKEN_IN:
369         if (p->devep == 1) {
370             if (s->mode == WACOM_MODE_HID)
371                 ret = usb_mouse_poll(s, p->data, p->len);
372             else if (s->mode == WACOM_MODE_WACOM)
373                 ret = usb_wacom_poll(s, p->data, p->len);
374             break;
375         }
376         /* Fall through.  */
377     case USB_TOKEN_OUT:
378     default:
379         ret = USB_RET_STALL;
380         break;
381     }
382     return ret;
383 }
384
385 static void usb_wacom_handle_destroy(USBDevice *dev)
386 {
387     USBWacomState *s = (USBWacomState *) dev;
388
389     qemu_remove_mouse_event_handler(s->eh_entry);
390     qemu_free(s);
391 }
392
393 USBDevice *usb_wacom_init(void)
394 {
395     USBWacomState *s;
396
397     s = qemu_mallocz(sizeof(USBWacomState));
398     if (!s)
399         return NULL;
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 }