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