• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
uclogic_params_pen_inrange_to_str(enum uclogic_params_pen_inrange inrange)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  */
uclogic_params_get_str_desc(__u8 ** pbuf,struct hid_device * hdev,__u8 idx,size_t len)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  */
uclogic_params_pen_cleanup(struct uclogic_params_pen * pen)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  */
uclogic_params_pen_init_v1(struct uclogic_params_pen * pen,bool * pfound,struct hid_device * hdev)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  */
uclogic_params_get_le24(const void * p)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  */
uclogic_params_pen_init_v2(struct uclogic_params_pen * pen,bool * pfound,struct hid_device * hdev)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  */
uclogic_params_frame_cleanup(struct uclogic_params_frame * frame)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  */
uclogic_params_frame_init_with_desc(struct uclogic_params_frame * frame,const __u8 * desc_ptr,size_t desc_size,unsigned int id)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  */
uclogic_params_frame_init_v1_buttonpad(struct uclogic_params_frame * frame,bool * pfound,struct hid_device * hdev)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  */
uclogic_params_cleanup(struct uclogic_params * params)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  */
uclogic_params_get_desc(const struct uclogic_params * params,__u8 ** pdesc,unsigned int * psize)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  */
uclogic_params_init_invalid(struct uclogic_params * params)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  */
uclogic_params_init_with_opt_desc(struct uclogic_params * params,struct hid_device * hdev,unsigned int orig_desc_size,__u8 * desc_ptr,unsigned int desc_size)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  */
uclogic_params_init_with_pen_unused(struct uclogic_params * params)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  */
uclogic_params_huion_init(struct uclogic_params * params,struct hid_device * hdev)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  */
uclogic_params_init(struct uclogic_params * params,struct hid_device * hdev)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 }
1137