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