Statistics
| Branch: | Revision:

root / hw / usb-wacom.c @ 87ecb68b

History | View | Annotate | Download (11.2 kB)

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
    if (!s)
401
        return NULL;
402
    s->dev.speed = USB_SPEED_FULL;
403
    s->dev.handle_packet = usb_generic_handle_packet;
404

    
405
    s->dev.handle_reset = usb_wacom_handle_reset;
406
    s->dev.handle_control = usb_wacom_handle_control;
407
    s->dev.handle_data = usb_wacom_handle_data;
408
    s->dev.handle_destroy = usb_wacom_handle_destroy;
409

    
410
    pstrcpy(s->dev.devname, sizeof(s->dev.devname),
411
            "QEMU PenPartner Tablet");
412

    
413
    return (USBDevice *) s;
414
}