1866 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * HID driver for UC-Logic devices not fully compliant with HID standard4 * - tablet initialization and parameter retrieval5 *6 * Copyright (c) 2018 Nikolai Kondrashov7 */8 9/*10 * This program is free software; you can redistribute it and/or modify it11 * under the terms of the GNU General Public License as published by the Free12 * Software Foundation; either version 2 of the License, or (at your option)13 * any later version.14 */15 16#include "hid-uclogic-params.h"17#include "hid-uclogic-rdesc.h"18#include "usbhid/usbhid.h"19#include "hid-ids.h"20#include <linux/ctype.h>21#include <linux/string.h>22#include <linux/unaligned.h>23 24/**25 * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type26 * to a string.27 * @inrange: The in-range reporting type to convert.28 *29 * Return:30 * * The string representing the type, or31 * * %NULL if the type is unknown.32 */33static const char *uclogic_params_pen_inrange_to_str(34 enum uclogic_params_pen_inrange inrange)35{36 switch (inrange) {37 case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:38 return "normal";39 case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:40 return "inverted";41 case UCLOGIC_PARAMS_PEN_INRANGE_NONE:42 return "none";43 default:44 return NULL;45 }46}47 48/**49 * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters50 * @hdev: The HID device the pen parameters describe.51 * @pen: The pen parameters to dump.52 *53 * Dump tablet interface pen parameters with hid_dbg(). The dump is indented54 * with a tab.55 */56static 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/**83 * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters84 * @hdev: The HID device the pen parameters describe.85 * @frame: The frame parameters to dump.86 *87 * Dump tablet interface frame parameters with hid_dbg(). The dump is88 * indented with two tabs.89 */90static 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);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);104 hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",105 frame->bitmap_dial_byte);106}107 108/**109 * uclogic_params_hid_dbg() - Dump tablet interface parameters110 * @hdev: The HID device the parameters describe.111 * @params: The parameters to dump.112 *113 * Dump tablet interface parameters with hid_dbg().114 */115void 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 137/**138 * uclogic_params_get_str_desc - retrieve a string descriptor from a HID139 * device interface, putting it into a kmalloc-allocated buffer as is, without140 * character encoding conversion.141 *142 * @pbuf: Location for the kmalloc-allocated buffer pointer containing143 * 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 string146 * 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, or153 * another negative errno code in case of other error.154 */155static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,156 __u8 idx, size_t len)157{158 int rc;159 struct usb_device *udev;160 __u8 *buf = NULL;161 162 /* Check arguments */163 if (hdev == NULL) {164 rc = -EINVAL;165 goto cleanup;166 }167 168 udev = hid_to_usb_dev(hdev);169 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,186 "failed retrieving string descriptor #%u: %d\n",187 idx, rc);188 goto cleanup;189 }190 191 if (pbuf != NULL) {192 *pbuf = buf;193 buf = NULL;194 }195 196cleanup:197 kfree(buf);198 return rc;199}200 201/**202 * uclogic_params_pen_cleanup - free resources used by struct203 * 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 */208static 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/**215 * uclogic_params_pen_init_v1() - initialize tablet interface pen216 * input and retrieve its parameters from the device, using v1 protocol.217 *218 * @pen: Pointer to the pen parameters to initialize (to be219 * cleaned up with uclogic_params_pen_cleanup()). Not modified in220 * 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 parameters222 * were found, and to false if not (e.g. device was223 * incompatible). Not modified in case of error. Cannot be NULL.224 * @hdev: The HID device of the tablet interface to initialize and get225 * parameters from. Cannot be NULL.226 *227 * Returns:228 * Zero, if successful. A negative errno code on error.229 */230static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,231 bool *pfound,232 struct hid_device *hdev)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 */242 s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];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 sniffing254 * 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 descriptor274 */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 descriptor298 */299 desc_ptr = uclogic_rdesc_template_apply(300 uclogic_rdesc_v1_pen_template_arr,301 uclogic_rdesc_v1_pen_template_size,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 parameters310 */311 memset(pen, 0, sizeof(*pen));312 pen->desc_ptr = desc_ptr;313 desc_ptr = NULL;314 pen->desc_size = uclogic_rdesc_v1_pen_template_size;315 pen->id = UCLOGIC_RDESC_V1_PEN_ID;316 pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;317 found = true;318finish:319 *pfound = found;320 rc = 0;321cleanup:322 kfree(desc_ptr);323 kfree(buf);324 return rc;325}326 327/**328 * uclogic_params_get_le24() - get a 24-bit little-endian number from a329 * buffer.330 *331 * @p: The pointer to the number buffer.332 *333 * Returns:334 * The retrieved number335 */336static 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 pen344 * input and retrieve its parameters from the device, using v2 protocol.345 *346 * @pen: Pointer to the pen parameters to initialize (to be347 * cleaned up with uclogic_params_pen_cleanup()). Not348 * modified in case of error, or if parameters are not349 * found. Cannot be NULL.350 * @pfound: Location for a flag which is set to true if the351 * parameters were found, and to false if not (e.g.352 * device was incompatible). Not modified in case of353 * error. Cannot be NULL.354 * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw355 * parameters, which could be used to identify the tablet356 * 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 raw360 * parameters. NULL, if not needed. Not modified in case361 * of error. Only set if *pfound is set to true.362 * @hdev: The HID device of the tablet interface to initialize363 * and get parameters from. Cannot be NULL.364 *365 * Returns:366 * Zero, if successful. A negative errno code on error.367 */368static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,369 bool *pfound,370 __u8 **pparams_ptr,371 size_t *pparams_len,372 struct hid_device *hdev)373{374 int rc;375 bool found = false;376 /* Buffer for (part of) the parameter string descriptor */377 __u8 *buf = NULL;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;385 s32 resolution;386 /* Pen report descriptor template parameters */387 s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];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 sniffing399 * the Windows driver traffic.400 * NOTE: This enables fully-functional tablet mode.401 */402 rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max);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;410 } else if (rc < params_len_min) {411 hid_dbg(hdev,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 ASCII421 * string (such as the model name) some tablets put into all422 * 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");431 goto finish;432 }433 434 /*435 * Fill report descriptor parameters from the string descriptor436 */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 }455 456 /*457 * Generate pen report descriptor458 */459 desc_ptr = uclogic_rdesc_template_apply(460 uclogic_rdesc_v2_pen_template_arr,461 uclogic_rdesc_v2_pen_template_size,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 parameters470 */471 memset(pen, 0, sizeof(*pen));472 pen->desc_ptr = desc_ptr;473 desc_ptr = NULL;474 pen->desc_size = uclogic_rdesc_v2_pen_template_size;475 pen->id = UCLOGIC_RDESC_V2_PEN_ID;476 pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;477 pen->fragmented_hires = true;478 pen->tilt_y_flipped = true;479 found = true;480 if (pparams_ptr != NULL) {481 *pparams_ptr = buf;482 buf = NULL;483 }484 if (pparams_len != NULL)485 *pparams_len = params_len;486 487finish:488 *pfound = found;489 rc = 0;490cleanup:491 kfree(desc_ptr);492 kfree(buf);493 return rc;494}495 496/**497 * uclogic_params_frame_cleanup - free resources used by struct498 * 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 */503static 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 control511 * parameters with a static report descriptor.512 *513 * @frame: Pointer to the frame parameters to initialize (to be cleaned514 * up with uclogic_params_frame_cleanup()). Not modified in case515 * 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 */524static 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/**547 * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame548 * controls.549 *550 * @frame: Pointer to the frame parameters to initialize (to be cleaned551 * up with uclogic_params_frame_cleanup()). Not modified in case552 * of error, or if parameters are not found. Cannot be NULL.553 * @pfound: Location for a flag which is set to true if the parameters554 * were found, and to false if not (e.g. device was555 * incompatible). Not modified in case of error. Cannot be NULL.556 * @hdev: The HID device of the tablet interface to initialize and get557 * parameters from. Cannot be NULL.558 *559 * Returns:560 * Zero, if successful. A negative errno code on error.561 */562static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,563 bool *pfound,564 struct hid_device *hdev)565{566 int rc;567 bool found = false;568 struct usb_device *usb_dev;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 578 usb_dev = hid_to_usb_dev(hdev);579 580 /*581 * Enable generic button mode582 */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,603 uclogic_rdesc_v1_frame_arr,604 uclogic_rdesc_v1_frame_size,605 UCLOGIC_RDESC_V1_FRAME_ID);606 if (rc != 0)607 goto cleanup;608 found = true;609 }610 611 *pfound = found;612 rc = 0;613cleanup:614 kfree(str_buf);615 return rc;616}617 618/**619 * uclogic_params_cleanup_event_hooks - free resources used by the list of raw620 * event hooks.621 * Can be called repeatedly.622 *623 * @params: Input parameters to cleanup. Cannot be NULL.624 */625static 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 643/**644 * uclogic_params_cleanup - free resources used by struct uclogic_params645 * (tablet interface's parameters).646 * Can be called repeatedly.647 *648 * @params: Input parameters to cleanup. Cannot be NULL.649 */650void uclogic_params_cleanup(struct uclogic_params *params)651{652 if (!params->invalid) {653 size_t i;654 kfree(params->desc_ptr);655 uclogic_params_pen_cleanup(¶ms->pen);656 for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)657 uclogic_params_frame_cleanup(¶ms->frame_list[i]);658 659 uclogic_params_cleanup_event_hooks(params);660 memset(params, 0, sizeof(*params));661 }662}663 664/**665 * uclogic_params_get_desc() - Get a replacement report descriptor for a666 * tablet's interface.667 *668 * @params: The parameters of a tablet interface to get report669 * descriptor for. Cannot be NULL.670 * @pdesc: Location for the resulting, kmalloc-allocated report671 * descriptor pointer, or for NULL, if there's no replacement672 * report descriptor. Not modified in case of error. Cannot be673 * NULL.674 * @psize: Location for the resulting report descriptor size, not set if675 * there's no replacement report descriptor. Not modified in case676 * 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 */683int uclogic_params_get_desc(const struct uclogic_params *params,684 const __u8 **pdesc,685 unsigned int *psize)686{687 int rc = -ENOMEM;688 bool present = false;689 unsigned int size = 0;690 __u8 *desc = NULL;691 size_t i;692 693 /* Check arguments */694 if (params == NULL || pdesc == NULL || psize == NULL)695 return -EINVAL;696 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 }722 723#undef ADD_DESC724 725 if (present) {726 *pdesc = desc;727 *psize = size;728 desc = NULL;729 }730 rc = 0;731cleanup:732 kfree(desc);733 return rc;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 with741 * uclogic_params_cleanup()). Cannot be NULL.742 */743static 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 interface750 * parameters with an optional replacement report descriptor. Only modify751 * report descriptor, if the original report descriptor matches the expected752 * size.753 *754 * @params: Parameters to initialize (to be cleaned with755 * uclogic_params_cleanup()). Not modified in case of756 * error. Cannot be NULL.757 * @hdev: The HID device of the tablet interface create the758 * parameters for. Cannot be NULL.759 * @orig_desc_size: Expected size of the original report descriptor to760 * 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 */769static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,770 struct hid_device *hdev,771 unsigned int orig_desc_size,772 const __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;811cleanup:812 kfree(desc_copy_ptr);813 return rc;814}815 816/**817 * uclogic_params_huion_init() - initialize a Huion tablet interface and discover818 * its parameters.819 *820 * @params: Parameters to fill in (to be cleaned with821 * 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 get824 * parameters from. Cannot be NULL.825 *826 * Returns:827 * Zero, if successful. A negative errno code on error.828 */829static int uclogic_params_huion_init(struct uclogic_params *params,830 struct hid_device *hdev)831{832 int rc;833 struct usb_device *udev;834 struct usb_interface *iface;835 __u8 bInterfaceNumber;836 bool found;837 /* The resulting parameters (noop) */838 struct uclogic_params p = {0, };839 static const char transition_ver[] = "HUION_T153_160607";840 char *ver_ptr = NULL;841 const size_t ver_len = sizeof(transition_ver) + 1;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, 0x3E849 };850 851 /* Check arguments */852 if (params == NULL || hdev == NULL) {853 rc = -EINVAL;854 goto cleanup;855 }856 857 udev = hid_to_usb_dev(hdev);858 iface = to_usb_interface(hdev->dev.parent);859 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;860 861 /* If it's a custom keyboard interface */862 if (bInterfaceNumber == 1) {863 /* Keep everything intact, but mark pen usage invalid */864 p.pen.usage_invalid = true;865 goto output;866 /* Else, if it's not a pen interface */867 } else if (bInterfaceNumber != 0) {868 uclogic_params_init_invalid(&p);869 goto output;870 }871 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);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 /* The firmware is used in userspace as unique identifier */888 strscpy(hdev->uniq, ver_ptr, sizeof(hdev->uniq));889 890 /* If this is a transition firmware */891 if (strcmp(ver_ptr, transition_ver) == 0) {892 hid_dbg(hdev,893 "transition firmware detected, not probing pen v2 parameters\n");894 } else {895 /* Try to probe v2 pen parameters */896 rc = uclogic_params_pen_init_v2(&p.pen, &found,897 ¶ms_ptr, ¶ms_len,898 hdev);899 if (rc != 0) {900 hid_err(hdev,901 "failed probing pen v2 parameters: %d\n", rc);902 goto cleanup;903 } else if (found) {904 hid_dbg(hdev, "pen v2 parameters found\n");905 /* Create v2 frame button parameters */906 rc = uclogic_params_frame_init_with_desc(907 &p.frame_list[0],908 uclogic_rdesc_v2_frame_buttons_arr,909 uclogic_rdesc_v2_frame_buttons_size,910 UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID);911 if (rc != 0) {912 hid_err(hdev,913 "failed creating v2 frame button parameters: %d\n",914 rc);915 goto cleanup;916 }917 918 /* Link from pen sub-report */919 p.pen.subreport_list[0].value = 0xe0;920 p.pen.subreport_list[0].id =921 UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID;922 923 /* If this is the model with touch ring */924 if (params_ptr != NULL &&925 params_len == sizeof(touch_ring_model_params_buf) &&926 memcmp(params_ptr, touch_ring_model_params_buf,927 params_len) == 0) {928 /* Create touch ring parameters */929 rc = uclogic_params_frame_init_with_desc(930 &p.frame_list[1],931 uclogic_rdesc_v2_frame_touch_ring_arr,932 uclogic_rdesc_v2_frame_touch_ring_size,933 UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);934 if (rc != 0) {935 hid_err(hdev,936 "failed creating v2 frame touch ring parameters: %d\n",937 rc);938 goto cleanup;939 }940 p.frame_list[1].suffix = "Touch Ring";941 p.frame_list[1].dev_id_byte =942 UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;943 p.frame_list[1].touch_byte = 5;944 p.frame_list[1].touch_max = 12;945 p.frame_list[1].touch_flip_at = 7;946 } else {947 /* Create touch strip parameters */948 rc = uclogic_params_frame_init_with_desc(949 &p.frame_list[1],950 uclogic_rdesc_v2_frame_touch_strip_arr,951 uclogic_rdesc_v2_frame_touch_strip_size,952 UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);953 if (rc != 0) {954 hid_err(hdev,955 "failed creating v2 frame touch strip parameters: %d\n",956 rc);957 goto cleanup;958 }959 p.frame_list[1].suffix = "Touch Strip";960 p.frame_list[1].dev_id_byte =961 UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;962 p.frame_list[1].touch_byte = 5;963 p.frame_list[1].touch_max = 8;964 }965 966 /* Link from pen sub-report */967 p.pen.subreport_list[1].value = 0xf0;968 p.pen.subreport_list[1].id =969 UCLOGIC_RDESC_V2_FRAME_TOUCH_ID;970 971 /* Create v2 frame dial parameters */972 rc = uclogic_params_frame_init_with_desc(973 &p.frame_list[2],974 uclogic_rdesc_v2_frame_dial_arr,975 uclogic_rdesc_v2_frame_dial_size,976 UCLOGIC_RDESC_V2_FRAME_DIAL_ID);977 if (rc != 0) {978 hid_err(hdev,979 "failed creating v2 frame dial parameters: %d\n",980 rc);981 goto cleanup;982 }983 p.frame_list[2].suffix = "Dial";984 p.frame_list[2].dev_id_byte =985 UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE;986 p.frame_list[2].bitmap_dial_byte = 5;987 988 /* Link from pen sub-report */989 p.pen.subreport_list[2].value = 0xf1;990 p.pen.subreport_list[2].id =991 UCLOGIC_RDESC_V2_FRAME_DIAL_ID;992 993 goto output;994 }995 hid_dbg(hdev, "pen v2 parameters not found\n");996 }997 998 /* Try to probe v1 pen parameters */999 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1000 if (rc != 0) {1001 hid_err(hdev,1002 "failed probing pen v1 parameters: %d\n", rc);1003 goto cleanup;1004 } else if (found) {1005 hid_dbg(hdev, "pen v1 parameters found\n");1006 /* Try to probe v1 frame */1007 rc = uclogic_params_frame_init_v1(&p.frame_list[0],1008 &found, hdev);1009 if (rc != 0) {1010 hid_err(hdev, "v1 frame probing failed: %d\n", rc);1011 goto cleanup;1012 }1013 hid_dbg(hdev, "frame v1 parameters%s found\n",1014 (found ? "" : " not"));1015 if (found) {1016 /* Link frame button subreports from pen reports */1017 p.pen.subreport_list[0].value = 0xe0;1018 p.pen.subreport_list[0].id =1019 UCLOGIC_RDESC_V1_FRAME_ID;1020 }1021 goto output;1022 }1023 hid_dbg(hdev, "pen v1 parameters not found\n");1024 1025 uclogic_params_init_invalid(&p);1026 1027output:1028 /* Output parameters */1029 memcpy(params, &p, sizeof(*params));1030 memset(&p, 0, sizeof(p));1031 rc = 0;1032cleanup:1033 kfree(params_ptr);1034 kfree(ver_ptr);1035 uclogic_params_cleanup(&p);1036 return rc;1037}1038 1039/**1040 * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or1041 * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.1042 *1043 * @hdev: The HID device of the tablet interface to initialize and get1044 * parameters from. Cannot be NULL.1045 * @magic_arr: The magic data that should be sent to probe the interface.1046 * Cannot be NULL.1047 * @magic_size: Size of the magic data.1048 * @endpoint: Endpoint where the magic data should be sent.1049 *1050 * Returns:1051 * Zero, if successful. A negative errno code on error.1052 */1053static int uclogic_probe_interface(struct hid_device *hdev, const u8 *magic_arr,1054 size_t magic_size, int endpoint)1055{1056 struct usb_device *udev;1057 unsigned int pipe = 0;1058 int sent;1059 u8 *buf = NULL;1060 int rc = 0;1061 1062 if (!hdev || !magic_arr) {1063 rc = -EINVAL;1064 goto cleanup;1065 }1066 1067 buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);1068 if (!buf) {1069 rc = -ENOMEM;1070 goto cleanup;1071 }1072 1073 udev = hid_to_usb_dev(hdev);1074 pipe = usb_sndintpipe(udev, endpoint);1075 1076 rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);1077 if (rc || sent != magic_size) {1078 hid_err(hdev, "Interface probing failed: %d\n", rc);1079 rc = -1;1080 goto cleanup;1081 }1082 1083 rc = 0;1084cleanup:1085 kfree(buf);1086 return rc;1087}1088 1089/**1090 * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing1091 * pen and frame parameters returned by UGEE v2 devices.1092 *1093 * @str_desc: String descriptor, cannot be NULL.1094 * @str_desc_size: Size of the string descriptor.1095 * @desc_params: Output description params list.1096 * @desc_params_size: Size of the output description params list.1097 * @frame_type: Output frame type.1098 *1099 * Returns:1100 * Zero, if successful. A negative errno code on error.1101 */1102static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc,1103 size_t str_desc_size,1104 s32 *desc_params,1105 size_t desc_params_size,1106 enum uclogic_params_frame_type *frame_type)1107{1108 s32 pen_x_lm, pen_y_lm;1109 s32 pen_x_pm, pen_y_pm;1110 s32 pen_pressure_lm;1111 s32 frame_num_buttons;1112 s32 resolution;1113 1114 /* Minimum descriptor length required, maximum seen so far is 14 */1115 const int min_str_desc_size = 12;1116 1117 if (!str_desc || str_desc_size < min_str_desc_size)1118 return -EINVAL;1119 1120 if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)1121 return -EINVAL;1122 1123 pen_x_lm = get_unaligned_le16(str_desc + 2);1124 pen_y_lm = get_unaligned_le16(str_desc + 4);1125 frame_num_buttons = str_desc[6];1126 *frame_type = str_desc[7];1127 pen_pressure_lm = get_unaligned_le16(str_desc + 8);1128 1129 resolution = get_unaligned_le16(str_desc + 10);1130 if (resolution == 0) {1131 pen_x_pm = 0;1132 pen_y_pm = 0;1133 } else {1134 pen_x_pm = pen_x_lm * 1000 / resolution;1135 pen_y_pm = pen_y_lm * 1000 / resolution;1136 }1137 1138 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm;1139 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm;1140 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm;1141 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm;1142 desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm;1143 desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons;1144 1145 return 0;1146}1147 1148/**1149 * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with1150 * buttons.1151 * @p: Parameters to fill in, cannot be NULL.1152 * @desc_params: Device description params list.1153 * @desc_params_size: Size of the description params list.1154 *1155 * Returns:1156 * Zero, if successful. A negative errno code on error.1157 */1158static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p,1159 const s32 *desc_params,1160 size_t desc_params_size)1161{1162 __u8 *rdesc_frame = NULL;1163 int rc = 0;1164 1165 if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)1166 return -EINVAL;1167 1168 rdesc_frame = uclogic_rdesc_template_apply(1169 uclogic_rdesc_ugee_v2_frame_btn_template_arr,1170 uclogic_rdesc_ugee_v2_frame_btn_template_size,1171 desc_params, UCLOGIC_RDESC_PH_ID_NUM);1172 if (!rdesc_frame)1173 return -ENOMEM;1174 1175 rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],1176 rdesc_frame,1177 uclogic_rdesc_ugee_v2_frame_btn_template_size,1178 UCLOGIC_RDESC_V1_FRAME_ID);1179 kfree(rdesc_frame);1180 return rc;1181}1182 1183/**1184 * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a1185 * bitmap dial.1186 * @p: Parameters to fill in, cannot be NULL.1187 * @desc_params: Device description params list.1188 * @desc_params_size: Size of the description params list.1189 *1190 * Returns:1191 * Zero, if successful. A negative errno code on error.1192 */1193static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p,1194 const s32 *desc_params,1195 size_t desc_params_size)1196{1197 __u8 *rdesc_frame = NULL;1198 int rc = 0;1199 1200 if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)1201 return -EINVAL;1202 1203 rdesc_frame = uclogic_rdesc_template_apply(1204 uclogic_rdesc_ugee_v2_frame_dial_template_arr,1205 uclogic_rdesc_ugee_v2_frame_dial_template_size,1206 desc_params, UCLOGIC_RDESC_PH_ID_NUM);1207 if (!rdesc_frame)1208 return -ENOMEM;1209 1210 rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],1211 rdesc_frame,1212 uclogic_rdesc_ugee_v2_frame_dial_template_size,1213 UCLOGIC_RDESC_V1_FRAME_ID);1214 kfree(rdesc_frame);1215 if (rc)1216 return rc;1217 1218 p->frame_list[0].bitmap_dial_byte = 7;1219 return 0;1220}1221 1222/**1223 * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a1224 * mouse.1225 * @p: Parameters to fill in, cannot be NULL.1226 *1227 * Returns:1228 * Zero, if successful. A negative errno code on error.1229 */1230static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p)1231{1232 int rc = 0;1233 1234 if (!p)1235 return -EINVAL;1236 1237 rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],1238 uclogic_rdesc_ugee_v2_frame_mouse_template_arr,1239 uclogic_rdesc_ugee_v2_frame_mouse_template_size,1240 UCLOGIC_RDESC_V1_FRAME_ID);1241 return rc;1242}1243 1244/**1245 * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has1246 * battery or not.1247 * @hdev: The HID device of the tablet interface.1248 *1249 * Returns:1250 * True if the device has battery, false otherwise.1251 */1252static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev)1253{1254 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);1255 1256 if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK)1257 return true;1258 1259 /* The XP-PEN Deco LW vendor, product and version are identical to the1260 * Deco L. The only difference reported by their firmware is the product1261 * name. Add a quirk to support battery reporting on the wireless1262 * version.1263 */1264 if (hdev->vendor == USB_VENDOR_ID_UGEE &&1265 hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) {1266 struct usb_device *udev = hid_to_usb_dev(hdev);1267 1268 if (strstarts(udev->product, "Deco LW"))1269 return true;1270 }1271 1272 return false;1273}1274 1275/**1276 * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting.1277 * @hdev: The HID device of the tablet interface, cannot be NULL.1278 * @p: Parameters to fill in, cannot be NULL.1279 *1280 * Returns:1281 * Zero, if successful. A negative errno code on error.1282 */1283static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev,1284 struct uclogic_params *p)1285{1286 int rc = 0;1287 1288 if (!hdev || !p)1289 return -EINVAL;1290 1291 /* Some tablets contain invalid characters in hdev->uniq, throwing a1292 * "hwmon: '<name>' is not a valid name attribute, please fix" error.1293 * Use the device vendor and product IDs instead.1294 */1295 snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor,1296 hdev->product);1297 1298 rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],1299 uclogic_rdesc_ugee_v2_battery_template_arr,1300 uclogic_rdesc_ugee_v2_battery_template_size,1301 UCLOGIC_RDESC_UGEE_V2_BATTERY_ID);1302 if (rc)1303 return rc;1304 1305 p->frame_list[1].suffix = "Battery";1306 p->pen.subreport_list[1].value = 0xf2;1307 p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID;1308 1309 return rc;1310}1311 1312/**1313 * uclogic_params_ugee_v2_reconnect_work() - When a wireless tablet looses1314 * connection to the USB dongle and reconnects, either because of its physical1315 * distance or because it was switches off and on using the frame's switch,1316 * uclogic_probe_interface() needs to be called again to enable the tablet.1317 *1318 * @work: The work that triggered this function.1319 */1320static void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work)1321{1322 struct uclogic_raw_event_hook *event_hook;1323 1324 event_hook = container_of(work, struct uclogic_raw_event_hook, work);1325 uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr,1326 uclogic_ugee_v2_probe_size,1327 uclogic_ugee_v2_probe_endpoint);1328}1329 1330/**1331 * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events1332 * to be hooked for UGEE v2 devices.1333 * @hdev: The HID device of the tablet interface to initialize and get1334 * parameters from.1335 * @p: Parameters to fill in, cannot be NULL.1336 *1337 * Returns:1338 * Zero, if successful. A negative errno code on error.1339 */1340static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev,1341 struct uclogic_params *p)1342{1343 struct uclogic_raw_event_hook *event_hook;1344 __u8 reconnect_event[] = {1345 /* Event received on wireless tablet reconnection */1346 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x001347 };1348 1349 if (!p)1350 return -EINVAL;1351 1352 /* The reconnection event is only received if the tablet has battery */1353 if (!uclogic_params_ugee_v2_has_battery(hdev))1354 return 0;1355 1356 p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL);1357 if (!p->event_hooks)1358 return -ENOMEM;1359 1360 INIT_LIST_HEAD(&p->event_hooks->list);1361 1362 event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL);1363 if (!event_hook)1364 return -ENOMEM;1365 1366 INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work);1367 event_hook->hdev = hdev;1368 event_hook->size = ARRAY_SIZE(reconnect_event);1369 event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL);1370 if (!event_hook->event)1371 return -ENOMEM;1372 1373 list_add_tail(&event_hook->list, &p->event_hooks->list);1374 1375 return 0;1376}1377 1378/**1379 * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by1380 * discovering their parameters.1381 *1382 * These tables, internally designed as v2 to differentiate them from older1383 * models, expect a payload of magic data in orther to be switched to the fully1384 * functional mode and expose their parameters in a similar way to the1385 * information present in uclogic_params_pen_init_v1() but with some1386 * differences.1387 *1388 * @params: Parameters to fill in (to be cleaned with1389 * uclogic_params_cleanup()). Not modified in case of error.1390 * Cannot be NULL.1391 * @hdev: The HID device of the tablet interface to initialize and get1392 * parameters from. Cannot be NULL.1393 *1394 * Returns:1395 * Zero, if successful. A negative errno code on error.1396 */1397static int uclogic_params_ugee_v2_init(struct uclogic_params *params,1398 struct hid_device *hdev)1399{1400 int rc = 0;1401 struct uclogic_drvdata *drvdata;1402 struct usb_interface *iface;1403 __u8 bInterfaceNumber;1404 const int str_desc_len = 12;1405 __u8 *str_desc = NULL;1406 __u8 *rdesc_pen = NULL;1407 s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];1408 enum uclogic_params_frame_type frame_type;1409 /* The resulting parameters (noop) */1410 struct uclogic_params p = {0, };1411 1412 if (!params || !hdev) {1413 rc = -EINVAL;1414 goto cleanup;1415 }1416 1417 drvdata = hid_get_drvdata(hdev);1418 iface = to_usb_interface(hdev->dev.parent);1419 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;1420 1421 if (bInterfaceNumber == 0) {1422 rc = uclogic_params_ugee_v2_init_frame_mouse(&p);1423 if (rc)1424 goto cleanup;1425 1426 goto output;1427 }1428 1429 if (bInterfaceNumber != 2) {1430 uclogic_params_init_invalid(&p);1431 goto output;1432 }1433 1434 /*1435 * Initialize the interface by sending magic data.1436 * The specific data was discovered by sniffing the Windows driver1437 * traffic.1438 */1439 rc = uclogic_probe_interface(hdev, uclogic_ugee_v2_probe_arr,1440 uclogic_ugee_v2_probe_size,1441 uclogic_ugee_v2_probe_endpoint);1442 if (rc) {1443 uclogic_params_init_invalid(&p);1444 goto output;1445 }1446 1447 /*1448 * Read the string descriptor containing pen and frame parameters.1449 * The specific string descriptor and data were discovered by sniffing1450 * the Windows driver traffic.1451 */1452 rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);1453 if (rc != str_desc_len) {1454 hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);1455 uclogic_params_init_invalid(&p);1456 goto output;1457 }1458 1459 rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len,1460 desc_params,1461 ARRAY_SIZE(desc_params),1462 &frame_type);1463 if (rc)1464 goto cleanup;1465 1466 kfree(str_desc);1467 str_desc = NULL;1468 1469 /* Initialize the pen interface */1470 rdesc_pen = uclogic_rdesc_template_apply(1471 uclogic_rdesc_ugee_v2_pen_template_arr,1472 uclogic_rdesc_ugee_v2_pen_template_size,1473 desc_params, ARRAY_SIZE(desc_params));1474 if (!rdesc_pen) {1475 rc = -ENOMEM;1476 goto cleanup;1477 }1478 1479 p.pen.desc_ptr = rdesc_pen;1480 p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;1481 p.pen.id = 0x02;1482 p.pen.subreport_list[0].value = 0xf0;1483 p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;1484 1485 /* Initialize the frame interface */1486 if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK)1487 frame_type = UCLOGIC_PARAMS_FRAME_MOUSE;1488 1489 switch (frame_type) {1490 case UCLOGIC_PARAMS_FRAME_DIAL:1491 case UCLOGIC_PARAMS_FRAME_MOUSE:1492 rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params,1493 ARRAY_SIZE(desc_params));1494 break;1495 case UCLOGIC_PARAMS_FRAME_BUTTONS:1496 default:1497 rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params,1498 ARRAY_SIZE(desc_params));1499 break;1500 }1501 1502 if (rc)1503 goto cleanup;1504 1505 /* Initialize the battery interface*/1506 if (uclogic_params_ugee_v2_has_battery(hdev)) {1507 rc = uclogic_params_ugee_v2_init_battery(hdev, &p);1508 if (rc) {1509 hid_err(hdev, "error initializing battery: %d\n", rc);1510 goto cleanup;1511 }1512 }1513 1514 /* Create a list of raw events to be ignored */1515 rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);1516 if (rc) {1517 hid_err(hdev, "error initializing event hook list: %d\n", rc);1518 goto cleanup;1519 }1520 1521output:1522 /* Output parameters */1523 memcpy(params, &p, sizeof(*params));1524 memset(&p, 0, sizeof(p));1525 rc = 0;1526cleanup:1527 kfree(str_desc);1528 uclogic_params_cleanup(&p);1529 return rc;1530}1531 1532/**1533 * uclogic_params_init() - initialize a tablet interface and discover its1534 * parameters.1535 *1536 * @params: Parameters to fill in (to be cleaned with1537 * uclogic_params_cleanup()). Not modified in case of error.1538 * Cannot be NULL.1539 * @hdev: The HID device of the tablet interface to initialize and get1540 * parameters from. Cannot be NULL. Must be using the USB low-level1541 * driver, i.e. be an actual USB tablet.1542 *1543 * Returns:1544 * Zero, if successful. A negative errno code on error.1545 */1546int uclogic_params_init(struct uclogic_params *params,1547 struct hid_device *hdev)1548{1549 int rc;1550 struct usb_device *udev;1551 __u8 bNumInterfaces;1552 struct usb_interface *iface;1553 __u8 bInterfaceNumber;1554 bool found;1555 /* The resulting parameters (noop) */1556 struct uclogic_params p = {0, };1557 1558 /* Check arguments */1559 if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {1560 rc = -EINVAL;1561 goto cleanup;1562 }1563 1564 udev = hid_to_usb_dev(hdev);1565 bNumInterfaces = udev->config->desc.bNumInterfaces;1566 iface = to_usb_interface(hdev->dev.parent);1567 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;1568 1569 /*1570 * Set replacement report descriptor if the original matches the1571 * specified size. Otherwise keep interface unchanged.1572 */1573#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \1574 uclogic_params_init_with_opt_desc( \1575 &p, hdev, \1576 UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \1577 uclogic_rdesc_##_new_desc_token##_arr, \1578 uclogic_rdesc_##_new_desc_token##_size)1579 1580#define VID_PID(_vid, _pid) \1581 (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))1582 1583 /*1584 * Handle specific interfaces for specific tablets.1585 *1586 * Observe the following logic:1587 *1588 * If the interface is recognized as producing certain useful input:1589 * Mark interface as valid.1590 * Output interface parameters.1591 * Else, if the interface is recognized as *not* producing any useful1592 * input:1593 * Mark interface as invalid.1594 * Else:1595 * Mark interface as valid.1596 * Output noop parameters.1597 *1598 * Rule of thumb: it is better to disable a broken interface than let1599 * it spew garbage input.1600 */1601 1602 switch (VID_PID(hdev->vendor, hdev->product)) {1603 case VID_PID(USB_VENDOR_ID_UCLOGIC,1604 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):1605 rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);1606 if (rc != 0)1607 goto cleanup;1608 break;1609 case VID_PID(USB_VENDOR_ID_UCLOGIC,1610 USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):1611 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);1612 if (rc != 0)1613 goto cleanup;1614 break;1615 case VID_PID(USB_VENDOR_ID_UCLOGIC,1616 USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):1617 if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {1618 if (bInterfaceNumber == 0) {1619 /* Try to probe v1 pen parameters */1620 rc = uclogic_params_pen_init_v1(&p.pen,1621 &found, hdev);1622 if (rc != 0) {1623 hid_err(hdev,1624 "pen probing failed: %d\n",1625 rc);1626 goto cleanup;1627 }1628 if (!found) {1629 hid_warn(hdev,1630 "pen parameters not found");1631 }1632 } else {1633 uclogic_params_init_invalid(&p);1634 }1635 } else {1636 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);1637 if (rc != 0)1638 goto cleanup;1639 }1640 break;1641 case VID_PID(USB_VENDOR_ID_UCLOGIC,1642 USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):1643 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);1644 if (rc != 0)1645 goto cleanup;1646 break;1647 case VID_PID(USB_VENDOR_ID_UCLOGIC,1648 USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):1649 rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);1650 if (rc != 0)1651 goto cleanup;1652 break;1653 case VID_PID(USB_VENDOR_ID_UCLOGIC,1654 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):1655 switch (bInterfaceNumber) {1656 case 0:1657 rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);1658 if (rc != 0)1659 goto cleanup;1660 break;1661 case 1:1662 rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);1663 if (rc != 0)1664 goto cleanup;1665 break;1666 case 2:1667 rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);1668 if (rc != 0)1669 goto cleanup;1670 break;1671 }1672 break;1673 case VID_PID(USB_VENDOR_ID_UCLOGIC,1674 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):1675 /*1676 * If it is not a three-interface version, which is known to1677 * respond to initialization.1678 */1679 if (bNumInterfaces != 3) {1680 switch (bInterfaceNumber) {1681 case 0:1682 rc = WITH_OPT_DESC(TWHA60_ORIG0,1683 twha60_fixed0);1684 if (rc != 0)1685 goto cleanup;1686 break;1687 case 1:1688 rc = WITH_OPT_DESC(TWHA60_ORIG1,1689 twha60_fixed1);1690 if (rc != 0)1691 goto cleanup;1692 break;1693 }1694 break;1695 }1696 fallthrough;1697 case VID_PID(USB_VENDOR_ID_HUION,1698 USB_DEVICE_ID_HUION_TABLET):1699 case VID_PID(USB_VENDOR_ID_HUION,1700 USB_DEVICE_ID_HUION_TABLET2):1701 case VID_PID(USB_VENDOR_ID_UCLOGIC,1702 USB_DEVICE_ID_HUION_TABLET):1703 case VID_PID(USB_VENDOR_ID_UCLOGIC,1704 USB_DEVICE_ID_YIYNOVA_TABLET):1705 case VID_PID(USB_VENDOR_ID_UCLOGIC,1706 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):1707 case VID_PID(USB_VENDOR_ID_UCLOGIC,1708 USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):1709 case VID_PID(USB_VENDOR_ID_UCLOGIC,1710 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):1711 case VID_PID(USB_VENDOR_ID_UCLOGIC,1712 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):1713 rc = uclogic_params_huion_init(&p, hdev);1714 if (rc != 0)1715 goto cleanup;1716 break;1717 case VID_PID(USB_VENDOR_ID_UGTIZER,1718 USB_DEVICE_ID_UGTIZER_TABLET_GP0610):1719 case VID_PID(USB_VENDOR_ID_UGTIZER,1720 USB_DEVICE_ID_UGTIZER_TABLET_GT5040):1721 case VID_PID(USB_VENDOR_ID_UGEE,1722 USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):1723 case VID_PID(USB_VENDOR_ID_UGEE,1724 USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):1725 case VID_PID(USB_VENDOR_ID_UGEE,1726 USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06):1727 case VID_PID(USB_VENDOR_ID_UGEE,1728 USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):1729 /* If this is the pen interface */1730 if (bInterfaceNumber == 1) {1731 /* Probe v1 pen parameters */1732 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1733 if (rc != 0) {1734 hid_err(hdev, "pen probing failed: %d\n", rc);1735 goto cleanup;1736 }1737 if (!found) {1738 hid_warn(hdev, "pen parameters not found");1739 uclogic_params_init_invalid(&p);1740 }1741 } else {1742 uclogic_params_init_invalid(&p);1743 }1744 break;1745 case VID_PID(USB_VENDOR_ID_UGEE,1746 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):1747 /* If this is the pen and frame interface */1748 if (bInterfaceNumber == 1) {1749 /* Probe v1 pen parameters */1750 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1751 if (rc != 0) {1752 hid_err(hdev, "pen probing failed: %d\n", rc);1753 goto cleanup;1754 }1755 /* Initialize frame parameters */1756 rc = uclogic_params_frame_init_with_desc(1757 &p.frame_list[0],1758 uclogic_rdesc_xppen_deco01_frame_arr,1759 uclogic_rdesc_xppen_deco01_frame_size,1760 0);1761 if (rc != 0)1762 goto cleanup;1763 } else {1764 uclogic_params_init_invalid(&p);1765 }1766 break;1767 case VID_PID(USB_VENDOR_ID_UGEE,1768 USB_DEVICE_ID_UGEE_PARBLO_A610_PRO):1769 case VID_PID(USB_VENDOR_ID_UGEE,1770 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2):1771 case VID_PID(USB_VENDOR_ID_UGEE,1772 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):1773 case VID_PID(USB_VENDOR_ID_UGEE,1774 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW):1775 case VID_PID(USB_VENDOR_ID_UGEE,1776 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S):1777 case VID_PID(USB_VENDOR_ID_UGEE,1778 USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW):1779 rc = uclogic_params_ugee_v2_init(&p, hdev);1780 if (rc != 0)1781 goto cleanup;1782 break;1783 case VID_PID(USB_VENDOR_ID_TRUST,1784 USB_DEVICE_ID_TRUST_PANORA_TABLET):1785 case VID_PID(USB_VENDOR_ID_UGEE,1786 USB_DEVICE_ID_UGEE_TABLET_G5):1787 /* Ignore non-pen interfaces */1788 if (bInterfaceNumber != 1) {1789 uclogic_params_init_invalid(&p);1790 break;1791 }1792 1793 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1794 if (rc != 0) {1795 hid_err(hdev, "pen probing failed: %d\n", rc);1796 goto cleanup;1797 } else if (found) {1798 rc = uclogic_params_frame_init_with_desc(1799 &p.frame_list[0],1800 uclogic_rdesc_ugee_g5_frame_arr,1801 uclogic_rdesc_ugee_g5_frame_size,1802 UCLOGIC_RDESC_UGEE_G5_FRAME_ID);1803 if (rc != 0) {1804 hid_err(hdev,1805 "failed creating frame parameters: %d\n",1806 rc);1807 goto cleanup;1808 }1809 p.frame_list[0].re_lsb =1810 UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;1811 p.frame_list[0].dev_id_byte =1812 UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;1813 } else {1814 hid_warn(hdev, "pen parameters not found");1815 uclogic_params_init_invalid(&p);1816 }1817 1818 break;1819 case VID_PID(USB_VENDOR_ID_UGEE,1820 USB_DEVICE_ID_UGEE_TABLET_EX07S):1821 /* Ignore non-pen interfaces */1822 if (bInterfaceNumber != 1) {1823 uclogic_params_init_invalid(&p);1824 break;1825 }1826 1827 rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1828 if (rc != 0) {1829 hid_err(hdev, "pen probing failed: %d\n", rc);1830 goto cleanup;1831 } else if (found) {1832 rc = uclogic_params_frame_init_with_desc(1833 &p.frame_list[0],1834 uclogic_rdesc_ugee_ex07_frame_arr,1835 uclogic_rdesc_ugee_ex07_frame_size,1836 0);1837 if (rc != 0) {1838 hid_err(hdev,1839 "failed creating frame parameters: %d\n",1840 rc);1841 goto cleanup;1842 }1843 } else {1844 hid_warn(hdev, "pen parameters not found");1845 uclogic_params_init_invalid(&p);1846 }1847 1848 break;1849 }1850 1851#undef VID_PID1852#undef WITH_OPT_DESC1853 1854 /* Output parameters */1855 memcpy(params, &p, sizeof(*params));1856 memset(&p, 0, sizeof(p));1857 rc = 0;1858cleanup:1859 uclogic_params_cleanup(&p);1860 return rc;1861}1862 1863#ifdef CONFIG_HID_KUNIT_TEST1864#include "hid-uclogic-params-test.c"1865#endif1866