aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNikolai Kondrashov <spbnick@gmail.com>2019-02-10 05:13:51 -0500
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>2019-02-21 06:00:53 -0500
commit9614219e9310ef19e66719bf37f9f68919bac08e (patch)
treee658e86c165670b6dbc7e49f44ef460415721d3e
parentff0c13d6d2edc9c4952c668f4503a51b5f101ab3 (diff)
HID: uclogic: Extract tablet parameter discovery into a module
Refactor and extract UC-Logic tablet initialization and parameter discovery into a module. For these tablets, the major part of parameter discovery cannot be separated from initialization so they have to be in the same module. Define explicitly and clearly what possible quirks the tablets may have to make the driver implementation easier and simpler. Signed-off-by: Nikolai Kondrashov <spbnick@gmail.com> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
-rw-r--r--drivers/hid/Makefile3
-rw-r--r--drivers/hid/hid-uclogic-core.c428
-rw-r--r--drivers/hid/hid-uclogic-params.c806
-rw-r--r--drivers/hid/hid-uclogic-params.h180
4 files changed, 1090 insertions, 327 deletions
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fb75366ea776..9b3a747af60d 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -109,7 +109,8 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o
109obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o 109obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
110obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o 110obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
111hid-uclogic-objs := hid-uclogic-core.o \ 111hid-uclogic-objs := hid-uclogic-core.o \
112 hid-uclogic-rdesc.o 112 hid-uclogic-rdesc.o \
113 hid-uclogic-params.o
113obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o 114obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
114obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o 115obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
115obj-$(CONFIG_HID_LED) += hid-led.o 116obj-$(CONFIG_HID_LED) += hid-led.o
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index 4042183ee9a3..72a3a43766cc 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -16,126 +16,48 @@
16#include <linux/device.h> 16#include <linux/device.h>
17#include <linux/hid.h> 17#include <linux/hid.h>
18#include <linux/module.h> 18#include <linux/module.h>
19#include <linux/usb.h>
20#include "usbhid/usbhid.h" 19#include "usbhid/usbhid.h"
21#include "hid-uclogic-rdesc.h" 20#include "hid-uclogic-params.h"
22 21
23#include "hid-ids.h" 22#include "hid-ids.h"
24 23
25/* Parameter indices */
26enum uclogic_prm {
27 UCLOGIC_PRM_X_LM = 1,
28 UCLOGIC_PRM_Y_LM = 2,
29 UCLOGIC_PRM_PRESSURE_LM = 4,
30 UCLOGIC_PRM_RESOLUTION = 5,
31 UCLOGIC_PRM_NUM
32};
33
34/* Driver data */ 24/* Driver data */
35struct uclogic_drvdata { 25struct uclogic_drvdata {
36 __u8 *rdesc; 26 /* Interface parameters */
37 unsigned int rsize; 27 struct uclogic_params params;
38 bool invert_pen_inrange; 28 /* Pointer to the replacement report descriptor. NULL if none. */
39 bool ignore_pen_usage; 29 __u8 *desc_ptr;
40 bool has_virtual_pad_interface; 30 /*
31 * Size of the replacement report descriptor.
32 * Only valid if desc_ptr is not NULL
33 */
34 unsigned int desc_size;
41}; 35};
42 36
43static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, 37static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
44 unsigned int *rsize) 38 unsigned int *rsize)
45{ 39{
46 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
47 __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
48 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 40 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
49 41
50 if (drvdata->rdesc != NULL) { 42 if (drvdata->desc_ptr != NULL) {
51 rdesc = drvdata->rdesc; 43 rdesc = drvdata->desc_ptr;
52 *rsize = drvdata->rsize; 44 *rsize = drvdata->desc_size;
53 return rdesc;
54 }
55
56 switch (hdev->product) {
57 case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
58 if (*rsize == UCLOGIC_RDESC_PF1209_ORIG_SIZE) {
59 rdesc = uclogic_rdesc_pf1209_fixed_arr;
60 *rsize = uclogic_rdesc_pf1209_fixed_size;
61 }
62 break;
63 case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U:
64 if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) {
65 rdesc = uclogic_rdesc_wp4030u_fixed_arr;
66 *rsize = uclogic_rdesc_wp4030u_fixed_size;
67 }
68 break;
69 case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U:
70 if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) {
71 rdesc = uclogic_rdesc_wp5540u_fixed_arr;
72 *rsize = uclogic_rdesc_wp5540u_fixed_size;
73 }
74 break;
75 case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U:
76 if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) {
77 rdesc = uclogic_rdesc_wp8060u_fixed_arr;
78 *rsize = uclogic_rdesc_wp8060u_fixed_size;
79 }
80 break;
81 case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062:
82 if (*rsize == UCLOGIC_RDESC_WP1062_ORIG_SIZE) {
83 rdesc = uclogic_rdesc_wp1062_fixed_arr;
84 *rsize = uclogic_rdesc_wp1062_fixed_size;
85 }
86 break;
87 case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850:
88 switch (iface_num) {
89 case 0:
90 if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG0_SIZE) {
91 rdesc = uclogic_rdesc_twhl850_fixed0_arr;
92 *rsize = uclogic_rdesc_twhl850_fixed0_size;
93 }
94 break;
95 case 1:
96 if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG1_SIZE) {
97 rdesc = uclogic_rdesc_twhl850_fixed1_arr;
98 *rsize = uclogic_rdesc_twhl850_fixed1_size;
99 }
100 break;
101 case 2:
102 if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG2_SIZE) {
103 rdesc = uclogic_rdesc_twhl850_fixed2_arr;
104 *rsize = uclogic_rdesc_twhl850_fixed2_size;
105 }
106 break;
107 }
108 break;
109 case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
110 switch (iface_num) {
111 case 0:
112 if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG0_SIZE) {
113 rdesc = uclogic_rdesc_twha60_fixed0_arr;
114 *rsize = uclogic_rdesc_twha60_fixed0_size;
115 }
116 break;
117 case 1:
118 if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG1_SIZE) {
119 rdesc = uclogic_rdesc_twha60_fixed1_arr;
120 *rsize = uclogic_rdesc_twha60_fixed1_size;
121 }
122 break;
123 }
124 break;
125 } 45 }
126
127 return rdesc; 46 return rdesc;
128} 47}
129 48
130static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, 49static int uclogic_input_mapping(struct hid_device *hdev,
131 struct hid_field *field, struct hid_usage *usage, 50 struct hid_input *hi,
132 unsigned long **bit, int *max) 51 struct hid_field *field,
52 struct hid_usage *usage,
53 unsigned long **bit,
54 int *max)
133{ 55{
134 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 56 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
57 struct uclogic_params *params = &drvdata->params;
135 58
136 /* discard the unused pen interface */ 59 /* discard the unused pen interface */
137 if ((drvdata->ignore_pen_usage) && 60 if (params->pen_unused && (field->application == HID_DG_PEN))
138 (field->application == HID_DG_PEN))
139 return -1; 61 return -1;
140 62
141 /* let hid-core decide what to do */ 63 /* let hid-core decide what to do */
@@ -189,160 +111,12 @@ static int uclogic_input_configured(struct hid_device *hdev,
189 return 0; 111 return 0;
190} 112}
191 113
192/**
193 * Enable fully-functional tablet mode and determine device parameters.
194 *
195 * @hdev: HID device
196 */
197static int uclogic_tablet_enable(struct hid_device *hdev)
198{
199 int rc;
200 struct usb_device *usb_dev = hid_to_usb_dev(hdev);
201 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
202 __le16 *buf = NULL;
203 size_t len;
204 s32 params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
205 s32 resolution;
206
207 /*
208 * Read string descriptor containing tablet parameters. The specific
209 * string descriptor and data were discovered by sniffing the Windows
210 * driver traffic.
211 * NOTE: This enables fully-functional tablet mode.
212 */
213 len = UCLOGIC_PRM_NUM * sizeof(*buf);
214 buf = kmalloc(len, GFP_KERNEL);
215 if (buf == NULL) {
216 rc = -ENOMEM;
217 goto cleanup;
218 }
219 rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
220 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
221 (USB_DT_STRING << 8) + 0x64,
222 0x0409, buf, len,
223 USB_CTRL_GET_TIMEOUT);
224 if (rc == -EPIPE) {
225 hid_err(hdev, "device parameters not found\n");
226 rc = -ENODEV;
227 goto cleanup;
228 } else if (rc < 0) {
229 hid_err(hdev, "failed to get device parameters: %d\n", rc);
230 rc = -ENODEV;
231 goto cleanup;
232 } else if (rc != len) {
233 hid_err(hdev, "invalid device parameters\n");
234 rc = -ENODEV;
235 goto cleanup;
236 }
237
238 /* Extract device parameters */
239 params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
240 le16_to_cpu(buf[UCLOGIC_PRM_X_LM]);
241 params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
242 le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]);
243 params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
244 le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]);
245 resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]);
246 if (resolution == 0) {
247 params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
248 params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
249 } else {
250 params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
251 params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] *
252 1000 / resolution;
253 params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
254 params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] *
255 1000 / resolution;
256 }
257
258 /* Format fixed report descriptor */
259 drvdata->rdesc = uclogic_rdesc_template_apply(
260 uclogic_rdesc_pen_template_arr,
261 uclogic_rdesc_pen_template_size,
262 params, ARRAY_SIZE(params));
263 if (drvdata->rdesc == NULL) {
264 rc = -ENOMEM;
265 goto cleanup;
266 }
267 drvdata->rsize = uclogic_rdesc_pen_template_size;
268
269 rc = 0;
270
271cleanup:
272 kfree(buf);
273 return rc;
274}
275
276/**
277 * Enable actual button mode.
278 *
279 * @hdev: HID device
280 */
281static int uclogic_button_enable(struct hid_device *hdev)
282{
283 int rc;
284 struct usb_device *usb_dev = hid_to_usb_dev(hdev);
285 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
286 char *str_buf;
287 size_t str_len = 16;
288 unsigned char *rdesc;
289 size_t rdesc_len;
290
291 str_buf = kzalloc(str_len, GFP_KERNEL);
292 if (str_buf == NULL) {
293 rc = -ENOMEM;
294 goto cleanup;
295 }
296
297 /* Enable abstract keyboard mode */
298 rc = usb_string(usb_dev, 0x7b, str_buf, str_len);
299 if (rc == -EPIPE) {
300 hid_info(hdev, "button mode setting not found\n");
301 rc = 0;
302 goto cleanup;
303 } else if (rc < 0) {
304 hid_err(hdev, "failed to enable abstract keyboard\n");
305 goto cleanup;
306 } else if (strncmp(str_buf, "HK On", rc)) {
307 hid_info(hdev, "invalid answer when requesting buttons: '%s'\n",
308 str_buf);
309 rc = -EINVAL;
310 goto cleanup;
311 }
312
313 /* Re-allocate fixed report descriptor */
314 rdesc_len = drvdata->rsize + uclogic_rdesc_buttonpad_size;
315 rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL);
316 if (!rdesc) {
317 rc = -ENOMEM;
318 goto cleanup;
319 }
320
321 memcpy(rdesc, drvdata->rdesc, drvdata->rsize);
322
323 /* Append the buttonpad descriptor */
324 memcpy(rdesc + drvdata->rsize, uclogic_rdesc_buttonpad_arr,
325 uclogic_rdesc_buttonpad_size);
326
327 /* clean up old rdesc and use the new one */
328 drvdata->rsize = rdesc_len;
329 devm_kfree(&hdev->dev, drvdata->rdesc);
330 drvdata->rdesc = rdesc;
331
332 rc = 0;
333
334cleanup:
335 kfree(str_buf);
336 return rc;
337}
338
339static int uclogic_probe(struct hid_device *hdev, 114static int uclogic_probe(struct hid_device *hdev,
340 const struct hid_device_id *id) 115 const struct hid_device_id *id)
341{ 116{
342 int rc; 117 int rc;
343 struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 118 struct uclogic_drvdata *drvdata = NULL;
344 struct usb_device *udev = hid_to_usb_dev(hdev); 119 bool params_initialized = false;
345 struct uclogic_drvdata *drvdata;
346 120
347 /* 121 /*
348 * libinput requires the pad interface to be on a different node 122 * libinput requires the pad interface to be on a different node
@@ -352,104 +126,97 @@ static int uclogic_probe(struct hid_device *hdev,
352 126
353 /* Allocate and assign driver data */ 127 /* Allocate and assign driver data */
354 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); 128 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
355 if (drvdata == NULL) 129 if (drvdata == NULL) {
356 return -ENOMEM; 130 rc = -ENOMEM;
357 131 goto failure;
132 }
358 hid_set_drvdata(hdev, drvdata); 133 hid_set_drvdata(hdev, drvdata);
359 134
360 switch (id->product) { 135 /* Initialize the device and retrieve interface parameters */
361 case USB_DEVICE_ID_HUION_TABLET: 136 rc = uclogic_params_init(&drvdata->params, hdev);
362 case USB_DEVICE_ID_YIYNOVA_TABLET: 137 if (rc != 0) {
363 case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: 138 hid_err(hdev, "failed probing parameters: %d\n", rc);
364 case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: 139 goto failure;
365 case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: 140 }
366 /* If this is the pen interface */ 141 params_initialized = true;
367 if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { 142 hid_dbg(hdev, "parameters:\n" UCLOGIC_PARAMS_FMT_STR,
368 rc = uclogic_tablet_enable(hdev); 143 UCLOGIC_PARAMS_FMT_ARGS(&drvdata->params));
369 if (rc) { 144 if (drvdata->params.invalid) {
370 hid_err(hdev, "tablet enabling failed\n"); 145 hid_info(hdev, "interface is invalid, ignoring\n");
371 return rc; 146 rc = -ENODEV;
372 } 147 goto failure;
373 drvdata->invert_pen_inrange = true; 148 }
374
375 rc = uclogic_button_enable(hdev);
376 drvdata->has_virtual_pad_interface = !rc;
377 } else {
378 drvdata->ignore_pen_usage = true;
379 }
380 break;
381 case USB_DEVICE_ID_UGTIZER_TABLET_GP0610:
382 case USB_DEVICE_ID_UGEE_TABLET_EX07S:
383 /* If this is the pen interface */
384 if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
385 rc = uclogic_tablet_enable(hdev);
386 if (rc) {
387 hid_err(hdev, "tablet enabling failed\n");
388 return rc;
389 }
390 drvdata->invert_pen_inrange = true;
391 } else {
392 drvdata->ignore_pen_usage = true;
393 }
394 break;
395 case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
396 /*
397 * If it is the three-interface version, which is known to
398 * respond to initialization.
399 */
400 if (udev->config->desc.bNumInterfaces == 3) {
401 /* If it is the pen interface */
402 if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
403 rc = uclogic_tablet_enable(hdev);
404 if (rc) {
405 hid_err(hdev, "tablet enabling failed\n");
406 return rc;
407 }
408 drvdata->invert_pen_inrange = true;
409 149
410 rc = uclogic_button_enable(hdev); 150 /* Generate replacement report descriptor */
411 drvdata->has_virtual_pad_interface = !rc; 151 rc = uclogic_params_get_desc(&drvdata->params,
412 } else { 152 &drvdata->desc_ptr,
413 drvdata->ignore_pen_usage = true; 153 &drvdata->desc_size);
414 } 154 if (rc) {
415 } 155 hid_err(hdev,
416 break; 156 "failed generating replacement report descriptor: %d\n",
157 rc);
158 goto failure;
417 } 159 }
418 160
419 rc = hid_parse(hdev); 161 rc = hid_parse(hdev);
420 if (rc) { 162 if (rc) {
421 hid_err(hdev, "parse failed\n"); 163 hid_err(hdev, "parse failed\n");
422 return rc; 164 goto failure;
423 } 165 }
424 166
425 rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 167 rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
426 if (rc) { 168 if (rc) {
427 hid_err(hdev, "hw start failed\n"); 169 hid_err(hdev, "hw start failed\n");
428 return rc; 170 goto failure;
429 } 171 }
430 172
431 return 0; 173 return 0;
174failure:
175 /* Assume "remove" might not be called if "probe" failed */
176 if (params_initialized)
177 uclogic_params_cleanup(&drvdata->params);
178 return rc;
432} 179}
433 180
434static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, 181static int uclogic_raw_event(struct hid_device *hdev,
435 u8 *data, int size) 182 struct hid_report *report,
183 u8 *data, int size)
436{ 184{
437 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 185 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
186 struct uclogic_params *params = &drvdata->params;
438 187
439 if ((report->type == HID_INPUT_REPORT) && 188 /* Tweak pen reports, if necessary */
440 (report->id == UCLOGIC_RDESC_PEN_ID) && 189 if (!params->pen_unused &&
190 (report->type == HID_INPUT_REPORT) &&
191 (report->id == params->pen.id) &&
441 (size >= 2)) { 192 (size >= 2)) {
442 if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) 193 /* If it's the "virtual" frame controls report */
443 /* Change to virtual frame button report ID */ 194 if (params->frame.id != 0 &&
444 data[0] = 0xf7; 195 data[1] & params->pen_frame_flag) {
445 else if (drvdata->invert_pen_inrange) 196 /* Change to virtual frame controls report ID */
197 data[0] = params->frame.id;
198 return 0;
199 }
200 /* If in-range reports are inverted */
201 if (params->pen.inrange ==
202 UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {
446 /* Invert the in-range bit */ 203 /* Invert the in-range bit */
447 data[1] ^= 0x40; 204 data[1] ^= 0x40;
205 }
448 } 206 }
449 207
450 return 0; 208 return 0;
451} 209}
452 210
211static void uclogic_remove(struct hid_device *hdev)
212{
213 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
214
215 hid_hw_stop(hdev);
216 kfree(drvdata->desc_ptr);
217 uclogic_params_cleanup(&drvdata->params);
218}
219
453static const struct hid_device_id uclogic_devices[] = { 220static const struct hid_device_id uclogic_devices[] = {
454 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 221 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
455 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, 222 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
@@ -465,14 +232,22 @@ static const struct hid_device_id uclogic_devices[] = {
465 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, 232 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
466 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 233 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
467 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, 234 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
468 { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, 235 { HID_USB_DEVICE(USB_VENDOR_ID_HUION,
469 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, 236 USB_DEVICE_ID_HUION_TABLET) },
470 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, 237 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
471 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, 238 USB_DEVICE_ID_HUION_TABLET) },
472 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, 239 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
473 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, 240 USB_DEVICE_ID_YIYNOVA_TABLET) },
474 { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, 241 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
475 { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, 242 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) },
243 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
244 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) },
245 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
246 USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
247 { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER,
248 USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
249 { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
250 USB_DEVICE_ID_UGEE_TABLET_EX07S) },
476 { } 251 { }
477}; 252};
478MODULE_DEVICE_TABLE(hid, uclogic_devices); 253MODULE_DEVICE_TABLE(hid, uclogic_devices);
@@ -481,6 +256,7 @@ static struct hid_driver uclogic_driver = {
481 .name = "uclogic", 256 .name = "uclogic",
482 .id_table = uclogic_devices, 257 .id_table = uclogic_devices,
483 .probe = uclogic_probe, 258 .probe = uclogic_probe,
259 .remove = uclogic_remove,
484 .report_fixup = uclogic_report_fixup, 260 .report_fixup = uclogic_report_fixup,
485 .raw_event = uclogic_raw_event, 261 .raw_event = uclogic_raw_event,
486 .input_mapping = uclogic_input_mapping, 262 .input_mapping = uclogic_input_mapping,
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
new file mode 100644
index 000000000000..2f8870d58f9a
--- /dev/null
+++ b/drivers/hid/hid-uclogic-params.c
@@ -0,0 +1,806 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * HID driver for UC-Logic devices not fully compliant with HID standard
4 * - tablet initialization and parameter retrieval
5 *
6 * Copyright (c) 2018 Nikolai Kondrashov
7 */
8
9/*
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
13 * any later version.
14 */
15
16#include "hid-uclogic-params.h"
17#include "hid-uclogic-rdesc.h"
18#include "usbhid/usbhid.h"
19#include "hid-ids.h"
20#include <linux/ctype.h>
21#include <asm/unaligned.h>
22
23/**
24 * Convert a pen in-range reporting type to a string.
25 *
26 * @inrange: The in-range reporting type to convert.
27 *
28 * Returns:
29 * The string representing the type, or NULL if the type is unknown.
30 */
31const char *uclogic_params_pen_inrange_to_str(
32 enum uclogic_params_pen_inrange inrange)
33{
34 switch (inrange) {
35 case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
36 return "normal";
37 case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
38 return "inverted";
39 default:
40 return NULL;
41 }
42}
43
44/**
45 * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
46 * device interface, putting it into a kmalloc-allocated buffer as is, without
47 * character encoding conversion.
48 *
49 * @pbuf: Location for the kmalloc-allocated buffer pointer containing
50 * the retrieved descriptor. Not modified in case of error.
51 * Can be NULL to have retrieved descriptor discarded.
52 * @hdev: The HID device of the tablet interface to retrieve the string
53 * descriptor from. Cannot be NULL.
54 * @idx: Index of the string descriptor to request from the device.
55 * @len: Length of the buffer to allocate and the data to retrieve.
56 *
57 * Returns:
58 * number of bytes retrieved (<= len),
59 * -EPIPE, if the descriptor was not found, or
60 * another negative errno code in case of other error.
61 */
62static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
63 __u8 idx, size_t len)
64{
65 int rc;
66 struct usb_device *udev = hid_to_usb_dev(hdev);
67 __u8 *buf = NULL;
68
69 /* Check arguments */
70 if (hdev == NULL) {
71 rc = -EINVAL;
72 goto cleanup;
73 }
74
75 buf = kmalloc(len, GFP_KERNEL);
76 if (buf == NULL) {
77 rc = -ENOMEM;
78 goto cleanup;
79 }
80
81 rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
82 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
83 (USB_DT_STRING << 8) + idx,
84 0x0409, buf, len,
85 USB_CTRL_GET_TIMEOUT);
86 if (rc == -EPIPE) {
87 hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
88 goto cleanup;
89 } else if (rc < 0) {
90 hid_err(hdev,
91 "failed retrieving string descriptor #%hhu: %d\n",
92 idx, rc);
93 goto cleanup;
94 }
95
96 if (pbuf != NULL) {
97 *pbuf = buf;
98 buf = NULL;
99 }
100
101cleanup:
102 kfree(buf);
103 return rc;
104}
105
106/**
107 * uclogic_params_pen_cleanup - free resources used by struct
108 * uclogic_params_pen (tablet interface's pen input parameters).
109 * Can be called repeatedly.
110 *
111 * @pen: Pen input parameters to cleanup. Cannot be NULL.
112 */
113static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
114{
115 kfree(pen->desc_ptr);
116 memset(pen, 0, sizeof(*pen));
117}
118
119/**
120 * uclogic_params_pen_init() - initialize tablet interface pen
121 * input and retrieve its parameters from the device.
122 *
123 * @pen: Pointer to the pen parameters to initialize (to be
124 * cleaned up with uclogic_params_pen_cleanup()). Not modified in
125 * case of error, or if parameters are not found. Cannot be NULL.
126 * @pfound: Location for a flag which is set to true if the parameters
127 * were found, and to false if not (e.g. device was
128 * incompatible). Not modified in case of error. Cannot be NULL.
129 * @hdev: The HID device of the tablet interface to initialize and get
130 * parameters from. Cannot be NULL.
131 *
132 * Returns:
133 * Zero, if successful. A negative errno code on error.
134 */
135static int uclogic_params_pen_init(struct uclogic_params_pen *pen,
136 bool *pfound,
137 struct hid_device *hdev)
138{
139 int rc;
140 bool found = false;
141 /* Buffer for (part of) the string descriptor */
142 __u8 *buf = NULL;
143 /* Minimum descriptor length required, maximum seen so far is 18 */
144 const int len = 12;
145 s32 resolution;
146 /* Pen report descriptor template parameters */
147 s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
148 __u8 *desc_ptr = NULL;
149
150 /* Check arguments */
151 if (pen == NULL || pfound == NULL || hdev == NULL) {
152 rc = -EINVAL;
153 goto cleanup;
154 }
155
156 /*
157 * Read string descriptor containing pen input parameters.
158 * The specific string descriptor and data were discovered by sniffing
159 * the Windows driver traffic.
160 * NOTE: This enables fully-functional tablet mode.
161 */
162 rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
163 if (rc == -EPIPE) {
164 hid_dbg(hdev,
165 "string descriptor with pen parameters not found, assuming not compatible\n");
166 goto finish;
167 } else if (rc < 0) {
168 hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
169 goto cleanup;
170 } else if (rc != len) {
171 hid_dbg(hdev,
172 "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
173 rc, len);
174 goto finish;
175 }
176
177 /*
178 * Fill report descriptor parameters from the string descriptor
179 */
180 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
181 get_unaligned_le16(buf + 2);
182 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
183 get_unaligned_le16(buf + 4);
184 desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
185 get_unaligned_le16(buf + 8);
186 resolution = get_unaligned_le16(buf + 10);
187 if (resolution == 0) {
188 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
189 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
190 } else {
191 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
192 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
193 resolution;
194 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
195 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
196 resolution;
197 }
198 kfree(buf);
199 buf = NULL;
200
201 /*
202 * Generate pen report descriptor
203 */
204 desc_ptr = uclogic_rdesc_template_apply(
205 uclogic_rdesc_pen_template_arr,
206 uclogic_rdesc_pen_template_size,
207 desc_params, ARRAY_SIZE(desc_params));
208 if (desc_ptr == NULL) {
209 rc = -ENOMEM;
210 goto cleanup;
211 }
212
213 /*
214 * Fill-in the parameters
215 */
216 memset(pen, 0, sizeof(*pen));
217 pen->desc_ptr = desc_ptr;
218 desc_ptr = NULL;
219 pen->desc_size = uclogic_rdesc_pen_template_size;
220 pen->id = UCLOGIC_RDESC_PEN_ID;
221 pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
222 found = true;
223finish:
224 *pfound = found;
225 rc = 0;
226cleanup:
227 kfree(desc_ptr);
228 kfree(buf);
229 return rc;
230}
231
232/**
233 * uclogic_params_frame_cleanup - free resources used by struct
234 * uclogic_params_frame (tablet interface's frame controls input parameters).
235 * Can be called repeatedly.
236 *
237 * @frame: Frame controls input parameters to cleanup. Cannot be NULL.
238 */
239static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
240{
241 kfree(frame->desc_ptr);
242 memset(frame, 0, sizeof(*frame));
243}
244
245/**
246 * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
247 * parameters with a static report descriptor.
248 *
249 * @frame: Pointer to the frame parameters to initialize (to be cleaned
250 * up with uclogic_params_frame_cleanup()). Not modified in case
251 * of error. Cannot be NULL.
252 * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero.
253 * @desc_size: Report descriptor size.
254 * @id: Report ID used for frame reports, if they should be tweaked,
255 * zero if not.
256 *
257 * Returns:
258 * Zero, if successful. A negative errno code on error.
259 */
260static int uclogic_params_frame_init_with_desc(
261 struct uclogic_params_frame *frame,
262 const __u8 *desc_ptr,
263 size_t desc_size,
264 unsigned int id)
265{
266 __u8 *copy_desc_ptr;
267
268 if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
269 return -EINVAL;
270
271 copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
272 if (copy_desc_ptr == NULL)
273 return -ENOMEM;
274
275 memset(frame, 0, sizeof(*frame));
276 frame->desc_ptr = copy_desc_ptr;
277 frame->desc_size = desc_size;
278 frame->id = id;
279 return 0;
280}
281
282/**
283 * uclogic_params_frame_init_buttonpad() - initialize abstract buttonpad
284 * on a tablet interface.
285 *
286 * @frame: Pointer to the frame parameters to initialize (to be cleaned
287 * up with uclogic_params_frame_cleanup()). Not modified in case
288 * of error, or if parameters are not found. Cannot be NULL.
289 * @pfound: Location for a flag which is set to true if the parameters
290 * were found, and to false if not (e.g. device was
291 * incompatible). Not modified in case of error. Cannot be NULL.
292 * @hdev: The HID device of the tablet interface to initialize and get
293 * parameters from. Cannot be NULL.
294 *
295 * Returns:
296 * Zero, if successful. A negative errno code on error.
297 */
298static int uclogic_params_frame_init_buttonpad(
299 struct uclogic_params_frame *frame,
300 bool *pfound,
301 struct hid_device *hdev)
302{
303 int rc;
304 bool found = false;
305 struct usb_device *usb_dev = hid_to_usb_dev(hdev);
306 char *str_buf = NULL;
307 const size_t str_len = 16;
308
309 /* Check arguments */
310 if (frame == NULL || pfound == NULL || hdev == NULL) {
311 rc = -EINVAL;
312 goto cleanup;
313 }
314
315 /*
316 * Enable generic button mode
317 */
318 str_buf = kzalloc(str_len, GFP_KERNEL);
319 if (str_buf == NULL) {
320 rc = -ENOMEM;
321 goto cleanup;
322 }
323
324 rc = usb_string(usb_dev, 123, str_buf, str_len);
325 if (rc == -EPIPE) {
326 hid_dbg(hdev,
327 "generic button -enabling string descriptor not found\n");
328 } else if (rc < 0) {
329 goto cleanup;
330 } else if (strncmp(str_buf, "HK On", rc) != 0) {
331 hid_dbg(hdev,
332 "invalid response to enabling generic buttons: \"%s\"\n",
333 str_buf);
334 } else {
335 hid_dbg(hdev, "generic buttons enabled\n");
336 rc = uclogic_params_frame_init_with_desc(
337 frame,
338 uclogic_rdesc_buttonpad_arr,
339 uclogic_rdesc_buttonpad_size,
340 UCLOGIC_RDESC_BUTTONPAD_ID);
341 if (rc != 0)
342 goto cleanup;
343 found = true;
344 }
345
346 *pfound = found;
347 rc = 0;
348cleanup:
349 kfree(str_buf);
350 return rc;
351}
352
353/**
354 * uclogic_params_cleanup - free resources used by struct uclogic_params
355 * (tablet interface's parameters).
356 * Can be called repeatedly.
357 *
358 * @params: Input parameters to cleanup. Cannot be NULL.
359 */
360void uclogic_params_cleanup(struct uclogic_params *params)
361{
362 if (!params->invalid) {
363 kfree(params->desc_ptr);
364 if (!params->pen_unused)
365 uclogic_params_pen_cleanup(&params->pen);
366 uclogic_params_frame_cleanup(&params->frame);
367 memset(params, 0, sizeof(*params));
368 }
369}
370
371/**
372 * Get a replacement report descriptor for a tablet's interface.
373 *
374 * @params: The parameters of a tablet interface to get report
375 * descriptor for. Cannot be NULL.
376 * @pdesc: Location for the resulting, kmalloc-allocated report
377 * descriptor pointer, or for NULL, if there's no replacement
378 * report descriptor. Not modified in case of error. Cannot be
379 * NULL.
380 * @psize: Location for the resulting report descriptor size, not set if
381 * there's no replacement report descriptor. Not modified in case
382 * of error. Cannot be NULL.
383 *
384 * Returns:
385 * Zero, if successful.
386 * -EINVAL, if invalid arguments are supplied.
387 * -ENOMEM, if failed to allocate memory.
388 */
389int uclogic_params_get_desc(const struct uclogic_params *params,
390 __u8 **pdesc,
391 unsigned int *psize)
392{
393 bool common_present;
394 bool pen_present;
395 bool frame_present;
396 unsigned int size;
397 __u8 *desc = NULL;
398
399 /* Check arguments */
400 if (params == NULL || pdesc == NULL || psize == NULL)
401 return -EINVAL;
402
403 size = 0;
404
405 common_present = (params->desc_ptr != NULL);
406 pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL);
407 frame_present = (params->frame.desc_ptr != NULL);
408
409 if (common_present)
410 size += params->desc_size;
411 if (pen_present)
412 size += params->pen.desc_size;
413 if (frame_present)
414 size += params->frame.desc_size;
415
416 if (common_present || pen_present || frame_present) {
417 __u8 *p;
418
419 desc = kmalloc(size, GFP_KERNEL);
420 if (desc == NULL)
421 return -ENOMEM;
422 p = desc;
423
424 if (common_present) {
425 memcpy(p, params->desc_ptr,
426 params->desc_size);
427 p += params->desc_size;
428 }
429 if (pen_present) {
430 memcpy(p, params->pen.desc_ptr,
431 params->pen.desc_size);
432 p += params->pen.desc_size;
433 }
434 if (frame_present) {
435 memcpy(p, params->frame.desc_ptr,
436 params->frame.desc_size);
437 p += params->frame.desc_size;
438 }
439
440 WARN_ON(p != desc + size);
441
442 *psize = size;
443 }
444
445 *pdesc = desc;
446 return 0;
447}
448
449/**
450 * uclogic_params_init_invalid() - initialize tablet interface parameters,
451 * specifying the interface is invalid.
452 *
453 * @params: Parameters to initialize (to be cleaned with
454 * uclogic_params_cleanup()). Cannot be NULL.
455 */
456static void uclogic_params_init_invalid(struct uclogic_params *params)
457{
458 params->invalid = true;
459}
460
461/**
462 * uclogic_params_init_with_opt_desc() - initialize tablet interface
463 * parameters with an optional replacement report descriptor. Only modify
464 * report descriptor, if the original report descriptor matches the expected
465 * size.
466 *
467 * @params: Parameters to initialize (to be cleaned with
468 * uclogic_params_cleanup()). Not modified in case of
469 * error. Cannot be NULL.
470 * @hdev: The HID device of the tablet interface create the
471 * parameters for. Cannot be NULL.
472 * @orig_desc_size: Expected size of the original report descriptor to
473 * be replaced.
474 * @desc_ptr: Pointer to the replacement report descriptor.
475 * Can be NULL, if desc_size is zero.
476 * @desc_size: Size of the replacement report descriptor.
477 *
478 * Returns:
479 * Zero, if successful. -EINVAL if an invalid argument was passed.
480 * -ENOMEM, if failed to allocate memory.
481 */
482static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
483 struct hid_device *hdev,
484 unsigned int orig_desc_size,
485 __u8 *desc_ptr,
486 unsigned int desc_size)
487{
488 __u8 *desc_copy_ptr = NULL;
489 unsigned int desc_copy_size;
490 int rc;
491
492 /* Check arguments */
493 if (params == NULL || hdev == NULL ||
494 (desc_ptr == NULL && desc_size != 0)) {
495 rc = -EINVAL;
496 goto cleanup;
497 }
498
499 /* Replace report descriptor, if it matches */
500 if (hdev->dev_rsize == orig_desc_size) {
501 hid_dbg(hdev,
502 "device report descriptor matches the expected size, replacing\n");
503 desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
504 if (desc_copy_ptr == NULL) {
505 rc = -ENOMEM;
506 goto cleanup;
507 }
508 desc_copy_size = desc_size;
509 } else {
510 hid_dbg(hdev,
511 "device report descriptor doesn't match the expected size (%u != %u), preserving\n",
512 hdev->dev_rsize, orig_desc_size);
513 desc_copy_ptr = NULL;
514 desc_copy_size = 0;
515 }
516
517 /* Output parameters */
518 memset(params, 0, sizeof(*params));
519 params->desc_ptr = desc_copy_ptr;
520 desc_copy_ptr = NULL;
521 params->desc_size = desc_copy_size;
522
523 rc = 0;
524cleanup:
525 kfree(desc_copy_ptr);
526 return rc;
527}
528
529/**
530 * uclogic_params_init_with_pen_unused() - initialize tablet interface
531 * parameters preserving original reports and generic HID processing, but
532 * disabling pen usage.
533 *
534 * @params: Parameters to initialize (to be cleaned with
535 * uclogic_params_cleanup()). Not modified in case of
536 * error. Cannot be NULL.
537 */
538static void uclogic_params_init_with_pen_unused(struct uclogic_params *params)
539{
540 memset(params, 0, sizeof(*params));
541 params->pen_unused = true;
542}
543
544/**
545 * uclogic_params_init() - initialize a Huion tablet interface and discover
546 * its parameters.
547 *
548 * @params: Parameters to fill in (to be cleaned with
549 * uclogic_params_cleanup()). Not modified in case of error.
550 * Cannot be NULL.
551 * @hdev: The HID device of the tablet interface to initialize and get
552 * parameters from. Cannot be NULL.
553 *
554 * Returns:
555 * Zero, if successful. A negative errno code on error.
556 */
557static int uclogic_params_huion_init(struct uclogic_params *params,
558 struct hid_device *hdev)
559{
560 int rc;
561 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
562 __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
563 bool found;
564 /* The resulting parameters (noop) */
565 struct uclogic_params p = {0, };
566
567 /* Check arguments */
568 if (params == NULL || hdev == NULL) {
569 rc = -EINVAL;
570 goto cleanup;
571 }
572
573 /* If it's not a pen interface */
574 if (bInterfaceNumber != 0) {
575 /* TODO: Consider marking the interface invalid */
576 uclogic_params_init_with_pen_unused(&p);
577 goto output;
578 }
579
580 /* Try to probe pen parameters */
581 rc = uclogic_params_pen_init(&p.pen, &found, hdev);
582 if (rc != 0) {
583 hid_err(hdev,
584 "failed probing pen parameters: %d\n", rc);
585 goto cleanup;
586 } else if (found) {
587 hid_dbg(hdev, "pen parameters found\n");
588 /* Try to probe buttonpad */
589 rc = uclogic_params_frame_init_buttonpad(
590 &p.frame,
591 &found, hdev);
592 if (rc != 0) {
593 hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc);
594 goto cleanup;
595 }
596 hid_dbg(hdev, "buttonpad parameters%s found\n",
597 (found ? "" : " not"));
598 if (found) {
599 /* Set bitmask marking frame reports */
600 p.pen_frame_flag = 0x20;
601 }
602 goto output;
603 }
604 hid_dbg(hdev, "pen parameters not found\n");
605
606 uclogic_params_init_invalid(&p);
607
608output:
609 /* Output parameters */
610 memcpy(params, &p, sizeof(*params));
611 memset(&p, 0, sizeof(p));
612 rc = 0;
613cleanup:
614 uclogic_params_cleanup(&p);
615 return rc;
616}
617
618/**
619 * uclogic_params_init() - initialize a tablet interface and discover its
620 * parameters.
621 *
622 * @params: Parameters to fill in (to be cleaned with
623 * uclogic_params_cleanup()). Not modified in case of error.
624 * Cannot be NULL.
625 * @hdev: The HID device of the tablet interface to initialize and get
626 * parameters from. Cannot be NULL.
627 *
628 * Returns:
629 * Zero, if successful. A negative errno code on error.
630 */
631int uclogic_params_init(struct uclogic_params *params,
632 struct hid_device *hdev)
633{
634 int rc;
635 struct usb_device *udev = hid_to_usb_dev(hdev);
636 __u8 bNumInterfaces = udev->config->desc.bNumInterfaces;
637 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
638 __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
639 bool found;
640 /* The resulting parameters (noop) */
641 struct uclogic_params p = {0, };
642
643 /* Check arguments */
644 if (params == NULL || hdev == NULL) {
645 rc = -EINVAL;
646 goto cleanup;
647 }
648
649 /*
650 * Set replacement report descriptor if the original matches the
651 * specified size. Otherwise keep interface unchanged.
652 */
653#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
654 uclogic_params_init_with_opt_desc( \
655 &p, hdev, \
656 UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \
657 uclogic_rdesc_##_new_desc_token##_arr, \
658 uclogic_rdesc_##_new_desc_token##_size)
659
660#define VID_PID(_vid, _pid) \
661 (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
662
663 /*
664 * Handle specific interfaces for specific tablets.
665 *
666 * Observe the following logic:
667 *
668 * If the interface is recognized as producing certain useful input:
669 * Mark interface as valid.
670 * Output interface parameters.
671 * Else, if the interface is recognized as *not* producing any useful
672 * input:
673 * Mark interface as invalid.
674 * Else:
675 * Mark interface as valid.
676 * Output noop parameters.
677 *
678 * Rule of thumb: it is better to disable a broken interface than let
679 * it spew garbage input.
680 */
681
682 switch (VID_PID(hdev->vendor, hdev->product)) {
683 case VID_PID(USB_VENDOR_ID_UCLOGIC,
684 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
685 rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
686 if (rc != 0)
687 goto cleanup;
688 break;
689 case VID_PID(USB_VENDOR_ID_UCLOGIC,
690 USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
691 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
692 if (rc != 0)
693 goto cleanup;
694 break;
695 case VID_PID(USB_VENDOR_ID_UCLOGIC,
696 USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
697 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
698 if (rc != 0)
699 goto cleanup;
700 break;
701 case VID_PID(USB_VENDOR_ID_UCLOGIC,
702 USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
703 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
704 if (rc != 0)
705 goto cleanup;
706 break;
707 case VID_PID(USB_VENDOR_ID_UCLOGIC,
708 USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
709 rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
710 if (rc != 0)
711 goto cleanup;
712 break;
713 case VID_PID(USB_VENDOR_ID_UCLOGIC,
714 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
715 switch (bInterfaceNumber) {
716 case 0:
717 rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
718 if (rc != 0)
719 goto cleanup;
720 break;
721 case 1:
722 rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
723 if (rc != 0)
724 goto cleanup;
725 break;
726 case 2:
727 rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
728 if (rc != 0)
729 goto cleanup;
730 break;
731 }
732 break;
733 case VID_PID(USB_VENDOR_ID_UCLOGIC,
734 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
735 /*
736 * If it is not a three-interface version, which is known to
737 * respond to initialization.
738 */
739 if (bNumInterfaces != 3) {
740 switch (bInterfaceNumber) {
741 case 0:
742 rc = WITH_OPT_DESC(TWHA60_ORIG0,
743 twha60_fixed0);
744 if (rc != 0)
745 goto cleanup;
746 break;
747 case 1:
748 rc = WITH_OPT_DESC(TWHA60_ORIG1,
749 twha60_fixed1);
750 if (rc != 0)
751 goto cleanup;
752 break;
753 }
754 break;
755 }
756 /* FALL THROUGH */
757 case VID_PID(USB_VENDOR_ID_HUION,
758 USB_DEVICE_ID_HUION_TABLET):
759 case VID_PID(USB_VENDOR_ID_UCLOGIC,
760 USB_DEVICE_ID_HUION_TABLET):
761 case VID_PID(USB_VENDOR_ID_UCLOGIC,
762 USB_DEVICE_ID_YIYNOVA_TABLET):
763 case VID_PID(USB_VENDOR_ID_UCLOGIC,
764 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
765 case VID_PID(USB_VENDOR_ID_UCLOGIC,
766 USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
767 case VID_PID(USB_VENDOR_ID_UCLOGIC,
768 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
769 rc = uclogic_params_huion_init(&p, hdev);
770 if (rc != 0)
771 goto cleanup;
772 break;
773 case VID_PID(USB_VENDOR_ID_UGTIZER,
774 USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
775 case VID_PID(USB_VENDOR_ID_UGEE,
776 USB_DEVICE_ID_UGEE_TABLET_EX07S):
777 /* If this is the pen interface */
778 if (bInterfaceNumber == 1) {
779 /* Probe pen parameters */
780 rc = uclogic_params_pen_init(&p.pen, &found, hdev);
781 if (rc != 0) {
782 hid_err(hdev, "pen probing failed: %d\n", rc);
783 goto cleanup;
784 }
785 if (!found) {
786 hid_warn(hdev, "pen parameters not found");
787 uclogic_params_init_invalid(&p);
788 }
789 } else {
790 /* TODO: Consider marking the interface invalid */
791 uclogic_params_init_with_pen_unused(&p);
792 }
793 break;
794 }
795
796#undef VID_PID
797#undef WITH_OPT_DESC
798
799 /* Output parameters */
800 memcpy(params, &p, sizeof(*params));
801 memset(&p, 0, sizeof(p));
802 rc = 0;
803cleanup:
804 uclogic_params_cleanup(&p);
805 return rc;
806}
diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h
new file mode 100644
index 000000000000..4c78d9dd0576
--- /dev/null
+++ b/drivers/hid/hid-uclogic-params.h
@@ -0,0 +1,180 @@
1/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * HID driver for UC-Logic devices not fully compliant with HID standard
4 * - tablet initialization and parameter retrieval
5 *
6 * Copyright (c) 2018 Nikolai Kondrashov
7 */
8
9/*
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
13 * any later version.
14 */
15
16#ifndef _HID_UCLOGIC_PARAMS_H
17#define _HID_UCLOGIC_PARAMS_H
18
19#include <linux/usb.h>
20#include <linux/hid.h>
21
22/* Types of pen in-range reporting */
23enum uclogic_params_pen_inrange {
24 /* Normal reports: zero - out of proximity, one - in proximity */
25 UCLOGIC_PARAMS_PEN_INRANGE_NORMAL = 0,
26 /* Inverted reports: zero - in proximity, one - out of proximity */
27 UCLOGIC_PARAMS_PEN_INRANGE_INVERTED,
28};
29
30/* Convert a pen in-range reporting type to a string */
31extern const char *uclogic_params_pen_inrange_to_str(
32 enum uclogic_params_pen_inrange inrange);
33
34/*
35 * Tablet interface's pen input parameters.
36 *
37 * Must use declarative (descriptive) language, not imperative, to simplify
38 * understanding and maintain consistency.
39 *
40 * Noop (preserving functionality) when filled with zeroes.
41 */
42struct uclogic_params_pen {
43 /*
44 * Pointer to report descriptor describing the inputs.
45 * Allocated with kmalloc.
46 */
47 __u8 *desc_ptr;
48 /*
49 * Size of the report descriptor.
50 * Only valid, if "desc_ptr" is not NULL.
51 */
52 unsigned int desc_size;
53 /* Report ID, if reports should be tweaked, zero if not */
54 unsigned int id;
55 /* Type of in-range reporting, only valid if "id" is not zero */
56 enum uclogic_params_pen_inrange inrange;
57};
58
59/*
60 * Parameters of frame control inputs of a tablet interface.
61 *
62 * Must use declarative (descriptive) language, not imperative, to simplify
63 * understanding and maintain consistency.
64 *
65 * Noop (preserving functionality) when filled with zeroes.
66 */
67struct uclogic_params_frame {
68 /*
69 * Pointer to report descriptor describing the inputs.
70 * Allocated with kmalloc.
71 */
72 __u8 *desc_ptr;
73 /*
74 * Size of the report descriptor.
75 * Only valid, if "desc_ptr" is not NULL.
76 */
77 unsigned int desc_size;
78 /*
79 * Report ID, if reports should be tweaked, zero if not.
80 */
81 unsigned int id;
82};
83
84/*
85 * Tablet interface report parameters.
86 *
87 * Must use declarative (descriptive) language, not imperative, to simplify
88 * understanding and maintain consistency.
89 *
90 * When filled with zeros represents a "noop" configuration - passes all
91 * reports unchanged and lets the generic HID driver handle everything.
92 *
93 * The resulting device report descriptor is assembled from all the report
94 * descriptor parts referenced by the structure. No order of assembly should
95 * be assumed. The structure represents original device report descriptor if
96 * all the parts are NULL.
97 */
98struct uclogic_params {
99 /*
100 * True if the whole interface is invalid, false otherwise.
101 */
102 bool invalid;
103 /*
104 * Pointer to the common part of the replacement report descriptor,
105 * allocated with kmalloc. NULL if no common part is needed.
106 * Only valid, if "invalid" is false.
107 */
108 __u8 *desc_ptr;
109 /*
110 * Size of the common part of the replacement report descriptor.
111 * Only valid, if "desc_ptr" is not NULL.
112 */
113 unsigned int desc_size;
114 /*
115 * True, if pen usage in report descriptor is invalid, when present.
116 * Only valid, if "invalid" is false.
117 */
118 bool pen_unused;
119 /*
120 * Pen parameters and optional report descriptor part.
121 * Only valid if "pen_unused" is valid and false.
122 */
123 struct uclogic_params_pen pen;
124 /*
125 * Frame control parameters and optional report descriptor part.
126 * Only valid, if "invalid" is false.
127 */
128 struct uclogic_params_frame frame;
129 /*
130 * Bitmask matching frame controls "sub-report" flag in the second
131 * byte of the pen report, or zero if it's not expected.
132 * Only valid if both "pen" and "frame" are valid, and "frame.id" is
133 * not zero.
134 */
135 __u8 pen_frame_flag;
136};
137
138/* Initialize a tablet interface and discover its parameters */
139extern int uclogic_params_init(struct uclogic_params *params,
140 struct hid_device *hdev);
141
142/* Tablet interface parameters *printf format string */
143#define UCLOGIC_PARAMS_FMT_STR \
144 ".invalid = %s\n" \
145 ".desc_ptr = %p\n" \
146 ".desc_size = %u\n" \
147 ".pen_unused = %s\n" \
148 ".pen.desc_ptr = %p\n" \
149 ".pen.desc_size = %u\n" \
150 ".pen.id = %u\n" \
151 ".pen.inrange = %s\n" \
152 ".frame.desc_ptr = %p\n" \
153 ".frame.desc_size = %u\n" \
154 ".frame.id = %u\n" \
155 ".pen_frame_flag = 0x%02x\n"
156
157/* Tablet interface parameters *printf format arguments */
158#define UCLOGIC_PARAMS_FMT_ARGS(_params) \
159 ((_params)->invalid ? "true" : "false"), \
160 (_params)->desc_ptr, \
161 (_params)->desc_size, \
162 ((_params)->pen_unused ? "true" : "false"), \
163 (_params)->pen.desc_ptr, \
164 (_params)->pen.desc_size, \
165 (_params)->pen.id, \
166 uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \
167 (_params)->frame.desc_ptr, \
168 (_params)->frame.desc_size, \
169 (_params)->frame.id, \
170 (_params)->pen_frame_flag
171
172/* Get a replacement report descriptor for a tablet's interface. */
173extern int uclogic_params_get_desc(const struct uclogic_params *params,
174 __u8 **pdesc,
175 unsigned int *psize);
176
177/* Free resources used by tablet interface's parameters */
178extern void uclogic_params_cleanup(struct uclogic_params *params);
179
180#endif /* _HID_UCLOGIC_PARAMS_H */