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(¶ms->pen);
517*4882a593Smuzhiyun uclogic_params_frame_cleanup(¶ms->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