Statistics
| Branch: | Revision:

root / hw / usb-wacom.c @ 17786d52

History | View | Annotate | Download (12 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
        qemu_activate_mouse_event_handler(s->eh_entry);
164
        s->mouse_grabbed = 1;
165
    }
166

    
167
    dx = int_clamp(s->dx, -128, 127);
168
    dy = int_clamp(s->dy, -128, 127);
169
    dz = int_clamp(s->dz, -128, 127);
170

    
171
    s->dx -= dx;
172
    s->dy -= dy;
173
    s->dz -= dz;
174

    
175
    b = 0;
176
    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
177
        b |= 0x01;
178
    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
179
        b |= 0x02;
180
    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
181
        b |= 0x04;
182

    
183
    buf[0] = b;
184
    buf[1] = dx;
185
    buf[2] = dy;
186
    l = 3;
187
    if (len >= 4) {
188
        buf[3] = dz;
189
        l = 4;
190
    }
191
    return l;
192
}
193

    
194
static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
195
{
196
    int b;
197

    
198
    if (!s->mouse_grabbed) {
199
        s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
200
                        "QEMU PenPartner tablet");
201
        qemu_activate_mouse_event_handler(s->eh_entry);
202
        s->mouse_grabbed = 1;
203
    }
204

    
205
    b = 0;
206
    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
207
        b |= 0x01;
208
    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
209
        b |= 0x40;
210
    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
211
        b |= 0x20; /* eraser */
212

    
213
    if (len < 7)
214
        return 0;
215

    
216
    buf[0] = s->mode;
217
    buf[5] = 0x00 | (b & 0xf0);
218
    buf[1] = s->x & 0xff;
219
    buf[2] = s->x >> 8;
220
    buf[3] = s->y & 0xff;
221
    buf[4] = s->y >> 8;
222
    if (b & 0x3f) {
223
        buf[6] = 0;
224
    } else {
225
        buf[6] = (unsigned char) -127;
226
    }
227

    
228
    return 7;
229
}
230

    
231
static void usb_wacom_handle_reset(USBDevice *dev)
232
{
233
    USBWacomState *s = (USBWacomState *) dev;
234

    
235
    s->dx = 0;
236
    s->dy = 0;
237
    s->dz = 0;
238
    s->x = 0;
239
    s->y = 0;
240
    s->buttons_state = 0;
241
    s->mode = WACOM_MODE_HID;
242
}
243

    
244
static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
245
                                    int index, int length, uint8_t *data)
246
{
247
    USBWacomState *s = (USBWacomState *) dev;
248
    int ret = 0;
249

    
250
    switch (request) {
251
    case DeviceRequest | USB_REQ_GET_STATUS:
252
        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
253
            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
254
        data[1] = 0x00;
255
        ret = 2;
256
        break;
257
    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
258
        if (value == USB_DEVICE_REMOTE_WAKEUP) {
259
            dev->remote_wakeup = 0;
260
        } else {
261
            goto fail;
262
        }
263
        ret = 0;
264
        break;
265
    case DeviceOutRequest | USB_REQ_SET_FEATURE:
266
        if (value == USB_DEVICE_REMOTE_WAKEUP) {
267
            dev->remote_wakeup = 1;
268
        } else {
269
            goto fail;
270
        }
271
        ret = 0;
272
        break;
273
    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
274
        dev->addr = value;
275
        ret = 0;
276
        break;
277
    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
278
        switch (value >> 8) {
279
        case USB_DT_DEVICE:
280
            memcpy(data, qemu_wacom_dev_descriptor,
281
                   sizeof(qemu_wacom_dev_descriptor));
282
            ret = sizeof(qemu_wacom_dev_descriptor);
283
            break;
284
        case USB_DT_CONFIG:
285
                   memcpy(data, qemu_wacom_config_descriptor,
286
                   sizeof(qemu_wacom_config_descriptor));
287
            ret = sizeof(qemu_wacom_config_descriptor);
288
            break;
289
        case USB_DT_STRING:
290
            switch (value & 0xff) {
291
            case 0:
292
                /* language ids */
293
                data[0] = 4;
294
                data[1] = 3;
295
                data[2] = 0x09;
296
                data[3] = 0x04;
297
                ret = 4;
298
                break;
299
            case 1:
300
                /* serial number */
301
                ret = set_usb_string(data, "1");
302
                break;
303
            case 2:
304
                ret = set_usb_string(data, "Wacom PenPartner");
305
                break;
306
            case 3:
307
                /* vendor description */
308
                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
309
                break;
310
            case 4:
311
                ret = set_usb_string(data, "Wacom Tablet");
312
                break;
313
            case 5:
314
                ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
315
                break;
316
            default:
317
                goto fail;
318
            }
319
            break;
320
        default:
321
            goto fail;
322
        }
323
        break;
324
    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
325
        data[0] = 1;
326
        ret = 1;
327
        break;
328
    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
329
        ret = 0;
330
        break;
331
    case DeviceRequest | USB_REQ_GET_INTERFACE:
332
        data[0] = 0;
333
        ret = 1;
334
        break;
335
    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
336
        ret = 0;
337
        break;
338
    case WACOM_SET_REPORT:
339
        if (s->mouse_grabbed) {
340
            qemu_remove_mouse_event_handler(s->eh_entry);
341
            s->mouse_grabbed = 0;
342
        }
343
        s->mode = data[0];
344
        ret = 0;
345
        break;
346
    case WACOM_GET_REPORT:
347
        data[0] = 0;
348
        data[1] = s->mode;
349
        ret = 2;
350
        break;
351
    /* USB HID requests */
352
    case HID_GET_REPORT:
353
        if (s->mode == WACOM_MODE_HID)
354
            ret = usb_mouse_poll(s, data, length);
355
        else if (s->mode == WACOM_MODE_WACOM)
356
            ret = usb_wacom_poll(s, data, length);
357
        break;
358
    case HID_GET_IDLE:
359
        ret = 1;
360
        data[0] = s->idle;
361
        break;
362
    case HID_SET_IDLE:
363
        s->idle = (uint8_t) (value >> 8);
364
        ret = 0;
365
        break;
366
    default:
367
    fail:
368
        ret = USB_RET_STALL;
369
        break;
370
    }
371
    return ret;
372
}
373

    
374
static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
375
{
376
    USBWacomState *s = (USBWacomState *) dev;
377
    int ret = 0;
378

    
379
    switch (p->pid) {
380
    case USB_TOKEN_IN:
381
        if (p->devep == 1) {
382
            if (!(s->changed || s->idle))
383
                return USB_RET_NAK;
384
            s->changed = 0;
385
            if (s->mode == WACOM_MODE_HID)
386
                ret = usb_mouse_poll(s, p->data, p->len);
387
            else if (s->mode == WACOM_MODE_WACOM)
388
                ret = usb_wacom_poll(s, p->data, p->len);
389
            break;
390
        }
391
        /* Fall through.  */
392
    case USB_TOKEN_OUT:
393
    default:
394
        ret = USB_RET_STALL;
395
        break;
396
    }
397
    return ret;
398
}
399

    
400
static void usb_wacom_handle_destroy(USBDevice *dev)
401
{
402
    USBWacomState *s = (USBWacomState *) dev;
403

    
404
    if (s->mouse_grabbed) {
405
        qemu_remove_mouse_event_handler(s->eh_entry);
406
        s->mouse_grabbed = 0;
407
    }
408
}
409

    
410
static int usb_wacom_initfn(USBDevice *dev)
411
{
412
    USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
413
    s->dev.speed = USB_SPEED_FULL;
414
    s->changed = 1;
415
    return 0;
416
}
417

    
418
static struct USBDeviceInfo wacom_info = {
419
    .product_desc   = "QEMU PenPartner Tablet",
420
    .qdev.name      = "usb-wacom-tablet",
421
    .qdev.desc      = "QEMU PenPartner Tablet",
422
    .usbdevice_name = "wacom-tablet",
423
    .qdev.size      = sizeof(USBWacomState),
424
    .init           = usb_wacom_initfn,
425
    .handle_packet  = usb_generic_handle_packet,
426
    .handle_reset   = usb_wacom_handle_reset,
427
    .handle_control = usb_wacom_handle_control,
428
    .handle_data    = usb_wacom_handle_data,
429
    .handle_destroy = usb_wacom_handle_destroy,
430
};
431

    
432
static void usb_wacom_register_devices(void)
433
{
434
    usb_qdev_register(&wacom_info);
435
}
436
device_init(usb_wacom_register_devices)