|
|
1.1 root 1: /*
2: * Wacom PenPartner USB tablet emulation.
3: *
4: * Copyright (c) 2006 Openedhand Ltd.
5: * Author: Andrzej Zaborowski <[email protected]>
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/hw.h"
29: #include "console.h"
30: #include "hw/usb.h"
31: #include "hw/usb/desc.h"
32:
33: /* Interface requests */
34: #define WACOM_GET_REPORT 0x2101
35: #define WACOM_SET_REPORT 0x2109
36:
37: /* HID interface requests */
38: #define HID_GET_REPORT 0xa101
39: #define HID_GET_IDLE 0xa102
40: #define HID_GET_PROTOCOL 0xa103
41: #define HID_SET_IDLE 0x210a
42: #define HID_SET_PROTOCOL 0x210b
43:
44: typedef struct USBWacomState {
45: USBDevice dev;
46: QEMUPutMouseEntry *eh_entry;
47: int dx, dy, dz, buttons_state;
48: int x, y;
49: int mouse_grabbed;
50: enum {
51: WACOM_MODE_HID = 1,
52: WACOM_MODE_WACOM = 2,
53: } mode;
54: uint8_t idle;
55: int changed;
56: } USBWacomState;
57:
58: enum {
59: STR_MANUFACTURER = 1,
60: STR_PRODUCT,
61: STR_SERIALNUMBER,
62: };
63:
64: static const USBDescStrings desc_strings = {
65: [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
66: [STR_PRODUCT] = "Wacom PenPartner",
67: [STR_SERIALNUMBER] = "1",
68: };
69:
70: static const USBDescIface desc_iface_wacom = {
71: .bInterfaceNumber = 0,
72: .bNumEndpoints = 1,
73: .bInterfaceClass = USB_CLASS_HID,
74: .bInterfaceSubClass = 0x01, /* boot */
75: .bInterfaceProtocol = 0x02,
76: .ndesc = 1,
77: .descs = (USBDescOther[]) {
78: {
79: /* HID descriptor */
80: .data = (uint8_t[]) {
81: 0x09, /* u8 bLength */
82: 0x21, /* u8 bDescriptorType */
83: 0x01, 0x10, /* u16 HID_class */
84: 0x00, /* u8 country_code */
85: 0x01, /* u8 num_descriptors */
86: 0x22, /* u8 type: Report */
87: 0x6e, 0, /* u16 len */
88: },
89: },
90: },
91: .eps = (USBDescEndpoint[]) {
92: {
93: .bEndpointAddress = USB_DIR_IN | 0x01,
94: .bmAttributes = USB_ENDPOINT_XFER_INT,
95: .wMaxPacketSize = 8,
96: .bInterval = 0x0a,
97: },
98: },
99: };
100:
101: static const USBDescDevice desc_device_wacom = {
102: .bcdUSB = 0x0110,
103: .bMaxPacketSize0 = 8,
104: .bNumConfigurations = 1,
105: .confs = (USBDescConfig[]) {
106: {
107: .bNumInterfaces = 1,
108: .bConfigurationValue = 1,
109: .bmAttributes = 0x80,
110: .bMaxPower = 40,
111: .nif = 1,
112: .ifs = &desc_iface_wacom,
113: },
114: },
115: };
116:
117: static const USBDesc desc_wacom = {
118: .id = {
119: .idVendor = 0x056a,
120: .idProduct = 0x0000,
121: .bcdDevice = 0x4210,
122: .iManufacturer = STR_MANUFACTURER,
123: .iProduct = STR_PRODUCT,
124: .iSerialNumber = STR_SERIALNUMBER,
125: },
126: .full = &desc_device_wacom,
127: .str = desc_strings,
128: };
129:
130: static void usb_mouse_event(void *opaque,
131: int dx1, int dy1, int dz1, int buttons_state)
132: {
133: USBWacomState *s = opaque;
134:
135: s->dx += dx1;
136: s->dy += dy1;
137: s->dz += dz1;
138: s->buttons_state = buttons_state;
139: s->changed = 1;
140: }
141:
142: static void usb_wacom_event(void *opaque,
143: int x, int y, int dz, int buttons_state)
144: {
145: USBWacomState *s = opaque;
146:
147: /* scale to Penpartner resolution */
148: s->x = (x * 5040 / 0x7FFF);
149: s->y = (y * 3780 / 0x7FFF);
150: s->dz += dz;
151: s->buttons_state = buttons_state;
152: s->changed = 1;
153: }
154:
155: static inline int int_clamp(int val, int vmin, int vmax)
156: {
157: if (val < vmin)
158: return vmin;
159: else if (val > vmax)
160: return vmax;
161: else
162: return val;
163: }
164:
165: static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
166: {
167: int dx, dy, dz, b, l;
168:
169: if (!s->mouse_grabbed) {
170: s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
171: "QEMU PenPartner tablet");
172: qemu_activate_mouse_event_handler(s->eh_entry);
173: s->mouse_grabbed = 1;
174: }
175:
176: dx = int_clamp(s->dx, -128, 127);
177: dy = int_clamp(s->dy, -128, 127);
178: dz = int_clamp(s->dz, -128, 127);
179:
180: s->dx -= dx;
181: s->dy -= dy;
182: s->dz -= dz;
183:
184: b = 0;
185: if (s->buttons_state & MOUSE_EVENT_LBUTTON)
186: b |= 0x01;
187: if (s->buttons_state & MOUSE_EVENT_RBUTTON)
188: b |= 0x02;
189: if (s->buttons_state & MOUSE_EVENT_MBUTTON)
190: b |= 0x04;
191:
192: buf[0] = b;
193: buf[1] = dx;
194: buf[2] = dy;
195: l = 3;
196: if (len >= 4) {
197: buf[3] = dz;
198: l = 4;
199: }
200: return l;
201: }
202:
203: static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
204: {
205: int b;
206:
207: if (!s->mouse_grabbed) {
208: s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
209: "QEMU PenPartner tablet");
210: qemu_activate_mouse_event_handler(s->eh_entry);
211: s->mouse_grabbed = 1;
212: }
213:
214: b = 0;
215: if (s->buttons_state & MOUSE_EVENT_LBUTTON)
216: b |= 0x01;
217: if (s->buttons_state & MOUSE_EVENT_RBUTTON)
218: b |= 0x40;
219: if (s->buttons_state & MOUSE_EVENT_MBUTTON)
220: b |= 0x20; /* eraser */
221:
222: if (len < 7)
223: return 0;
224:
225: buf[0] = s->mode;
226: buf[5] = 0x00 | (b & 0xf0);
227: buf[1] = s->x & 0xff;
228: buf[2] = s->x >> 8;
229: buf[3] = s->y & 0xff;
230: buf[4] = s->y >> 8;
231: if (b & 0x3f) {
232: buf[6] = 0;
233: } else {
234: buf[6] = (unsigned char) -127;
235: }
236:
237: return 7;
238: }
239:
240: static void usb_wacom_handle_reset(USBDevice *dev)
241: {
242: USBWacomState *s = (USBWacomState *) dev;
243:
244: s->dx = 0;
245: s->dy = 0;
246: s->dz = 0;
247: s->x = 0;
248: s->y = 0;
249: s->buttons_state = 0;
250: s->mode = WACOM_MODE_HID;
251: }
252:
253: static int usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
254: int request, int value, int index, int length, uint8_t *data)
255: {
256: USBWacomState *s = (USBWacomState *) dev;
257: int ret;
258:
259: ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
260: if (ret >= 0) {
261: return ret;
262: }
263:
264: ret = 0;
265: switch (request) {
266: case WACOM_SET_REPORT:
267: if (s->mouse_grabbed) {
268: qemu_remove_mouse_event_handler(s->eh_entry);
269: s->mouse_grabbed = 0;
270: }
271: s->mode = data[0];
272: ret = 0;
273: break;
274: case WACOM_GET_REPORT:
275: data[0] = 0;
276: data[1] = s->mode;
277: ret = 2;
278: break;
279: /* USB HID requests */
280: case HID_GET_REPORT:
281: if (s->mode == WACOM_MODE_HID)
282: ret = usb_mouse_poll(s, data, length);
283: else if (s->mode == WACOM_MODE_WACOM)
284: ret = usb_wacom_poll(s, data, length);
285: break;
286: case HID_GET_IDLE:
287: ret = 1;
288: data[0] = s->idle;
289: break;
290: case HID_SET_IDLE:
291: s->idle = (uint8_t) (value >> 8);
292: ret = 0;
293: break;
294: default:
295: ret = USB_RET_STALL;
296: break;
297: }
298: return ret;
299: }
300:
301: static int usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
302: {
303: USBWacomState *s = (USBWacomState *) dev;
304: uint8_t buf[p->iov.size];
305: int ret = 0;
306:
307: switch (p->pid) {
308: case USB_TOKEN_IN:
309: if (p->ep->nr == 1) {
310: if (!(s->changed || s->idle))
311: return USB_RET_NAK;
312: s->changed = 0;
313: if (s->mode == WACOM_MODE_HID)
314: ret = usb_mouse_poll(s, buf, p->iov.size);
315: else if (s->mode == WACOM_MODE_WACOM)
316: ret = usb_wacom_poll(s, buf, p->iov.size);
317: usb_packet_copy(p, buf, ret);
318: break;
319: }
320: /* Fall through. */
321: case USB_TOKEN_OUT:
322: default:
323: ret = USB_RET_STALL;
324: break;
325: }
326: return ret;
327: }
328:
329: static void usb_wacom_handle_destroy(USBDevice *dev)
330: {
331: USBWacomState *s = (USBWacomState *) dev;
332:
333: if (s->mouse_grabbed) {
334: qemu_remove_mouse_event_handler(s->eh_entry);
335: s->mouse_grabbed = 0;
336: }
337: }
338:
339: static int usb_wacom_initfn(USBDevice *dev)
340: {
341: USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
342: usb_desc_create_serial(dev);
343: usb_desc_init(dev);
344: s->changed = 1;
345: return 0;
346: }
347:
348: static const VMStateDescription vmstate_usb_wacom = {
349: .name = "usb-wacom",
350: .unmigratable = 1,
351: };
352:
353: static void usb_wacom_class_init(ObjectClass *klass, void *data)
354: {
355: DeviceClass *dc = DEVICE_CLASS(klass);
356: USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
357:
358: uc->product_desc = "QEMU PenPartner Tablet";
359: uc->usb_desc = &desc_wacom;
360: uc->init = usb_wacom_initfn;
361: uc->handle_reset = usb_wacom_handle_reset;
362: uc->handle_control = usb_wacom_handle_control;
363: uc->handle_data = usb_wacom_handle_data;
364: uc->handle_destroy = usb_wacom_handle_destroy;
365: dc->desc = "QEMU PenPartner Tablet";
366: dc->vmsd = &vmstate_usb_wacom;
367: }
368:
369: static TypeInfo wacom_info = {
370: .name = "usb-wacom-tablet",
371: .parent = TYPE_USB_DEVICE,
372: .instance_size = sizeof(USBWacomState),
373: .class_init = usb_wacom_class_init,
374: };
375:
376: static void usb_wacom_register_types(void)
377: {
378: type_register_static(&wacom_info);
379: usb_legacy_register("usb-wacom-tablet", "wacom-tablet", NULL);
380: }
381:
382: type_init(usb_wacom_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.