dev-wacom.c 10 KB

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