Commit | Line | Data |
---|---|---|
9614219e NK |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * HID driver for UC-Logic devices not fully compliant with HID standard | |
4 | * - tablet initialization and parameter retrieval | |
5 | * | |
6 | * Copyright (c) 2018 Nikolai Kondrashov | |
7 | */ | |
8 | ||
9 | /* | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the Free | |
12 | * Software Foundation; either version 2 of the License, or (at your option) | |
13 | * any later version. | |
14 | */ | |
15 | ||
16 | #include "hid-uclogic-params.h" | |
17 | #include "hid-uclogic-rdesc.h" | |
18 | #include "usbhid/usbhid.h" | |
19 | #include "hid-ids.h" | |
20 | #include <linux/ctype.h> | |
f9ce4db0 | 21 | #include <linux/string.h> |
9614219e NK |
22 | #include <asm/unaligned.h> |
23 | ||
24 | /** | |
5abb5445 LJ |
25 | * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type |
26 | * to a string. | |
9614219e NK |
27 | * @inrange: The in-range reporting type to convert. |
28 | * | |
d5e649a5 BS |
29 | * Return: |
30 | * * The string representing the type, or | |
31 | * * %NULL if the type is unknown. | |
9614219e | 32 | */ |
a228809f NK |
33 | static const char *uclogic_params_pen_inrange_to_str( |
34 | enum uclogic_params_pen_inrange inrange) | |
9614219e NK |
35 | { |
36 | switch (inrange) { | |
37 | case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: | |
38 | return "normal"; | |
39 | case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: | |
40 | return "inverted"; | |
01309e29 NK |
41 | case UCLOGIC_PARAMS_PEN_INRANGE_NONE: |
42 | return "none"; | |
9614219e NK |
43 | default: |
44 | return NULL; | |
45 | } | |
46 | } | |
47 | ||
a228809f | 48 | /** |
d5e649a5 | 49 | * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters |
a228809f NK |
50 | * @hdev: The HID device the pen parameters describe. |
51 | * @pen: The pen parameters to dump. | |
d5e649a5 BS |
52 | * |
53 | * Dump tablet interface pen parameters with hid_dbg(). The dump is indented | |
54 | * with a tab. | |
a228809f NK |
55 | */ |
56 | static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, | |
57 | const struct uclogic_params_pen *pen) | |
58 | { | |
59 | size_t i; | |
60 | ||
61 | hid_dbg(hdev, "\t.usage_invalid = %s\n", | |
62 | (pen->usage_invalid ? "true" : "false")); | |
63 | hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr); | |
64 | hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size); | |
65 | hid_dbg(hdev, "\t.id = %u\n", pen->id); | |
66 | hid_dbg(hdev, "\t.subreport_list = {\n"); | |
67 | for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) { | |
68 | hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n", | |
69 | pen->subreport_list[i].value, | |
70 | pen->subreport_list[i].id, | |
71 | i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : ""); | |
72 | } | |
73 | hid_dbg(hdev, "\t}\n"); | |
74 | hid_dbg(hdev, "\t.inrange = %s\n", | |
75 | uclogic_params_pen_inrange_to_str(pen->inrange)); | |
76 | hid_dbg(hdev, "\t.fragmented_hires = %s\n", | |
77 | (pen->fragmented_hires ? "true" : "false")); | |
78 | hid_dbg(hdev, "\t.tilt_y_flipped = %s\n", | |
79 | (pen->tilt_y_flipped ? "true" : "false")); | |
80 | } | |
81 | ||
82 | /** | |
d5e649a5 | 83 | * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters |
a228809f NK |
84 | * @hdev: The HID device the pen parameters describe. |
85 | * @frame: The frame parameters to dump. | |
d5e649a5 BS |
86 | * |
87 | * Dump tablet interface frame parameters with hid_dbg(). The dump is | |
88 | * indented with two tabs. | |
a228809f NK |
89 | */ |
90 | static void uclogic_params_frame_hid_dbg( | |
91 | const struct hid_device *hdev, | |
92 | const struct uclogic_params_frame *frame) | |
93 | { | |
94 | hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr); | |
95 | hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size); | |
96 | hid_dbg(hdev, "\t\t.id = %u\n", frame->id); | |
97 | hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix); | |
98 | hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb); | |
99 | hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte); | |
caf7e934 NK |
100 | hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte); |
101 | hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max); | |
102 | hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n", | |
103 | frame->touch_flip_at); | |
a228809f NK |
104 | hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n", |
105 | frame->bitmap_dial_byte); | |
106 | } | |
107 | ||
108 | /** | |
d5e649a5 | 109 | * uclogic_params_hid_dbg() - Dump tablet interface parameters |
a228809f NK |
110 | * @hdev: The HID device the parameters describe. |
111 | * @params: The parameters to dump. | |
d5e649a5 BS |
112 | * |
113 | * Dump tablet interface parameters with hid_dbg(). | |
a228809f NK |
114 | */ |
115 | void uclogic_params_hid_dbg(const struct hid_device *hdev, | |
116 | const struct uclogic_params *params) | |
117 | { | |
118 | size_t i; | |
119 | ||
120 | hid_dbg(hdev, ".invalid = %s\n", | |
121 | params->invalid ? "true" : "false"); | |
122 | hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr); | |
123 | hid_dbg(hdev, ".desc_size = %u\n", params->desc_size); | |
124 | hid_dbg(hdev, ".pen = {\n"); | |
125 | uclogic_params_pen_hid_dbg(hdev, ¶ms->pen); | |
126 | hid_dbg(hdev, "\t}\n"); | |
127 | hid_dbg(hdev, ".frame_list = {\n"); | |
128 | for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { | |
129 | hid_dbg(hdev, "\t{\n"); | |
130 | uclogic_params_frame_hid_dbg(hdev, ¶ms->frame_list[i]); | |
131 | hid_dbg(hdev, "\t}%s\n", | |
132 | i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : ""); | |
133 | } | |
134 | hid_dbg(hdev, "}\n"); | |
135 | } | |
136 | ||
9614219e NK |
137 | /** |
138 | * uclogic_params_get_str_desc - retrieve a string descriptor from a HID | |
139 | * device interface, putting it into a kmalloc-allocated buffer as is, without | |
140 | * character encoding conversion. | |
141 | * | |
142 | * @pbuf: Location for the kmalloc-allocated buffer pointer containing | |
143 | * the retrieved descriptor. Not modified in case of error. | |
144 | * Can be NULL to have retrieved descriptor discarded. | |
145 | * @hdev: The HID device of the tablet interface to retrieve the string | |
146 | * descriptor from. Cannot be NULL. | |
147 | * @idx: Index of the string descriptor to request from the device. | |
148 | * @len: Length of the buffer to allocate and the data to retrieve. | |
149 | * | |
150 | * Returns: | |
151 | * number of bytes retrieved (<= len), | |
152 | * -EPIPE, if the descriptor was not found, or | |
153 | * another negative errno code in case of other error. | |
154 | */ | |
155 | static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, | |
156 | __u8 idx, size_t len) | |
157 | { | |
158 | int rc; | |
0a94131d | 159 | struct usb_device *udev; |
9614219e NK |
160 | __u8 *buf = NULL; |
161 | ||
162 | /* Check arguments */ | |
163 | if (hdev == NULL) { | |
164 | rc = -EINVAL; | |
165 | goto cleanup; | |
166 | } | |
167 | ||
0a94131d JE |
168 | udev = hid_to_usb_dev(hdev); |
169 | ||
9614219e NK |
170 | buf = kmalloc(len, GFP_KERNEL); |
171 | if (buf == NULL) { | |
172 | rc = -ENOMEM; | |
173 | goto cleanup; | |
174 | } | |
175 | ||
176 | rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), | |
177 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, | |
178 | (USB_DT_STRING << 8) + idx, | |
179 | 0x0409, buf, len, | |
180 | USB_CTRL_GET_TIMEOUT); | |
181 | if (rc == -EPIPE) { | |
182 | hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); | |
183 | goto cleanup; | |
184 | } else if (rc < 0) { | |
185 | hid_err(hdev, | |
a876e7e2 | 186 | "failed retrieving string descriptor #%u: %d\n", |
9614219e NK |
187 | idx, rc); |
188 | goto cleanup; | |
189 | } | |
190 | ||
191 | if (pbuf != NULL) { | |
192 | *pbuf = buf; | |
193 | buf = NULL; | |
194 | } | |
195 | ||
196 | cleanup: | |
197 | kfree(buf); | |
198 | return rc; | |
199 | } | |
200 | ||
201 | /** | |
202 | * uclogic_params_pen_cleanup - free resources used by struct | |
203 | * uclogic_params_pen (tablet interface's pen input parameters). | |
204 | * Can be called repeatedly. | |
205 | * | |
206 | * @pen: Pen input parameters to cleanup. Cannot be NULL. | |
207 | */ | |
208 | static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) | |
209 | { | |
210 | kfree(pen->desc_ptr); | |
211 | memset(pen, 0, sizeof(*pen)); | |
212 | } | |
213 | ||
214 | /** | |
eecb5b84 NK |
215 | * uclogic_params_pen_init_v1() - initialize tablet interface pen |
216 | * input and retrieve its parameters from the device, using v1 protocol. | |
9614219e NK |
217 | * |
218 | * @pen: Pointer to the pen parameters to initialize (to be | |
219 | * cleaned up with uclogic_params_pen_cleanup()). Not modified in | |
220 | * case of error, or if parameters are not found. Cannot be NULL. | |
221 | * @pfound: Location for a flag which is set to true if the parameters | |
222 | * were found, and to false if not (e.g. device was | |
223 | * incompatible). Not modified in case of error. Cannot be NULL. | |
224 | * @hdev: The HID device of the tablet interface to initialize and get | |
225 | * parameters from. Cannot be NULL. | |
226 | * | |
227 | * Returns: | |
228 | * Zero, if successful. A negative errno code on error. | |
229 | */ | |
eecb5b84 NK |
230 | static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, |
231 | bool *pfound, | |
232 | struct hid_device *hdev) | |
9614219e NK |
233 | { |
234 | int rc; | |
235 | bool found = false; | |
236 | /* Buffer for (part of) the string descriptor */ | |
237 | __u8 *buf = NULL; | |
238 | /* Minimum descriptor length required, maximum seen so far is 18 */ | |
239 | const int len = 12; | |
240 | s32 resolution; | |
241 | /* Pen report descriptor template parameters */ | |
76e645be | 242 | s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; |
9614219e NK |
243 | __u8 *desc_ptr = NULL; |
244 | ||
245 | /* Check arguments */ | |
246 | if (pen == NULL || pfound == NULL || hdev == NULL) { | |
247 | rc = -EINVAL; | |
248 | goto cleanup; | |
249 | } | |
250 | ||
251 | /* | |
252 | * Read string descriptor containing pen input parameters. | |
253 | * The specific string descriptor and data were discovered by sniffing | |
254 | * the Windows driver traffic. | |
255 | * NOTE: This enables fully-functional tablet mode. | |
256 | */ | |
257 | rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); | |
258 | if (rc == -EPIPE) { | |
259 | hid_dbg(hdev, | |
260 | "string descriptor with pen parameters not found, assuming not compatible\n"); | |
261 | goto finish; | |
262 | } else if (rc < 0) { | |
263 | hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); | |
264 | goto cleanup; | |
265 | } else if (rc != len) { | |
266 | hid_dbg(hdev, | |
267 | "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", | |
268 | rc, len); | |
269 | goto finish; | |
270 | } | |
271 | ||
272 | /* | |
273 | * Fill report descriptor parameters from the string descriptor | |
274 | */ | |
275 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = | |
276 | get_unaligned_le16(buf + 2); | |
277 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = | |
278 | get_unaligned_le16(buf + 4); | |
279 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = | |
280 | get_unaligned_le16(buf + 8); | |
281 | resolution = get_unaligned_le16(buf + 10); | |
282 | if (resolution == 0) { | |
283 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; | |
284 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; | |
285 | } else { | |
286 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = | |
287 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / | |
288 | resolution; | |
289 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = | |
290 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / | |
291 | resolution; | |
292 | } | |
293 | kfree(buf); | |
294 | buf = NULL; | |
295 | ||
296 | /* | |
297 | * Generate pen report descriptor | |
298 | */ | |
299 | desc_ptr = uclogic_rdesc_template_apply( | |
a985de58 NK |
300 | uclogic_rdesc_v1_pen_template_arr, |
301 | uclogic_rdesc_v1_pen_template_size, | |
9614219e NK |
302 | desc_params, ARRAY_SIZE(desc_params)); |
303 | if (desc_ptr == NULL) { | |
304 | rc = -ENOMEM; | |
305 | goto cleanup; | |
306 | } | |
307 | ||
308 | /* | |
309 | * Fill-in the parameters | |
310 | */ | |
311 | memset(pen, 0, sizeof(*pen)); | |
312 | pen->desc_ptr = desc_ptr; | |
313 | desc_ptr = NULL; | |
a985de58 NK |
314 | pen->desc_size = uclogic_rdesc_v1_pen_template_size; |
315 | pen->id = UCLOGIC_RDESC_V1_PEN_ID; | |
9614219e NK |
316 | pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; |
317 | found = true; | |
318 | finish: | |
319 | *pfound = found; | |
320 | rc = 0; | |
321 | cleanup: | |
322 | kfree(desc_ptr); | |
323 | kfree(buf); | |
324 | return rc; | |
325 | } | |
326 | ||
2c3a88c6 NK |
327 | /** |
328 | * uclogic_params_get_le24() - get a 24-bit little-endian number from a | |
329 | * buffer. | |
330 | * | |
331 | * @p: The pointer to the number buffer. | |
332 | * | |
333 | * Returns: | |
334 | * The retrieved number | |
335 | */ | |
336 | static s32 uclogic_params_get_le24(const void *p) | |
337 | { | |
338 | const __u8 *b = p; | |
339 | return b[0] | (b[1] << 8UL) | (b[2] << 16UL); | |
340 | } | |
341 | ||
342 | /** | |
343 | * uclogic_params_pen_init_v2() - initialize tablet interface pen | |
344 | * input and retrieve its parameters from the device, using v2 protocol. | |
345 | * | |
945d5dd5 NK |
346 | * @pen: Pointer to the pen parameters to initialize (to be |
347 | * cleaned up with uclogic_params_pen_cleanup()). Not | |
348 | * modified in case of error, or if parameters are not | |
349 | * found. Cannot be NULL. | |
350 | * @pfound: Location for a flag which is set to true if the | |
351 | * parameters were found, and to false if not (e.g. | |
352 | * device was incompatible). Not modified in case of | |
353 | * error. Cannot be NULL. | |
354 | * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw | |
355 | * parameters, which could be used to identify the tablet | |
356 | * to some extent. Should be freed with kfree after use. | |
357 | * NULL, if not needed. Not modified in case of error. | |
358 | * Only set if *pfound is set to true. | |
359 | * @pparams_len: Location for the length of the retrieved raw | |
360 | * parameters. NULL, if not needed. Not modified in case | |
361 | * of error. Only set if *pfound is set to true. | |
362 | * @hdev: The HID device of the tablet interface to initialize | |
363 | * and get parameters from. Cannot be NULL. | |
2c3a88c6 NK |
364 | * |
365 | * Returns: | |
366 | * Zero, if successful. A negative errno code on error. | |
367 | */ | |
368 | static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, | |
369 | bool *pfound, | |
945d5dd5 NK |
370 | __u8 **pparams_ptr, |
371 | size_t *pparams_len, | |
2c3a88c6 NK |
372 | struct hid_device *hdev) |
373 | { | |
374 | int rc; | |
375 | bool found = false; | |
945d5dd5 | 376 | /* Buffer for (part of) the parameter string descriptor */ |
2c3a88c6 | 377 | __u8 *buf = NULL; |
945d5dd5 NK |
378 | /* Parameter string descriptor required length */ |
379 | const int params_len_min = 18; | |
380 | /* Parameter string descriptor accepted length */ | |
381 | const int params_len_max = 32; | |
382 | /* Parameter string descriptor received length */ | |
383 | int params_len; | |
384 | size_t i; | |
2c3a88c6 NK |
385 | s32 resolution; |
386 | /* Pen report descriptor template parameters */ | |
76e645be | 387 | s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; |
2c3a88c6 NK |
388 | __u8 *desc_ptr = NULL; |
389 | ||
390 | /* Check arguments */ | |
391 | if (pen == NULL || pfound == NULL || hdev == NULL) { | |
392 | rc = -EINVAL; | |
393 | goto cleanup; | |
394 | } | |
395 | ||
396 | /* | |
397 | * Read string descriptor containing pen input parameters. | |
398 | * The specific string descriptor and data were discovered by sniffing | |
399 | * the Windows driver traffic. | |
400 | * NOTE: This enables fully-functional tablet mode. | |
401 | */ | |
945d5dd5 | 402 | rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max); |
2c3a88c6 NK |
403 | if (rc == -EPIPE) { |
404 | hid_dbg(hdev, | |
405 | "string descriptor with pen parameters not found, assuming not compatible\n"); | |
406 | goto finish; | |
407 | } else if (rc < 0) { | |
408 | hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); | |
409 | goto cleanup; | |
945d5dd5 | 410 | } else if (rc < params_len_min) { |
2c3a88c6 | 411 | hid_dbg(hdev, |
945d5dd5 NK |
412 | "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n", |
413 | rc, params_len_min); | |
414 | goto finish; | |
415 | } | |
416 | ||
417 | params_len = rc; | |
418 | ||
419 | /* | |
420 | * Check it's not just a catch-all UTF-16LE-encoded ASCII | |
421 | * string (such as the model name) some tablets put into all | |
422 | * unknown string descriptors. | |
423 | */ | |
424 | for (i = 2; | |
425 | i < params_len && | |
426 | (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); | |
427 | i += 2); | |
428 | if (i >= params_len) { | |
429 | hid_dbg(hdev, | |
430 | "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); | |
2c3a88c6 | 431 | goto finish; |
2c3a88c6 NK |
432 | } |
433 | ||
434 | /* | |
435 | * Fill report descriptor parameters from the string descriptor | |
436 | */ | |
437 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = | |
438 | uclogic_params_get_le24(buf + 2); | |
439 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = | |
440 | uclogic_params_get_le24(buf + 5); | |
441 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = | |
442 | get_unaligned_le16(buf + 8); | |
443 | resolution = get_unaligned_le16(buf + 10); | |
444 | if (resolution == 0) { | |
445 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; | |
446 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; | |
447 | } else { | |
448 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = | |
449 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / | |
450 | resolution; | |
451 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = | |
452 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / | |
453 | resolution; | |
454 | } | |
2c3a88c6 NK |
455 | |
456 | /* | |
457 | * Generate pen report descriptor | |
458 | */ | |
459 | desc_ptr = uclogic_rdesc_template_apply( | |
a985de58 NK |
460 | uclogic_rdesc_v2_pen_template_arr, |
461 | uclogic_rdesc_v2_pen_template_size, | |
2c3a88c6 NK |
462 | desc_params, ARRAY_SIZE(desc_params)); |
463 | if (desc_ptr == NULL) { | |
464 | rc = -ENOMEM; | |
465 | goto cleanup; | |
466 | } | |
467 | ||
468 | /* | |
469 | * Fill-in the parameters | |
470 | */ | |
471 | memset(pen, 0, sizeof(*pen)); | |
472 | pen->desc_ptr = desc_ptr; | |
473 | desc_ptr = NULL; | |
a985de58 NK |
474 | pen->desc_size = uclogic_rdesc_v2_pen_template_size; |
475 | pen->id = UCLOGIC_RDESC_V2_PEN_ID; | |
2c3a88c6 NK |
476 | pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; |
477 | pen->fragmented_hires = true; | |
1324c5ac | 478 | pen->tilt_y_flipped = true; |
2c3a88c6 | 479 | found = true; |
945d5dd5 NK |
480 | if (pparams_ptr != NULL) { |
481 | *pparams_ptr = buf; | |
482 | buf = NULL; | |
483 | } | |
484 | if (pparams_len != NULL) | |
485 | *pparams_len = params_len; | |
486 | ||
2c3a88c6 NK |
487 | finish: |
488 | *pfound = found; | |
489 | rc = 0; | |
490 | cleanup: | |
491 | kfree(desc_ptr); | |
492 | kfree(buf); | |
493 | return rc; | |
494 | } | |
495 | ||
9614219e NK |
496 | /** |
497 | * uclogic_params_frame_cleanup - free resources used by struct | |
498 | * uclogic_params_frame (tablet interface's frame controls input parameters). | |
499 | * Can be called repeatedly. | |
500 | * | |
501 | * @frame: Frame controls input parameters to cleanup. Cannot be NULL. | |
502 | */ | |
503 | static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) | |
504 | { | |
505 | kfree(frame->desc_ptr); | |
506 | memset(frame, 0, sizeof(*frame)); | |
507 | } | |
508 | ||
509 | /** | |
510 | * uclogic_params_frame_init_with_desc() - initialize tablet's frame control | |
511 | * parameters with a static report descriptor. | |
512 | * | |
513 | * @frame: Pointer to the frame parameters to initialize (to be cleaned | |
514 | * up with uclogic_params_frame_cleanup()). Not modified in case | |
515 | * of error. Cannot be NULL. | |
516 | * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. | |
517 | * @desc_size: Report descriptor size. | |
518 | * @id: Report ID used for frame reports, if they should be tweaked, | |
519 | * zero if not. | |
520 | * | |
521 | * Returns: | |
522 | * Zero, if successful. A negative errno code on error. | |
523 | */ | |
524 | static int uclogic_params_frame_init_with_desc( | |
525 | struct uclogic_params_frame *frame, | |
526 | const __u8 *desc_ptr, | |
527 | size_t desc_size, | |
528 | unsigned int id) | |
529 | { | |
530 | __u8 *copy_desc_ptr; | |
531 | ||
532 | if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) | |
533 | return -EINVAL; | |
534 | ||
535 | copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); | |
536 | if (copy_desc_ptr == NULL) | |
537 | return -ENOMEM; | |
538 | ||
539 | memset(frame, 0, sizeof(*frame)); | |
540 | frame->desc_ptr = copy_desc_ptr; | |
541 | frame->desc_size = desc_size; | |
542 | frame->id = id; | |
543 | return 0; | |
544 | } | |
545 | ||
546 | /** | |
2e28f3e0 NK |
547 | * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame |
548 | * controls. | |
9614219e NK |
549 | * |
550 | * @frame: Pointer to the frame parameters to initialize (to be cleaned | |
551 | * up with uclogic_params_frame_cleanup()). Not modified in case | |
552 | * of error, or if parameters are not found. Cannot be NULL. | |
553 | * @pfound: Location for a flag which is set to true if the parameters | |
554 | * were found, and to false if not (e.g. device was | |
555 | * incompatible). Not modified in case of error. Cannot be NULL. | |
556 | * @hdev: The HID device of the tablet interface to initialize and get | |
557 | * parameters from. Cannot be NULL. | |
558 | * | |
559 | * Returns: | |
560 | * Zero, if successful. A negative errno code on error. | |
561 | */ | |
2e28f3e0 | 562 | static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame, |
9614219e NK |
563 | bool *pfound, |
564 | struct hid_device *hdev) | |
565 | { | |
566 | int rc; | |
567 | bool found = false; | |
aa320fdb | 568 | struct usb_device *usb_dev; |
9614219e NK |
569 | char *str_buf = NULL; |
570 | const size_t str_len = 16; | |
571 | ||
572 | /* Check arguments */ | |
573 | if (frame == NULL || pfound == NULL || hdev == NULL) { | |
574 | rc = -EINVAL; | |
575 | goto cleanup; | |
576 | } | |
577 | ||
aa320fdb JE |
578 | usb_dev = hid_to_usb_dev(hdev); |
579 | ||
9614219e NK |
580 | /* |
581 | * Enable generic button mode | |
582 | */ | |
583 | str_buf = kzalloc(str_len, GFP_KERNEL); | |
584 | if (str_buf == NULL) { | |
585 | rc = -ENOMEM; | |
586 | goto cleanup; | |
587 | } | |
588 | ||
589 | rc = usb_string(usb_dev, 123, str_buf, str_len); | |
590 | if (rc == -EPIPE) { | |
591 | hid_dbg(hdev, | |
592 | "generic button -enabling string descriptor not found\n"); | |
593 | } else if (rc < 0) { | |
594 | goto cleanup; | |
595 | } else if (strncmp(str_buf, "HK On", rc) != 0) { | |
596 | hid_dbg(hdev, | |
597 | "invalid response to enabling generic buttons: \"%s\"\n", | |
598 | str_buf); | |
599 | } else { | |
600 | hid_dbg(hdev, "generic buttons enabled\n"); | |
601 | rc = uclogic_params_frame_init_with_desc( | |
602 | frame, | |
a985de58 NK |
603 | uclogic_rdesc_v1_frame_arr, |
604 | uclogic_rdesc_v1_frame_size, | |
605 | UCLOGIC_RDESC_V1_FRAME_ID); | |
9614219e NK |
606 | if (rc != 0) |
607 | goto cleanup; | |
608 | found = true; | |
609 | } | |
610 | ||
611 | *pfound = found; | |
612 | rc = 0; | |
613 | cleanup: | |
614 | kfree(str_buf); | |
615 | return rc; | |
616 | } | |
617 | ||
a251d657 JE |
618 | /** |
619 | * uclogic_params_cleanup_event_hooks - free resources used by the list of raw | |
620 | * event hooks. | |
621 | * Can be called repeatedly. | |
622 | * | |
623 | * @params: Input parameters to cleanup. Cannot be NULL. | |
624 | */ | |
625 | static void uclogic_params_cleanup_event_hooks(struct uclogic_params *params) | |
626 | { | |
627 | struct uclogic_raw_event_hook *curr, *n; | |
628 | ||
629 | if (!params || !params->event_hooks) | |
630 | return; | |
631 | ||
632 | list_for_each_entry_safe(curr, n, ¶ms->event_hooks->list, list) { | |
633 | cancel_work_sync(&curr->work); | |
634 | list_del(&curr->list); | |
635 | kfree(curr->event); | |
636 | kfree(curr); | |
637 | } | |
638 | ||
639 | kfree(params->event_hooks); | |
640 | params->event_hooks = NULL; | |
641 | } | |
642 | ||
9614219e NK |
643 | /** |
644 | * uclogic_params_cleanup - free resources used by struct uclogic_params | |
645 | * (tablet interface's parameters). | |
646 | * Can be called repeatedly. | |
647 | * | |
648 | * @params: Input parameters to cleanup. Cannot be NULL. | |
649 | */ | |
650 | void uclogic_params_cleanup(struct uclogic_params *params) | |
651 | { | |
652 | if (!params->invalid) { | |
337fa051 | 653 | size_t i; |
9614219e | 654 | kfree(params->desc_ptr); |
606dadc1 | 655 | uclogic_params_pen_cleanup(¶ms->pen); |
337fa051 NK |
656 | for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) |
657 | uclogic_params_frame_cleanup(¶ms->frame_list[i]); | |
658 | ||
a251d657 | 659 | uclogic_params_cleanup_event_hooks(params); |
9614219e NK |
660 | memset(params, 0, sizeof(*params)); |
661 | } | |
662 | } | |
663 | ||
664 | /** | |
5abb5445 LJ |
665 | * uclogic_params_get_desc() - Get a replacement report descriptor for a |
666 | * tablet's interface. | |
9614219e NK |
667 | * |
668 | * @params: The parameters of a tablet interface to get report | |
669 | * descriptor for. Cannot be NULL. | |
670 | * @pdesc: Location for the resulting, kmalloc-allocated report | |
671 | * descriptor pointer, or for NULL, if there's no replacement | |
672 | * report descriptor. Not modified in case of error. Cannot be | |
673 | * NULL. | |
674 | * @psize: Location for the resulting report descriptor size, not set if | |
675 | * there's no replacement report descriptor. Not modified in case | |
676 | * of error. Cannot be NULL. | |
677 | * | |
678 | * Returns: | |
679 | * Zero, if successful. | |
680 | * -EINVAL, if invalid arguments are supplied. | |
681 | * -ENOMEM, if failed to allocate memory. | |
682 | */ | |
683 | int uclogic_params_get_desc(const struct uclogic_params *params, | |
684 | __u8 **pdesc, | |
685 | unsigned int *psize) | |
686 | { | |
337fa051 NK |
687 | int rc = -ENOMEM; |
688 | bool present = false; | |
689 | unsigned int size = 0; | |
9614219e | 690 | __u8 *desc = NULL; |
337fa051 | 691 | size_t i; |
9614219e NK |
692 | |
693 | /* Check arguments */ | |
694 | if (params == NULL || pdesc == NULL || psize == NULL) | |
695 | return -EINVAL; | |
696 | ||
337fa051 NK |
697 | /* Concatenate descriptors */ |
698 | #define ADD_DESC(_desc_ptr, _desc_size) \ | |
699 | do { \ | |
700 | unsigned int new_size; \ | |
701 | __u8 *new_desc; \ | |
702 | if ((_desc_ptr) == NULL) { \ | |
703 | break; \ | |
704 | } \ | |
705 | new_size = size + (_desc_size); \ | |
706 | new_desc = krealloc(desc, new_size, GFP_KERNEL); \ | |
707 | if (new_desc == NULL) { \ | |
708 | goto cleanup; \ | |
709 | } \ | |
710 | memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \ | |
711 | desc = new_desc; \ | |
712 | size = new_size; \ | |
713 | present = true; \ | |
714 | } while (0) | |
715 | ||
716 | ADD_DESC(params->desc_ptr, params->desc_size); | |
717 | ADD_DESC(params->pen.desc_ptr, params->pen.desc_size); | |
718 | for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { | |
719 | ADD_DESC(params->frame_list[i].desc_ptr, | |
720 | params->frame_list[i].desc_size); | |
721 | } | |
9614219e | 722 | |
337fa051 | 723 | #undef ADD_DESC |
9614219e | 724 | |
337fa051 NK |
725 | if (present) { |
726 | *pdesc = desc; | |
9614219e | 727 | *psize = size; |
337fa051 | 728 | desc = NULL; |
9614219e | 729 | } |
337fa051 NK |
730 | rc = 0; |
731 | cleanup: | |
732 | kfree(desc); | |
733 | return rc; | |
9614219e NK |
734 | } |
735 | ||
736 | /** | |
737 | * uclogic_params_init_invalid() - initialize tablet interface parameters, | |
738 | * specifying the interface is invalid. | |
739 | * | |
740 | * @params: Parameters to initialize (to be cleaned with | |
741 | * uclogic_params_cleanup()). Cannot be NULL. | |
742 | */ | |
743 | static void uclogic_params_init_invalid(struct uclogic_params *params) | |
744 | { | |
745 | params->invalid = true; | |
746 | } | |
747 | ||
748 | /** | |
749 | * uclogic_params_init_with_opt_desc() - initialize tablet interface | |
750 | * parameters with an optional replacement report descriptor. Only modify | |
751 | * report descriptor, if the original report descriptor matches the expected | |
752 | * size. | |
753 | * | |
754 | * @params: Parameters to initialize (to be cleaned with | |
755 | * uclogic_params_cleanup()). Not modified in case of | |
756 | * error. Cannot be NULL. | |
757 | * @hdev: The HID device of the tablet interface create the | |
758 | * parameters for. Cannot be NULL. | |
759 | * @orig_desc_size: Expected size of the original report descriptor to | |
760 | * be replaced. | |
761 | * @desc_ptr: Pointer to the replacement report descriptor. | |
762 | * Can be NULL, if desc_size is zero. | |
763 | * @desc_size: Size of the replacement report descriptor. | |
764 | * | |
765 | * Returns: | |
766 | * Zero, if successful. -EINVAL if an invalid argument was passed. | |
767 | * -ENOMEM, if failed to allocate memory. | |
768 | */ | |
769 | static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, | |
770 | struct hid_device *hdev, | |
771 | unsigned int orig_desc_size, | |
772 | __u8 *desc_ptr, | |
773 | unsigned int desc_size) | |
774 | { | |
775 | __u8 *desc_copy_ptr = NULL; | |
776 | unsigned int desc_copy_size; | |
777 | int rc; | |
778 | ||
779 | /* Check arguments */ | |
780 | if (params == NULL || hdev == NULL || | |
781 | (desc_ptr == NULL && desc_size != 0)) { | |
782 | rc = -EINVAL; | |
783 | goto cleanup; | |
784 | } | |
785 | ||
786 | /* Replace report descriptor, if it matches */ | |
787 | if (hdev->dev_rsize == orig_desc_size) { | |
788 | hid_dbg(hdev, | |
789 | "device report descriptor matches the expected size, replacing\n"); | |
790 | desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); | |
791 | if (desc_copy_ptr == NULL) { | |
792 | rc = -ENOMEM; | |
793 | goto cleanup; | |
794 | } | |
795 | desc_copy_size = desc_size; | |
796 | } else { | |
797 | hid_dbg(hdev, | |
798 | "device report descriptor doesn't match the expected size (%u != %u), preserving\n", | |
799 | hdev->dev_rsize, orig_desc_size); | |
800 | desc_copy_ptr = NULL; | |
801 | desc_copy_size = 0; | |
802 | } | |
803 | ||
804 | /* Output parameters */ | |
805 | memset(params, 0, sizeof(*params)); | |
806 | params->desc_ptr = desc_copy_ptr; | |
807 | desc_copy_ptr = NULL; | |
808 | params->desc_size = desc_copy_size; | |
809 | ||
810 | rc = 0; | |
811 | cleanup: | |
812 | kfree(desc_copy_ptr); | |
813 | return rc; | |
814 | } | |
815 | ||
9614219e | 816 | /** |
5abb5445 | 817 | * uclogic_params_huion_init() - initialize a Huion tablet interface and discover |
9614219e NK |
818 | * its parameters. |
819 | * | |
820 | * @params: Parameters to fill in (to be cleaned with | |
821 | * uclogic_params_cleanup()). Not modified in case of error. | |
822 | * Cannot be NULL. | |
823 | * @hdev: The HID device of the tablet interface to initialize and get | |
824 | * parameters from. Cannot be NULL. | |
825 | * | |
826 | * Returns: | |
827 | * Zero, if successful. A negative errno code on error. | |
828 | */ | |
829 | static int uclogic_params_huion_init(struct uclogic_params *params, | |
830 | struct hid_device *hdev) | |
831 | { | |
832 | int rc; | |
ff6b548a JE |
833 | struct usb_device *udev; |
834 | struct usb_interface *iface; | |
835 | __u8 bInterfaceNumber; | |
9614219e NK |
836 | bool found; |
837 | /* The resulting parameters (noop) */ | |
838 | struct uclogic_params p = {0, }; | |
2c3a88c6 NK |
839 | static const char transition_ver[] = "HUION_T153_160607"; |
840 | char *ver_ptr = NULL; | |
841 | const size_t ver_len = sizeof(transition_ver) + 1; | |
118dfdea NK |
842 | __u8 *params_ptr = NULL; |
843 | size_t params_len = 0; | |
844 | /* Parameters string descriptor of a model with touch ring (HS610) */ | |
845 | const __u8 touch_ring_model_params_buf[] = { | |
846 | 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, | |
847 | 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, | |
848 | 0x04, 0x3C, 0x3E | |
849 | }; | |
9614219e NK |
850 | |
851 | /* Check arguments */ | |
852 | if (params == NULL || hdev == NULL) { | |
853 | rc = -EINVAL; | |
854 | goto cleanup; | |
855 | } | |
856 | ||
ff6b548a JE |
857 | udev = hid_to_usb_dev(hdev); |
858 | iface = to_usb_interface(hdev->dev.parent); | |
859 | bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; | |
860 | ||
d64a6e44 NK |
861 | /* If it's a custom keyboard interface */ |
862 | if (bInterfaceNumber == 1) { | |
4c60bc7d NK |
863 | /* Keep everything intact, but mark pen usage invalid */ |
864 | p.pen.usage_invalid = true; | |
d64a6e44 NK |
865 | goto output; |
866 | /* Else, if it's not a pen interface */ | |
867 | } else if (bInterfaceNumber != 0) { | |
606dadc1 | 868 | uclogic_params_init_invalid(&p); |
9614219e NK |
869 | goto output; |
870 | } | |
871 | ||
2c3a88c6 NK |
872 | /* Try to get firmware version */ |
873 | ver_ptr = kzalloc(ver_len, GFP_KERNEL); | |
874 | if (ver_ptr == NULL) { | |
875 | rc = -ENOMEM; | |
876 | goto cleanup; | |
877 | } | |
878 | rc = usb_string(udev, 201, ver_ptr, ver_len); | |
2c3a88c6 NK |
879 | if (rc == -EPIPE) { |
880 | *ver_ptr = '\0'; | |
881 | } else if (rc < 0) { | |
882 | hid_err(hdev, | |
883 | "failed retrieving Huion firmware version: %d\n", rc); | |
884 | goto cleanup; | |
885 | } | |
886 | ||
887 | /* If this is a transition firmware */ | |
888 | if (strcmp(ver_ptr, transition_ver) == 0) { | |
889 | hid_dbg(hdev, | |
890 | "transition firmware detected, not probing pen v2 parameters\n"); | |
891 | } else { | |
892 | /* Try to probe v2 pen parameters */ | |
945d5dd5 | 893 | rc = uclogic_params_pen_init_v2(&p.pen, &found, |
118dfdea NK |
894 | ¶ms_ptr, ¶ms_len, |
895 | hdev); | |
2c3a88c6 NK |
896 | if (rc != 0) { |
897 | hid_err(hdev, | |
898 | "failed probing pen v2 parameters: %d\n", rc); | |
899 | goto cleanup; | |
900 | } else if (found) { | |
901 | hid_dbg(hdev, "pen v2 parameters found\n"); | |
c3e6e59a | 902 | /* Create v2 frame button parameters */ |
2c3a88c6 | 903 | rc = uclogic_params_frame_init_with_desc( |
337fa051 | 904 | &p.frame_list[0], |
c3e6e59a NK |
905 | uclogic_rdesc_v2_frame_buttons_arr, |
906 | uclogic_rdesc_v2_frame_buttons_size, | |
907 | UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); | |
2c3a88c6 NK |
908 | if (rc != 0) { |
909 | hid_err(hdev, | |
c3e6e59a | 910 | "failed creating v2 frame button parameters: %d\n", |
2c3a88c6 NK |
911 | rc); |
912 | goto cleanup; | |
913 | } | |
c3e6e59a | 914 | |
118dfdea NK |
915 | /* Link from pen sub-report */ |
916 | p.pen.subreport_list[0].value = 0xe0; | |
917 | p.pen.subreport_list[0].id = | |
918 | UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; | |
919 | ||
920 | /* If this is the model with touch ring */ | |
921 | if (params_ptr != NULL && | |
922 | params_len == sizeof(touch_ring_model_params_buf) && | |
923 | memcmp(params_ptr, touch_ring_model_params_buf, | |
924 | params_len) == 0) { | |
925 | /* Create touch ring parameters */ | |
926 | rc = uclogic_params_frame_init_with_desc( | |
c3e6e59a NK |
927 | &p.frame_list[1], |
928 | uclogic_rdesc_v2_frame_touch_ring_arr, | |
929 | uclogic_rdesc_v2_frame_touch_ring_size, | |
caf7e934 | 930 | UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); |
118dfdea NK |
931 | if (rc != 0) { |
932 | hid_err(hdev, | |
933 | "failed creating v2 frame touch ring parameters: %d\n", | |
934 | rc); | |
935 | goto cleanup; | |
936 | } | |
937 | p.frame_list[1].suffix = "Touch Ring"; | |
938 | p.frame_list[1].dev_id_byte = | |
939 | UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; | |
940 | p.frame_list[1].touch_byte = 5; | |
941 | p.frame_list[1].touch_max = 12; | |
942 | p.frame_list[1].touch_flip_at = 7; | |
943 | } else { | |
944 | /* Create touch strip parameters */ | |
945 | rc = uclogic_params_frame_init_with_desc( | |
946 | &p.frame_list[1], | |
947 | uclogic_rdesc_v2_frame_touch_strip_arr, | |
948 | uclogic_rdesc_v2_frame_touch_strip_size, | |
949 | UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); | |
950 | if (rc != 0) { | |
951 | hid_err(hdev, | |
952 | "failed creating v2 frame touch strip parameters: %d\n", | |
953 | rc); | |
954 | goto cleanup; | |
955 | } | |
956 | p.frame_list[1].suffix = "Touch Strip"; | |
957 | p.frame_list[1].dev_id_byte = | |
958 | UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; | |
959 | p.frame_list[1].touch_byte = 5; | |
960 | p.frame_list[1].touch_max = 8; | |
c3e6e59a | 961 | } |
118dfdea NK |
962 | |
963 | /* Link from pen sub-report */ | |
964 | p.pen.subreport_list[1].value = 0xf0; | |
965 | p.pen.subreport_list[1].id = | |
966 | UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; | |
c3e6e59a | 967 | |
6facd076 NK |
968 | /* Create v2 frame dial parameters */ |
969 | rc = uclogic_params_frame_init_with_desc( | |
970 | &p.frame_list[2], | |
971 | uclogic_rdesc_v2_frame_dial_arr, | |
972 | uclogic_rdesc_v2_frame_dial_size, | |
973 | UCLOGIC_RDESC_V2_FRAME_DIAL_ID); | |
974 | if (rc != 0) { | |
975 | hid_err(hdev, | |
976 | "failed creating v2 frame dial parameters: %d\n", | |
977 | rc); | |
978 | goto cleanup; | |
979 | } | |
980 | p.frame_list[2].suffix = "Dial"; | |
981 | p.frame_list[2].dev_id_byte = | |
982 | UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; | |
983 | p.frame_list[2].bitmap_dial_byte = 5; | |
984 | ||
118dfdea | 985 | /* Link from pen sub-report */ |
6facd076 NK |
986 | p.pen.subreport_list[2].value = 0xf1; |
987 | p.pen.subreport_list[2].id = | |
988 | UCLOGIC_RDESC_V2_FRAME_DIAL_ID; | |
118dfdea | 989 | |
2c3a88c6 NK |
990 | goto output; |
991 | } | |
992 | hid_dbg(hdev, "pen v2 parameters not found\n"); | |
993 | } | |
994 | ||
eecb5b84 NK |
995 | /* Try to probe v1 pen parameters */ |
996 | rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); | |
9614219e NK |
997 | if (rc != 0) { |
998 | hid_err(hdev, | |
eecb5b84 | 999 | "failed probing pen v1 parameters: %d\n", rc); |
9614219e NK |
1000 | goto cleanup; |
1001 | } else if (found) { | |
eecb5b84 | 1002 | hid_dbg(hdev, "pen v1 parameters found\n"); |
2e28f3e0 | 1003 | /* Try to probe v1 frame */ |
337fa051 | 1004 | rc = uclogic_params_frame_init_v1(&p.frame_list[0], |
2e28f3e0 | 1005 | &found, hdev); |
9614219e | 1006 | if (rc != 0) { |
2e28f3e0 | 1007 | hid_err(hdev, "v1 frame probing failed: %d\n", rc); |
9614219e NK |
1008 | goto cleanup; |
1009 | } | |
2e28f3e0 | 1010 | hid_dbg(hdev, "frame v1 parameters%s found\n", |
9614219e NK |
1011 | (found ? "" : " not")); |
1012 | if (found) { | |
8b013098 | 1013 | /* Link frame button subreports from pen reports */ |
e6be956f | 1014 | p.pen.subreport_list[0].value = 0xe0; |
8b013098 | 1015 | p.pen.subreport_list[0].id = |
a985de58 | 1016 | UCLOGIC_RDESC_V1_FRAME_ID; |
9614219e NK |
1017 | } |
1018 | goto output; | |
1019 | } | |
eecb5b84 | 1020 | hid_dbg(hdev, "pen v1 parameters not found\n"); |
9614219e NK |
1021 | |
1022 | uclogic_params_init_invalid(&p); | |
1023 | ||
1024 | output: | |
1025 | /* Output parameters */ | |
1026 | memcpy(params, &p, sizeof(*params)); | |
1027 | memset(&p, 0, sizeof(p)); | |
1028 | rc = 0; | |
1029 | cleanup: | |
118dfdea | 1030 | kfree(params_ptr); |
2c3a88c6 | 1031 | kfree(ver_ptr); |
9614219e NK |
1032 | uclogic_params_cleanup(&p); |
1033 | return rc; | |
1034 | } | |
1035 | ||
0cb1fc09 JE |
1036 | /** |
1037 | * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or | |
1038 | * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data. | |
1039 | * | |
1040 | * @hdev: The HID device of the tablet interface to initialize and get | |
1041 | * parameters from. Cannot be NULL. | |
1042 | * @magic_arr: The magic data that should be sent to probe the interface. | |
1043 | * Cannot be NULL. | |
1044 | * @magic_size: Size of the magic data. | |
1045 | * @endpoint: Endpoint where the magic data should be sent. | |
1046 | * | |
1047 | * Returns: | |
1048 | * Zero, if successful. A negative errno code on error. | |
1049 | */ | |
bd85c131 JE |
1050 | static int uclogic_probe_interface(struct hid_device *hdev, const u8 *magic_arr, |
1051 | size_t magic_size, int endpoint) | |
0cb1fc09 JE |
1052 | { |
1053 | struct usb_device *udev; | |
1054 | unsigned int pipe = 0; | |
1055 | int sent; | |
1056 | u8 *buf = NULL; | |
1057 | int rc = 0; | |
1058 | ||
1059 | if (!hdev || !magic_arr) { | |
1060 | rc = -EINVAL; | |
1061 | goto cleanup; | |
1062 | } | |
1063 | ||
1064 | buf = kmemdup(magic_arr, magic_size, GFP_KERNEL); | |
1065 | if (!buf) { | |
1066 | rc = -ENOMEM; | |
1067 | goto cleanup; | |
1068 | } | |
1069 | ||
1070 | udev = hid_to_usb_dev(hdev); | |
1071 | pipe = usb_sndintpipe(udev, endpoint); | |
1072 | ||
1073 | rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000); | |
1074 | if (rc || sent != magic_size) { | |
1075 | hid_err(hdev, "Interface probing failed: %d\n", rc); | |
1076 | rc = -1; | |
1077 | goto cleanup; | |
1078 | } | |
1079 | ||
1080 | rc = 0; | |
1081 | cleanup: | |
1082 | kfree(buf); | |
1083 | return rc; | |
1084 | } | |
1085 | ||
a64cbf3c JE |
1086 | /** |
1087 | * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing | |
1088 | * pen and frame parameters returned by UGEE v2 devices. | |
1089 | * | |
1090 | * @str_desc: String descriptor, cannot be NULL. | |
1091 | * @str_desc_size: Size of the string descriptor. | |
1092 | * @desc_params: Output description params list. | |
1093 | * @desc_params_size: Size of the output description params list. | |
a092986f | 1094 | * @frame_type: Output frame type. |
a64cbf3c JE |
1095 | * |
1096 | * Returns: | |
1097 | * Zero, if successful. A negative errno code on error. | |
1098 | */ | |
1099 | static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc, | |
1100 | size_t str_desc_size, | |
1101 | s32 *desc_params, | |
a092986f JE |
1102 | size_t desc_params_size, |
1103 | enum uclogic_params_frame_type *frame_type) | |
a64cbf3c JE |
1104 | { |
1105 | s32 pen_x_lm, pen_y_lm; | |
1106 | s32 pen_x_pm, pen_y_pm; | |
1107 | s32 pen_pressure_lm; | |
1108 | s32 frame_num_buttons; | |
1109 | s32 resolution; | |
1110 | ||
1111 | /* Minimum descriptor length required, maximum seen so far is 14 */ | |
1112 | const int min_str_desc_size = 12; | |
1113 | ||
1114 | if (!str_desc || str_desc_size < min_str_desc_size) | |
1115 | return -EINVAL; | |
1116 | ||
1117 | if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) | |
1118 | return -EINVAL; | |
1119 | ||
1120 | pen_x_lm = get_unaligned_le16(str_desc + 2); | |
1121 | pen_y_lm = get_unaligned_le16(str_desc + 4); | |
1122 | frame_num_buttons = str_desc[6]; | |
a092986f | 1123 | *frame_type = str_desc[7]; |
a64cbf3c JE |
1124 | pen_pressure_lm = get_unaligned_le16(str_desc + 8); |
1125 | ||
1126 | resolution = get_unaligned_le16(str_desc + 10); | |
1127 | if (resolution == 0) { | |
1128 | pen_x_pm = 0; | |
1129 | pen_y_pm = 0; | |
1130 | } else { | |
1131 | pen_x_pm = pen_x_lm * 1000 / resolution; | |
1132 | pen_y_pm = pen_y_lm * 1000 / resolution; | |
1133 | } | |
1134 | ||
1135 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm; | |
1136 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm; | |
1137 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm; | |
1138 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm; | |
1139 | desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm; | |
1140 | desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons; | |
1141 | ||
1142 | return 0; | |
1143 | } | |
1144 | ||
86402296 JE |
1145 | /** |
1146 | * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with | |
1147 | * buttons. | |
1148 | * @p: Parameters to fill in, cannot be NULL. | |
1149 | * @desc_params: Device description params list. | |
1150 | * @desc_params_size: Size of the description params list. | |
1151 | * | |
1152 | * Returns: | |
1153 | * Zero, if successful. A negative errno code on error. | |
1154 | */ | |
1155 | static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p, | |
1156 | const s32 *desc_params, | |
1157 | size_t desc_params_size) | |
1158 | { | |
1159 | __u8 *rdesc_frame = NULL; | |
1160 | int rc = 0; | |
1161 | ||
1162 | if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) | |
1163 | return -EINVAL; | |
1164 | ||
1165 | rdesc_frame = uclogic_rdesc_template_apply( | |
1166 | uclogic_rdesc_ugee_v2_frame_btn_template_arr, | |
1167 | uclogic_rdesc_ugee_v2_frame_btn_template_size, | |
1168 | desc_params, UCLOGIC_RDESC_PH_ID_NUM); | |
1169 | if (!rdesc_frame) | |
1170 | return -ENOMEM; | |
1171 | ||
1172 | rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], | |
1173 | rdesc_frame, | |
1174 | uclogic_rdesc_ugee_v2_frame_btn_template_size, | |
1175 | UCLOGIC_RDESC_V1_FRAME_ID); | |
1176 | kfree(rdesc_frame); | |
1177 | return rc; | |
1178 | } | |
1179 | ||
b67439d7 JE |
1180 | /** |
1181 | * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a | |
1182 | * bitmap dial. | |
1183 | * @p: Parameters to fill in, cannot be NULL. | |
1184 | * @desc_params: Device description params list. | |
1185 | * @desc_params_size: Size of the description params list. | |
1186 | * | |
1187 | * Returns: | |
1188 | * Zero, if successful. A negative errno code on error. | |
1189 | */ | |
1190 | static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p, | |
1191 | const s32 *desc_params, | |
1192 | size_t desc_params_size) | |
1193 | { | |
1194 | __u8 *rdesc_frame = NULL; | |
1195 | int rc = 0; | |
1196 | ||
1197 | if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) | |
1198 | return -EINVAL; | |
1199 | ||
1200 | rdesc_frame = uclogic_rdesc_template_apply( | |
1201 | uclogic_rdesc_ugee_v2_frame_dial_template_arr, | |
1202 | uclogic_rdesc_ugee_v2_frame_dial_template_size, | |
1203 | desc_params, UCLOGIC_RDESC_PH_ID_NUM); | |
1204 | if (!rdesc_frame) | |
1205 | return -ENOMEM; | |
1206 | ||
1207 | rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], | |
1208 | rdesc_frame, | |
1209 | uclogic_rdesc_ugee_v2_frame_dial_template_size, | |
1210 | UCLOGIC_RDESC_V1_FRAME_ID); | |
1211 | kfree(rdesc_frame); | |
1212 | if (rc) | |
1213 | return rc; | |
1214 | ||
1215 | p->frame_list[0].bitmap_dial_byte = 7; | |
1216 | return 0; | |
1217 | } | |
1218 | ||
387dcab7 JE |
1219 | /** |
1220 | * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a | |
1221 | * mouse. | |
1222 | * @p: Parameters to fill in, cannot be NULL. | |
1223 | * | |
1224 | * Returns: | |
1225 | * Zero, if successful. A negative errno code on error. | |
1226 | */ | |
1227 | static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p) | |
1228 | { | |
1229 | int rc = 0; | |
1230 | ||
1231 | if (!p) | |
1232 | return -EINVAL; | |
1233 | ||
1234 | rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], | |
1235 | uclogic_rdesc_ugee_v2_frame_mouse_template_arr, | |
1236 | uclogic_rdesc_ugee_v2_frame_mouse_template_size, | |
1237 | UCLOGIC_RDESC_V1_FRAME_ID); | |
1238 | return rc; | |
1239 | } | |
1240 | ||
f9ce4db0 JE |
1241 | /** |
1242 | * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has | |
1243 | * battery or not. | |
1244 | * @hdev: The HID device of the tablet interface. | |
1245 | * | |
1246 | * Returns: | |
1247 | * True if the device has battery, false otherwise. | |
1248 | */ | |
1249 | static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev) | |
1250 | { | |
f60c377f JE |
1251 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); |
1252 | ||
1253 | if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK) | |
1254 | return true; | |
1255 | ||
f9ce4db0 JE |
1256 | /* The XP-PEN Deco LW vendor, product and version are identical to the |
1257 | * Deco L. The only difference reported by their firmware is the product | |
1258 | * name. Add a quirk to support battery reporting on the wireless | |
1259 | * version. | |
1260 | */ | |
1261 | if (hdev->vendor == USB_VENDOR_ID_UGEE && | |
1262 | hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) { | |
1263 | struct usb_device *udev = hid_to_usb_dev(hdev); | |
1264 | ||
1265 | if (strstarts(udev->product, "Deco LW")) | |
1266 | return true; | |
1267 | } | |
1268 | ||
1269 | return false; | |
1270 | } | |
1271 | ||
1272 | /** | |
1273 | * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting. | |
1274 | * @hdev: The HID device of the tablet interface, cannot be NULL. | |
1275 | * @p: Parameters to fill in, cannot be NULL. | |
1276 | * | |
1277 | * Returns: | |
1278 | * Zero, if successful. A negative errno code on error. | |
1279 | */ | |
1280 | static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev, | |
1281 | struct uclogic_params *p) | |
1282 | { | |
1283 | int rc = 0; | |
1284 | ||
1285 | if (!hdev || !p) | |
1286 | return -EINVAL; | |
1287 | ||
1288 | /* Some tablets contain invalid characters in hdev->uniq, throwing a | |
1289 | * "hwmon: '<name>' is not a valid name attribute, please fix" error. | |
1290 | * Use the device vendor and product IDs instead. | |
1291 | */ | |
1292 | snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor, | |
1293 | hdev->product); | |
1294 | ||
1295 | rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], | |
1296 | uclogic_rdesc_ugee_v2_battery_template_arr, | |
1297 | uclogic_rdesc_ugee_v2_battery_template_size, | |
1298 | UCLOGIC_RDESC_UGEE_V2_BATTERY_ID); | |
1299 | if (rc) | |
1300 | return rc; | |
1301 | ||
1302 | p->frame_list[1].suffix = "Battery"; | |
1303 | p->pen.subreport_list[1].value = 0xf2; | |
1304 | p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID; | |
1305 | ||
1306 | return rc; | |
1307 | } | |
1308 | ||
a251d657 JE |
1309 | /** |
1310 | * uclogic_params_ugee_v2_reconnect_work() - When a wireless tablet looses | |
1311 | * connection to the USB dongle and reconnects, either because of its physical | |
1312 | * distance or because it was switches off and on using the frame's switch, | |
1313 | * uclogic_probe_interface() needs to be called again to enable the tablet. | |
1314 | * | |
1315 | * @work: The work that triggered this function. | |
1316 | */ | |
1317 | static void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work) | |
1318 | { | |
1319 | struct uclogic_raw_event_hook *event_hook; | |
1320 | ||
1321 | event_hook = container_of(work, struct uclogic_raw_event_hook, work); | |
1322 | uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr, | |
1323 | uclogic_ugee_v2_probe_size, | |
1324 | uclogic_ugee_v2_probe_endpoint); | |
1325 | } | |
1326 | ||
1327 | /** | |
1328 | * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events | |
1329 | * to be hooked for UGEE v2 devices. | |
1330 | * @hdev: The HID device of the tablet interface to initialize and get | |
1331 | * parameters from. | |
1332 | * @p: Parameters to fill in, cannot be NULL. | |
1333 | * | |
1334 | * Returns: | |
1335 | * Zero, if successful. A negative errno code on error. | |
1336 | */ | |
1337 | static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev, | |
1338 | struct uclogic_params *p) | |
1339 | { | |
1340 | struct uclogic_raw_event_hook *event_hook; | |
1341 | __u8 reconnect_event[] = { | |
1342 | /* Event received on wireless tablet reconnection */ | |
1343 | 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | |
1344 | }; | |
1345 | ||
1346 | if (!p) | |
1347 | return -EINVAL; | |
1348 | ||
1349 | /* The reconnection event is only received if the tablet has battery */ | |
1350 | if (!uclogic_params_ugee_v2_has_battery(hdev)) | |
1351 | return 0; | |
1352 | ||
1353 | p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL); | |
1354 | if (!p->event_hooks) | |
1355 | return -ENOMEM; | |
1356 | ||
1357 | INIT_LIST_HEAD(&p->event_hooks->list); | |
1358 | ||
1359 | event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL); | |
1360 | if (!event_hook) | |
1361 | return -ENOMEM; | |
1362 | ||
1363 | INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work); | |
1364 | event_hook->hdev = hdev; | |
1365 | event_hook->size = ARRAY_SIZE(reconnect_event); | |
1366 | event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL); | |
1367 | if (!event_hook->event) | |
1368 | return -ENOMEM; | |
1369 | ||
1370 | list_add_tail(&event_hook->list, &p->event_hooks->list); | |
1371 | ||
1372 | return 0; | |
1373 | } | |
1374 | ||
0cb1fc09 JE |
1375 | /** |
1376 | * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by | |
1377 | * discovering their parameters. | |
1378 | * | |
1379 | * These tables, internally designed as v2 to differentiate them from older | |
1380 | * models, expect a payload of magic data in orther to be switched to the fully | |
1381 | * functional mode and expose their parameters in a similar way to the | |
1382 | * information present in uclogic_params_pen_init_v1() but with some | |
1383 | * differences. | |
1384 | * | |
1385 | * @params: Parameters to fill in (to be cleaned with | |
1386 | * uclogic_params_cleanup()). Not modified in case of error. | |
1387 | * Cannot be NULL. | |
1388 | * @hdev: The HID device of the tablet interface to initialize and get | |
1389 | * parameters from. Cannot be NULL. | |
1390 | * | |
1391 | * Returns: | |
1392 | * Zero, if successful. A negative errno code on error. | |
1393 | */ | |
1394 | static int uclogic_params_ugee_v2_init(struct uclogic_params *params, | |
1395 | struct hid_device *hdev) | |
1396 | { | |
1397 | int rc = 0; | |
14b71e6a | 1398 | struct uclogic_drvdata *drvdata; |
0cb1fc09 JE |
1399 | struct usb_interface *iface; |
1400 | __u8 bInterfaceNumber; | |
1401 | const int str_desc_len = 12; | |
1402 | __u8 *str_desc = NULL; | |
1403 | __u8 *rdesc_pen = NULL; | |
0cb1fc09 | 1404 | s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; |
a092986f | 1405 | enum uclogic_params_frame_type frame_type; |
0cb1fc09 JE |
1406 | /* The resulting parameters (noop) */ |
1407 | struct uclogic_params p = {0, }; | |
1408 | ||
1409 | if (!params || !hdev) { | |
1410 | rc = -EINVAL; | |
1411 | goto cleanup; | |
1412 | } | |
1413 | ||
14b71e6a | 1414 | drvdata = hid_get_drvdata(hdev); |
0cb1fc09 JE |
1415 | iface = to_usb_interface(hdev->dev.parent); |
1416 | bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; | |
387dcab7 JE |
1417 | |
1418 | if (bInterfaceNumber == 0) { | |
1419 | rc = uclogic_params_ugee_v2_init_frame_mouse(&p); | |
1420 | if (rc) | |
1421 | goto cleanup; | |
1422 | ||
1423 | goto output; | |
1424 | } | |
1425 | ||
0cb1fc09 JE |
1426 | if (bInterfaceNumber != 2) { |
1427 | uclogic_params_init_invalid(&p); | |
1428 | goto output; | |
1429 | } | |
1430 | ||
1431 | /* | |
1432 | * Initialize the interface by sending magic data. | |
1433 | * The specific data was discovered by sniffing the Windows driver | |
1434 | * traffic. | |
1435 | */ | |
bd85c131 JE |
1436 | rc = uclogic_probe_interface(hdev, uclogic_ugee_v2_probe_arr, |
1437 | uclogic_ugee_v2_probe_size, | |
1438 | uclogic_ugee_v2_probe_endpoint); | |
0cb1fc09 JE |
1439 | if (rc) { |
1440 | uclogic_params_init_invalid(&p); | |
1441 | goto output; | |
1442 | } | |
1443 | ||
1444 | /* | |
1445 | * Read the string descriptor containing pen and frame parameters. | |
1446 | * The specific string descriptor and data were discovered by sniffing | |
1447 | * the Windows driver traffic. | |
1448 | */ | |
1449 | rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); | |
1450 | if (rc != str_desc_len) { | |
1451 | hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); | |
1452 | uclogic_params_init_invalid(&p); | |
1453 | goto output; | |
1454 | } | |
1455 | ||
a64cbf3c JE |
1456 | rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len, |
1457 | desc_params, | |
a092986f JE |
1458 | ARRAY_SIZE(desc_params), |
1459 | &frame_type); | |
a64cbf3c JE |
1460 | if (rc) |
1461 | goto cleanup; | |
1462 | ||
0cb1fc09 JE |
1463 | kfree(str_desc); |
1464 | str_desc = NULL; | |
1465 | ||
1466 | /* Initialize the pen interface */ | |
1467 | rdesc_pen = uclogic_rdesc_template_apply( | |
1468 | uclogic_rdesc_ugee_v2_pen_template_arr, | |
1469 | uclogic_rdesc_ugee_v2_pen_template_size, | |
1470 | desc_params, ARRAY_SIZE(desc_params)); | |
1471 | if (!rdesc_pen) { | |
1472 | rc = -ENOMEM; | |
1473 | goto cleanup; | |
1474 | } | |
1475 | ||
1476 | p.pen.desc_ptr = rdesc_pen; | |
1477 | p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; | |
1478 | p.pen.id = 0x02; | |
1479 | p.pen.subreport_list[0].value = 0xf0; | |
1480 | p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; | |
1481 | ||
1482 | /* Initialize the frame interface */ | |
14b71e6a JE |
1483 | if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK) |
1484 | frame_type = UCLOGIC_PARAMS_FRAME_MOUSE; | |
1485 | ||
a092986f | 1486 | switch (frame_type) { |
b67439d7 JE |
1487 | case UCLOGIC_PARAMS_FRAME_DIAL: |
1488 | case UCLOGIC_PARAMS_FRAME_MOUSE: | |
1489 | rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params, | |
1490 | ARRAY_SIZE(desc_params)); | |
1491 | break; | |
a092986f JE |
1492 | case UCLOGIC_PARAMS_FRAME_BUTTONS: |
1493 | default: | |
1494 | rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params, | |
1495 | ARRAY_SIZE(desc_params)); | |
1496 | break; | |
1497 | } | |
1498 | ||
86402296 | 1499 | if (rc) |
0cb1fc09 | 1500 | goto cleanup; |
0cb1fc09 | 1501 | |
f9ce4db0 JE |
1502 | /* Initialize the battery interface*/ |
1503 | if (uclogic_params_ugee_v2_has_battery(hdev)) { | |
1504 | rc = uclogic_params_ugee_v2_init_battery(hdev, &p); | |
1505 | if (rc) { | |
1506 | hid_err(hdev, "error initializing battery: %d\n", rc); | |
1507 | goto cleanup; | |
1508 | } | |
1509 | } | |
1510 | ||
a251d657 JE |
1511 | /* Create a list of raw events to be ignored */ |
1512 | rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p); | |
1513 | if (rc) { | |
1514 | hid_err(hdev, "error initializing event hook list: %d\n", rc); | |
1515 | goto cleanup; | |
1516 | } | |
1517 | ||
0cb1fc09 JE |
1518 | output: |
1519 | /* Output parameters */ | |
1520 | memcpy(params, &p, sizeof(*params)); | |
1521 | memset(&p, 0, sizeof(p)); | |
1522 | rc = 0; | |
1523 | cleanup: | |
1524 | kfree(str_desc); | |
1525 | uclogic_params_cleanup(&p); | |
1526 | return rc; | |
1527 | } | |
1528 | ||
9614219e NK |
1529 | /** |
1530 | * uclogic_params_init() - initialize a tablet interface and discover its | |
1531 | * parameters. | |
1532 | * | |
1533 | * @params: Parameters to fill in (to be cleaned with | |
1534 | * uclogic_params_cleanup()). Not modified in case of error. | |
1535 | * Cannot be NULL. | |
1536 | * @hdev: The HID device of the tablet interface to initialize and get | |
8547b778 NK |
1537 | * parameters from. Cannot be NULL. Must be using the USB low-level |
1538 | * driver, i.e. be an actual USB tablet. | |
9614219e NK |
1539 | * |
1540 | * Returns: | |
1541 | * Zero, if successful. A negative errno code on error. | |
1542 | */ | |
1543 | int uclogic_params_init(struct uclogic_params *params, | |
1544 | struct hid_device *hdev) | |
1545 | { | |
1546 | int rc; | |
f364c571 JE |
1547 | struct usb_device *udev; |
1548 | __u8 bNumInterfaces; | |
1549 | struct usb_interface *iface; | |
1550 | __u8 bInterfaceNumber; | |
9614219e NK |
1551 | bool found; |
1552 | /* The resulting parameters (noop) */ | |
1553 | struct uclogic_params p = {0, }; | |
1554 | ||
1555 | /* Check arguments */ | |
f83baa0c | 1556 | if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { |
9614219e NK |
1557 | rc = -EINVAL; |
1558 | goto cleanup; | |
1559 | } | |
1560 | ||
f364c571 JE |
1561 | udev = hid_to_usb_dev(hdev); |
1562 | bNumInterfaces = udev->config->desc.bNumInterfaces; | |
1563 | iface = to_usb_interface(hdev->dev.parent); | |
1564 | bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; | |
1565 | ||
9614219e NK |
1566 | /* |
1567 | * Set replacement report descriptor if the original matches the | |
1568 | * specified size. Otherwise keep interface unchanged. | |
1569 | */ | |
1570 | #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ | |
1571 | uclogic_params_init_with_opt_desc( \ | |
1572 | &p, hdev, \ | |
1573 | UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ | |
1574 | uclogic_rdesc_##_new_desc_token##_arr, \ | |
1575 | uclogic_rdesc_##_new_desc_token##_size) | |
1576 | ||
1577 | #define VID_PID(_vid, _pid) \ | |
1578 | (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) | |
1579 | ||
1580 | /* | |
1581 | * Handle specific interfaces for specific tablets. | |
1582 | * | |
1583 | * Observe the following logic: | |
1584 | * | |
1585 | * If the interface is recognized as producing certain useful input: | |
1586 | * Mark interface as valid. | |
1587 | * Output interface parameters. | |
1588 | * Else, if the interface is recognized as *not* producing any useful | |
1589 | * input: | |
1590 | * Mark interface as invalid. | |
1591 | * Else: | |
1592 | * Mark interface as valid. | |
1593 | * Output noop parameters. | |
1594 | * | |
1595 | * Rule of thumb: it is better to disable a broken interface than let | |
1596 | * it spew garbage input. | |
1597 | */ | |
1598 | ||
1599 | switch (VID_PID(hdev->vendor, hdev->product)) { | |
1600 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1601 | USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): | |
1602 | rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); | |
1603 | if (rc != 0) | |
1604 | goto cleanup; | |
1605 | break; | |
1606 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1607 | USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): | |
1608 | rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); | |
1609 | if (rc != 0) | |
1610 | goto cleanup; | |
1611 | break; | |
1612 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1613 | USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): | |
9c17f735 NK |
1614 | if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { |
1615 | if (bInterfaceNumber == 0) { | |
1616 | /* Try to probe v1 pen parameters */ | |
1617 | rc = uclogic_params_pen_init_v1(&p.pen, | |
1618 | &found, hdev); | |
1619 | if (rc != 0) { | |
1620 | hid_err(hdev, | |
1621 | "pen probing failed: %d\n", | |
1622 | rc); | |
1623 | goto cleanup; | |
1624 | } | |
1625 | if (!found) { | |
1626 | hid_warn(hdev, | |
1627 | "pen parameters not found"); | |
1628 | } | |
1629 | } else { | |
1630 | uclogic_params_init_invalid(&p); | |
1631 | } | |
1632 | } else { | |
1633 | rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); | |
1634 | if (rc != 0) | |
1635 | goto cleanup; | |
1636 | } | |
9614219e NK |
1637 | break; |
1638 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1639 | USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): | |
1640 | rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); | |
1641 | if (rc != 0) | |
1642 | goto cleanup; | |
1643 | break; | |
1644 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1645 | USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): | |
1646 | rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); | |
1647 | if (rc != 0) | |
1648 | goto cleanup; | |
1649 | break; | |
1650 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1651 | USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): | |
1652 | switch (bInterfaceNumber) { | |
1653 | case 0: | |
1654 | rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); | |
1655 | if (rc != 0) | |
1656 | goto cleanup; | |
1657 | break; | |
1658 | case 1: | |
1659 | rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); | |
1660 | if (rc != 0) | |
1661 | goto cleanup; | |
1662 | break; | |
1663 | case 2: | |
1664 | rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); | |
1665 | if (rc != 0) | |
1666 | goto cleanup; | |
1667 | break; | |
1668 | } | |
1669 | break; | |
1670 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1671 | USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): | |
1672 | /* | |
1673 | * If it is not a three-interface version, which is known to | |
1674 | * respond to initialization. | |
1675 | */ | |
1676 | if (bNumInterfaces != 3) { | |
1677 | switch (bInterfaceNumber) { | |
1678 | case 0: | |
1679 | rc = WITH_OPT_DESC(TWHA60_ORIG0, | |
1680 | twha60_fixed0); | |
1681 | if (rc != 0) | |
1682 | goto cleanup; | |
1683 | break; | |
1684 | case 1: | |
1685 | rc = WITH_OPT_DESC(TWHA60_ORIG1, | |
1686 | twha60_fixed1); | |
1687 | if (rc != 0) | |
1688 | goto cleanup; | |
1689 | break; | |
1690 | } | |
1691 | break; | |
1692 | } | |
df561f66 | 1693 | fallthrough; |
9614219e NK |
1694 | case VID_PID(USB_VENDOR_ID_HUION, |
1695 | USB_DEVICE_ID_HUION_TABLET): | |
315ffcc9 | 1696 | case VID_PID(USB_VENDOR_ID_HUION, |
85e86071 | 1697 | USB_DEVICE_ID_HUION_TABLET2): |
9614219e NK |
1698 | case VID_PID(USB_VENDOR_ID_UCLOGIC, |
1699 | USB_DEVICE_ID_HUION_TABLET): | |
1700 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1701 | USB_DEVICE_ID_YIYNOVA_TABLET): | |
1702 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1703 | USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): | |
1704 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1705 | USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): | |
1706 | case VID_PID(USB_VENDOR_ID_UCLOGIC, | |
1707 | USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): | |
0c15efe9 NK |
1708 | case VID_PID(USB_VENDOR_ID_UCLOGIC, |
1709 | USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): | |
9614219e NK |
1710 | rc = uclogic_params_huion_init(&p, hdev); |
1711 | if (rc != 0) | |
1712 | goto cleanup; | |
1713 | break; | |
1714 | case VID_PID(USB_VENDOR_ID_UGTIZER, | |
1715 | USB_DEVICE_ID_UGTIZER_TABLET_GP0610): | |
022fc531 MS |
1716 | case VID_PID(USB_VENDOR_ID_UGTIZER, |
1717 | USB_DEVICE_ID_UGTIZER_TABLET_GT5040): | |
c3e5a67c NK |
1718 | case VID_PID(USB_VENDOR_ID_UGEE, |
1719 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): | |
492a9e9a NK |
1720 | case VID_PID(USB_VENDOR_ID_UGEE, |
1721 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): | |
61b1db5a RR |
1722 | case VID_PID(USB_VENDOR_ID_UGEE, |
1723 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06): | |
88bb346d WX |
1724 | case VID_PID(USB_VENDOR_ID_UGEE, |
1725 | USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): | |
9614219e NK |
1726 | /* If this is the pen interface */ |
1727 | if (bInterfaceNumber == 1) { | |
eecb5b84 NK |
1728 | /* Probe v1 pen parameters */ |
1729 | rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); | |
9614219e NK |
1730 | if (rc != 0) { |
1731 | hid_err(hdev, "pen probing failed: %d\n", rc); | |
1732 | goto cleanup; | |
1733 | } | |
1734 | if (!found) { | |
1735 | hid_warn(hdev, "pen parameters not found"); | |
1736 | uclogic_params_init_invalid(&p); | |
1737 | } | |
1738 | } else { | |
606dadc1 | 1739 | uclogic_params_init_invalid(&p); |
08367be1 NK |
1740 | } |
1741 | break; | |
1742 | case VID_PID(USB_VENDOR_ID_UGEE, | |
1743 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): | |
1744 | /* If this is the pen and frame interface */ | |
1745 | if (bInterfaceNumber == 1) { | |
1746 | /* Probe v1 pen parameters */ | |
1747 | rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); | |
1748 | if (rc != 0) { | |
1749 | hid_err(hdev, "pen probing failed: %d\n", rc); | |
1750 | goto cleanup; | |
1751 | } | |
1752 | /* Initialize frame parameters */ | |
1753 | rc = uclogic_params_frame_init_with_desc( | |
337fa051 | 1754 | &p.frame_list[0], |
08367be1 NK |
1755 | uclogic_rdesc_xppen_deco01_frame_arr, |
1756 | uclogic_rdesc_xppen_deco01_frame_size, | |
1757 | 0); | |
1758 | if (rc != 0) | |
1759 | goto cleanup; | |
1760 | } else { | |
606dadc1 | 1761 | uclogic_params_init_invalid(&p); |
9614219e | 1762 | } |
e902ed93 | 1763 | break; |
7495fb7e JE |
1764 | case VID_PID(USB_VENDOR_ID_UGEE, |
1765 | USB_DEVICE_ID_UGEE_PARBLO_A610_PRO): | |
0cb1fc09 JE |
1766 | case VID_PID(USB_VENDOR_ID_UGEE, |
1767 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): | |
93b40b3e JE |
1768 | case VID_PID(USB_VENDOR_ID_UGEE, |
1769 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S): | |
7744ca57 JE |
1770 | case VID_PID(USB_VENDOR_ID_UGEE, |
1771 | USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW): | |
0cb1fc09 JE |
1772 | rc = uclogic_params_ugee_v2_init(&p, hdev); |
1773 | if (rc != 0) | |
1774 | goto cleanup; | |
1775 | break; | |
f7271b2a CK |
1776 | case VID_PID(USB_VENDOR_ID_TRUST, |
1777 | USB_DEVICE_ID_TRUST_PANORA_TABLET): | |
e902ed93 NK |
1778 | case VID_PID(USB_VENDOR_ID_UGEE, |
1779 | USB_DEVICE_ID_UGEE_TABLET_G5): | |
1780 | /* Ignore non-pen interfaces */ | |
1781 | if (bInterfaceNumber != 1) { | |
1782 | uclogic_params_init_invalid(&p); | |
1783 | break; | |
1784 | } | |
1785 | ||
1786 | rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); | |
1787 | if (rc != 0) { | |
1788 | hid_err(hdev, "pen probing failed: %d\n", rc); | |
1789 | goto cleanup; | |
1790 | } else if (found) { | |
1791 | rc = uclogic_params_frame_init_with_desc( | |
337fa051 | 1792 | &p.frame_list[0], |
e902ed93 NK |
1793 | uclogic_rdesc_ugee_g5_frame_arr, |
1794 | uclogic_rdesc_ugee_g5_frame_size, | |
1795 | UCLOGIC_RDESC_UGEE_G5_FRAME_ID); | |
1796 | if (rc != 0) { | |
1797 | hid_err(hdev, | |
2e28f3e0 | 1798 | "failed creating frame parameters: %d\n", |
e902ed93 NK |
1799 | rc); |
1800 | goto cleanup; | |
1801 | } | |
337fa051 | 1802 | p.frame_list[0].re_lsb = |
e902ed93 | 1803 | UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; |
337fa051 | 1804 | p.frame_list[0].dev_id_byte = |
e902ed93 NK |
1805 | UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; |
1806 | } else { | |
1807 | hid_warn(hdev, "pen parameters not found"); | |
1808 | uclogic_params_init_invalid(&p); | |
1809 | } | |
1810 | ||
1ee7c685 NK |
1811 | break; |
1812 | case VID_PID(USB_VENDOR_ID_UGEE, | |
1813 | USB_DEVICE_ID_UGEE_TABLET_EX07S): | |
1814 | /* Ignore non-pen interfaces */ | |
1815 | if (bInterfaceNumber != 1) { | |
1816 | uclogic_params_init_invalid(&p); | |
1817 | break; | |
1818 | } | |
1819 | ||
1820 | rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); | |
1821 | if (rc != 0) { | |
1822 | hid_err(hdev, "pen probing failed: %d\n", rc); | |
1823 | goto cleanup; | |
1824 | } else if (found) { | |
1825 | rc = uclogic_params_frame_init_with_desc( | |
337fa051 | 1826 | &p.frame_list[0], |
2e28f3e0 NK |
1827 | uclogic_rdesc_ugee_ex07_frame_arr, |
1828 | uclogic_rdesc_ugee_ex07_frame_size, | |
1ee7c685 NK |
1829 | 0); |
1830 | if (rc != 0) { | |
1831 | hid_err(hdev, | |
2e28f3e0 | 1832 | "failed creating frame parameters: %d\n", |
1ee7c685 NK |
1833 | rc); |
1834 | goto cleanup; | |
1835 | } | |
1836 | } else { | |
1837 | hid_warn(hdev, "pen parameters not found"); | |
1838 | uclogic_params_init_invalid(&p); | |
1839 | } | |
1840 | ||
9614219e NK |
1841 | break; |
1842 | } | |
1843 | ||
1844 | #undef VID_PID | |
1845 | #undef WITH_OPT_DESC | |
1846 | ||
1847 | /* Output parameters */ | |
1848 | memcpy(params, &p, sizeof(*params)); | |
1849 | memset(&p, 0, sizeof(p)); | |
1850 | rc = 0; | |
1851 | cleanup: | |
1852 | uclogic_params_cleanup(&p); | |
1853 | return rc; | |
1854 | } | |
a64cbf3c JE |
1855 | |
1856 | #ifdef CONFIG_HID_KUNIT_TEST | |
1857 | #include "hid-uclogic-params-test.c" | |
1858 | #endif |