aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-uclogic-params.c
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 /drivers/hid/hid-uclogic-params.c
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>
Diffstat (limited to 'drivers/hid/hid-uclogic-params.c')
-rw-r--r--drivers/hid/hid-uclogic-params.c806
1 files changed, 806 insertions, 0 deletions
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}