dev-wacom.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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 "hw/usb/hid.h"
  32. #include "migration/vmstate.h"
  33. #include "qemu/module.h"
  34. #include "desc.h"
  35. #include "qom/object.h"
  36. /* Interface requests */
  37. #define WACOM_GET_REPORT 0x2101
  38. #define WACOM_SET_REPORT 0x2109
  39. struct USBWacomState {
  40. USBDevice dev;
  41. USBEndpoint *intr;
  42. QEMUPutMouseEntry *eh_entry;
  43. int dx, dy, dz, buttons_state;
  44. int x, y;
  45. int mouse_grabbed;
  46. enum {
  47. WACOM_MODE_HID = 1,
  48. WACOM_MODE_WACOM = 2,
  49. } mode;
  50. uint8_t idle;
  51. int changed;
  52. };
  53. #define TYPE_USB_WACOM "usb-wacom-tablet"
  54. OBJECT_DECLARE_SIMPLE_TYPE(USBWacomState, USB_WACOM)
  55. enum {
  56. STR_MANUFACTURER = 1,
  57. STR_PRODUCT,
  58. STR_SERIALNUMBER,
  59. };
  60. static const USBDescStrings desc_strings = {
  61. [STR_MANUFACTURER] = "QEMU",
  62. [STR_PRODUCT] = "Wacom PenPartner",
  63. [STR_SERIALNUMBER] = "1",
  64. };
  65. static const uint8_t qemu_wacom_hid_report_descriptor[] = {
  66. 0x05, 0x01, /* Usage Page (Desktop) */
  67. 0x09, 0x02, /* Usage (Mouse) */
  68. 0xa1, 0x01, /* Collection (Application) */
  69. 0x85, 0x01, /* Report ID (1) */
  70. 0x09, 0x01, /* Usage (Pointer) */
  71. 0xa1, 0x00, /* Collection (Physical) */
  72. 0x05, 0x09, /* Usage Page (Button) */
  73. 0x19, 0x01, /* Usage Minimum (01h) */
  74. 0x29, 0x03, /* Usage Maximum (03h) */
  75. 0x15, 0x00, /* Logical Minimum (0) */
  76. 0x25, 0x01, /* Logical Maximum (1) */
  77. 0x95, 0x03, /* Report Count (3) */
  78. 0x75, 0x01, /* Report Size (1) */
  79. 0x81, 0x02, /* Input (Data, Variable, Absolute) */
  80. 0x95, 0x01, /* Report Count (1) */
  81. 0x75, 0x05, /* Report Size (5) */
  82. 0x81, 0x01, /* Input (Constant) */
  83. 0x05, 0x01, /* Usage Page (Desktop) */
  84. 0x09, 0x30, /* Usage (X) */
  85. 0x09, 0x31, /* Usage (Y) */
  86. 0x09, 0x38, /* Usage (Wheel) */
  87. 0x15, 0x81, /* Logical Minimum (-127) */
  88. 0x25, 0x7f, /* Logical Maximum (127) */
  89. 0x75, 0x08, /* Report Size (8) */
  90. 0x95, 0x03, /* Report Count (3) */
  91. 0x81, 0x06, /* Input (Data, Variable, Relative) */
  92. 0x95, 0x03, /* Report Count (3) */
  93. 0x81, 0x01, /* Input (Constant) */
  94. 0xc0, /* End Collection */
  95. 0xc0, /* End Collection */
  96. 0x05, 0x0d, /* Usage Page (Digitizer) */
  97. 0x09, 0x01, /* Usage (Digitizer) */
  98. 0xa1, 0x01, /* Collection (Application) */
  99. 0x85, 0x02, /* Report ID (2) */
  100. 0xa1, 0x00, /* Collection (Physical) */
  101. 0x06, 0x00, 0xff,/* Usage Page (ff00h), vendor-defined */
  102. 0x09, 0x01, /* Usage (01h) */
  103. 0x15, 0x00, /* Logical Minimum (0) */
  104. 0x26, 0xff, 0x00,/* Logical Maximum (255) */
  105. 0x75, 0x08, /* Report Size (8) */
  106. 0x95, 0x07, /* Report Count (7) */
  107. 0x81, 0x02, /* Input (Data, Variable, Absolute) */
  108. 0xc0, /* End Collection */
  109. 0x09, 0x01, /* Usage (01h) */
  110. 0x85, 0x63, /* Report ID (99) */
  111. 0x95, 0x07, /* Report Count (7) */
  112. 0x81, 0x02, /* Input (Data, Variable, Absolute) */
  113. 0x09, 0x01, /* Usage (01h) */
  114. 0x85, 0x02, /* Report ID (2) */
  115. 0x95, 0x01, /* Report Count (1) */
  116. 0xb1, 0x02, /* Feature (Variable) */
  117. 0x09, 0x01, /* Usage (01h) */
  118. 0x85, 0x03, /* Report ID (3) */
  119. 0x95, 0x01, /* Report Count (1) */
  120. 0xb1, 0x02, /* Feature (Variable) */
  121. 0xc0 /* End Collection */
  122. };
  123. static const USBDescIface desc_iface_wacom = {
  124. .bInterfaceNumber = 0,
  125. .bNumEndpoints = 1,
  126. .bInterfaceClass = USB_CLASS_HID,
  127. .bInterfaceSubClass = 0x01, /* boot */
  128. .bInterfaceProtocol = 0x02,
  129. .ndesc = 1,
  130. .descs = (USBDescOther[]) {
  131. {
  132. /* HID descriptor */
  133. .data = (uint8_t[]) {
  134. 0x09, /* u8 bLength */
  135. USB_DT_HID, /* u8 bDescriptorType */
  136. 0x01, 0x10, /* u16 HID_class */
  137. 0x00, /* u8 country_code */
  138. 0x01, /* u8 num_descriptors */
  139. USB_DT_REPORT, /* u8 type: Report */
  140. sizeof(qemu_wacom_hid_report_descriptor), 0, /* u16 len */
  141. },
  142. },
  143. },
  144. .eps = (USBDescEndpoint[]) {
  145. {
  146. .bEndpointAddress = USB_DIR_IN | 0x01,
  147. .bmAttributes = USB_ENDPOINT_XFER_INT,
  148. .wMaxPacketSize = 8,
  149. .bInterval = 0x0a,
  150. },
  151. },
  152. };
  153. static const USBDescDevice desc_device_wacom = {
  154. .bcdUSB = 0x0110,
  155. .bMaxPacketSize0 = 8,
  156. .bNumConfigurations = 1,
  157. .confs = (USBDescConfig[]) {
  158. {
  159. .bNumInterfaces = 1,
  160. .bConfigurationValue = 1,
  161. .bmAttributes = USB_CFG_ATT_ONE,
  162. .bMaxPower = 40,
  163. .nif = 1,
  164. .ifs = &desc_iface_wacom,
  165. },
  166. },
  167. };
  168. static const USBDesc desc_wacom = {
  169. .id = {
  170. .idVendor = 0x056a,
  171. .idProduct = 0x0000,
  172. .bcdDevice = 0x4210,
  173. .iManufacturer = STR_MANUFACTURER,
  174. .iProduct = STR_PRODUCT,
  175. .iSerialNumber = STR_SERIALNUMBER,
  176. },
  177. .full = &desc_device_wacom,
  178. .str = desc_strings,
  179. };
  180. static void usb_mouse_event(void *opaque,
  181. int dx1, int dy1, int dz1, int buttons_state)
  182. {
  183. USBWacomState *s = opaque;
  184. s->dx += dx1;
  185. s->dy += dy1;
  186. s->dz += dz1;
  187. s->buttons_state = buttons_state;
  188. s->changed = 1;
  189. usb_wakeup(s->intr, 0);
  190. }
  191. static void usb_wacom_event(void *opaque,
  192. int x, int y, int dz, int buttons_state)
  193. {
  194. USBWacomState *s = opaque;
  195. /* scale to Penpartner resolution */
  196. s->x = (x * 5040 / 0x7FFF);
  197. s->y = (y * 3780 / 0x7FFF);
  198. s->dz += dz;
  199. s->buttons_state = buttons_state;
  200. s->changed = 1;
  201. usb_wakeup(s->intr, 0);
  202. }
  203. static inline int int_clamp(int val, int vmin, int vmax)
  204. {
  205. if (val < vmin)
  206. return vmin;
  207. else if (val > vmax)
  208. return vmax;
  209. else
  210. return val;
  211. }
  212. static int usb_mouse_poll(USBWacomState *s, uint8_t *buf, int len)
  213. {
  214. int dx, dy, dz, b, l;
  215. if (!s->mouse_grabbed) {
  216. s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0,
  217. "QEMU PenPartner tablet");
  218. qemu_activate_mouse_event_handler(s->eh_entry);
  219. s->mouse_grabbed = 1;
  220. }
  221. dx = int_clamp(s->dx, -128, 127);
  222. dy = int_clamp(s->dy, -128, 127);
  223. dz = int_clamp(s->dz, -128, 127);
  224. s->dx -= dx;
  225. s->dy -= dy;
  226. s->dz -= dz;
  227. b = 0;
  228. if (s->buttons_state & MOUSE_EVENT_LBUTTON)
  229. b |= 0x01;
  230. if (s->buttons_state & MOUSE_EVENT_RBUTTON)
  231. b |= 0x02;
  232. if (s->buttons_state & MOUSE_EVENT_MBUTTON)
  233. b |= 0x04;
  234. buf[0] = b;
  235. buf[1] = dx;
  236. buf[2] = dy;
  237. l = 3;
  238. if (len >= 4) {
  239. buf[3] = dz;
  240. l = 4;
  241. }
  242. return l;
  243. }
  244. static int usb_wacom_poll(USBWacomState *s, uint8_t *buf, int len)
  245. {
  246. int b;
  247. if (!s->mouse_grabbed) {
  248. s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1,
  249. "QEMU PenPartner tablet");
  250. qemu_activate_mouse_event_handler(s->eh_entry);
  251. s->mouse_grabbed = 1;
  252. }
  253. b = 0;
  254. if (s->buttons_state & MOUSE_EVENT_LBUTTON)
  255. b |= 0x01;
  256. if (s->buttons_state & MOUSE_EVENT_RBUTTON)
  257. b |= 0x40;
  258. if (s->buttons_state & MOUSE_EVENT_MBUTTON)
  259. b |= 0x20; /* eraser */
  260. if (len < 7)
  261. return 0;
  262. buf[0] = s->mode;
  263. buf[5] = 0x00 | (b & 0xf0);
  264. buf[1] = s->x & 0xff;
  265. buf[2] = s->x >> 8;
  266. buf[3] = s->y & 0xff;
  267. buf[4] = s->y >> 8;
  268. if (b & 0x3f) {
  269. buf[6] = 0;
  270. } else {
  271. buf[6] = (unsigned char) -127;
  272. }
  273. return 7;
  274. }
  275. static void usb_wacom_handle_reset(USBDevice *dev)
  276. {
  277. USBWacomState *s = (USBWacomState *) dev;
  278. s->dx = 0;
  279. s->dy = 0;
  280. s->dz = 0;
  281. s->x = 0;
  282. s->y = 0;
  283. s->buttons_state = 0;
  284. s->mode = WACOM_MODE_HID;
  285. }
  286. static void usb_wacom_handle_control(USBDevice *dev, USBPacket *p,
  287. int request, int value, int index, int length, uint8_t *data)
  288. {
  289. USBWacomState *s = (USBWacomState *) dev;
  290. int ret;
  291. ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
  292. if (ret >= 0) {
  293. return;
  294. }
  295. switch (request) {
  296. case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
  297. switch (value >> 8) {
  298. case 0x22:
  299. memcpy(data, qemu_wacom_hid_report_descriptor,
  300. sizeof(qemu_wacom_hid_report_descriptor));
  301. p->actual_length = sizeof(qemu_wacom_hid_report_descriptor);
  302. break;
  303. default:
  304. return;
  305. }
  306. break;
  307. case WACOM_SET_REPORT:
  308. if (s->mouse_grabbed) {
  309. qemu_remove_mouse_event_handler(s->eh_entry);
  310. s->mouse_grabbed = 0;
  311. }
  312. s->mode = data[0];
  313. break;
  314. case WACOM_GET_REPORT:
  315. data[0] = 0;
  316. data[1] = s->mode;
  317. p->actual_length = 2;
  318. break;
  319. /* USB HID requests */
  320. case HID_GET_REPORT:
  321. if (s->mode == WACOM_MODE_HID)
  322. p->actual_length = usb_mouse_poll(s, data, length);
  323. else if (s->mode == WACOM_MODE_WACOM)
  324. p->actual_length = usb_wacom_poll(s, data, length);
  325. break;
  326. case HID_GET_IDLE:
  327. data[0] = s->idle;
  328. p->actual_length = 1;
  329. break;
  330. case HID_SET_IDLE:
  331. s->idle = (uint8_t) (value >> 8);
  332. break;
  333. default:
  334. p->status = USB_RET_STALL;
  335. break;
  336. }
  337. }
  338. static void usb_wacom_handle_data(USBDevice *dev, USBPacket *p)
  339. {
  340. USBWacomState *s = (USBWacomState *) dev;
  341. g_autofree uint8_t *buf = g_malloc(p->iov.size);
  342. int len = 0;
  343. switch (p->pid) {
  344. case USB_TOKEN_IN:
  345. if (p->ep->nr == 1) {
  346. if (!(s->changed || s->idle)) {
  347. p->status = USB_RET_NAK;
  348. return;
  349. }
  350. s->changed = 0;
  351. if (s->mode == WACOM_MODE_HID)
  352. len = usb_mouse_poll(s, buf, p->iov.size);
  353. else if (s->mode == WACOM_MODE_WACOM)
  354. len = usb_wacom_poll(s, buf, p->iov.size);
  355. usb_packet_copy(p, buf, len);
  356. break;
  357. }
  358. /* Fall through. */
  359. case USB_TOKEN_OUT:
  360. default:
  361. p->status = USB_RET_STALL;
  362. }
  363. }
  364. static void usb_wacom_unrealize(USBDevice *dev)
  365. {
  366. USBWacomState *s = (USBWacomState *) dev;
  367. if (s->mouse_grabbed) {
  368. qemu_remove_mouse_event_handler(s->eh_entry);
  369. s->mouse_grabbed = 0;
  370. }
  371. }
  372. static void usb_wacom_realize(USBDevice *dev, Error **errp)
  373. {
  374. USBWacomState *s = USB_WACOM(dev);
  375. usb_desc_create_serial(dev);
  376. usb_desc_init(dev);
  377. s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1);
  378. s->changed = 1;
  379. }
  380. static const VMStateDescription vmstate_usb_wacom = {
  381. .name = "usb-wacom",
  382. .unmigratable = 1,
  383. };
  384. static void usb_wacom_class_init(ObjectClass *klass, void *data)
  385. {
  386. DeviceClass *dc = DEVICE_CLASS(klass);
  387. USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
  388. uc->product_desc = "QEMU PenPartner Tablet";
  389. uc->usb_desc = &desc_wacom;
  390. uc->realize = usb_wacom_realize;
  391. uc->handle_reset = usb_wacom_handle_reset;
  392. uc->handle_control = usb_wacom_handle_control;
  393. uc->handle_data = usb_wacom_handle_data;
  394. uc->unrealize = usb_wacom_unrealize;
  395. set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
  396. dc->desc = "QEMU PenPartner Tablet";
  397. dc->vmsd = &vmstate_usb_wacom;
  398. }
  399. static const TypeInfo wacom_info = {
  400. .name = TYPE_USB_WACOM,
  401. .parent = TYPE_USB_DEVICE,
  402. .instance_size = sizeof(USBWacomState),
  403. .class_init = usb_wacom_class_init,
  404. };
  405. static void usb_wacom_register_types(void)
  406. {
  407. type_register_static(&wacom_info);
  408. usb_legacy_register(TYPE_USB_WACOM, "wacom-tablet", NULL);
  409. }
  410. type_init(usb_wacom_register_types)