hid-huion.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * HID driver for Huion devices not fully compliant with HID standard
  3. *
  4. * Copyright (c) 2013 Martin Rusko
  5. * Copyright (c) 2014 Nikolai Kondrashov
  6. */
  7. /*
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the Free
  10. * Software Foundation; either version 2 of the License, or (at your option)
  11. * any later version.
  12. */
  13. #include <linux/device.h>
  14. #include <linux/hid.h>
  15. #include <linux/module.h>
  16. #include <linux/usb.h>
  17. #include <asm/unaligned.h>
  18. #include "usbhid/usbhid.h"
  19. #include "hid-ids.h"
  20. /* Report descriptor template placeholder head */
  21. #define HUION_PH_HEAD 0xFE, 0xED, 0x1D
  22. /* Report descriptor template placeholder IDs */
  23. enum huion_ph_id {
  24. HUION_PH_ID_X_LM,
  25. HUION_PH_ID_X_PM,
  26. HUION_PH_ID_Y_LM,
  27. HUION_PH_ID_Y_PM,
  28. HUION_PH_ID_PRESSURE_LM,
  29. HUION_PH_ID_NUM
  30. };
  31. /* Report descriptor template placeholder */
  32. #define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID
  33. /* Fixed report descriptor template */
  34. static const __u8 huion_tablet_rdesc_template[] = {
  35. 0x05, 0x0D, /* Usage Page (Digitizer), */
  36. 0x09, 0x02, /* Usage (Pen), */
  37. 0xA1, 0x01, /* Collection (Application), */
  38. 0x85, 0x07, /* Report ID (7), */
  39. 0x09, 0x20, /* Usage (Stylus), */
  40. 0xA0, /* Collection (Physical), */
  41. 0x14, /* Logical Minimum (0), */
  42. 0x25, 0x01, /* Logical Maximum (1), */
  43. 0x75, 0x01, /* Report Size (1), */
  44. 0x09, 0x42, /* Usage (Tip Switch), */
  45. 0x09, 0x44, /* Usage (Barrel Switch), */
  46. 0x09, 0x46, /* Usage (Tablet Pick), */
  47. 0x95, 0x03, /* Report Count (3), */
  48. 0x81, 0x02, /* Input (Variable), */
  49. 0x95, 0x03, /* Report Count (3), */
  50. 0x81, 0x03, /* Input (Constant, Variable), */
  51. 0x09, 0x32, /* Usage (In Range), */
  52. 0x95, 0x01, /* Report Count (1), */
  53. 0x81, 0x02, /* Input (Variable), */
  54. 0x95, 0x01, /* Report Count (1), */
  55. 0x81, 0x03, /* Input (Constant, Variable), */
  56. 0x75, 0x10, /* Report Size (16), */
  57. 0x95, 0x01, /* Report Count (1), */
  58. 0xA4, /* Push, */
  59. 0x05, 0x01, /* Usage Page (Desktop), */
  60. 0x65, 0x13, /* Unit (Inch), */
  61. 0x55, 0xFD, /* Unit Exponent (-3), */
  62. 0x34, /* Physical Minimum (0), */
  63. 0x09, 0x30, /* Usage (X), */
  64. 0x27, HUION_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */
  65. 0x47, HUION_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */
  66. 0x81, 0x02, /* Input (Variable), */
  67. 0x09, 0x31, /* Usage (Y), */
  68. 0x27, HUION_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */
  69. 0x47, HUION_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */
  70. 0x81, 0x02, /* Input (Variable), */
  71. 0xB4, /* Pop, */
  72. 0x09, 0x30, /* Usage (Tip Pressure), */
  73. 0x27,
  74. HUION_PH(PRESSURE_LM), /* Logical Maximum (PLACEHOLDER), */
  75. 0x81, 0x02, /* Input (Variable), */
  76. 0xC0, /* End Collection, */
  77. 0xC0 /* End Collection */
  78. };
  79. /* Parameter indices */
  80. enum huion_prm {
  81. HUION_PRM_X_LM = 1,
  82. HUION_PRM_Y_LM = 2,
  83. HUION_PRM_PRESSURE_LM = 4,
  84. HUION_PRM_RESOLUTION = 5,
  85. HUION_PRM_NUM
  86. };
  87. /* Driver data */
  88. struct huion_drvdata {
  89. __u8 *rdesc;
  90. unsigned int rsize;
  91. };
  92. static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc,
  93. unsigned int *rsize)
  94. {
  95. struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
  96. switch (hdev->product) {
  97. case USB_DEVICE_ID_HUION_TABLET:
  98. if (drvdata->rdesc != NULL) {
  99. rdesc = drvdata->rdesc;
  100. *rsize = drvdata->rsize;
  101. }
  102. break;
  103. }
  104. return rdesc;
  105. }
  106. /**
  107. * Enable fully-functional tablet mode and determine device parameters.
  108. *
  109. * @hdev: HID device
  110. */
  111. static int huion_tablet_enable(struct hid_device *hdev)
  112. {
  113. int rc;
  114. struct usb_device *usb_dev = hid_to_usb_dev(hdev);
  115. struct huion_drvdata *drvdata = hid_get_drvdata(hdev);
  116. __le16 *buf = NULL;
  117. size_t len;
  118. s32 params[HUION_PH_ID_NUM];
  119. s32 resolution;
  120. __u8 *p;
  121. s32 v;
  122. /*
  123. * Read string descriptor containing tablet parameters. The specific
  124. * string descriptor and data were discovered by sniffing the Windows
  125. * driver traffic.
  126. * NOTE: This enables fully-functional tablet mode.
  127. */
  128. len = HUION_PRM_NUM * sizeof(*buf);
  129. buf = kmalloc(len, GFP_KERNEL);
  130. if (buf == NULL) {
  131. hid_err(hdev, "failed to allocate parameter buffer\n");
  132. rc = -ENOMEM;
  133. goto cleanup;
  134. }
  135. rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
  136. USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
  137. (USB_DT_STRING << 8) + 0x64,
  138. 0x0409, buf, len,
  139. USB_CTRL_GET_TIMEOUT);
  140. if (rc == -EPIPE) {
  141. hid_err(hdev, "device parameters not found\n");
  142. rc = -ENODEV;
  143. goto cleanup;
  144. } else if (rc < 0) {
  145. hid_err(hdev, "failed to get device parameters: %d\n", rc);
  146. rc = -ENODEV;
  147. goto cleanup;
  148. } else if (rc != len) {
  149. hid_err(hdev, "invalid device parameters\n");
  150. rc = -ENODEV;
  151. goto cleanup;
  152. }
  153. /* Extract device parameters */
  154. params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[HUION_PRM_X_LM]);
  155. params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[HUION_PRM_Y_LM]);
  156. params[HUION_PH_ID_PRESSURE_LM] =
  157. le16_to_cpu(buf[HUION_PRM_PRESSURE_LM]);
  158. resolution = le16_to_cpu(buf[HUION_PRM_RESOLUTION]);
  159. if (resolution == 0) {
  160. params[HUION_PH_ID_X_PM] = 0;
  161. params[HUION_PH_ID_Y_PM] = 0;
  162. } else {
  163. params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] *
  164. 1000 / resolution;
  165. params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] *
  166. 1000 / resolution;
  167. }
  168. /* Allocate fixed report descriptor */
  169. drvdata->rdesc = devm_kmalloc(&hdev->dev,
  170. sizeof(huion_tablet_rdesc_template),
  171. GFP_KERNEL);
  172. if (drvdata->rdesc == NULL) {
  173. hid_err(hdev, "failed to allocate fixed rdesc\n");
  174. rc = -ENOMEM;
  175. goto cleanup;
  176. }
  177. drvdata->rsize = sizeof(huion_tablet_rdesc_template);
  178. /* Format fixed report descriptor */
  179. memcpy(drvdata->rdesc, huion_tablet_rdesc_template,
  180. drvdata->rsize);
  181. for (p = drvdata->rdesc;
  182. p <= drvdata->rdesc + drvdata->rsize - 4;) {
  183. if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D &&
  184. p[3] < sizeof(params)) {
  185. v = params[p[3]];
  186. put_unaligned(cpu_to_le32(v), (s32 *)p);
  187. p += 4;
  188. } else {
  189. p++;
  190. }
  191. }
  192. rc = 0;
  193. cleanup:
  194. kfree(buf);
  195. return rc;
  196. }
  197. static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id)
  198. {
  199. int rc;
  200. struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  201. struct huion_drvdata *drvdata;
  202. /* Allocate and assign driver data */
  203. drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
  204. if (drvdata == NULL) {
  205. hid_err(hdev, "failed to allocate driver data\n");
  206. return -ENOMEM;
  207. }
  208. hid_set_drvdata(hdev, drvdata);
  209. switch (id->product) {
  210. case USB_DEVICE_ID_HUION_TABLET:
  211. /* If this is the pen interface */
  212. if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
  213. rc = huion_tablet_enable(hdev);
  214. if (rc) {
  215. hid_err(hdev, "tablet enabling failed\n");
  216. return rc;
  217. }
  218. }
  219. break;
  220. }
  221. rc = hid_parse(hdev);
  222. if (rc) {
  223. hid_err(hdev, "parse failed\n");
  224. return rc;
  225. }
  226. rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  227. if (rc) {
  228. hid_err(hdev, "hw start failed\n");
  229. return rc;
  230. }
  231. return 0;
  232. }
  233. static int huion_raw_event(struct hid_device *hdev, struct hid_report *report,
  234. u8 *data, int size)
  235. {
  236. struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  237. /* If this is a pen input report */
  238. if (intf->cur_altsetting->desc.bInterfaceNumber == 0 &&
  239. report->type == HID_INPUT_REPORT &&
  240. report->id == 0x07 && size >= 2)
  241. /* Invert the in-range bit */
  242. data[1] ^= 0x40;
  243. return 0;
  244. }
  245. static const struct hid_device_id huion_devices[] = {
  246. { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
  247. { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
  248. { }
  249. };
  250. MODULE_DEVICE_TABLE(hid, huion_devices);
  251. static struct hid_driver huion_driver = {
  252. .name = "huion",
  253. .id_table = huion_devices,
  254. .probe = huion_probe,
  255. .report_fixup = huion_report_fixup,
  256. .raw_event = huion_raw_event,
  257. };
  258. module_hid_driver(huion_driver);
  259. MODULE_AUTHOR("Martin Rusko");
  260. MODULE_DESCRIPTION("Huion HID driver");
  261. MODULE_LICENSE("GPL");