2
0

dev-wacom.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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 "qemu/osdep.h"
  29. #include "ui/console.h"
  30. #include "hw/usb.h"
  31. #include "migration/vmstate.h"
  32. #include "qemu/module.h"
  33. #include "desc.h"
  34. /* Interface requests */
  35. #define WACOM_GET_REPORT 0x2101
  36. #define WACOM_SET_REPORT 0x2109
  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. typedef struct USBWacomState {
  44. USBDevice dev;
  45. USBEndpoint *intr;
  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. #define TYPE_USB_WACOM "usb-wacom-tablet"
  58. #define USB_WACOM(obj) OBJECT_CHECK(USBWacomState, (obj), TYPE_USB_WACOM)
  59. enum {
  60. STR_MANUFACTURER = 1,
  61. STR_PRODUCT,
  62. STR_SERIALNUMBER,
  63. };
  64. static const USBDescStrings desc_strings = {
  65. [STR_MANUFACTURER] = "QEMU",
  66. [STR_PRODUCT] = "Wacom PenPartner",
  67. [STR_SERIALNUMBER] = "1",
  68. };
  69. static const USBDescIface desc_iface_wacom = {
  70. .bInterfaceNumber = 0,
  71. .bNumEndpoints = 1,
  72. .bInterfaceClass = USB_CLASS_HID,
  73. .bInterfaceSubClass = 0x01, /* boot */
  74. .bInterfaceProtocol = 0x02,
  75. .ndesc = 1,
  76. .descs = (USBDescOther[]) {
  77. {
  78. /* HID descriptor */
  79. .data = (uint8_t[]) {
  80. 0x09, /* u8 bLength */
  81. 0x21, /* u8 bDescriptorType */
  82. 0x01, 0x10, /* u16 HID_class */
  83. 0x00, /* u8 country_code */
  84. 0x01, /* u8 num_descriptors */
  85. 0x22, /* u8 type: Report */
  86. 0x6e, 0, /* u16 len */
  87. },
  88. },
  89. },
  90. .eps = (USBDescEndpoint[]) {
  91. {
  92. .bEndpointAddress = USB_DIR_IN | 0x01,
  93. .bmAttributes = USB_ENDPOINT_XFER_INT,
  94. .wMaxPacketSize = 8,
  95. .bInterval = 0x0a,
  96. },
  97. },
  98. };
  99. static const USBDescDevice desc_device_wacom = {
  100. .bcdUSB = 0x0110,
  101. .bMaxPacketSize0 = 8,
  102. .bNumConfigurations = 1,
  103. .confs = (USBDescConfig[]) {
  104. {
  105. .bNumInterfaces = 1,
  106. .bConfigurationValue = 1,
  107. .bmAttributes = USB_CFG_ATT_ONE,
  108. .bMaxPower = 40,
  109. .nif = 1,
  110. .ifs = &desc_iface_wacom,
  111. },
  112. },
  113. };
  114. static const USBDesc desc_wacom = {
  115. .id = {
  116. .idVendor = 0x056a,
  117. .idProduct = 0x0000,
  118. .bcdDevice = 0x4210,
  119. .iManufacturer = STR_MANUFACTURER,
  120. .iProduct = STR_PRODUCT,
  121. .iSerialNumber = STR_SERIALNUMBER,
  122. },
  123. .full = &desc_device_wacom,
  124. .str = desc_strings,
  125. };
  126. static void usb_mouse_event(void *opaque,
  127. int dx1, int dy1, int dz1, int buttons_state)
  128. {
  129. USBWacomState *s = opaque;
  130. s->dx += dx1;
  131. s->dy += dy1;
  132. s->dz += dz1;
  133. s->buttons_state = buttons_state;
  134. s->changed = 1;
  135. usb_wakeup(s->intr, 0);
  136. }
  137. static void usb_wacom_event(void *opaque,
  138. int x, int y, int dz, int buttons_state)
  139. {
  140. USBWacomState *s = opaque;
  141. /* scale to Penpartner resolution */
  142. s->x = (x * 5040 / 0x7FFF);
  143. s->y = (y * 3780 / 0x7FFF);
  144. s->dz += dz;
  145. s->buttons_state = buttons_state;
  146. s->changed = 1;
  147. usb_wakeup(s->intr, 0);
  148. }
  149. static inline int int_clamp(int val, int vmin, int vmax)
  150. {
  151. if (val < vmin)
  152. return vmin;
  153. else if (val > vmax)
  154. return vmax;
  155. else
  156. return val;
  157. }
  158. static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
  159. {
  160. int dx, dy, dz, b, l;
  161. if (!s->mouse_grabbed) {
  162. s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
  163. "QEMU PenPartner tablet");
  164. qemu_activate_mouse_event_handler(s->eh_entry);
  165. s->mouse_grabbed = 1;
  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. s->dx -= dx;
  171. s->dy -= dy;
  172. s->dz -= dz;
  173. b = 0;
  174. if (s->buttons_state & MOUSE_EVENT_LBUTTON)
  175. b |= 0x01;
  176. if (s->buttons_state & MOUSE_EVENT_RBUTTON)
  177. b |= 0x02;
  178. if (s->buttons_state & MOUSE_EVENT_MBUTTON)
  179. b |= 0x04;
  180. buf[0] = b;
  181. buf[1] = dx;
  182. buf[2] = dy;
  183. l = 3;
  184. if (len >= 4) {
  185. buf[3] = dz;
  186. l = 4;
  187. }
  188. return l;
  189. }
  190. static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
  191. {
  192. int b;
  193. if (!s->mouse_grabbed) {
  194. s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
  195. "QEMU PenPartner tablet");
  196. qemu_activate_mouse_event_handler(s->eh_entry);
  197. s->mouse_grabbed = 1;
  198. }
  199. b = 0;
  200. if (s->buttons_state & MOUSE_EVENT_LBUTTON)
  201. b |= 0x01;
  202. if (s->buttons_state & MOUSE_EVENT_RBUTTON)
  203. b |= 0x40;
  204. if (s->buttons_state & MOUSE_EVENT_MBUTTON)
  205. b |= 0x20; /* eraser */
  206. if (len < 7)
  207. return 0;
  208. buf[0] = s->mode;
  209. buf[5] = 0x00 | (b & 0xf0);
  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. if (b & 0x3f) {
  215. buf[6] = 0;
  216. } else {
  217. buf[6] = (unsigned char) -127;
  218. }
  219. return 7;
  220. }
  221. static void usb_wacom_handle_reset(USBDevice *dev)
  222. {
  223. USBWacomState *s = (USBWacomState *) dev;
  224. s->dx = 0;
  225. s->dy = 0;
  226. s->dz = 0;
  227. s->x = 0;
  228. s->y = 0;
  229. s->buttons_state = 0;
  230. s->mode = WACOM_MODE_HID;
  231. }
  232. static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
  233. int request, int value, int index, int length, uint8_t *data)
  234. {
  235. USBWacomState *s = (USBWacomState *) dev;
  236. int ret;
  237. ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
  238. if (ret >= 0) {
  239. return;
  240. }
  241. switch (request) {
  242. case WACOM_SET_REPORT:
  243. if (s->mouse_grabbed) {
  244. qemu_remove_mouse_event_handler(s->eh_entry);
  245. s->mouse_grabbed = 0;
  246. }
  247. s->mode = data[0];
  248. break;
  249. case WACOM_GET_REPORT:
  250. data[0] = 0;
  251. data[1] = s->mode;
  252. p->actual_length = 2;
  253. break;
  254. /* USB HID requests */
  255. case HID_GET_REPORT:
  256. if (s->mode == WACOM_MODE_HID)
  257. p->actual_length = usb_mouse_poll(s, data, length);
  258. else if (s->mode == WACOM_MODE_WACOM)
  259. p->actual_length = usb_wacom_poll(s, data, length);
  260. break;
  261. case HID_GET_IDLE:
  262. data[0] = s->idle;
  263. p->actual_length = 1;
  264. break;
  265. case HID_SET_IDLE:
  266. s->idle = (uint8_t) (value >> 8);
  267. break;
  268. default:
  269. p->status = USB_RET_STALL;
  270. break;
  271. }
  272. }
  273. static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
  274. {
  275. USBWacomState *s = (USBWacomState *) dev;
  276. uint8_t buf[p->iov.size];
  277. int len = 0;
  278. switch (p->pid) {
  279. case USB_TOKEN_IN:
  280. if (p->ep->nr == 1) {
  281. if (!(s->changed || s->idle)) {
  282. p->status = USB_RET_NAK;
  283. return;
  284. }
  285. s->changed = 0;
  286. if (s->mode == WACOM_MODE_HID)
  287. len = usb_mouse_poll(s, buf, p->iov.size);
  288. else if (s->mode == WACOM_MODE_WACOM)
  289. len = usb_wacom_poll(s, buf, p->iov.size);
  290. usb_packet_copy(p, buf, len);
  291. break;
  292. }
  293. /* Fall through. */
  294. case USB_TOKEN_OUT:
  295. default:
  296. p->status = USB_RET_STALL;
  297. }
  298. }
  299. static void usb_wacom_unrealize(USBDevice *dev, Error **errp)
  300. {
  301. USBWacomState *s = (USBWacomState *) dev;
  302. if (s->mouse_grabbed) {
  303. qemu_remove_mouse_event_handler(s->eh_entry);
  304. s->mouse_grabbed = 0;
  305. }
  306. }
  307. static void usb_wacom_realize(USBDevice *dev, Error **errp)
  308. {
  309. USBWacomState *s = USB_WACOM(dev);
  310. usb_desc_create_serial(dev);
  311. usb_desc_init(dev);
  312. s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
  313. s->changed = 1;
  314. }
  315. static const VMStateDescription vmstate_usb_wacom = {
  316. .name = "usb-wacom",
  317. .unmigratable = 1,
  318. };
  319. static void usb_wacom_class_init(ObjectClass *klass, void *data)
  320. {
  321. DeviceClass *dc = DEVICE_CLASS(klass);
  322. USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
  323. uc->product_desc = "QEMU PenPartner Tablet";
  324. uc->usb_desc = &desc_wacom;
  325. uc->realize = usb_wacom_realize;
  326. uc->handle_reset = usb_wacom_handle_reset;
  327. uc->handle_control = usb_wacom_handle_control;
  328. uc->handle_data = usb_wacom_handle_data;
  329. uc->unrealize = usb_wacom_unrealize;
  330. set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
  331. dc->desc = "QEMU PenPartner Tablet";
  332. dc->vmsd = &vmstate_usb_wacom;
  333. }
  334. static const TypeInfo wacom_info = {
  335. .name = TYPE_USB_WACOM,
  336. .parent = TYPE_USB_DEVICE,
  337. .instance_size = sizeof(USBWacomState),
  338. .class_init = usb_wacom_class_init,
  339. };
  340. static void usb_wacom_register_types(void)
  341. {
  342. type_register_static(&wacom_info);
  343. usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL);
  344. }
  345. type_init(usb_wacom_register_types)