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