GNU Linux-libre 5.10.153-gnu1
[releases.git] / drivers / hid / hid-uclogic-params.c
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  */
31 const 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         case UCLOGIC_PARAMS_PEN_INRANGE_NONE:
40                 return "none";
41         default:
42                 return NULL;
43         }
44 }
45
46 /**
47  * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
48  * device interface, putting it into a kmalloc-allocated buffer as is, without
49  * character encoding conversion.
50  *
51  * @pbuf:       Location for the kmalloc-allocated buffer pointer containing
52  *              the retrieved descriptor. Not modified in case of error.
53  *              Can be NULL to have retrieved descriptor discarded.
54  * @hdev:       The HID device of the tablet interface to retrieve the string
55  *              descriptor from. Cannot be NULL.
56  * @idx:        Index of the string descriptor to request from the device.
57  * @len:        Length of the buffer to allocate and the data to retrieve.
58  *
59  * Returns:
60  *      number of bytes retrieved (<= len),
61  *      -EPIPE, if the descriptor was not found, or
62  *      another negative errno code in case of other error.
63  */
64 static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
65                                         __u8 idx, size_t len)
66 {
67         int rc;
68         struct usb_device *udev;
69         __u8 *buf = NULL;
70
71         /* Check arguments */
72         if (hdev == NULL) {
73                 rc = -EINVAL;
74                 goto cleanup;
75         }
76
77         udev = hid_to_usb_dev(hdev);
78
79         buf = kmalloc(len, GFP_KERNEL);
80         if (buf == NULL) {
81                 rc = -ENOMEM;
82                 goto cleanup;
83         }
84
85         rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
86                                 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
87                                 (USB_DT_STRING << 8) + idx,
88                                 0x0409, buf, len,
89                                 USB_CTRL_GET_TIMEOUT);
90         if (rc == -EPIPE) {
91                 hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
92                 goto cleanup;
93         } else if (rc < 0) {
94                 hid_err(hdev,
95                         "failed retrieving string descriptor #%hhu: %d\n",
96                         idx, rc);
97                 goto cleanup;
98         }
99
100         if (pbuf != NULL) {
101                 *pbuf = buf;
102                 buf = NULL;
103         }
104
105 cleanup:
106         kfree(buf);
107         return rc;
108 }
109
110 /**
111  * uclogic_params_pen_cleanup - free resources used by struct
112  * uclogic_params_pen (tablet interface's pen input parameters).
113  * Can be called repeatedly.
114  *
115  * @pen:        Pen input parameters to cleanup. Cannot be NULL.
116  */
117 static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
118 {
119         kfree(pen->desc_ptr);
120         memset(pen, 0, sizeof(*pen));
121 }
122
123 /**
124  * uclogic_params_pen_init_v1() - initialize tablet interface pen
125  * input and retrieve its parameters from the device, using v1 protocol.
126  *
127  * @pen:        Pointer to the pen parameters to initialize (to be
128  *              cleaned up with uclogic_params_pen_cleanup()). Not modified in
129  *              case of error, or if parameters are not found. Cannot be NULL.
130  * @pfound:     Location for a flag which is set to true if the parameters
131  *              were found, and to false if not (e.g. device was
132  *              incompatible). Not modified in case of error. Cannot be NULL.
133  * @hdev:       The HID device of the tablet interface to initialize and get
134  *              parameters from. Cannot be NULL.
135  *
136  * Returns:
137  *      Zero, if successful. A negative errno code on error.
138  */
139 static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
140                                       bool *pfound,
141                                       struct hid_device *hdev)
142 {
143         int rc;
144         bool found = false;
145         /* Buffer for (part of) the string descriptor */
146         __u8 *buf = NULL;
147         /* Minimum descriptor length required, maximum seen so far is 18 */
148         const int len = 12;
149         s32 resolution;
150         /* Pen report descriptor template parameters */
151         s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
152         __u8 *desc_ptr = NULL;
153
154         /* Check arguments */
155         if (pen == NULL || pfound == NULL || hdev == NULL) {
156                 rc = -EINVAL;
157                 goto cleanup;
158         }
159
160         /*
161          * Read string descriptor containing pen input parameters.
162          * The specific string descriptor and data were discovered by sniffing
163          * the Windows driver traffic.
164          * NOTE: This enables fully-functional tablet mode.
165          */
166         rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
167         if (rc == -EPIPE) {
168                 hid_dbg(hdev,
169                         "string descriptor with pen parameters not found, assuming not compatible\n");
170                 goto finish;
171         } else if (rc < 0) {
172                 hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
173                 goto cleanup;
174         } else if (rc != len) {
175                 hid_dbg(hdev,
176                         "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
177                         rc, len);
178                 goto finish;
179         }
180
181         /*
182          * Fill report descriptor parameters from the string descriptor
183          */
184         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
185                 get_unaligned_le16(buf + 2);
186         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
187                 get_unaligned_le16(buf + 4);
188         desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
189                 get_unaligned_le16(buf + 8);
190         resolution = get_unaligned_le16(buf + 10);
191         if (resolution == 0) {
192                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
193                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
194         } else {
195                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
196                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
197                         resolution;
198                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
199                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
200                         resolution;
201         }
202         kfree(buf);
203         buf = NULL;
204
205         /*
206          * Generate pen report descriptor
207          */
208         desc_ptr = uclogic_rdesc_template_apply(
209                                 uclogic_rdesc_pen_v1_template_arr,
210                                 uclogic_rdesc_pen_v1_template_size,
211                                 desc_params, ARRAY_SIZE(desc_params));
212         if (desc_ptr == NULL) {
213                 rc = -ENOMEM;
214                 goto cleanup;
215         }
216
217         /*
218          * Fill-in the parameters
219          */
220         memset(pen, 0, sizeof(*pen));
221         pen->desc_ptr = desc_ptr;
222         desc_ptr = NULL;
223         pen->desc_size = uclogic_rdesc_pen_v1_template_size;
224         pen->id = UCLOGIC_RDESC_PEN_V1_ID;
225         pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
226         found = true;
227 finish:
228         *pfound = found;
229         rc = 0;
230 cleanup:
231         kfree(desc_ptr);
232         kfree(buf);
233         return rc;
234 }
235
236 /**
237  * uclogic_params_get_le24() - get a 24-bit little-endian number from a
238  * buffer.
239  *
240  * @p:  The pointer to the number buffer.
241  *
242  * Returns:
243  *      The retrieved number
244  */
245 static s32 uclogic_params_get_le24(const void *p)
246 {
247         const __u8 *b = p;
248         return b[0] | (b[1] << 8UL) | (b[2] << 16UL);
249 }
250
251 /**
252  * uclogic_params_pen_init_v2() - initialize tablet interface pen
253  * input and retrieve its parameters from the device, using v2 protocol.
254  *
255  * @pen:        Pointer to the pen parameters to initialize (to be
256  *              cleaned up with uclogic_params_pen_cleanup()). Not modified in
257  *              case of error, or if parameters are not found. Cannot be NULL.
258  * @pfound:     Location for a flag which is set to true if the parameters
259  *              were found, and to false if not (e.g. device was
260  *              incompatible). Not modified in case of error. Cannot be NULL.
261  * @hdev:       The HID device of the tablet interface to initialize and get
262  *              parameters from. Cannot be NULL.
263  *
264  * Returns:
265  *      Zero, if successful. A negative errno code on error.
266  */
267 static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
268                                         bool *pfound,
269                                         struct hid_device *hdev)
270 {
271         int rc;
272         bool found = false;
273         /* Buffer for (part of) the string descriptor */
274         __u8 *buf = NULL;
275         /* Descriptor length required */
276         const int len = 18;
277         s32 resolution;
278         /* Pen report descriptor template parameters */
279         s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
280         __u8 *desc_ptr = NULL;
281
282         /* Check arguments */
283         if (pen == NULL || pfound == NULL || hdev == NULL) {
284                 rc = -EINVAL;
285                 goto cleanup;
286         }
287
288         /*
289          * Read string descriptor containing pen input parameters.
290          * The specific string descriptor and data were discovered by sniffing
291          * the Windows driver traffic.
292          * NOTE: This enables fully-functional tablet mode.
293          */
294         rc = uclogic_params_get_str_desc(&buf, hdev, 200, len);
295         if (rc == -EPIPE) {
296                 hid_dbg(hdev,
297                         "string descriptor with pen parameters not found, assuming not compatible\n");
298                 goto finish;
299         } else if (rc < 0) {
300                 hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
301                 goto cleanup;
302         } else if (rc != len) {
303                 hid_dbg(hdev,
304                         "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
305                         rc, len);
306                 goto finish;
307         } else {
308                 size_t i;
309                 /*
310                  * Check it's not just a catch-all UTF-16LE-encoded ASCII
311                  * string (such as the model name) some tablets put into all
312                  * unknown string descriptors.
313                  */
314                 for (i = 2;
315                      i < len &&
316                         (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
317                      i += 2);
318                 if (i >= len) {
319                         hid_dbg(hdev,
320                                 "string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
321                         goto finish;
322                 }
323         }
324
325         /*
326          * Fill report descriptor parameters from the string descriptor
327          */
328         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
329                 uclogic_params_get_le24(buf + 2);
330         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
331                 uclogic_params_get_le24(buf + 5);
332         desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
333                 get_unaligned_le16(buf + 8);
334         resolution = get_unaligned_le16(buf + 10);
335         if (resolution == 0) {
336                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
337                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
338         } else {
339                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
340                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
341                         resolution;
342                 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
343                         desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
344                         resolution;
345         }
346         kfree(buf);
347         buf = NULL;
348
349         /*
350          * Generate pen report descriptor
351          */
352         desc_ptr = uclogic_rdesc_template_apply(
353                                 uclogic_rdesc_pen_v2_template_arr,
354                                 uclogic_rdesc_pen_v2_template_size,
355                                 desc_params, ARRAY_SIZE(desc_params));
356         if (desc_ptr == NULL) {
357                 rc = -ENOMEM;
358                 goto cleanup;
359         }
360
361         /*
362          * Fill-in the parameters
363          */
364         memset(pen, 0, sizeof(*pen));
365         pen->desc_ptr = desc_ptr;
366         desc_ptr = NULL;
367         pen->desc_size = uclogic_rdesc_pen_v2_template_size;
368         pen->id = UCLOGIC_RDESC_PEN_V2_ID;
369         pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
370         pen->fragmented_hires = true;
371         found = true;
372 finish:
373         *pfound = found;
374         rc = 0;
375 cleanup:
376         kfree(desc_ptr);
377         kfree(buf);
378         return rc;
379 }
380
381 /**
382  * uclogic_params_frame_cleanup - free resources used by struct
383  * uclogic_params_frame (tablet interface's frame controls input parameters).
384  * Can be called repeatedly.
385  *
386  * @frame:      Frame controls input parameters to cleanup. Cannot be NULL.
387  */
388 static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
389 {
390         kfree(frame->desc_ptr);
391         memset(frame, 0, sizeof(*frame));
392 }
393
394 /**
395  * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
396  * parameters with a static report descriptor.
397  *
398  * @frame:      Pointer to the frame parameters to initialize (to be cleaned
399  *              up with uclogic_params_frame_cleanup()). Not modified in case
400  *              of error. Cannot be NULL.
401  * @desc_ptr:   Report descriptor pointer. Can be NULL, if desc_size is zero.
402  * @desc_size:  Report descriptor size.
403  * @id:         Report ID used for frame reports, if they should be tweaked,
404  *              zero if not.
405  *
406  * Returns:
407  *      Zero, if successful. A negative errno code on error.
408  */
409 static int uclogic_params_frame_init_with_desc(
410                                         struct uclogic_params_frame *frame,
411                                         const __u8 *desc_ptr,
412                                         size_t desc_size,
413                                         unsigned int id)
414 {
415         __u8 *copy_desc_ptr;
416
417         if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
418                 return -EINVAL;
419
420         copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
421         if (copy_desc_ptr == NULL)
422                 return -ENOMEM;
423
424         memset(frame, 0, sizeof(*frame));
425         frame->desc_ptr = copy_desc_ptr;
426         frame->desc_size = desc_size;
427         frame->id = id;
428         return 0;
429 }
430
431 /**
432  * uclogic_params_frame_init_v1_buttonpad() - initialize abstract buttonpad
433  * on a v1 tablet interface.
434  *
435  * @frame:      Pointer to the frame parameters to initialize (to be cleaned
436  *              up with uclogic_params_frame_cleanup()). Not modified in case
437  *              of error, or if parameters are not found. Cannot be NULL.
438  * @pfound:     Location for a flag which is set to true if the parameters
439  *              were found, and to false if not (e.g. device was
440  *              incompatible). Not modified in case of error. Cannot be NULL.
441  * @hdev:       The HID device of the tablet interface to initialize and get
442  *              parameters from. Cannot be NULL.
443  *
444  * Returns:
445  *      Zero, if successful. A negative errno code on error.
446  */
447 static int uclogic_params_frame_init_v1_buttonpad(
448                                         struct uclogic_params_frame *frame,
449                                         bool *pfound,
450                                         struct hid_device *hdev)
451 {
452         int rc;
453         bool found = false;
454         struct usb_device *usb_dev;
455         char *str_buf = NULL;
456         const size_t str_len = 16;
457
458         /* Check arguments */
459         if (frame == NULL || pfound == NULL || hdev == NULL) {
460                 rc = -EINVAL;
461                 goto cleanup;
462         }
463
464         usb_dev = hid_to_usb_dev(hdev);
465
466         /*
467          * Enable generic button mode
468          */
469         str_buf = kzalloc(str_len, GFP_KERNEL);
470         if (str_buf == NULL) {
471                 rc = -ENOMEM;
472                 goto cleanup;
473         }
474
475         rc = usb_string(usb_dev, 123, str_buf, str_len);
476         if (rc == -EPIPE) {
477                 hid_dbg(hdev,
478                         "generic button -enabling string descriptor not found\n");
479         } else if (rc < 0) {
480                 goto cleanup;
481         } else if (strncmp(str_buf, "HK On", rc) != 0) {
482                 hid_dbg(hdev,
483                         "invalid response to enabling generic buttons: \"%s\"\n",
484                         str_buf);
485         } else {
486                 hid_dbg(hdev, "generic buttons enabled\n");
487                 rc = uclogic_params_frame_init_with_desc(
488                                 frame,
489                                 uclogic_rdesc_buttonpad_v1_arr,
490                                 uclogic_rdesc_buttonpad_v1_size,
491                                 UCLOGIC_RDESC_BUTTONPAD_V1_ID);
492                 if (rc != 0)
493                         goto cleanup;
494                 found = true;
495         }
496
497         *pfound = found;
498         rc = 0;
499 cleanup:
500         kfree(str_buf);
501         return rc;
502 }
503
504 /**
505  * uclogic_params_cleanup - free resources used by struct uclogic_params
506  * (tablet interface's parameters).
507  * Can be called repeatedly.
508  *
509  * @params:     Input parameters to cleanup. Cannot be NULL.
510  */
511 void uclogic_params_cleanup(struct uclogic_params *params)
512 {
513         if (!params->invalid) {
514                 kfree(params->desc_ptr);
515                 if (!params->pen_unused)
516                         uclogic_params_pen_cleanup(&params->pen);
517                 uclogic_params_frame_cleanup(&params->frame);
518                 memset(params, 0, sizeof(*params));
519         }
520 }
521
522 /**
523  * Get a replacement report descriptor for a tablet's interface.
524  *
525  * @params:     The parameters of a tablet interface to get report
526  *              descriptor for. Cannot be NULL.
527  * @pdesc:      Location for the resulting, kmalloc-allocated report
528  *              descriptor pointer, or for NULL, if there's no replacement
529  *              report descriptor. Not modified in case of error. Cannot be
530  *              NULL.
531  * @psize:      Location for the resulting report descriptor size, not set if
532  *              there's no replacement report descriptor. Not modified in case
533  *              of error. Cannot be NULL.
534  *
535  * Returns:
536  *      Zero, if successful.
537  *      -EINVAL, if invalid arguments are supplied.
538  *      -ENOMEM, if failed to allocate memory.
539  */
540 int uclogic_params_get_desc(const struct uclogic_params *params,
541                                 __u8 **pdesc,
542                                 unsigned int *psize)
543 {
544         bool common_present;
545         bool pen_present;
546         bool frame_present;
547         unsigned int size;
548         __u8 *desc = NULL;
549
550         /* Check arguments */
551         if (params == NULL || pdesc == NULL || psize == NULL)
552                 return -EINVAL;
553
554         size = 0;
555
556         common_present = (params->desc_ptr != NULL);
557         pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL);
558         frame_present = (params->frame.desc_ptr != NULL);
559
560         if (common_present)
561                 size += params->desc_size;
562         if (pen_present)
563                 size += params->pen.desc_size;
564         if (frame_present)
565                 size += params->frame.desc_size;
566
567         if (common_present || pen_present || frame_present) {
568                 __u8 *p;
569
570                 desc = kmalloc(size, GFP_KERNEL);
571                 if (desc == NULL)
572                         return -ENOMEM;
573                 p = desc;
574
575                 if (common_present) {
576                         memcpy(p, params->desc_ptr,
577                                 params->desc_size);
578                         p += params->desc_size;
579                 }
580                 if (pen_present) {
581                         memcpy(p, params->pen.desc_ptr,
582                                 params->pen.desc_size);
583                         p += params->pen.desc_size;
584                 }
585                 if (frame_present) {
586                         memcpy(p, params->frame.desc_ptr,
587                                 params->frame.desc_size);
588                         p += params->frame.desc_size;
589                 }
590
591                 WARN_ON(p != desc + size);
592
593                 *psize = size;
594         }
595
596         *pdesc = desc;
597         return 0;
598 }
599
600 /**
601  * uclogic_params_init_invalid() - initialize tablet interface parameters,
602  * specifying the interface is invalid.
603  *
604  * @params:             Parameters to initialize (to be cleaned with
605  *                      uclogic_params_cleanup()). Cannot be NULL.
606  */
607 static void uclogic_params_init_invalid(struct uclogic_params *params)
608 {
609         params->invalid = true;
610 }
611
612 /**
613  * uclogic_params_init_with_opt_desc() - initialize tablet interface
614  * parameters with an optional replacement report descriptor. Only modify
615  * report descriptor, if the original report descriptor matches the expected
616  * size.
617  *
618  * @params:             Parameters to initialize (to be cleaned with
619  *                      uclogic_params_cleanup()). Not modified in case of
620  *                      error. Cannot be NULL.
621  * @hdev:               The HID device of the tablet interface create the
622  *                      parameters for. Cannot be NULL.
623  * @orig_desc_size:     Expected size of the original report descriptor to
624  *                      be replaced.
625  * @desc_ptr:           Pointer to the replacement report descriptor.
626  *                      Can be NULL, if desc_size is zero.
627  * @desc_size:          Size of the replacement report descriptor.
628  *
629  * Returns:
630  *      Zero, if successful. -EINVAL if an invalid argument was passed.
631  *      -ENOMEM, if failed to allocate memory.
632  */
633 static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
634                                              struct hid_device *hdev,
635                                              unsigned int orig_desc_size,
636                                              __u8 *desc_ptr,
637                                              unsigned int desc_size)
638 {
639         __u8 *desc_copy_ptr = NULL;
640         unsigned int desc_copy_size;
641         int rc;
642
643         /* Check arguments */
644         if (params == NULL || hdev == NULL ||
645             (desc_ptr == NULL && desc_size != 0)) {
646                 rc = -EINVAL;
647                 goto cleanup;
648         }
649
650         /* Replace report descriptor, if it matches */
651         if (hdev->dev_rsize == orig_desc_size) {
652                 hid_dbg(hdev,
653                         "device report descriptor matches the expected size, replacing\n");
654                 desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
655                 if (desc_copy_ptr == NULL) {
656                         rc = -ENOMEM;
657                         goto cleanup;
658                 }
659                 desc_copy_size = desc_size;
660         } else {
661                 hid_dbg(hdev,
662                         "device report descriptor doesn't match the expected size (%u != %u), preserving\n",
663                         hdev->dev_rsize, orig_desc_size);
664                 desc_copy_ptr = NULL;
665                 desc_copy_size = 0;
666         }
667
668         /* Output parameters */
669         memset(params, 0, sizeof(*params));
670         params->desc_ptr = desc_copy_ptr;
671         desc_copy_ptr = NULL;
672         params->desc_size = desc_copy_size;
673
674         rc = 0;
675 cleanup:
676         kfree(desc_copy_ptr);
677         return rc;
678 }
679
680 /**
681  * uclogic_params_init_with_pen_unused() - initialize tablet interface
682  * parameters preserving original reports and generic HID processing, but
683  * disabling pen usage.
684  *
685  * @params:             Parameters to initialize (to be cleaned with
686  *                      uclogic_params_cleanup()). Not modified in case of
687  *                      error. Cannot be NULL.
688  */
689 static void uclogic_params_init_with_pen_unused(struct uclogic_params *params)
690 {
691         memset(params, 0, sizeof(*params));
692         params->pen_unused = true;
693 }
694
695 /**
696  * uclogic_params_init() - initialize a Huion tablet interface and discover
697  * its parameters.
698  *
699  * @params:     Parameters to fill in (to be cleaned with
700  *              uclogic_params_cleanup()). Not modified in case of error.
701  *              Cannot be NULL.
702  * @hdev:       The HID device of the tablet interface to initialize and get
703  *              parameters from. Cannot be NULL.
704  *
705  * Returns:
706  *      Zero, if successful. A negative errno code on error.
707  */
708 static int uclogic_params_huion_init(struct uclogic_params *params,
709                                      struct hid_device *hdev)
710 {
711         int rc;
712         struct usb_device *udev;
713         struct usb_interface *iface;
714         __u8 bInterfaceNumber;
715         bool found;
716         /* The resulting parameters (noop) */
717         struct uclogic_params p = {0, };
718         static const char transition_ver[] = "HUION_T153_160607";
719         char *ver_ptr = NULL;
720         const size_t ver_len = sizeof(transition_ver) + 1;
721
722         /* Check arguments */
723         if (params == NULL || hdev == NULL) {
724                 rc = -EINVAL;
725                 goto cleanup;
726         }
727
728         udev = hid_to_usb_dev(hdev);
729         iface = to_usb_interface(hdev->dev.parent);
730         bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
731
732         /* If it's not a pen interface */
733         if (bInterfaceNumber != 0) {
734                 /* TODO: Consider marking the interface invalid */
735                 uclogic_params_init_with_pen_unused(&p);
736                 goto output;
737         }
738
739         /* Try to get firmware version */
740         ver_ptr = kzalloc(ver_len, GFP_KERNEL);
741         if (ver_ptr == NULL) {
742                 rc = -ENOMEM;
743                 goto cleanup;
744         }
745         rc = usb_string(udev, 201, ver_ptr, ver_len);
746         if (rc == -EPIPE) {
747                 *ver_ptr = '\0';
748         } else if (rc < 0) {
749                 hid_err(hdev,
750                         "failed retrieving Huion firmware version: %d\n", rc);
751                 goto cleanup;
752         }
753
754         /* If this is a transition firmware */
755         if (strcmp(ver_ptr, transition_ver) == 0) {
756                 hid_dbg(hdev,
757                         "transition firmware detected, not probing pen v2 parameters\n");
758         } else {
759                 /* Try to probe v2 pen parameters */
760                 rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev);
761                 if (rc != 0) {
762                         hid_err(hdev,
763                                 "failed probing pen v2 parameters: %d\n", rc);
764                         goto cleanup;
765                 } else if (found) {
766                         hid_dbg(hdev, "pen v2 parameters found\n");
767                         /* Create v2 buttonpad parameters */
768                         rc = uclogic_params_frame_init_with_desc(
769                                         &p.frame,
770                                         uclogic_rdesc_buttonpad_v2_arr,
771                                         uclogic_rdesc_buttonpad_v2_size,
772                                         UCLOGIC_RDESC_BUTTONPAD_V2_ID);
773                         if (rc != 0) {
774                                 hid_err(hdev,
775                                         "failed creating v2 buttonpad parameters: %d\n",
776                                         rc);
777                                 goto cleanup;
778                         }
779                         /* Set bitmask marking frame reports in pen reports */
780                         p.pen_frame_flag = 0x20;
781                         goto output;
782                 }
783                 hid_dbg(hdev, "pen v2 parameters not found\n");
784         }
785
786         /* Try to probe v1 pen parameters */
787         rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
788         if (rc != 0) {
789                 hid_err(hdev,
790                         "failed probing pen v1 parameters: %d\n", rc);
791                 goto cleanup;
792         } else if (found) {
793                 hid_dbg(hdev, "pen v1 parameters found\n");
794                 /* Try to probe v1 buttonpad */
795                 rc = uclogic_params_frame_init_v1_buttonpad(
796                                                 &p.frame,
797                                                 &found, hdev);
798                 if (rc != 0) {
799                         hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc);
800                         goto cleanup;
801                 }
802                 hid_dbg(hdev, "buttonpad v1 parameters%s found\n",
803                         (found ? "" : " not"));
804                 if (found) {
805                         /* Set bitmask marking frame reports */
806                         p.pen_frame_flag = 0x20;
807                 }
808                 goto output;
809         }
810         hid_dbg(hdev, "pen v1 parameters not found\n");
811
812         uclogic_params_init_invalid(&p);
813
814 output:
815         /* Output parameters */
816         memcpy(params, &p, sizeof(*params));
817         memset(&p, 0, sizeof(p));
818         rc = 0;
819 cleanup:
820         kfree(ver_ptr);
821         uclogic_params_cleanup(&p);
822         return rc;
823 }
824
825 /**
826  * uclogic_params_init() - initialize a tablet interface and discover its
827  * parameters.
828  *
829  * @params:     Parameters to fill in (to be cleaned with
830  *              uclogic_params_cleanup()). Not modified in case of error.
831  *              Cannot be NULL.
832  * @hdev:       The HID device of the tablet interface to initialize and get
833  *              parameters from. Cannot be NULL. Must be using the USB low-level
834  *              driver, i.e. be an actual USB tablet.
835  *
836  * Returns:
837  *      Zero, if successful. A negative errno code on error.
838  */
839 int uclogic_params_init(struct uclogic_params *params,
840                         struct hid_device *hdev)
841 {
842         int rc;
843         struct usb_device *udev;
844         __u8  bNumInterfaces;
845         struct usb_interface *iface;
846         __u8 bInterfaceNumber;
847         bool found;
848         /* The resulting parameters (noop) */
849         struct uclogic_params p = {0, };
850
851         /* Check arguments */
852         if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {
853                 rc = -EINVAL;
854                 goto cleanup;
855         }
856
857         udev = hid_to_usb_dev(hdev);
858         bNumInterfaces = udev->config->desc.bNumInterfaces;
859         iface = to_usb_interface(hdev->dev.parent);
860         bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
861
862         /*
863          * Set replacement report descriptor if the original matches the
864          * specified size. Otherwise keep interface unchanged.
865          */
866 #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
867         uclogic_params_init_with_opt_desc(                  \
868                 &p, hdev,                                   \
869                 UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \
870                 uclogic_rdesc_##_new_desc_token##_arr,      \
871                 uclogic_rdesc_##_new_desc_token##_size)
872
873 #define VID_PID(_vid, _pid) \
874         (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
875
876         /*
877          * Handle specific interfaces for specific tablets.
878          *
879          * Observe the following logic:
880          *
881          * If the interface is recognized as producing certain useful input:
882          *      Mark interface as valid.
883          *      Output interface parameters.
884          * Else, if the interface is recognized as *not* producing any useful
885          * input:
886          *      Mark interface as invalid.
887          * Else:
888          *      Mark interface as valid.
889          *      Output noop parameters.
890          *
891          * Rule of thumb: it is better to disable a broken interface than let
892          *                it spew garbage input.
893          */
894
895         switch (VID_PID(hdev->vendor, hdev->product)) {
896         case VID_PID(USB_VENDOR_ID_UCLOGIC,
897                      USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
898                 rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
899                 if (rc != 0)
900                         goto cleanup;
901                 break;
902         case VID_PID(USB_VENDOR_ID_UCLOGIC,
903                      USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
904                 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
905                 if (rc != 0)
906                         goto cleanup;
907                 break;
908         case VID_PID(USB_VENDOR_ID_UCLOGIC,
909                      USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
910                 if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {
911                         if (bInterfaceNumber == 0) {
912                                 /* Try to probe v1 pen parameters */
913                                 rc = uclogic_params_pen_init_v1(&p.pen,
914                                                                 &found, hdev);
915                                 if (rc != 0) {
916                                         hid_err(hdev,
917                                                 "pen probing failed: %d\n",
918                                                 rc);
919                                         goto cleanup;
920                                 }
921                                 if (!found) {
922                                         hid_warn(hdev,
923                                                  "pen parameters not found");
924                                 }
925                         } else {
926                                 uclogic_params_init_invalid(&p);
927                         }
928                 } else {
929                         rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
930                         if (rc != 0)
931                                 goto cleanup;
932                 }
933                 break;
934         case VID_PID(USB_VENDOR_ID_UCLOGIC,
935                      USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
936                 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
937                 if (rc != 0)
938                         goto cleanup;
939                 break;
940         case VID_PID(USB_VENDOR_ID_UCLOGIC,
941                      USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
942                 rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
943                 if (rc != 0)
944                         goto cleanup;
945                 break;
946         case VID_PID(USB_VENDOR_ID_UCLOGIC,
947                      USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
948                 switch (bInterfaceNumber) {
949                 case 0:
950                         rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
951                         if (rc != 0)
952                                 goto cleanup;
953                         break;
954                 case 1:
955                         rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
956                         if (rc != 0)
957                                 goto cleanup;
958                         break;
959                 case 2:
960                         rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
961                         if (rc != 0)
962                                 goto cleanup;
963                         break;
964                 }
965                 break;
966         case VID_PID(USB_VENDOR_ID_UCLOGIC,
967                      USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
968                 /*
969                  * If it is not a three-interface version, which is known to
970                  * respond to initialization.
971                  */
972                 if (bNumInterfaces != 3) {
973                         switch (bInterfaceNumber) {
974                         case 0:
975                                 rc = WITH_OPT_DESC(TWHA60_ORIG0,
976                                                         twha60_fixed0);
977                                 if (rc != 0)
978                                         goto cleanup;
979                                 break;
980                         case 1:
981                                 rc = WITH_OPT_DESC(TWHA60_ORIG1,
982                                                         twha60_fixed1);
983                                 if (rc != 0)
984                                         goto cleanup;
985                                 break;
986                         }
987                         break;
988                 }
989                 fallthrough;
990         case VID_PID(USB_VENDOR_ID_HUION,
991                      USB_DEVICE_ID_HUION_TABLET):
992         case VID_PID(USB_VENDOR_ID_HUION,
993                      USB_DEVICE_ID_HUION_HS64):
994         case VID_PID(USB_VENDOR_ID_UCLOGIC,
995                      USB_DEVICE_ID_HUION_TABLET):
996         case VID_PID(USB_VENDOR_ID_UCLOGIC,
997                      USB_DEVICE_ID_YIYNOVA_TABLET):
998         case VID_PID(USB_VENDOR_ID_UCLOGIC,
999                      USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
1000         case VID_PID(USB_VENDOR_ID_UCLOGIC,
1001                      USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
1002         case VID_PID(USB_VENDOR_ID_UCLOGIC,
1003                      USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
1004         case VID_PID(USB_VENDOR_ID_UCLOGIC,
1005                      USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):
1006                 rc = uclogic_params_huion_init(&p, hdev);
1007                 if (rc != 0)
1008                         goto cleanup;
1009                 break;
1010         case VID_PID(USB_VENDOR_ID_UGTIZER,
1011                      USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
1012         case VID_PID(USB_VENDOR_ID_UGTIZER,
1013                      USB_DEVICE_ID_UGTIZER_TABLET_GT5040):
1014         case VID_PID(USB_VENDOR_ID_UGEE,
1015                      USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
1016         case VID_PID(USB_VENDOR_ID_UGEE,
1017                      USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
1018         case VID_PID(USB_VENDOR_ID_UGEE,
1019                      USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
1020                 /* If this is the pen interface */
1021                 if (bInterfaceNumber == 1) {
1022                         /* Probe v1 pen parameters */
1023                         rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1024                         if (rc != 0) {
1025                                 hid_err(hdev, "pen probing failed: %d\n", rc);
1026                                 goto cleanup;
1027                         }
1028                         if (!found) {
1029                                 hid_warn(hdev, "pen parameters not found");
1030                                 uclogic_params_init_invalid(&p);
1031                         }
1032                 } else {
1033                         /* TODO: Consider marking the interface invalid */
1034                         uclogic_params_init_with_pen_unused(&p);
1035                 }
1036                 break;
1037         case VID_PID(USB_VENDOR_ID_UGEE,
1038                      USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
1039                 /* If this is the pen and frame interface */
1040                 if (bInterfaceNumber == 1) {
1041                         /* Probe v1 pen parameters */
1042                         rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1043                         if (rc != 0) {
1044                                 hid_err(hdev, "pen probing failed: %d\n", rc);
1045                                 goto cleanup;
1046                         }
1047                         /* Initialize frame parameters */
1048                         rc = uclogic_params_frame_init_with_desc(
1049                                 &p.frame,
1050                                 uclogic_rdesc_xppen_deco01_frame_arr,
1051                                 uclogic_rdesc_xppen_deco01_frame_size,
1052                                 0);
1053                         if (rc != 0)
1054                                 goto cleanup;
1055                 } else {
1056                         /* TODO: Consider marking the interface invalid */
1057                         uclogic_params_init_with_pen_unused(&p);
1058                 }
1059                 break;
1060         case VID_PID(USB_VENDOR_ID_UGEE,
1061                      USB_DEVICE_ID_UGEE_TABLET_G5):
1062                 /* Ignore non-pen interfaces */
1063                 if (bInterfaceNumber != 1) {
1064                         uclogic_params_init_invalid(&p);
1065                         break;
1066                 }
1067
1068                 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1069                 if (rc != 0) {
1070                         hid_err(hdev, "pen probing failed: %d\n", rc);
1071                         goto cleanup;
1072                 } else if (found) {
1073                         rc = uclogic_params_frame_init_with_desc(
1074                                 &p.frame,
1075                                 uclogic_rdesc_ugee_g5_frame_arr,
1076                                 uclogic_rdesc_ugee_g5_frame_size,
1077                                 UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
1078                         if (rc != 0) {
1079                                 hid_err(hdev,
1080                                         "failed creating buttonpad parameters: %d\n",
1081                                         rc);
1082                                 goto cleanup;
1083                         }
1084                         p.frame.re_lsb =
1085                                 UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
1086                         p.frame.dev_id_byte =
1087                                 UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
1088                 } else {
1089                         hid_warn(hdev, "pen parameters not found");
1090                         uclogic_params_init_invalid(&p);
1091                 }
1092
1093                 break;
1094         case VID_PID(USB_VENDOR_ID_UGEE,
1095                      USB_DEVICE_ID_UGEE_TABLET_EX07S):
1096                 /* Ignore non-pen interfaces */
1097                 if (bInterfaceNumber != 1) {
1098                         uclogic_params_init_invalid(&p);
1099                         break;
1100                 }
1101
1102                 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1103                 if (rc != 0) {
1104                         hid_err(hdev, "pen probing failed: %d\n", rc);
1105                         goto cleanup;
1106                 } else if (found) {
1107                         rc = uclogic_params_frame_init_with_desc(
1108                                 &p.frame,
1109                                 uclogic_rdesc_ugee_ex07_buttonpad_arr,
1110                                 uclogic_rdesc_ugee_ex07_buttonpad_size,
1111                                 0);
1112                         if (rc != 0) {
1113                                 hid_err(hdev,
1114                                         "failed creating buttonpad parameters: %d\n",
1115                                         rc);
1116                                 goto cleanup;
1117                         }
1118                 } else {
1119                         hid_warn(hdev, "pen parameters not found");
1120                         uclogic_params_init_invalid(&p);
1121                 }
1122
1123                 break;
1124         }
1125
1126 #undef VID_PID
1127 #undef WITH_OPT_DESC
1128
1129         /* Output parameters */
1130         memcpy(params, &p, sizeof(*params));
1131         memset(&p, 0, sizeof(p));
1132         rc = 0;
1133 cleanup:
1134         uclogic_params_cleanup(&p);
1135         return rc;
1136 }