162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for UC-Logic devices not fully compliant with HID standard 462306a36Sopenharmony_ci * - tablet initialization and parameter retrieval 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2018 Nikolai Kondrashov 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 1162306a36Sopenharmony_ci * under the terms of the GNU General Public License as published by the Free 1262306a36Sopenharmony_ci * Software Foundation; either version 2 of the License, or (at your option) 1362306a36Sopenharmony_ci * any later version. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "hid-uclogic-params.h" 1762306a36Sopenharmony_ci#include "hid-uclogic-rdesc.h" 1862306a36Sopenharmony_ci#include "usbhid/usbhid.h" 1962306a36Sopenharmony_ci#include "hid-ids.h" 2062306a36Sopenharmony_ci#include <linux/ctype.h> 2162306a36Sopenharmony_ci#include <linux/string.h> 2262306a36Sopenharmony_ci#include <asm/unaligned.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type 2662306a36Sopenharmony_ci * to a string. 2762306a36Sopenharmony_ci * @inrange: The in-range reporting type to convert. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Return: 3062306a36Sopenharmony_ci * * The string representing the type, or 3162306a36Sopenharmony_ci * * %NULL if the type is unknown. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_cistatic const char *uclogic_params_pen_inrange_to_str( 3462306a36Sopenharmony_ci enum uclogic_params_pen_inrange inrange) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci switch (inrange) { 3762306a36Sopenharmony_ci case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: 3862306a36Sopenharmony_ci return "normal"; 3962306a36Sopenharmony_ci case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: 4062306a36Sopenharmony_ci return "inverted"; 4162306a36Sopenharmony_ci case UCLOGIC_PARAMS_PEN_INRANGE_NONE: 4262306a36Sopenharmony_ci return "none"; 4362306a36Sopenharmony_ci default: 4462306a36Sopenharmony_ci return NULL; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/** 4962306a36Sopenharmony_ci * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters 5062306a36Sopenharmony_ci * @hdev: The HID device the pen parameters describe. 5162306a36Sopenharmony_ci * @pen: The pen parameters to dump. 5262306a36Sopenharmony_ci * 5362306a36Sopenharmony_ci * Dump tablet interface pen parameters with hid_dbg(). The dump is indented 5462306a36Sopenharmony_ci * with a tab. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistatic void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, 5762306a36Sopenharmony_ci const struct uclogic_params_pen *pen) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci size_t i; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci hid_dbg(hdev, "\t.usage_invalid = %s\n", 6262306a36Sopenharmony_ci (pen->usage_invalid ? "true" : "false")); 6362306a36Sopenharmony_ci hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr); 6462306a36Sopenharmony_ci hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size); 6562306a36Sopenharmony_ci hid_dbg(hdev, "\t.id = %u\n", pen->id); 6662306a36Sopenharmony_ci hid_dbg(hdev, "\t.subreport_list = {\n"); 6762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) { 6862306a36Sopenharmony_ci hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n", 6962306a36Sopenharmony_ci pen->subreport_list[i].value, 7062306a36Sopenharmony_ci pen->subreport_list[i].id, 7162306a36Sopenharmony_ci i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : ""); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci hid_dbg(hdev, "\t}\n"); 7462306a36Sopenharmony_ci hid_dbg(hdev, "\t.inrange = %s\n", 7562306a36Sopenharmony_ci uclogic_params_pen_inrange_to_str(pen->inrange)); 7662306a36Sopenharmony_ci hid_dbg(hdev, "\t.fragmented_hires = %s\n", 7762306a36Sopenharmony_ci (pen->fragmented_hires ? "true" : "false")); 7862306a36Sopenharmony_ci hid_dbg(hdev, "\t.tilt_y_flipped = %s\n", 7962306a36Sopenharmony_ci (pen->tilt_y_flipped ? "true" : "false")); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/** 8362306a36Sopenharmony_ci * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters 8462306a36Sopenharmony_ci * @hdev: The HID device the pen parameters describe. 8562306a36Sopenharmony_ci * @frame: The frame parameters to dump. 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * Dump tablet interface frame parameters with hid_dbg(). The dump is 8862306a36Sopenharmony_ci * indented with two tabs. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_cistatic void uclogic_params_frame_hid_dbg( 9162306a36Sopenharmony_ci const struct hid_device *hdev, 9262306a36Sopenharmony_ci const struct uclogic_params_frame *frame) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr); 9562306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size); 9662306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.id = %u\n", frame->id); 9762306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix); 9862306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb); 9962306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte); 10062306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte); 10162306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max); 10262306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n", 10362306a36Sopenharmony_ci frame->touch_flip_at); 10462306a36Sopenharmony_ci hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n", 10562306a36Sopenharmony_ci frame->bitmap_dial_byte); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/** 10962306a36Sopenharmony_ci * uclogic_params_hid_dbg() - Dump tablet interface parameters 11062306a36Sopenharmony_ci * @hdev: The HID device the parameters describe. 11162306a36Sopenharmony_ci * @params: The parameters to dump. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Dump tablet interface parameters with hid_dbg(). 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_civoid uclogic_params_hid_dbg(const struct hid_device *hdev, 11662306a36Sopenharmony_ci const struct uclogic_params *params) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci size_t i; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci hid_dbg(hdev, ".invalid = %s\n", 12162306a36Sopenharmony_ci params->invalid ? "true" : "false"); 12262306a36Sopenharmony_ci hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr); 12362306a36Sopenharmony_ci hid_dbg(hdev, ".desc_size = %u\n", params->desc_size); 12462306a36Sopenharmony_ci hid_dbg(hdev, ".pen = {\n"); 12562306a36Sopenharmony_ci uclogic_params_pen_hid_dbg(hdev, ¶ms->pen); 12662306a36Sopenharmony_ci hid_dbg(hdev, "\t}\n"); 12762306a36Sopenharmony_ci hid_dbg(hdev, ".frame_list = {\n"); 12862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 12962306a36Sopenharmony_ci hid_dbg(hdev, "\t{\n"); 13062306a36Sopenharmony_ci uclogic_params_frame_hid_dbg(hdev, ¶ms->frame_list[i]); 13162306a36Sopenharmony_ci hid_dbg(hdev, "\t}%s\n", 13262306a36Sopenharmony_ci i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : ""); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci hid_dbg(hdev, "}\n"); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * uclogic_params_get_str_desc - retrieve a string descriptor from a HID 13962306a36Sopenharmony_ci * device interface, putting it into a kmalloc-allocated buffer as is, without 14062306a36Sopenharmony_ci * character encoding conversion. 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * @pbuf: Location for the kmalloc-allocated buffer pointer containing 14362306a36Sopenharmony_ci * the retrieved descriptor. Not modified in case of error. 14462306a36Sopenharmony_ci * Can be NULL to have retrieved descriptor discarded. 14562306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to retrieve the string 14662306a36Sopenharmony_ci * descriptor from. Cannot be NULL. 14762306a36Sopenharmony_ci * @idx: Index of the string descriptor to request from the device. 14862306a36Sopenharmony_ci * @len: Length of the buffer to allocate and the data to retrieve. 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Returns: 15162306a36Sopenharmony_ci * number of bytes retrieved (<= len), 15262306a36Sopenharmony_ci * -EPIPE, if the descriptor was not found, or 15362306a36Sopenharmony_ci * another negative errno code in case of other error. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cistatic int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, 15662306a36Sopenharmony_ci __u8 idx, size_t len) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int rc; 15962306a36Sopenharmony_ci struct usb_device *udev; 16062306a36Sopenharmony_ci __u8 *buf = NULL; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Check arguments */ 16362306a36Sopenharmony_ci if (hdev == NULL) { 16462306a36Sopenharmony_ci rc = -EINVAL; 16562306a36Sopenharmony_ci goto cleanup; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci udev = hid_to_usb_dev(hdev); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 17162306a36Sopenharmony_ci if (buf == NULL) { 17262306a36Sopenharmony_ci rc = -ENOMEM; 17362306a36Sopenharmony_ci goto cleanup; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 17762306a36Sopenharmony_ci USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, 17862306a36Sopenharmony_ci (USB_DT_STRING << 8) + idx, 17962306a36Sopenharmony_ci 0x0409, buf, len, 18062306a36Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 18162306a36Sopenharmony_ci if (rc == -EPIPE) { 18262306a36Sopenharmony_ci hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); 18362306a36Sopenharmony_ci goto cleanup; 18462306a36Sopenharmony_ci } else if (rc < 0) { 18562306a36Sopenharmony_ci hid_err(hdev, 18662306a36Sopenharmony_ci "failed retrieving string descriptor #%u: %d\n", 18762306a36Sopenharmony_ci idx, rc); 18862306a36Sopenharmony_ci goto cleanup; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (pbuf != NULL) { 19262306a36Sopenharmony_ci *pbuf = buf; 19362306a36Sopenharmony_ci buf = NULL; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cicleanup: 19762306a36Sopenharmony_ci kfree(buf); 19862306a36Sopenharmony_ci return rc; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/** 20262306a36Sopenharmony_ci * uclogic_params_pen_cleanup - free resources used by struct 20362306a36Sopenharmony_ci * uclogic_params_pen (tablet interface's pen input parameters). 20462306a36Sopenharmony_ci * Can be called repeatedly. 20562306a36Sopenharmony_ci * 20662306a36Sopenharmony_ci * @pen: Pen input parameters to cleanup. Cannot be NULL. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_cistatic void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci kfree(pen->desc_ptr); 21162306a36Sopenharmony_ci memset(pen, 0, sizeof(*pen)); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/** 21562306a36Sopenharmony_ci * uclogic_params_pen_init_v1() - initialize tablet interface pen 21662306a36Sopenharmony_ci * input and retrieve its parameters from the device, using v1 protocol. 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * @pen: Pointer to the pen parameters to initialize (to be 21962306a36Sopenharmony_ci * cleaned up with uclogic_params_pen_cleanup()). Not modified in 22062306a36Sopenharmony_ci * case of error, or if parameters are not found. Cannot be NULL. 22162306a36Sopenharmony_ci * @pfound: Location for a flag which is set to true if the parameters 22262306a36Sopenharmony_ci * were found, and to false if not (e.g. device was 22362306a36Sopenharmony_ci * incompatible). Not modified in case of error. Cannot be NULL. 22462306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 22562306a36Sopenharmony_ci * parameters from. Cannot be NULL. 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Returns: 22862306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistatic int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, 23162306a36Sopenharmony_ci bool *pfound, 23262306a36Sopenharmony_ci struct hid_device *hdev) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int rc; 23562306a36Sopenharmony_ci bool found = false; 23662306a36Sopenharmony_ci /* Buffer for (part of) the string descriptor */ 23762306a36Sopenharmony_ci __u8 *buf = NULL; 23862306a36Sopenharmony_ci /* Minimum descriptor length required, maximum seen so far is 18 */ 23962306a36Sopenharmony_ci const int len = 12; 24062306a36Sopenharmony_ci s32 resolution; 24162306a36Sopenharmony_ci /* Pen report descriptor template parameters */ 24262306a36Sopenharmony_ci s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 24362306a36Sopenharmony_ci __u8 *desc_ptr = NULL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Check arguments */ 24662306a36Sopenharmony_ci if (pen == NULL || pfound == NULL || hdev == NULL) { 24762306a36Sopenharmony_ci rc = -EINVAL; 24862306a36Sopenharmony_ci goto cleanup; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* 25262306a36Sopenharmony_ci * Read string descriptor containing pen input parameters. 25362306a36Sopenharmony_ci * The specific string descriptor and data were discovered by sniffing 25462306a36Sopenharmony_ci * the Windows driver traffic. 25562306a36Sopenharmony_ci * NOTE: This enables fully-functional tablet mode. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); 25862306a36Sopenharmony_ci if (rc == -EPIPE) { 25962306a36Sopenharmony_ci hid_dbg(hdev, 26062306a36Sopenharmony_ci "string descriptor with pen parameters not found, assuming not compatible\n"); 26162306a36Sopenharmony_ci goto finish; 26262306a36Sopenharmony_ci } else if (rc < 0) { 26362306a36Sopenharmony_ci hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 26462306a36Sopenharmony_ci goto cleanup; 26562306a36Sopenharmony_ci } else if (rc != len) { 26662306a36Sopenharmony_ci hid_dbg(hdev, 26762306a36Sopenharmony_ci "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", 26862306a36Sopenharmony_ci rc, len); 26962306a36Sopenharmony_ci goto finish; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * Fill report descriptor parameters from the string descriptor 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 27662306a36Sopenharmony_ci get_unaligned_le16(buf + 2); 27762306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 27862306a36Sopenharmony_ci get_unaligned_le16(buf + 4); 27962306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 28062306a36Sopenharmony_ci get_unaligned_le16(buf + 8); 28162306a36Sopenharmony_ci resolution = get_unaligned_le16(buf + 10); 28262306a36Sopenharmony_ci if (resolution == 0) { 28362306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 28462306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 28762306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 28862306a36Sopenharmony_ci resolution; 28962306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 29062306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 29162306a36Sopenharmony_ci resolution; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci kfree(buf); 29462306a36Sopenharmony_ci buf = NULL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* 29762306a36Sopenharmony_ci * Generate pen report descriptor 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci desc_ptr = uclogic_rdesc_template_apply( 30062306a36Sopenharmony_ci uclogic_rdesc_v1_pen_template_arr, 30162306a36Sopenharmony_ci uclogic_rdesc_v1_pen_template_size, 30262306a36Sopenharmony_ci desc_params, ARRAY_SIZE(desc_params)); 30362306a36Sopenharmony_ci if (desc_ptr == NULL) { 30462306a36Sopenharmony_ci rc = -ENOMEM; 30562306a36Sopenharmony_ci goto cleanup; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* 30962306a36Sopenharmony_ci * Fill-in the parameters 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci memset(pen, 0, sizeof(*pen)); 31262306a36Sopenharmony_ci pen->desc_ptr = desc_ptr; 31362306a36Sopenharmony_ci desc_ptr = NULL; 31462306a36Sopenharmony_ci pen->desc_size = uclogic_rdesc_v1_pen_template_size; 31562306a36Sopenharmony_ci pen->id = UCLOGIC_RDESC_V1_PEN_ID; 31662306a36Sopenharmony_ci pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; 31762306a36Sopenharmony_ci found = true; 31862306a36Sopenharmony_cifinish: 31962306a36Sopenharmony_ci *pfound = found; 32062306a36Sopenharmony_ci rc = 0; 32162306a36Sopenharmony_cicleanup: 32262306a36Sopenharmony_ci kfree(desc_ptr); 32362306a36Sopenharmony_ci kfree(buf); 32462306a36Sopenharmony_ci return rc; 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/** 32862306a36Sopenharmony_ci * uclogic_params_get_le24() - get a 24-bit little-endian number from a 32962306a36Sopenharmony_ci * buffer. 33062306a36Sopenharmony_ci * 33162306a36Sopenharmony_ci * @p: The pointer to the number buffer. 33262306a36Sopenharmony_ci * 33362306a36Sopenharmony_ci * Returns: 33462306a36Sopenharmony_ci * The retrieved number 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic s32 uclogic_params_get_le24(const void *p) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci const __u8 *b = p; 33962306a36Sopenharmony_ci return b[0] | (b[1] << 8UL) | (b[2] << 16UL); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci/** 34362306a36Sopenharmony_ci * uclogic_params_pen_init_v2() - initialize tablet interface pen 34462306a36Sopenharmony_ci * input and retrieve its parameters from the device, using v2 protocol. 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * @pen: Pointer to the pen parameters to initialize (to be 34762306a36Sopenharmony_ci * cleaned up with uclogic_params_pen_cleanup()). Not 34862306a36Sopenharmony_ci * modified in case of error, or if parameters are not 34962306a36Sopenharmony_ci * found. Cannot be NULL. 35062306a36Sopenharmony_ci * @pfound: Location for a flag which is set to true if the 35162306a36Sopenharmony_ci * parameters were found, and to false if not (e.g. 35262306a36Sopenharmony_ci * device was incompatible). Not modified in case of 35362306a36Sopenharmony_ci * error. Cannot be NULL. 35462306a36Sopenharmony_ci * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw 35562306a36Sopenharmony_ci * parameters, which could be used to identify the tablet 35662306a36Sopenharmony_ci * to some extent. Should be freed with kfree after use. 35762306a36Sopenharmony_ci * NULL, if not needed. Not modified in case of error. 35862306a36Sopenharmony_ci * Only set if *pfound is set to true. 35962306a36Sopenharmony_ci * @pparams_len: Location for the length of the retrieved raw 36062306a36Sopenharmony_ci * parameters. NULL, if not needed. Not modified in case 36162306a36Sopenharmony_ci * of error. Only set if *pfound is set to true. 36262306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize 36362306a36Sopenharmony_ci * and get parameters from. Cannot be NULL. 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * Returns: 36662306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, 36962306a36Sopenharmony_ci bool *pfound, 37062306a36Sopenharmony_ci __u8 **pparams_ptr, 37162306a36Sopenharmony_ci size_t *pparams_len, 37262306a36Sopenharmony_ci struct hid_device *hdev) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci int rc; 37562306a36Sopenharmony_ci bool found = false; 37662306a36Sopenharmony_ci /* Buffer for (part of) the parameter string descriptor */ 37762306a36Sopenharmony_ci __u8 *buf = NULL; 37862306a36Sopenharmony_ci /* Parameter string descriptor required length */ 37962306a36Sopenharmony_ci const int params_len_min = 18; 38062306a36Sopenharmony_ci /* Parameter string descriptor accepted length */ 38162306a36Sopenharmony_ci const int params_len_max = 32; 38262306a36Sopenharmony_ci /* Parameter string descriptor received length */ 38362306a36Sopenharmony_ci int params_len; 38462306a36Sopenharmony_ci size_t i; 38562306a36Sopenharmony_ci s32 resolution; 38662306a36Sopenharmony_ci /* Pen report descriptor template parameters */ 38762306a36Sopenharmony_ci s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 38862306a36Sopenharmony_ci __u8 *desc_ptr = NULL; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* Check arguments */ 39162306a36Sopenharmony_ci if (pen == NULL || pfound == NULL || hdev == NULL) { 39262306a36Sopenharmony_ci rc = -EINVAL; 39362306a36Sopenharmony_ci goto cleanup; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* 39762306a36Sopenharmony_ci * Read string descriptor containing pen input parameters. 39862306a36Sopenharmony_ci * The specific string descriptor and data were discovered by sniffing 39962306a36Sopenharmony_ci * the Windows driver traffic. 40062306a36Sopenharmony_ci * NOTE: This enables fully-functional tablet mode. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max); 40362306a36Sopenharmony_ci if (rc == -EPIPE) { 40462306a36Sopenharmony_ci hid_dbg(hdev, 40562306a36Sopenharmony_ci "string descriptor with pen parameters not found, assuming not compatible\n"); 40662306a36Sopenharmony_ci goto finish; 40762306a36Sopenharmony_ci } else if (rc < 0) { 40862306a36Sopenharmony_ci hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 40962306a36Sopenharmony_ci goto cleanup; 41062306a36Sopenharmony_ci } else if (rc < params_len_min) { 41162306a36Sopenharmony_ci hid_dbg(hdev, 41262306a36Sopenharmony_ci "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n", 41362306a36Sopenharmony_ci rc, params_len_min); 41462306a36Sopenharmony_ci goto finish; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci params_len = rc; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * Check it's not just a catch-all UTF-16LE-encoded ASCII 42162306a36Sopenharmony_ci * string (such as the model name) some tablets put into all 42262306a36Sopenharmony_ci * unknown string descriptors. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci for (i = 2; 42562306a36Sopenharmony_ci i < params_len && 42662306a36Sopenharmony_ci (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); 42762306a36Sopenharmony_ci i += 2); 42862306a36Sopenharmony_ci if (i >= params_len) { 42962306a36Sopenharmony_ci hid_dbg(hdev, 43062306a36Sopenharmony_ci "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); 43162306a36Sopenharmony_ci goto finish; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * Fill report descriptor parameters from the string descriptor 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 43862306a36Sopenharmony_ci uclogic_params_get_le24(buf + 2); 43962306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 44062306a36Sopenharmony_ci uclogic_params_get_le24(buf + 5); 44162306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 44262306a36Sopenharmony_ci get_unaligned_le16(buf + 8); 44362306a36Sopenharmony_ci resolution = get_unaligned_le16(buf + 10); 44462306a36Sopenharmony_ci if (resolution == 0) { 44562306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 44662306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 44762306a36Sopenharmony_ci } else { 44862306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 44962306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 45062306a36Sopenharmony_ci resolution; 45162306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 45262306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 45362306a36Sopenharmony_ci resolution; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* 45762306a36Sopenharmony_ci * Generate pen report descriptor 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci desc_ptr = uclogic_rdesc_template_apply( 46062306a36Sopenharmony_ci uclogic_rdesc_v2_pen_template_arr, 46162306a36Sopenharmony_ci uclogic_rdesc_v2_pen_template_size, 46262306a36Sopenharmony_ci desc_params, ARRAY_SIZE(desc_params)); 46362306a36Sopenharmony_ci if (desc_ptr == NULL) { 46462306a36Sopenharmony_ci rc = -ENOMEM; 46562306a36Sopenharmony_ci goto cleanup; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * Fill-in the parameters 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci memset(pen, 0, sizeof(*pen)); 47262306a36Sopenharmony_ci pen->desc_ptr = desc_ptr; 47362306a36Sopenharmony_ci desc_ptr = NULL; 47462306a36Sopenharmony_ci pen->desc_size = uclogic_rdesc_v2_pen_template_size; 47562306a36Sopenharmony_ci pen->id = UCLOGIC_RDESC_V2_PEN_ID; 47662306a36Sopenharmony_ci pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; 47762306a36Sopenharmony_ci pen->fragmented_hires = true; 47862306a36Sopenharmony_ci pen->tilt_y_flipped = true; 47962306a36Sopenharmony_ci found = true; 48062306a36Sopenharmony_ci if (pparams_ptr != NULL) { 48162306a36Sopenharmony_ci *pparams_ptr = buf; 48262306a36Sopenharmony_ci buf = NULL; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci if (pparams_len != NULL) 48562306a36Sopenharmony_ci *pparams_len = params_len; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cifinish: 48862306a36Sopenharmony_ci *pfound = found; 48962306a36Sopenharmony_ci rc = 0; 49062306a36Sopenharmony_cicleanup: 49162306a36Sopenharmony_ci kfree(desc_ptr); 49262306a36Sopenharmony_ci kfree(buf); 49362306a36Sopenharmony_ci return rc; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/** 49762306a36Sopenharmony_ci * uclogic_params_frame_cleanup - free resources used by struct 49862306a36Sopenharmony_ci * uclogic_params_frame (tablet interface's frame controls input parameters). 49962306a36Sopenharmony_ci * Can be called repeatedly. 50062306a36Sopenharmony_ci * 50162306a36Sopenharmony_ci * @frame: Frame controls input parameters to cleanup. Cannot be NULL. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_cistatic void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci kfree(frame->desc_ptr); 50662306a36Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci/** 51062306a36Sopenharmony_ci * uclogic_params_frame_init_with_desc() - initialize tablet's frame control 51162306a36Sopenharmony_ci * parameters with a static report descriptor. 51262306a36Sopenharmony_ci * 51362306a36Sopenharmony_ci * @frame: Pointer to the frame parameters to initialize (to be cleaned 51462306a36Sopenharmony_ci * up with uclogic_params_frame_cleanup()). Not modified in case 51562306a36Sopenharmony_ci * of error. Cannot be NULL. 51662306a36Sopenharmony_ci * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. 51762306a36Sopenharmony_ci * @desc_size: Report descriptor size. 51862306a36Sopenharmony_ci * @id: Report ID used for frame reports, if they should be tweaked, 51962306a36Sopenharmony_ci * zero if not. 52062306a36Sopenharmony_ci * 52162306a36Sopenharmony_ci * Returns: 52262306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_cistatic int uclogic_params_frame_init_with_desc( 52562306a36Sopenharmony_ci struct uclogic_params_frame *frame, 52662306a36Sopenharmony_ci const __u8 *desc_ptr, 52762306a36Sopenharmony_ci size_t desc_size, 52862306a36Sopenharmony_ci unsigned int id) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci __u8 *copy_desc_ptr; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) 53362306a36Sopenharmony_ci return -EINVAL; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 53662306a36Sopenharmony_ci if (copy_desc_ptr == NULL) 53762306a36Sopenharmony_ci return -ENOMEM; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci memset(frame, 0, sizeof(*frame)); 54062306a36Sopenharmony_ci frame->desc_ptr = copy_desc_ptr; 54162306a36Sopenharmony_ci frame->desc_size = desc_size; 54262306a36Sopenharmony_ci frame->id = id; 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/** 54762306a36Sopenharmony_ci * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame 54862306a36Sopenharmony_ci * controls. 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * @frame: Pointer to the frame parameters to initialize (to be cleaned 55162306a36Sopenharmony_ci * up with uclogic_params_frame_cleanup()). Not modified in case 55262306a36Sopenharmony_ci * of error, or if parameters are not found. Cannot be NULL. 55362306a36Sopenharmony_ci * @pfound: Location for a flag which is set to true if the parameters 55462306a36Sopenharmony_ci * were found, and to false if not (e.g. device was 55562306a36Sopenharmony_ci * incompatible). Not modified in case of error. Cannot be NULL. 55662306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 55762306a36Sopenharmony_ci * parameters from. Cannot be NULL. 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * Returns: 56062306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_cistatic int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame, 56362306a36Sopenharmony_ci bool *pfound, 56462306a36Sopenharmony_ci struct hid_device *hdev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci int rc; 56762306a36Sopenharmony_ci bool found = false; 56862306a36Sopenharmony_ci struct usb_device *usb_dev; 56962306a36Sopenharmony_ci char *str_buf = NULL; 57062306a36Sopenharmony_ci const size_t str_len = 16; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Check arguments */ 57362306a36Sopenharmony_ci if (frame == NULL || pfound == NULL || hdev == NULL) { 57462306a36Sopenharmony_ci rc = -EINVAL; 57562306a36Sopenharmony_ci goto cleanup; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci usb_dev = hid_to_usb_dev(hdev); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * Enable generic button mode 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci str_buf = kzalloc(str_len, GFP_KERNEL); 58462306a36Sopenharmony_ci if (str_buf == NULL) { 58562306a36Sopenharmony_ci rc = -ENOMEM; 58662306a36Sopenharmony_ci goto cleanup; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci rc = usb_string(usb_dev, 123, str_buf, str_len); 59062306a36Sopenharmony_ci if (rc == -EPIPE) { 59162306a36Sopenharmony_ci hid_dbg(hdev, 59262306a36Sopenharmony_ci "generic button -enabling string descriptor not found\n"); 59362306a36Sopenharmony_ci } else if (rc < 0) { 59462306a36Sopenharmony_ci goto cleanup; 59562306a36Sopenharmony_ci } else if (strncmp(str_buf, "HK On", rc) != 0) { 59662306a36Sopenharmony_ci hid_dbg(hdev, 59762306a36Sopenharmony_ci "invalid response to enabling generic buttons: \"%s\"\n", 59862306a36Sopenharmony_ci str_buf); 59962306a36Sopenharmony_ci } else { 60062306a36Sopenharmony_ci hid_dbg(hdev, "generic buttons enabled\n"); 60162306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 60262306a36Sopenharmony_ci frame, 60362306a36Sopenharmony_ci uclogic_rdesc_v1_frame_arr, 60462306a36Sopenharmony_ci uclogic_rdesc_v1_frame_size, 60562306a36Sopenharmony_ci UCLOGIC_RDESC_V1_FRAME_ID); 60662306a36Sopenharmony_ci if (rc != 0) 60762306a36Sopenharmony_ci goto cleanup; 60862306a36Sopenharmony_ci found = true; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci *pfound = found; 61262306a36Sopenharmony_ci rc = 0; 61362306a36Sopenharmony_cicleanup: 61462306a36Sopenharmony_ci kfree(str_buf); 61562306a36Sopenharmony_ci return rc; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/** 61962306a36Sopenharmony_ci * uclogic_params_cleanup_event_hooks - free resources used by the list of raw 62062306a36Sopenharmony_ci * event hooks. 62162306a36Sopenharmony_ci * Can be called repeatedly. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * @params: Input parameters to cleanup. Cannot be NULL. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_cistatic void uclogic_params_cleanup_event_hooks(struct uclogic_params *params) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct uclogic_raw_event_hook *curr, *n; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (!params || !params->event_hooks) 63062306a36Sopenharmony_ci return; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci list_for_each_entry_safe(curr, n, ¶ms->event_hooks->list, list) { 63362306a36Sopenharmony_ci cancel_work_sync(&curr->work); 63462306a36Sopenharmony_ci list_del(&curr->list); 63562306a36Sopenharmony_ci kfree(curr->event); 63662306a36Sopenharmony_ci kfree(curr); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci kfree(params->event_hooks); 64062306a36Sopenharmony_ci params->event_hooks = NULL; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/** 64462306a36Sopenharmony_ci * uclogic_params_cleanup - free resources used by struct uclogic_params 64562306a36Sopenharmony_ci * (tablet interface's parameters). 64662306a36Sopenharmony_ci * Can be called repeatedly. 64762306a36Sopenharmony_ci * 64862306a36Sopenharmony_ci * @params: Input parameters to cleanup. Cannot be NULL. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_civoid uclogic_params_cleanup(struct uclogic_params *params) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci if (!params->invalid) { 65362306a36Sopenharmony_ci size_t i; 65462306a36Sopenharmony_ci kfree(params->desc_ptr); 65562306a36Sopenharmony_ci uclogic_params_pen_cleanup(¶ms->pen); 65662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) 65762306a36Sopenharmony_ci uclogic_params_frame_cleanup(¶ms->frame_list[i]); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci uclogic_params_cleanup_event_hooks(params); 66062306a36Sopenharmony_ci memset(params, 0, sizeof(*params)); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/** 66562306a36Sopenharmony_ci * uclogic_params_get_desc() - Get a replacement report descriptor for a 66662306a36Sopenharmony_ci * tablet's interface. 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * @params: The parameters of a tablet interface to get report 66962306a36Sopenharmony_ci * descriptor for. Cannot be NULL. 67062306a36Sopenharmony_ci * @pdesc: Location for the resulting, kmalloc-allocated report 67162306a36Sopenharmony_ci * descriptor pointer, or for NULL, if there's no replacement 67262306a36Sopenharmony_ci * report descriptor. Not modified in case of error. Cannot be 67362306a36Sopenharmony_ci * NULL. 67462306a36Sopenharmony_ci * @psize: Location for the resulting report descriptor size, not set if 67562306a36Sopenharmony_ci * there's no replacement report descriptor. Not modified in case 67662306a36Sopenharmony_ci * of error. Cannot be NULL. 67762306a36Sopenharmony_ci * 67862306a36Sopenharmony_ci * Returns: 67962306a36Sopenharmony_ci * Zero, if successful. 68062306a36Sopenharmony_ci * -EINVAL, if invalid arguments are supplied. 68162306a36Sopenharmony_ci * -ENOMEM, if failed to allocate memory. 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_ciint uclogic_params_get_desc(const struct uclogic_params *params, 68462306a36Sopenharmony_ci __u8 **pdesc, 68562306a36Sopenharmony_ci unsigned int *psize) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci int rc = -ENOMEM; 68862306a36Sopenharmony_ci bool present = false; 68962306a36Sopenharmony_ci unsigned int size = 0; 69062306a36Sopenharmony_ci __u8 *desc = NULL; 69162306a36Sopenharmony_ci size_t i; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* Check arguments */ 69462306a36Sopenharmony_ci if (params == NULL || pdesc == NULL || psize == NULL) 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* Concatenate descriptors */ 69862306a36Sopenharmony_ci#define ADD_DESC(_desc_ptr, _desc_size) \ 69962306a36Sopenharmony_ci do { \ 70062306a36Sopenharmony_ci unsigned int new_size; \ 70162306a36Sopenharmony_ci __u8 *new_desc; \ 70262306a36Sopenharmony_ci if ((_desc_ptr) == NULL) { \ 70362306a36Sopenharmony_ci break; \ 70462306a36Sopenharmony_ci } \ 70562306a36Sopenharmony_ci new_size = size + (_desc_size); \ 70662306a36Sopenharmony_ci new_desc = krealloc(desc, new_size, GFP_KERNEL); \ 70762306a36Sopenharmony_ci if (new_desc == NULL) { \ 70862306a36Sopenharmony_ci goto cleanup; \ 70962306a36Sopenharmony_ci } \ 71062306a36Sopenharmony_ci memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \ 71162306a36Sopenharmony_ci desc = new_desc; \ 71262306a36Sopenharmony_ci size = new_size; \ 71362306a36Sopenharmony_ci present = true; \ 71462306a36Sopenharmony_ci } while (0) 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ADD_DESC(params->desc_ptr, params->desc_size); 71762306a36Sopenharmony_ci ADD_DESC(params->pen.desc_ptr, params->pen.desc_size); 71862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 71962306a36Sopenharmony_ci ADD_DESC(params->frame_list[i].desc_ptr, 72062306a36Sopenharmony_ci params->frame_list[i].desc_size); 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci#undef ADD_DESC 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (present) { 72662306a36Sopenharmony_ci *pdesc = desc; 72762306a36Sopenharmony_ci *psize = size; 72862306a36Sopenharmony_ci desc = NULL; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci rc = 0; 73162306a36Sopenharmony_cicleanup: 73262306a36Sopenharmony_ci kfree(desc); 73362306a36Sopenharmony_ci return rc; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/** 73762306a36Sopenharmony_ci * uclogic_params_init_invalid() - initialize tablet interface parameters, 73862306a36Sopenharmony_ci * specifying the interface is invalid. 73962306a36Sopenharmony_ci * 74062306a36Sopenharmony_ci * @params: Parameters to initialize (to be cleaned with 74162306a36Sopenharmony_ci * uclogic_params_cleanup()). Cannot be NULL. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_cistatic void uclogic_params_init_invalid(struct uclogic_params *params) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci params->invalid = true; 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci/** 74962306a36Sopenharmony_ci * uclogic_params_init_with_opt_desc() - initialize tablet interface 75062306a36Sopenharmony_ci * parameters with an optional replacement report descriptor. Only modify 75162306a36Sopenharmony_ci * report descriptor, if the original report descriptor matches the expected 75262306a36Sopenharmony_ci * size. 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci * @params: Parameters to initialize (to be cleaned with 75562306a36Sopenharmony_ci * uclogic_params_cleanup()). Not modified in case of 75662306a36Sopenharmony_ci * error. Cannot be NULL. 75762306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface create the 75862306a36Sopenharmony_ci * parameters for. Cannot be NULL. 75962306a36Sopenharmony_ci * @orig_desc_size: Expected size of the original report descriptor to 76062306a36Sopenharmony_ci * be replaced. 76162306a36Sopenharmony_ci * @desc_ptr: Pointer to the replacement report descriptor. 76262306a36Sopenharmony_ci * Can be NULL, if desc_size is zero. 76362306a36Sopenharmony_ci * @desc_size: Size of the replacement report descriptor. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * Returns: 76662306a36Sopenharmony_ci * Zero, if successful. -EINVAL if an invalid argument was passed. 76762306a36Sopenharmony_ci * -ENOMEM, if failed to allocate memory. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_cistatic int uclogic_params_init_with_opt_desc(struct uclogic_params *params, 77062306a36Sopenharmony_ci struct hid_device *hdev, 77162306a36Sopenharmony_ci unsigned int orig_desc_size, 77262306a36Sopenharmony_ci __u8 *desc_ptr, 77362306a36Sopenharmony_ci unsigned int desc_size) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci __u8 *desc_copy_ptr = NULL; 77662306a36Sopenharmony_ci unsigned int desc_copy_size; 77762306a36Sopenharmony_ci int rc; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* Check arguments */ 78062306a36Sopenharmony_ci if (params == NULL || hdev == NULL || 78162306a36Sopenharmony_ci (desc_ptr == NULL && desc_size != 0)) { 78262306a36Sopenharmony_ci rc = -EINVAL; 78362306a36Sopenharmony_ci goto cleanup; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* Replace report descriptor, if it matches */ 78762306a36Sopenharmony_ci if (hdev->dev_rsize == orig_desc_size) { 78862306a36Sopenharmony_ci hid_dbg(hdev, 78962306a36Sopenharmony_ci "device report descriptor matches the expected size, replacing\n"); 79062306a36Sopenharmony_ci desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 79162306a36Sopenharmony_ci if (desc_copy_ptr == NULL) { 79262306a36Sopenharmony_ci rc = -ENOMEM; 79362306a36Sopenharmony_ci goto cleanup; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci desc_copy_size = desc_size; 79662306a36Sopenharmony_ci } else { 79762306a36Sopenharmony_ci hid_dbg(hdev, 79862306a36Sopenharmony_ci "device report descriptor doesn't match the expected size (%u != %u), preserving\n", 79962306a36Sopenharmony_ci hdev->dev_rsize, orig_desc_size); 80062306a36Sopenharmony_ci desc_copy_ptr = NULL; 80162306a36Sopenharmony_ci desc_copy_size = 0; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Output parameters */ 80562306a36Sopenharmony_ci memset(params, 0, sizeof(*params)); 80662306a36Sopenharmony_ci params->desc_ptr = desc_copy_ptr; 80762306a36Sopenharmony_ci desc_copy_ptr = NULL; 80862306a36Sopenharmony_ci params->desc_size = desc_copy_size; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci rc = 0; 81162306a36Sopenharmony_cicleanup: 81262306a36Sopenharmony_ci kfree(desc_copy_ptr); 81362306a36Sopenharmony_ci return rc; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/** 81762306a36Sopenharmony_ci * uclogic_params_huion_init() - initialize a Huion tablet interface and discover 81862306a36Sopenharmony_ci * its parameters. 81962306a36Sopenharmony_ci * 82062306a36Sopenharmony_ci * @params: Parameters to fill in (to be cleaned with 82162306a36Sopenharmony_ci * uclogic_params_cleanup()). Not modified in case of error. 82262306a36Sopenharmony_ci * Cannot be NULL. 82362306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 82462306a36Sopenharmony_ci * parameters from. Cannot be NULL. 82562306a36Sopenharmony_ci * 82662306a36Sopenharmony_ci * Returns: 82762306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_cistatic int uclogic_params_huion_init(struct uclogic_params *params, 83062306a36Sopenharmony_ci struct hid_device *hdev) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci int rc; 83362306a36Sopenharmony_ci struct usb_device *udev; 83462306a36Sopenharmony_ci struct usb_interface *iface; 83562306a36Sopenharmony_ci __u8 bInterfaceNumber; 83662306a36Sopenharmony_ci bool found; 83762306a36Sopenharmony_ci /* The resulting parameters (noop) */ 83862306a36Sopenharmony_ci struct uclogic_params p = {0, }; 83962306a36Sopenharmony_ci static const char transition_ver[] = "HUION_T153_160607"; 84062306a36Sopenharmony_ci char *ver_ptr = NULL; 84162306a36Sopenharmony_ci const size_t ver_len = sizeof(transition_ver) + 1; 84262306a36Sopenharmony_ci __u8 *params_ptr = NULL; 84362306a36Sopenharmony_ci size_t params_len = 0; 84462306a36Sopenharmony_ci /* Parameters string descriptor of a model with touch ring (HS610) */ 84562306a36Sopenharmony_ci const __u8 touch_ring_model_params_buf[] = { 84662306a36Sopenharmony_ci 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, 84762306a36Sopenharmony_ci 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, 84862306a36Sopenharmony_ci 0x04, 0x3C, 0x3E 84962306a36Sopenharmony_ci }; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* Check arguments */ 85262306a36Sopenharmony_ci if (params == NULL || hdev == NULL) { 85362306a36Sopenharmony_ci rc = -EINVAL; 85462306a36Sopenharmony_ci goto cleanup; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci udev = hid_to_usb_dev(hdev); 85862306a36Sopenharmony_ci iface = to_usb_interface(hdev->dev.parent); 85962306a36Sopenharmony_ci bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* If it's a custom keyboard interface */ 86262306a36Sopenharmony_ci if (bInterfaceNumber == 1) { 86362306a36Sopenharmony_ci /* Keep everything intact, but mark pen usage invalid */ 86462306a36Sopenharmony_ci p.pen.usage_invalid = true; 86562306a36Sopenharmony_ci goto output; 86662306a36Sopenharmony_ci /* Else, if it's not a pen interface */ 86762306a36Sopenharmony_ci } else if (bInterfaceNumber != 0) { 86862306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 86962306a36Sopenharmony_ci goto output; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Try to get firmware version */ 87362306a36Sopenharmony_ci ver_ptr = kzalloc(ver_len, GFP_KERNEL); 87462306a36Sopenharmony_ci if (ver_ptr == NULL) { 87562306a36Sopenharmony_ci rc = -ENOMEM; 87662306a36Sopenharmony_ci goto cleanup; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci rc = usb_string(udev, 201, ver_ptr, ver_len); 87962306a36Sopenharmony_ci if (rc == -EPIPE) { 88062306a36Sopenharmony_ci *ver_ptr = '\0'; 88162306a36Sopenharmony_ci } else if (rc < 0) { 88262306a36Sopenharmony_ci hid_err(hdev, 88362306a36Sopenharmony_ci "failed retrieving Huion firmware version: %d\n", rc); 88462306a36Sopenharmony_ci goto cleanup; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci /* If this is a transition firmware */ 88862306a36Sopenharmony_ci if (strcmp(ver_ptr, transition_ver) == 0) { 88962306a36Sopenharmony_ci hid_dbg(hdev, 89062306a36Sopenharmony_ci "transition firmware detected, not probing pen v2 parameters\n"); 89162306a36Sopenharmony_ci } else { 89262306a36Sopenharmony_ci /* Try to probe v2 pen parameters */ 89362306a36Sopenharmony_ci rc = uclogic_params_pen_init_v2(&p.pen, &found, 89462306a36Sopenharmony_ci ¶ms_ptr, ¶ms_len, 89562306a36Sopenharmony_ci hdev); 89662306a36Sopenharmony_ci if (rc != 0) { 89762306a36Sopenharmony_ci hid_err(hdev, 89862306a36Sopenharmony_ci "failed probing pen v2 parameters: %d\n", rc); 89962306a36Sopenharmony_ci goto cleanup; 90062306a36Sopenharmony_ci } else if (found) { 90162306a36Sopenharmony_ci hid_dbg(hdev, "pen v2 parameters found\n"); 90262306a36Sopenharmony_ci /* Create v2 frame button parameters */ 90362306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 90462306a36Sopenharmony_ci &p.frame_list[0], 90562306a36Sopenharmony_ci uclogic_rdesc_v2_frame_buttons_arr, 90662306a36Sopenharmony_ci uclogic_rdesc_v2_frame_buttons_size, 90762306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); 90862306a36Sopenharmony_ci if (rc != 0) { 90962306a36Sopenharmony_ci hid_err(hdev, 91062306a36Sopenharmony_ci "failed creating v2 frame button parameters: %d\n", 91162306a36Sopenharmony_ci rc); 91262306a36Sopenharmony_ci goto cleanup; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Link from pen sub-report */ 91662306a36Sopenharmony_ci p.pen.subreport_list[0].value = 0xe0; 91762306a36Sopenharmony_ci p.pen.subreport_list[0].id = 91862306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* If this is the model with touch ring */ 92162306a36Sopenharmony_ci if (params_ptr != NULL && 92262306a36Sopenharmony_ci params_len == sizeof(touch_ring_model_params_buf) && 92362306a36Sopenharmony_ci memcmp(params_ptr, touch_ring_model_params_buf, 92462306a36Sopenharmony_ci params_len) == 0) { 92562306a36Sopenharmony_ci /* Create touch ring parameters */ 92662306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 92762306a36Sopenharmony_ci &p.frame_list[1], 92862306a36Sopenharmony_ci uclogic_rdesc_v2_frame_touch_ring_arr, 92962306a36Sopenharmony_ci uclogic_rdesc_v2_frame_touch_ring_size, 93062306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 93162306a36Sopenharmony_ci if (rc != 0) { 93262306a36Sopenharmony_ci hid_err(hdev, 93362306a36Sopenharmony_ci "failed creating v2 frame touch ring parameters: %d\n", 93462306a36Sopenharmony_ci rc); 93562306a36Sopenharmony_ci goto cleanup; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci p.frame_list[1].suffix = "Touch Ring"; 93862306a36Sopenharmony_ci p.frame_list[1].dev_id_byte = 93962306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 94062306a36Sopenharmony_ci p.frame_list[1].touch_byte = 5; 94162306a36Sopenharmony_ci p.frame_list[1].touch_max = 12; 94262306a36Sopenharmony_ci p.frame_list[1].touch_flip_at = 7; 94362306a36Sopenharmony_ci } else { 94462306a36Sopenharmony_ci /* Create touch strip parameters */ 94562306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 94662306a36Sopenharmony_ci &p.frame_list[1], 94762306a36Sopenharmony_ci uclogic_rdesc_v2_frame_touch_strip_arr, 94862306a36Sopenharmony_ci uclogic_rdesc_v2_frame_touch_strip_size, 94962306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 95062306a36Sopenharmony_ci if (rc != 0) { 95162306a36Sopenharmony_ci hid_err(hdev, 95262306a36Sopenharmony_ci "failed creating v2 frame touch strip parameters: %d\n", 95362306a36Sopenharmony_ci rc); 95462306a36Sopenharmony_ci goto cleanup; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci p.frame_list[1].suffix = "Touch Strip"; 95762306a36Sopenharmony_ci p.frame_list[1].dev_id_byte = 95862306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 95962306a36Sopenharmony_ci p.frame_list[1].touch_byte = 5; 96062306a36Sopenharmony_ci p.frame_list[1].touch_max = 8; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Link from pen sub-report */ 96462306a36Sopenharmony_ci p.pen.subreport_list[1].value = 0xf0; 96562306a36Sopenharmony_ci p.pen.subreport_list[1].id = 96662306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* Create v2 frame dial parameters */ 96962306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 97062306a36Sopenharmony_ci &p.frame_list[2], 97162306a36Sopenharmony_ci uclogic_rdesc_v2_frame_dial_arr, 97262306a36Sopenharmony_ci uclogic_rdesc_v2_frame_dial_size, 97362306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_DIAL_ID); 97462306a36Sopenharmony_ci if (rc != 0) { 97562306a36Sopenharmony_ci hid_err(hdev, 97662306a36Sopenharmony_ci "failed creating v2 frame dial parameters: %d\n", 97762306a36Sopenharmony_ci rc); 97862306a36Sopenharmony_ci goto cleanup; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci p.frame_list[2].suffix = "Dial"; 98162306a36Sopenharmony_ci p.frame_list[2].dev_id_byte = 98262306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; 98362306a36Sopenharmony_ci p.frame_list[2].bitmap_dial_byte = 5; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* Link from pen sub-report */ 98662306a36Sopenharmony_ci p.pen.subreport_list[2].value = 0xf1; 98762306a36Sopenharmony_ci p.pen.subreport_list[2].id = 98862306a36Sopenharmony_ci UCLOGIC_RDESC_V2_FRAME_DIAL_ID; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci goto output; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci hid_dbg(hdev, "pen v2 parameters not found\n"); 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Try to probe v1 pen parameters */ 99662306a36Sopenharmony_ci rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 99762306a36Sopenharmony_ci if (rc != 0) { 99862306a36Sopenharmony_ci hid_err(hdev, 99962306a36Sopenharmony_ci "failed probing pen v1 parameters: %d\n", rc); 100062306a36Sopenharmony_ci goto cleanup; 100162306a36Sopenharmony_ci } else if (found) { 100262306a36Sopenharmony_ci hid_dbg(hdev, "pen v1 parameters found\n"); 100362306a36Sopenharmony_ci /* Try to probe v1 frame */ 100462306a36Sopenharmony_ci rc = uclogic_params_frame_init_v1(&p.frame_list[0], 100562306a36Sopenharmony_ci &found, hdev); 100662306a36Sopenharmony_ci if (rc != 0) { 100762306a36Sopenharmony_ci hid_err(hdev, "v1 frame probing failed: %d\n", rc); 100862306a36Sopenharmony_ci goto cleanup; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci hid_dbg(hdev, "frame v1 parameters%s found\n", 101162306a36Sopenharmony_ci (found ? "" : " not")); 101262306a36Sopenharmony_ci if (found) { 101362306a36Sopenharmony_ci /* Link frame button subreports from pen reports */ 101462306a36Sopenharmony_ci p.pen.subreport_list[0].value = 0xe0; 101562306a36Sopenharmony_ci p.pen.subreport_list[0].id = 101662306a36Sopenharmony_ci UCLOGIC_RDESC_V1_FRAME_ID; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci goto output; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci hid_dbg(hdev, "pen v1 parameters not found\n"); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cioutput: 102562306a36Sopenharmony_ci /* Output parameters */ 102662306a36Sopenharmony_ci memcpy(params, &p, sizeof(*params)); 102762306a36Sopenharmony_ci memset(&p, 0, sizeof(p)); 102862306a36Sopenharmony_ci rc = 0; 102962306a36Sopenharmony_cicleanup: 103062306a36Sopenharmony_ci kfree(params_ptr); 103162306a36Sopenharmony_ci kfree(ver_ptr); 103262306a36Sopenharmony_ci uclogic_params_cleanup(&p); 103362306a36Sopenharmony_ci return rc; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/** 103762306a36Sopenharmony_ci * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or 103862306a36Sopenharmony_ci * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data. 103962306a36Sopenharmony_ci * 104062306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 104162306a36Sopenharmony_ci * parameters from. Cannot be NULL. 104262306a36Sopenharmony_ci * @magic_arr: The magic data that should be sent to probe the interface. 104362306a36Sopenharmony_ci * Cannot be NULL. 104462306a36Sopenharmony_ci * @magic_size: Size of the magic data. 104562306a36Sopenharmony_ci * @endpoint: Endpoint where the magic data should be sent. 104662306a36Sopenharmony_ci * 104762306a36Sopenharmony_ci * Returns: 104862306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_cistatic int uclogic_probe_interface(struct hid_device *hdev, const u8 *magic_arr, 105162306a36Sopenharmony_ci size_t magic_size, int endpoint) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci struct usb_device *udev; 105462306a36Sopenharmony_ci unsigned int pipe = 0; 105562306a36Sopenharmony_ci int sent; 105662306a36Sopenharmony_ci u8 *buf = NULL; 105762306a36Sopenharmony_ci int rc = 0; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (!hdev || !magic_arr) { 106062306a36Sopenharmony_ci rc = -EINVAL; 106162306a36Sopenharmony_ci goto cleanup; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci buf = kmemdup(magic_arr, magic_size, GFP_KERNEL); 106562306a36Sopenharmony_ci if (!buf) { 106662306a36Sopenharmony_ci rc = -ENOMEM; 106762306a36Sopenharmony_ci goto cleanup; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci udev = hid_to_usb_dev(hdev); 107162306a36Sopenharmony_ci pipe = usb_sndintpipe(udev, endpoint); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000); 107462306a36Sopenharmony_ci if (rc || sent != magic_size) { 107562306a36Sopenharmony_ci hid_err(hdev, "Interface probing failed: %d\n", rc); 107662306a36Sopenharmony_ci rc = -1; 107762306a36Sopenharmony_ci goto cleanup; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci rc = 0; 108162306a36Sopenharmony_cicleanup: 108262306a36Sopenharmony_ci kfree(buf); 108362306a36Sopenharmony_ci return rc; 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci/** 108762306a36Sopenharmony_ci * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing 108862306a36Sopenharmony_ci * pen and frame parameters returned by UGEE v2 devices. 108962306a36Sopenharmony_ci * 109062306a36Sopenharmony_ci * @str_desc: String descriptor, cannot be NULL. 109162306a36Sopenharmony_ci * @str_desc_size: Size of the string descriptor. 109262306a36Sopenharmony_ci * @desc_params: Output description params list. 109362306a36Sopenharmony_ci * @desc_params_size: Size of the output description params list. 109462306a36Sopenharmony_ci * @frame_type: Output frame type. 109562306a36Sopenharmony_ci * 109662306a36Sopenharmony_ci * Returns: 109762306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 109862306a36Sopenharmony_ci */ 109962306a36Sopenharmony_cistatic int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc, 110062306a36Sopenharmony_ci size_t str_desc_size, 110162306a36Sopenharmony_ci s32 *desc_params, 110262306a36Sopenharmony_ci size_t desc_params_size, 110362306a36Sopenharmony_ci enum uclogic_params_frame_type *frame_type) 110462306a36Sopenharmony_ci{ 110562306a36Sopenharmony_ci s32 pen_x_lm, pen_y_lm; 110662306a36Sopenharmony_ci s32 pen_x_pm, pen_y_pm; 110762306a36Sopenharmony_ci s32 pen_pressure_lm; 110862306a36Sopenharmony_ci s32 frame_num_buttons; 110962306a36Sopenharmony_ci s32 resolution; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* Minimum descriptor length required, maximum seen so far is 14 */ 111262306a36Sopenharmony_ci const int min_str_desc_size = 12; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (!str_desc || str_desc_size < min_str_desc_size) 111562306a36Sopenharmony_ci return -EINVAL; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 111862306a36Sopenharmony_ci return -EINVAL; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci pen_x_lm = get_unaligned_le16(str_desc + 2); 112162306a36Sopenharmony_ci pen_y_lm = get_unaligned_le16(str_desc + 4); 112262306a36Sopenharmony_ci frame_num_buttons = str_desc[6]; 112362306a36Sopenharmony_ci *frame_type = str_desc[7]; 112462306a36Sopenharmony_ci pen_pressure_lm = get_unaligned_le16(str_desc + 8); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci resolution = get_unaligned_le16(str_desc + 10); 112762306a36Sopenharmony_ci if (resolution == 0) { 112862306a36Sopenharmony_ci pen_x_pm = 0; 112962306a36Sopenharmony_ci pen_y_pm = 0; 113062306a36Sopenharmony_ci } else { 113162306a36Sopenharmony_ci pen_x_pm = pen_x_lm * 1000 / resolution; 113262306a36Sopenharmony_ci pen_y_pm = pen_y_lm * 1000 / resolution; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm; 113662306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm; 113762306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm; 113862306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm; 113962306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm; 114062306a36Sopenharmony_ci desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci/** 114662306a36Sopenharmony_ci * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with 114762306a36Sopenharmony_ci * buttons. 114862306a36Sopenharmony_ci * @p: Parameters to fill in, cannot be NULL. 114962306a36Sopenharmony_ci * @desc_params: Device description params list. 115062306a36Sopenharmony_ci * @desc_params_size: Size of the description params list. 115162306a36Sopenharmony_ci * 115262306a36Sopenharmony_ci * Returns: 115362306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 115462306a36Sopenharmony_ci */ 115562306a36Sopenharmony_cistatic int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p, 115662306a36Sopenharmony_ci const s32 *desc_params, 115762306a36Sopenharmony_ci size_t desc_params_size) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci __u8 *rdesc_frame = NULL; 116062306a36Sopenharmony_ci int rc = 0; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 116362306a36Sopenharmony_ci return -EINVAL; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci rdesc_frame = uclogic_rdesc_template_apply( 116662306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_btn_template_arr, 116762306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_btn_template_size, 116862306a36Sopenharmony_ci desc_params, UCLOGIC_RDESC_PH_ID_NUM); 116962306a36Sopenharmony_ci if (!rdesc_frame) 117062306a36Sopenharmony_ci return -ENOMEM; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], 117362306a36Sopenharmony_ci rdesc_frame, 117462306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_btn_template_size, 117562306a36Sopenharmony_ci UCLOGIC_RDESC_V1_FRAME_ID); 117662306a36Sopenharmony_ci kfree(rdesc_frame); 117762306a36Sopenharmony_ci return rc; 117862306a36Sopenharmony_ci} 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci/** 118162306a36Sopenharmony_ci * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a 118262306a36Sopenharmony_ci * bitmap dial. 118362306a36Sopenharmony_ci * @p: Parameters to fill in, cannot be NULL. 118462306a36Sopenharmony_ci * @desc_params: Device description params list. 118562306a36Sopenharmony_ci * @desc_params_size: Size of the description params list. 118662306a36Sopenharmony_ci * 118762306a36Sopenharmony_ci * Returns: 118862306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_cistatic int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p, 119162306a36Sopenharmony_ci const s32 *desc_params, 119262306a36Sopenharmony_ci size_t desc_params_size) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci __u8 *rdesc_frame = NULL; 119562306a36Sopenharmony_ci int rc = 0; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 119862306a36Sopenharmony_ci return -EINVAL; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci rdesc_frame = uclogic_rdesc_template_apply( 120162306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_dial_template_arr, 120262306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_dial_template_size, 120362306a36Sopenharmony_ci desc_params, UCLOGIC_RDESC_PH_ID_NUM); 120462306a36Sopenharmony_ci if (!rdesc_frame) 120562306a36Sopenharmony_ci return -ENOMEM; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], 120862306a36Sopenharmony_ci rdesc_frame, 120962306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_dial_template_size, 121062306a36Sopenharmony_ci UCLOGIC_RDESC_V1_FRAME_ID); 121162306a36Sopenharmony_ci kfree(rdesc_frame); 121262306a36Sopenharmony_ci if (rc) 121362306a36Sopenharmony_ci return rc; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci p->frame_list[0].bitmap_dial_byte = 7; 121662306a36Sopenharmony_ci return 0; 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci/** 122062306a36Sopenharmony_ci * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a 122162306a36Sopenharmony_ci * mouse. 122262306a36Sopenharmony_ci * @p: Parameters to fill in, cannot be NULL. 122362306a36Sopenharmony_ci * 122462306a36Sopenharmony_ci * Returns: 122562306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 122662306a36Sopenharmony_ci */ 122762306a36Sopenharmony_cistatic int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci int rc = 0; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci if (!p) 123262306a36Sopenharmony_ci return -EINVAL; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], 123562306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_mouse_template_arr, 123662306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_frame_mouse_template_size, 123762306a36Sopenharmony_ci UCLOGIC_RDESC_V1_FRAME_ID); 123862306a36Sopenharmony_ci return rc; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci/** 124262306a36Sopenharmony_ci * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has 124362306a36Sopenharmony_ci * battery or not. 124462306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface. 124562306a36Sopenharmony_ci * 124662306a36Sopenharmony_ci * Returns: 124762306a36Sopenharmony_ci * True if the device has battery, false otherwise. 124862306a36Sopenharmony_ci */ 124962306a36Sopenharmony_cistatic bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev) 125062306a36Sopenharmony_ci{ 125162306a36Sopenharmony_ci struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK) 125462306a36Sopenharmony_ci return true; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* The XP-PEN Deco LW vendor, product and version are identical to the 125762306a36Sopenharmony_ci * Deco L. The only difference reported by their firmware is the product 125862306a36Sopenharmony_ci * name. Add a quirk to support battery reporting on the wireless 125962306a36Sopenharmony_ci * version. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_ci if (hdev->vendor == USB_VENDOR_ID_UGEE && 126262306a36Sopenharmony_ci hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) { 126362306a36Sopenharmony_ci struct usb_device *udev = hid_to_usb_dev(hdev); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci if (strstarts(udev->product, "Deco LW")) 126662306a36Sopenharmony_ci return true; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci return false; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci/** 127362306a36Sopenharmony_ci * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting. 127462306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface, cannot be NULL. 127562306a36Sopenharmony_ci * @p: Parameters to fill in, cannot be NULL. 127662306a36Sopenharmony_ci * 127762306a36Sopenharmony_ci * Returns: 127862306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_cistatic int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev, 128162306a36Sopenharmony_ci struct uclogic_params *p) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci int rc = 0; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (!hdev || !p) 128662306a36Sopenharmony_ci return -EINVAL; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* Some tablets contain invalid characters in hdev->uniq, throwing a 128962306a36Sopenharmony_ci * "hwmon: '<name>' is not a valid name attribute, please fix" error. 129062306a36Sopenharmony_ci * Use the device vendor and product IDs instead. 129162306a36Sopenharmony_ci */ 129262306a36Sopenharmony_ci snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor, 129362306a36Sopenharmony_ci hdev->product); 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], 129662306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_battery_template_arr, 129762306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_battery_template_size, 129862306a36Sopenharmony_ci UCLOGIC_RDESC_UGEE_V2_BATTERY_ID); 129962306a36Sopenharmony_ci if (rc) 130062306a36Sopenharmony_ci return rc; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci p->frame_list[1].suffix = "Battery"; 130362306a36Sopenharmony_ci p->pen.subreport_list[1].value = 0xf2; 130462306a36Sopenharmony_ci p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci return rc; 130762306a36Sopenharmony_ci} 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci/** 131062306a36Sopenharmony_ci * uclogic_params_ugee_v2_reconnect_work() - When a wireless tablet looses 131162306a36Sopenharmony_ci * connection to the USB dongle and reconnects, either because of its physical 131262306a36Sopenharmony_ci * distance or because it was switches off and on using the frame's switch, 131362306a36Sopenharmony_ci * uclogic_probe_interface() needs to be called again to enable the tablet. 131462306a36Sopenharmony_ci * 131562306a36Sopenharmony_ci * @work: The work that triggered this function. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_cistatic void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci struct uclogic_raw_event_hook *event_hook; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci event_hook = container_of(work, struct uclogic_raw_event_hook, work); 132262306a36Sopenharmony_ci uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr, 132362306a36Sopenharmony_ci uclogic_ugee_v2_probe_size, 132462306a36Sopenharmony_ci uclogic_ugee_v2_probe_endpoint); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci/** 132862306a36Sopenharmony_ci * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events 132962306a36Sopenharmony_ci * to be hooked for UGEE v2 devices. 133062306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 133162306a36Sopenharmony_ci * parameters from. 133262306a36Sopenharmony_ci * @p: Parameters to fill in, cannot be NULL. 133362306a36Sopenharmony_ci * 133462306a36Sopenharmony_ci * Returns: 133562306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 133662306a36Sopenharmony_ci */ 133762306a36Sopenharmony_cistatic int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev, 133862306a36Sopenharmony_ci struct uclogic_params *p) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci struct uclogic_raw_event_hook *event_hook; 134162306a36Sopenharmony_ci __u8 reconnect_event[] = { 134262306a36Sopenharmony_ci /* Event received on wireless tablet reconnection */ 134362306a36Sopenharmony_ci 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 134462306a36Sopenharmony_ci }; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci if (!p) 134762306a36Sopenharmony_ci return -EINVAL; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci /* The reconnection event is only received if the tablet has battery */ 135062306a36Sopenharmony_ci if (!uclogic_params_ugee_v2_has_battery(hdev)) 135162306a36Sopenharmony_ci return 0; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL); 135462306a36Sopenharmony_ci if (!p->event_hooks) 135562306a36Sopenharmony_ci return -ENOMEM; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci INIT_LIST_HEAD(&p->event_hooks->list); 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL); 136062306a36Sopenharmony_ci if (!event_hook) 136162306a36Sopenharmony_ci return -ENOMEM; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work); 136462306a36Sopenharmony_ci event_hook->hdev = hdev; 136562306a36Sopenharmony_ci event_hook->size = ARRAY_SIZE(reconnect_event); 136662306a36Sopenharmony_ci event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL); 136762306a36Sopenharmony_ci if (!event_hook->event) 136862306a36Sopenharmony_ci return -ENOMEM; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci list_add_tail(&event_hook->list, &p->event_hooks->list); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci return 0; 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci/** 137662306a36Sopenharmony_ci * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by 137762306a36Sopenharmony_ci * discovering their parameters. 137862306a36Sopenharmony_ci * 137962306a36Sopenharmony_ci * These tables, internally designed as v2 to differentiate them from older 138062306a36Sopenharmony_ci * models, expect a payload of magic data in orther to be switched to the fully 138162306a36Sopenharmony_ci * functional mode and expose their parameters in a similar way to the 138262306a36Sopenharmony_ci * information present in uclogic_params_pen_init_v1() but with some 138362306a36Sopenharmony_ci * differences. 138462306a36Sopenharmony_ci * 138562306a36Sopenharmony_ci * @params: Parameters to fill in (to be cleaned with 138662306a36Sopenharmony_ci * uclogic_params_cleanup()). Not modified in case of error. 138762306a36Sopenharmony_ci * Cannot be NULL. 138862306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 138962306a36Sopenharmony_ci * parameters from. Cannot be NULL. 139062306a36Sopenharmony_ci * 139162306a36Sopenharmony_ci * Returns: 139262306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 139362306a36Sopenharmony_ci */ 139462306a36Sopenharmony_cistatic int uclogic_params_ugee_v2_init(struct uclogic_params *params, 139562306a36Sopenharmony_ci struct hid_device *hdev) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci int rc = 0; 139862306a36Sopenharmony_ci struct uclogic_drvdata *drvdata; 139962306a36Sopenharmony_ci struct usb_interface *iface; 140062306a36Sopenharmony_ci __u8 bInterfaceNumber; 140162306a36Sopenharmony_ci const int str_desc_len = 12; 140262306a36Sopenharmony_ci __u8 *str_desc = NULL; 140362306a36Sopenharmony_ci __u8 *rdesc_pen = NULL; 140462306a36Sopenharmony_ci s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 140562306a36Sopenharmony_ci enum uclogic_params_frame_type frame_type; 140662306a36Sopenharmony_ci /* The resulting parameters (noop) */ 140762306a36Sopenharmony_ci struct uclogic_params p = {0, }; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (!params || !hdev) { 141062306a36Sopenharmony_ci rc = -EINVAL; 141162306a36Sopenharmony_ci goto cleanup; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci drvdata = hid_get_drvdata(hdev); 141562306a36Sopenharmony_ci iface = to_usb_interface(hdev->dev.parent); 141662306a36Sopenharmony_ci bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci if (bInterfaceNumber == 0) { 141962306a36Sopenharmony_ci rc = uclogic_params_ugee_v2_init_frame_mouse(&p); 142062306a36Sopenharmony_ci if (rc) 142162306a36Sopenharmony_ci goto cleanup; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci goto output; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (bInterfaceNumber != 2) { 142762306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 142862306a36Sopenharmony_ci goto output; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci /* 143262306a36Sopenharmony_ci * Initialize the interface by sending magic data. 143362306a36Sopenharmony_ci * The specific data was discovered by sniffing the Windows driver 143462306a36Sopenharmony_ci * traffic. 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_ci rc = uclogic_probe_interface(hdev, uclogic_ugee_v2_probe_arr, 143762306a36Sopenharmony_ci uclogic_ugee_v2_probe_size, 143862306a36Sopenharmony_ci uclogic_ugee_v2_probe_endpoint); 143962306a36Sopenharmony_ci if (rc) { 144062306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 144162306a36Sopenharmony_ci goto output; 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* 144562306a36Sopenharmony_ci * Read the string descriptor containing pen and frame parameters. 144662306a36Sopenharmony_ci * The specific string descriptor and data were discovered by sniffing 144762306a36Sopenharmony_ci * the Windows driver traffic. 144862306a36Sopenharmony_ci */ 144962306a36Sopenharmony_ci rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); 145062306a36Sopenharmony_ci if (rc != str_desc_len) { 145162306a36Sopenharmony_ci hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); 145262306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 145362306a36Sopenharmony_ci goto output; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len, 145762306a36Sopenharmony_ci desc_params, 145862306a36Sopenharmony_ci ARRAY_SIZE(desc_params), 145962306a36Sopenharmony_ci &frame_type); 146062306a36Sopenharmony_ci if (rc) 146162306a36Sopenharmony_ci goto cleanup; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci kfree(str_desc); 146462306a36Sopenharmony_ci str_desc = NULL; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci /* Initialize the pen interface */ 146762306a36Sopenharmony_ci rdesc_pen = uclogic_rdesc_template_apply( 146862306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_pen_template_arr, 146962306a36Sopenharmony_ci uclogic_rdesc_ugee_v2_pen_template_size, 147062306a36Sopenharmony_ci desc_params, ARRAY_SIZE(desc_params)); 147162306a36Sopenharmony_ci if (!rdesc_pen) { 147262306a36Sopenharmony_ci rc = -ENOMEM; 147362306a36Sopenharmony_ci goto cleanup; 147462306a36Sopenharmony_ci } 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci p.pen.desc_ptr = rdesc_pen; 147762306a36Sopenharmony_ci p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; 147862306a36Sopenharmony_ci p.pen.id = 0x02; 147962306a36Sopenharmony_ci p.pen.subreport_list[0].value = 0xf0; 148062306a36Sopenharmony_ci p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci /* Initialize the frame interface */ 148362306a36Sopenharmony_ci if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK) 148462306a36Sopenharmony_ci frame_type = UCLOGIC_PARAMS_FRAME_MOUSE; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci switch (frame_type) { 148762306a36Sopenharmony_ci case UCLOGIC_PARAMS_FRAME_DIAL: 148862306a36Sopenharmony_ci case UCLOGIC_PARAMS_FRAME_MOUSE: 148962306a36Sopenharmony_ci rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params, 149062306a36Sopenharmony_ci ARRAY_SIZE(desc_params)); 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci case UCLOGIC_PARAMS_FRAME_BUTTONS: 149362306a36Sopenharmony_ci default: 149462306a36Sopenharmony_ci rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params, 149562306a36Sopenharmony_ci ARRAY_SIZE(desc_params)); 149662306a36Sopenharmony_ci break; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (rc) 150062306a36Sopenharmony_ci goto cleanup; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci /* Initialize the battery interface*/ 150362306a36Sopenharmony_ci if (uclogic_params_ugee_v2_has_battery(hdev)) { 150462306a36Sopenharmony_ci rc = uclogic_params_ugee_v2_init_battery(hdev, &p); 150562306a36Sopenharmony_ci if (rc) { 150662306a36Sopenharmony_ci hid_err(hdev, "error initializing battery: %d\n", rc); 150762306a36Sopenharmony_ci goto cleanup; 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* Create a list of raw events to be ignored */ 151262306a36Sopenharmony_ci rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p); 151362306a36Sopenharmony_ci if (rc) { 151462306a36Sopenharmony_ci hid_err(hdev, "error initializing event hook list: %d\n", rc); 151562306a36Sopenharmony_ci goto cleanup; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_cioutput: 151962306a36Sopenharmony_ci /* Output parameters */ 152062306a36Sopenharmony_ci memcpy(params, &p, sizeof(*params)); 152162306a36Sopenharmony_ci memset(&p, 0, sizeof(p)); 152262306a36Sopenharmony_ci rc = 0; 152362306a36Sopenharmony_cicleanup: 152462306a36Sopenharmony_ci kfree(str_desc); 152562306a36Sopenharmony_ci uclogic_params_cleanup(&p); 152662306a36Sopenharmony_ci return rc; 152762306a36Sopenharmony_ci} 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci/** 153062306a36Sopenharmony_ci * uclogic_params_init() - initialize a tablet interface and discover its 153162306a36Sopenharmony_ci * parameters. 153262306a36Sopenharmony_ci * 153362306a36Sopenharmony_ci * @params: Parameters to fill in (to be cleaned with 153462306a36Sopenharmony_ci * uclogic_params_cleanup()). Not modified in case of error. 153562306a36Sopenharmony_ci * Cannot be NULL. 153662306a36Sopenharmony_ci * @hdev: The HID device of the tablet interface to initialize and get 153762306a36Sopenharmony_ci * parameters from. Cannot be NULL. Must be using the USB low-level 153862306a36Sopenharmony_ci * driver, i.e. be an actual USB tablet. 153962306a36Sopenharmony_ci * 154062306a36Sopenharmony_ci * Returns: 154162306a36Sopenharmony_ci * Zero, if successful. A negative errno code on error. 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_ciint uclogic_params_init(struct uclogic_params *params, 154462306a36Sopenharmony_ci struct hid_device *hdev) 154562306a36Sopenharmony_ci{ 154662306a36Sopenharmony_ci int rc; 154762306a36Sopenharmony_ci struct usb_device *udev; 154862306a36Sopenharmony_ci __u8 bNumInterfaces; 154962306a36Sopenharmony_ci struct usb_interface *iface; 155062306a36Sopenharmony_ci __u8 bInterfaceNumber; 155162306a36Sopenharmony_ci bool found; 155262306a36Sopenharmony_ci /* The resulting parameters (noop) */ 155362306a36Sopenharmony_ci struct uclogic_params p = {0, }; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* Check arguments */ 155662306a36Sopenharmony_ci if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { 155762306a36Sopenharmony_ci rc = -EINVAL; 155862306a36Sopenharmony_ci goto cleanup; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci udev = hid_to_usb_dev(hdev); 156262306a36Sopenharmony_ci bNumInterfaces = udev->config->desc.bNumInterfaces; 156362306a36Sopenharmony_ci iface = to_usb_interface(hdev->dev.parent); 156462306a36Sopenharmony_ci bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* 156762306a36Sopenharmony_ci * Set replacement report descriptor if the original matches the 156862306a36Sopenharmony_ci * specified size. Otherwise keep interface unchanged. 156962306a36Sopenharmony_ci */ 157062306a36Sopenharmony_ci#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 157162306a36Sopenharmony_ci uclogic_params_init_with_opt_desc( \ 157262306a36Sopenharmony_ci &p, hdev, \ 157362306a36Sopenharmony_ci UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 157462306a36Sopenharmony_ci uclogic_rdesc_##_new_desc_token##_arr, \ 157562306a36Sopenharmony_ci uclogic_rdesc_##_new_desc_token##_size) 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci#define VID_PID(_vid, _pid) \ 157862306a36Sopenharmony_ci (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci /* 158162306a36Sopenharmony_ci * Handle specific interfaces for specific tablets. 158262306a36Sopenharmony_ci * 158362306a36Sopenharmony_ci * Observe the following logic: 158462306a36Sopenharmony_ci * 158562306a36Sopenharmony_ci * If the interface is recognized as producing certain useful input: 158662306a36Sopenharmony_ci * Mark interface as valid. 158762306a36Sopenharmony_ci * Output interface parameters. 158862306a36Sopenharmony_ci * Else, if the interface is recognized as *not* producing any useful 158962306a36Sopenharmony_ci * input: 159062306a36Sopenharmony_ci * Mark interface as invalid. 159162306a36Sopenharmony_ci * Else: 159262306a36Sopenharmony_ci * Mark interface as valid. 159362306a36Sopenharmony_ci * Output noop parameters. 159462306a36Sopenharmony_ci * 159562306a36Sopenharmony_ci * Rule of thumb: it is better to disable a broken interface than let 159662306a36Sopenharmony_ci * it spew garbage input. 159762306a36Sopenharmony_ci */ 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci switch (VID_PID(hdev->vendor, hdev->product)) { 160062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 160162306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 160262306a36Sopenharmony_ci rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 160362306a36Sopenharmony_ci if (rc != 0) 160462306a36Sopenharmony_ci goto cleanup; 160562306a36Sopenharmony_ci break; 160662306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 160762306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 160862306a36Sopenharmony_ci rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 160962306a36Sopenharmony_ci if (rc != 0) 161062306a36Sopenharmony_ci goto cleanup; 161162306a36Sopenharmony_ci break; 161262306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 161362306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 161462306a36Sopenharmony_ci if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { 161562306a36Sopenharmony_ci if (bInterfaceNumber == 0) { 161662306a36Sopenharmony_ci /* Try to probe v1 pen parameters */ 161762306a36Sopenharmony_ci rc = uclogic_params_pen_init_v1(&p.pen, 161862306a36Sopenharmony_ci &found, hdev); 161962306a36Sopenharmony_ci if (rc != 0) { 162062306a36Sopenharmony_ci hid_err(hdev, 162162306a36Sopenharmony_ci "pen probing failed: %d\n", 162262306a36Sopenharmony_ci rc); 162362306a36Sopenharmony_ci goto cleanup; 162462306a36Sopenharmony_ci } 162562306a36Sopenharmony_ci if (!found) { 162662306a36Sopenharmony_ci hid_warn(hdev, 162762306a36Sopenharmony_ci "pen parameters not found"); 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci } else { 163062306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci } else { 163362306a36Sopenharmony_ci rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 163462306a36Sopenharmony_ci if (rc != 0) 163562306a36Sopenharmony_ci goto cleanup; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci break; 163862306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 163962306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 164062306a36Sopenharmony_ci rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 164162306a36Sopenharmony_ci if (rc != 0) 164262306a36Sopenharmony_ci goto cleanup; 164362306a36Sopenharmony_ci break; 164462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 164562306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 164662306a36Sopenharmony_ci rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 164762306a36Sopenharmony_ci if (rc != 0) 164862306a36Sopenharmony_ci goto cleanup; 164962306a36Sopenharmony_ci break; 165062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 165162306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 165262306a36Sopenharmony_ci switch (bInterfaceNumber) { 165362306a36Sopenharmony_ci case 0: 165462306a36Sopenharmony_ci rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 165562306a36Sopenharmony_ci if (rc != 0) 165662306a36Sopenharmony_ci goto cleanup; 165762306a36Sopenharmony_ci break; 165862306a36Sopenharmony_ci case 1: 165962306a36Sopenharmony_ci rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 166062306a36Sopenharmony_ci if (rc != 0) 166162306a36Sopenharmony_ci goto cleanup; 166262306a36Sopenharmony_ci break; 166362306a36Sopenharmony_ci case 2: 166462306a36Sopenharmony_ci rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 166562306a36Sopenharmony_ci if (rc != 0) 166662306a36Sopenharmony_ci goto cleanup; 166762306a36Sopenharmony_ci break; 166862306a36Sopenharmony_ci } 166962306a36Sopenharmony_ci break; 167062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 167162306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 167262306a36Sopenharmony_ci /* 167362306a36Sopenharmony_ci * If it is not a three-interface version, which is known to 167462306a36Sopenharmony_ci * respond to initialization. 167562306a36Sopenharmony_ci */ 167662306a36Sopenharmony_ci if (bNumInterfaces != 3) { 167762306a36Sopenharmony_ci switch (bInterfaceNumber) { 167862306a36Sopenharmony_ci case 0: 167962306a36Sopenharmony_ci rc = WITH_OPT_DESC(TWHA60_ORIG0, 168062306a36Sopenharmony_ci twha60_fixed0); 168162306a36Sopenharmony_ci if (rc != 0) 168262306a36Sopenharmony_ci goto cleanup; 168362306a36Sopenharmony_ci break; 168462306a36Sopenharmony_ci case 1: 168562306a36Sopenharmony_ci rc = WITH_OPT_DESC(TWHA60_ORIG1, 168662306a36Sopenharmony_ci twha60_fixed1); 168762306a36Sopenharmony_ci if (rc != 0) 168862306a36Sopenharmony_ci goto cleanup; 168962306a36Sopenharmony_ci break; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci break; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci fallthrough; 169462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_HUION, 169562306a36Sopenharmony_ci USB_DEVICE_ID_HUION_TABLET): 169662306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_HUION, 169762306a36Sopenharmony_ci USB_DEVICE_ID_HUION_TABLET2): 169862306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 169962306a36Sopenharmony_ci USB_DEVICE_ID_HUION_TABLET): 170062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 170162306a36Sopenharmony_ci USB_DEVICE_ID_YIYNOVA_TABLET): 170262306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 170362306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 170462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 170562306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 170662306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 170762306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 170862306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UCLOGIC, 170962306a36Sopenharmony_ci USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 171062306a36Sopenharmony_ci rc = uclogic_params_huion_init(&p, hdev); 171162306a36Sopenharmony_ci if (rc != 0) 171262306a36Sopenharmony_ci goto cleanup; 171362306a36Sopenharmony_ci break; 171462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGTIZER, 171562306a36Sopenharmony_ci USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 171662306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGTIZER, 171762306a36Sopenharmony_ci USB_DEVICE_ID_UGTIZER_TABLET_GT5040): 171862306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 171962306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): 172062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 172162306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 172262306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 172362306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06): 172462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 172562306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): 172662306a36Sopenharmony_ci /* If this is the pen interface */ 172762306a36Sopenharmony_ci if (bInterfaceNumber == 1) { 172862306a36Sopenharmony_ci /* Probe v1 pen parameters */ 172962306a36Sopenharmony_ci rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 173062306a36Sopenharmony_ci if (rc != 0) { 173162306a36Sopenharmony_ci hid_err(hdev, "pen probing failed: %d\n", rc); 173262306a36Sopenharmony_ci goto cleanup; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci if (!found) { 173562306a36Sopenharmony_ci hid_warn(hdev, "pen parameters not found"); 173662306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci } else { 173962306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci break; 174262306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 174362306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): 174462306a36Sopenharmony_ci /* If this is the pen and frame interface */ 174562306a36Sopenharmony_ci if (bInterfaceNumber == 1) { 174662306a36Sopenharmony_ci /* Probe v1 pen parameters */ 174762306a36Sopenharmony_ci rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 174862306a36Sopenharmony_ci if (rc != 0) { 174962306a36Sopenharmony_ci hid_err(hdev, "pen probing failed: %d\n", rc); 175062306a36Sopenharmony_ci goto cleanup; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci /* Initialize frame parameters */ 175362306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 175462306a36Sopenharmony_ci &p.frame_list[0], 175562306a36Sopenharmony_ci uclogic_rdesc_xppen_deco01_frame_arr, 175662306a36Sopenharmony_ci uclogic_rdesc_xppen_deco01_frame_size, 175762306a36Sopenharmony_ci 0); 175862306a36Sopenharmony_ci if (rc != 0) 175962306a36Sopenharmony_ci goto cleanup; 176062306a36Sopenharmony_ci } else { 176162306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci break; 176462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 176562306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_PARBLO_A610_PRO): 176662306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 176762306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2): 176862306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 176962306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): 177062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 177162306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW): 177262306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 177362306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S): 177462306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 177562306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW): 177662306a36Sopenharmony_ci rc = uclogic_params_ugee_v2_init(&p, hdev); 177762306a36Sopenharmony_ci if (rc != 0) 177862306a36Sopenharmony_ci goto cleanup; 177962306a36Sopenharmony_ci break; 178062306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_TRUST, 178162306a36Sopenharmony_ci USB_DEVICE_ID_TRUST_PANORA_TABLET): 178262306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 178362306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_TABLET_G5): 178462306a36Sopenharmony_ci /* Ignore non-pen interfaces */ 178562306a36Sopenharmony_ci if (bInterfaceNumber != 1) { 178662306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 178762306a36Sopenharmony_ci break; 178862306a36Sopenharmony_ci } 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 179162306a36Sopenharmony_ci if (rc != 0) { 179262306a36Sopenharmony_ci hid_err(hdev, "pen probing failed: %d\n", rc); 179362306a36Sopenharmony_ci goto cleanup; 179462306a36Sopenharmony_ci } else if (found) { 179562306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 179662306a36Sopenharmony_ci &p.frame_list[0], 179762306a36Sopenharmony_ci uclogic_rdesc_ugee_g5_frame_arr, 179862306a36Sopenharmony_ci uclogic_rdesc_ugee_g5_frame_size, 179962306a36Sopenharmony_ci UCLOGIC_RDESC_UGEE_G5_FRAME_ID); 180062306a36Sopenharmony_ci if (rc != 0) { 180162306a36Sopenharmony_ci hid_err(hdev, 180262306a36Sopenharmony_ci "failed creating frame parameters: %d\n", 180362306a36Sopenharmony_ci rc); 180462306a36Sopenharmony_ci goto cleanup; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci p.frame_list[0].re_lsb = 180762306a36Sopenharmony_ci UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; 180862306a36Sopenharmony_ci p.frame_list[0].dev_id_byte = 180962306a36Sopenharmony_ci UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; 181062306a36Sopenharmony_ci } else { 181162306a36Sopenharmony_ci hid_warn(hdev, "pen parameters not found"); 181262306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci break; 181662306a36Sopenharmony_ci case VID_PID(USB_VENDOR_ID_UGEE, 181762306a36Sopenharmony_ci USB_DEVICE_ID_UGEE_TABLET_EX07S): 181862306a36Sopenharmony_ci /* Ignore non-pen interfaces */ 181962306a36Sopenharmony_ci if (bInterfaceNumber != 1) { 182062306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 182162306a36Sopenharmony_ci break; 182262306a36Sopenharmony_ci } 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 182562306a36Sopenharmony_ci if (rc != 0) { 182662306a36Sopenharmony_ci hid_err(hdev, "pen probing failed: %d\n", rc); 182762306a36Sopenharmony_ci goto cleanup; 182862306a36Sopenharmony_ci } else if (found) { 182962306a36Sopenharmony_ci rc = uclogic_params_frame_init_with_desc( 183062306a36Sopenharmony_ci &p.frame_list[0], 183162306a36Sopenharmony_ci uclogic_rdesc_ugee_ex07_frame_arr, 183262306a36Sopenharmony_ci uclogic_rdesc_ugee_ex07_frame_size, 183362306a36Sopenharmony_ci 0); 183462306a36Sopenharmony_ci if (rc != 0) { 183562306a36Sopenharmony_ci hid_err(hdev, 183662306a36Sopenharmony_ci "failed creating frame parameters: %d\n", 183762306a36Sopenharmony_ci rc); 183862306a36Sopenharmony_ci goto cleanup; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci } else { 184162306a36Sopenharmony_ci hid_warn(hdev, "pen parameters not found"); 184262306a36Sopenharmony_ci uclogic_params_init_invalid(&p); 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci break; 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci#undef VID_PID 184962306a36Sopenharmony_ci#undef WITH_OPT_DESC 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* Output parameters */ 185262306a36Sopenharmony_ci memcpy(params, &p, sizeof(*params)); 185362306a36Sopenharmony_ci memset(&p, 0, sizeof(p)); 185462306a36Sopenharmony_ci rc = 0; 185562306a36Sopenharmony_cicleanup: 185662306a36Sopenharmony_ci uclogic_params_cleanup(&p); 185762306a36Sopenharmony_ci return rc; 185862306a36Sopenharmony_ci} 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci#ifdef CONFIG_HID_KUNIT_TEST 186162306a36Sopenharmony_ci#include "hid-uclogic-params-test.c" 186262306a36Sopenharmony_ci#endif 1863