Statistics
| Branch: | Revision:

root / hw / usb-wacom.c @ 556cd098

History | View | Annotate | Download (11.7 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
    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)