19614219eSNikolai Kondrashov // SPDX-License-Identifier: GPL-2.0+ 29614219eSNikolai Kondrashov /* 39614219eSNikolai Kondrashov * HID driver for UC-Logic devices not fully compliant with HID standard 49614219eSNikolai Kondrashov * - tablet initialization and parameter retrieval 59614219eSNikolai Kondrashov * 69614219eSNikolai Kondrashov * Copyright (c) 2018 Nikolai Kondrashov 79614219eSNikolai Kondrashov */ 89614219eSNikolai Kondrashov 99614219eSNikolai Kondrashov /* 109614219eSNikolai Kondrashov * This program is free software; you can redistribute it and/or modify it 119614219eSNikolai Kondrashov * under the terms of the GNU General Public License as published by the Free 129614219eSNikolai Kondrashov * Software Foundation; either version 2 of the License, or (at your option) 139614219eSNikolai Kondrashov * any later version. 149614219eSNikolai Kondrashov */ 159614219eSNikolai Kondrashov 169614219eSNikolai Kondrashov #include "hid-uclogic-params.h" 179614219eSNikolai Kondrashov #include "hid-uclogic-rdesc.h" 189614219eSNikolai Kondrashov #include "usbhid/usbhid.h" 199614219eSNikolai Kondrashov #include "hid-ids.h" 209614219eSNikolai Kondrashov #include <linux/ctype.h> 219614219eSNikolai Kondrashov #include <asm/unaligned.h> 229614219eSNikolai Kondrashov 239614219eSNikolai Kondrashov /** 245abb5445SLee Jones * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type 255abb5445SLee Jones * to a string. 269614219eSNikolai Kondrashov * 279614219eSNikolai Kondrashov * @inrange: The in-range reporting type to convert. 289614219eSNikolai Kondrashov * 299614219eSNikolai Kondrashov * Returns: 309614219eSNikolai Kondrashov * The string representing the type, or NULL if the type is unknown. 319614219eSNikolai Kondrashov */ 32a228809fSNikolai Kondrashov static const char *uclogic_params_pen_inrange_to_str( 339614219eSNikolai Kondrashov enum uclogic_params_pen_inrange inrange) 349614219eSNikolai Kondrashov { 359614219eSNikolai Kondrashov switch (inrange) { 369614219eSNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: 379614219eSNikolai Kondrashov return "normal"; 389614219eSNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: 399614219eSNikolai Kondrashov return "inverted"; 4001309e29SNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_NONE: 4101309e29SNikolai Kondrashov return "none"; 429614219eSNikolai Kondrashov default: 439614219eSNikolai Kondrashov return NULL; 449614219eSNikolai Kondrashov } 459614219eSNikolai Kondrashov } 469614219eSNikolai Kondrashov 479614219eSNikolai Kondrashov /** 48a228809fSNikolai Kondrashov * Dump tablet interface pen parameters with hid_dbg(), indented with one tab. 49a228809fSNikolai Kondrashov * 50a228809fSNikolai Kondrashov * @hdev: The HID device the pen parameters describe. 51a228809fSNikolai Kondrashov * @pen: The pen parameters to dump. 52a228809fSNikolai Kondrashov */ 53a228809fSNikolai Kondrashov static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, 54a228809fSNikolai Kondrashov const struct uclogic_params_pen *pen) 55a228809fSNikolai Kondrashov { 56a228809fSNikolai Kondrashov size_t i; 57a228809fSNikolai Kondrashov 58a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.usage_invalid = %s\n", 59a228809fSNikolai Kondrashov (pen->usage_invalid ? "true" : "false")); 60a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr); 61a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size); 62a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.id = %u\n", pen->id); 63a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.subreport_list = {\n"); 64a228809fSNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) { 65a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n", 66a228809fSNikolai Kondrashov pen->subreport_list[i].value, 67a228809fSNikolai Kondrashov pen->subreport_list[i].id, 68a228809fSNikolai Kondrashov i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : ""); 69a228809fSNikolai Kondrashov } 70a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}\n"); 71a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.inrange = %s\n", 72a228809fSNikolai Kondrashov uclogic_params_pen_inrange_to_str(pen->inrange)); 73a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.fragmented_hires = %s\n", 74a228809fSNikolai Kondrashov (pen->fragmented_hires ? "true" : "false")); 75a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.tilt_y_flipped = %s\n", 76a228809fSNikolai Kondrashov (pen->tilt_y_flipped ? "true" : "false")); 77a228809fSNikolai Kondrashov } 78a228809fSNikolai Kondrashov 79a228809fSNikolai Kondrashov /** 80a228809fSNikolai Kondrashov * Dump tablet interface frame parameters with hid_dbg(), indented with two 81a228809fSNikolai Kondrashov * tabs. 82a228809fSNikolai Kondrashov * 83a228809fSNikolai Kondrashov * @hdev: The HID device the pen parameters describe. 84a228809fSNikolai Kondrashov * @frame: The frame parameters to dump. 85a228809fSNikolai Kondrashov */ 86a228809fSNikolai Kondrashov static void uclogic_params_frame_hid_dbg( 87a228809fSNikolai Kondrashov const struct hid_device *hdev, 88a228809fSNikolai Kondrashov const struct uclogic_params_frame *frame) 89a228809fSNikolai Kondrashov { 90a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr); 91a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size); 92a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.id = %u\n", frame->id); 93a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix); 94a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb); 95a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte); 96caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte); 97caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max); 98caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n", 99caf7e934SNikolai Kondrashov frame->touch_flip_at); 100a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n", 101a228809fSNikolai Kondrashov frame->bitmap_dial_byte); 102a228809fSNikolai Kondrashov } 103a228809fSNikolai Kondrashov 104a228809fSNikolai Kondrashov /** 105a228809fSNikolai Kondrashov * Dump tablet interface parameters with hid_dbg(). 106a228809fSNikolai Kondrashov * 107a228809fSNikolai Kondrashov * @hdev: The HID device the parameters describe. 108a228809fSNikolai Kondrashov * @params: The parameters to dump. 109a228809fSNikolai Kondrashov */ 110a228809fSNikolai Kondrashov void uclogic_params_hid_dbg(const struct hid_device *hdev, 111a228809fSNikolai Kondrashov const struct uclogic_params *params) 112a228809fSNikolai Kondrashov { 113a228809fSNikolai Kondrashov size_t i; 114a228809fSNikolai Kondrashov 115a228809fSNikolai Kondrashov hid_dbg(hdev, ".invalid = %s\n", 116a228809fSNikolai Kondrashov params->invalid ? "true" : "false"); 117a228809fSNikolai Kondrashov hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr); 118a228809fSNikolai Kondrashov hid_dbg(hdev, ".desc_size = %u\n", params->desc_size); 119a228809fSNikolai Kondrashov hid_dbg(hdev, ".pen = {\n"); 120a228809fSNikolai Kondrashov uclogic_params_pen_hid_dbg(hdev, ¶ms->pen); 121a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}\n"); 122a228809fSNikolai Kondrashov hid_dbg(hdev, ".frame_list = {\n"); 123a228809fSNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 124a228809fSNikolai Kondrashov hid_dbg(hdev, "\t{\n"); 125a228809fSNikolai Kondrashov uclogic_params_frame_hid_dbg(hdev, ¶ms->frame_list[i]); 126a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}%s\n", 127a228809fSNikolai Kondrashov i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : ""); 128a228809fSNikolai Kondrashov } 129a228809fSNikolai Kondrashov hid_dbg(hdev, "}\n"); 130a228809fSNikolai Kondrashov } 131a228809fSNikolai Kondrashov 132a228809fSNikolai Kondrashov /** 1339614219eSNikolai Kondrashov * uclogic_params_get_str_desc - retrieve a string descriptor from a HID 1349614219eSNikolai Kondrashov * device interface, putting it into a kmalloc-allocated buffer as is, without 1359614219eSNikolai Kondrashov * character encoding conversion. 1369614219eSNikolai Kondrashov * 1379614219eSNikolai Kondrashov * @pbuf: Location for the kmalloc-allocated buffer pointer containing 1389614219eSNikolai Kondrashov * the retrieved descriptor. Not modified in case of error. 1399614219eSNikolai Kondrashov * Can be NULL to have retrieved descriptor discarded. 1409614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to retrieve the string 1419614219eSNikolai Kondrashov * descriptor from. Cannot be NULL. 1429614219eSNikolai Kondrashov * @idx: Index of the string descriptor to request from the device. 1439614219eSNikolai Kondrashov * @len: Length of the buffer to allocate and the data to retrieve. 1449614219eSNikolai Kondrashov * 1459614219eSNikolai Kondrashov * Returns: 1469614219eSNikolai Kondrashov * number of bytes retrieved (<= len), 1479614219eSNikolai Kondrashov * -EPIPE, if the descriptor was not found, or 1489614219eSNikolai Kondrashov * another negative errno code in case of other error. 1499614219eSNikolai Kondrashov */ 1509614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, 1519614219eSNikolai Kondrashov __u8 idx, size_t len) 1529614219eSNikolai Kondrashov { 1539614219eSNikolai Kondrashov int rc; 1540a94131dSJosé Expósito struct usb_device *udev; 1559614219eSNikolai Kondrashov __u8 *buf = NULL; 1569614219eSNikolai Kondrashov 1579614219eSNikolai Kondrashov /* Check arguments */ 1589614219eSNikolai Kondrashov if (hdev == NULL) { 1599614219eSNikolai Kondrashov rc = -EINVAL; 1609614219eSNikolai Kondrashov goto cleanup; 1619614219eSNikolai Kondrashov } 1629614219eSNikolai Kondrashov 1630a94131dSJosé Expósito udev = hid_to_usb_dev(hdev); 1640a94131dSJosé Expósito 1659614219eSNikolai Kondrashov buf = kmalloc(len, GFP_KERNEL); 1669614219eSNikolai Kondrashov if (buf == NULL) { 1679614219eSNikolai Kondrashov rc = -ENOMEM; 1689614219eSNikolai Kondrashov goto cleanup; 1699614219eSNikolai Kondrashov } 1709614219eSNikolai Kondrashov 1719614219eSNikolai Kondrashov rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1729614219eSNikolai Kondrashov USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, 1739614219eSNikolai Kondrashov (USB_DT_STRING << 8) + idx, 1749614219eSNikolai Kondrashov 0x0409, buf, len, 1759614219eSNikolai Kondrashov USB_CTRL_GET_TIMEOUT); 1769614219eSNikolai Kondrashov if (rc == -EPIPE) { 1779614219eSNikolai Kondrashov hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); 1789614219eSNikolai Kondrashov goto cleanup; 1799614219eSNikolai Kondrashov } else if (rc < 0) { 1809614219eSNikolai Kondrashov hid_err(hdev, 181a876e7e2STom Rix "failed retrieving string descriptor #%u: %d\n", 1829614219eSNikolai Kondrashov idx, rc); 1839614219eSNikolai Kondrashov goto cleanup; 1849614219eSNikolai Kondrashov } 1859614219eSNikolai Kondrashov 1869614219eSNikolai Kondrashov if (pbuf != NULL) { 1879614219eSNikolai Kondrashov *pbuf = buf; 1889614219eSNikolai Kondrashov buf = NULL; 1899614219eSNikolai Kondrashov } 1909614219eSNikolai Kondrashov 1919614219eSNikolai Kondrashov cleanup: 1929614219eSNikolai Kondrashov kfree(buf); 1939614219eSNikolai Kondrashov return rc; 1949614219eSNikolai Kondrashov } 1959614219eSNikolai Kondrashov 1969614219eSNikolai Kondrashov /** 1979614219eSNikolai Kondrashov * uclogic_params_pen_cleanup - free resources used by struct 1989614219eSNikolai Kondrashov * uclogic_params_pen (tablet interface's pen input parameters). 1999614219eSNikolai Kondrashov * Can be called repeatedly. 2009614219eSNikolai Kondrashov * 2019614219eSNikolai Kondrashov * @pen: Pen input parameters to cleanup. Cannot be NULL. 2029614219eSNikolai Kondrashov */ 2039614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) 2049614219eSNikolai Kondrashov { 2059614219eSNikolai Kondrashov kfree(pen->desc_ptr); 2069614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 2079614219eSNikolai Kondrashov } 2089614219eSNikolai Kondrashov 2099614219eSNikolai Kondrashov /** 210eecb5b84SNikolai Kondrashov * uclogic_params_pen_init_v1() - initialize tablet interface pen 211eecb5b84SNikolai Kondrashov * input and retrieve its parameters from the device, using v1 protocol. 2129614219eSNikolai Kondrashov * 2139614219eSNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 2149614219eSNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not modified in 2159614219eSNikolai Kondrashov * case of error, or if parameters are not found. Cannot be NULL. 2169614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 2179614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 2189614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 2199614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 2209614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 2219614219eSNikolai Kondrashov * 2229614219eSNikolai Kondrashov * Returns: 2239614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 2249614219eSNikolai Kondrashov */ 225eecb5b84SNikolai Kondrashov static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, 2269614219eSNikolai Kondrashov bool *pfound, 2279614219eSNikolai Kondrashov struct hid_device *hdev) 2289614219eSNikolai Kondrashov { 2299614219eSNikolai Kondrashov int rc; 2309614219eSNikolai Kondrashov bool found = false; 2319614219eSNikolai Kondrashov /* Buffer for (part of) the string descriptor */ 2329614219eSNikolai Kondrashov __u8 *buf = NULL; 2339614219eSNikolai Kondrashov /* Minimum descriptor length required, maximum seen so far is 18 */ 2349614219eSNikolai Kondrashov const int len = 12; 2359614219eSNikolai Kondrashov s32 resolution; 2369614219eSNikolai Kondrashov /* Pen report descriptor template parameters */ 23776e645beSJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 2389614219eSNikolai Kondrashov __u8 *desc_ptr = NULL; 2399614219eSNikolai Kondrashov 2409614219eSNikolai Kondrashov /* Check arguments */ 2419614219eSNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 2429614219eSNikolai Kondrashov rc = -EINVAL; 2439614219eSNikolai Kondrashov goto cleanup; 2449614219eSNikolai Kondrashov } 2459614219eSNikolai Kondrashov 2469614219eSNikolai Kondrashov /* 2479614219eSNikolai Kondrashov * Read string descriptor containing pen input parameters. 2489614219eSNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 2499614219eSNikolai Kondrashov * the Windows driver traffic. 2509614219eSNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 2519614219eSNikolai Kondrashov */ 2529614219eSNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); 2539614219eSNikolai Kondrashov if (rc == -EPIPE) { 2549614219eSNikolai Kondrashov hid_dbg(hdev, 2559614219eSNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 2569614219eSNikolai Kondrashov goto finish; 2579614219eSNikolai Kondrashov } else if (rc < 0) { 2589614219eSNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 2599614219eSNikolai Kondrashov goto cleanup; 2609614219eSNikolai Kondrashov } else if (rc != len) { 2619614219eSNikolai Kondrashov hid_dbg(hdev, 2629614219eSNikolai Kondrashov "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", 2639614219eSNikolai Kondrashov rc, len); 2649614219eSNikolai Kondrashov goto finish; 2659614219eSNikolai Kondrashov } 2669614219eSNikolai Kondrashov 2679614219eSNikolai Kondrashov /* 2689614219eSNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 2699614219eSNikolai Kondrashov */ 2709614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 2719614219eSNikolai Kondrashov get_unaligned_le16(buf + 2); 2729614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 2739614219eSNikolai Kondrashov get_unaligned_le16(buf + 4); 2749614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 2759614219eSNikolai Kondrashov get_unaligned_le16(buf + 8); 2769614219eSNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 2779614219eSNikolai Kondrashov if (resolution == 0) { 2789614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 2799614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 2809614219eSNikolai Kondrashov } else { 2819614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 2829614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 2839614219eSNikolai Kondrashov resolution; 2849614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 2859614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 2869614219eSNikolai Kondrashov resolution; 2879614219eSNikolai Kondrashov } 2889614219eSNikolai Kondrashov kfree(buf); 2899614219eSNikolai Kondrashov buf = NULL; 2909614219eSNikolai Kondrashov 2919614219eSNikolai Kondrashov /* 2929614219eSNikolai Kondrashov * Generate pen report descriptor 2939614219eSNikolai Kondrashov */ 2949614219eSNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 295a985de58SNikolai Kondrashov uclogic_rdesc_v1_pen_template_arr, 296a985de58SNikolai Kondrashov uclogic_rdesc_v1_pen_template_size, 2979614219eSNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 2989614219eSNikolai Kondrashov if (desc_ptr == NULL) { 2999614219eSNikolai Kondrashov rc = -ENOMEM; 3009614219eSNikolai Kondrashov goto cleanup; 3019614219eSNikolai Kondrashov } 3029614219eSNikolai Kondrashov 3039614219eSNikolai Kondrashov /* 3049614219eSNikolai Kondrashov * Fill-in the parameters 3059614219eSNikolai Kondrashov */ 3069614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 3079614219eSNikolai Kondrashov pen->desc_ptr = desc_ptr; 3089614219eSNikolai Kondrashov desc_ptr = NULL; 309a985de58SNikolai Kondrashov pen->desc_size = uclogic_rdesc_v1_pen_template_size; 310a985de58SNikolai Kondrashov pen->id = UCLOGIC_RDESC_V1_PEN_ID; 3119614219eSNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; 3129614219eSNikolai Kondrashov found = true; 3139614219eSNikolai Kondrashov finish: 3149614219eSNikolai Kondrashov *pfound = found; 3159614219eSNikolai Kondrashov rc = 0; 3169614219eSNikolai Kondrashov cleanup: 3179614219eSNikolai Kondrashov kfree(desc_ptr); 3189614219eSNikolai Kondrashov kfree(buf); 3199614219eSNikolai Kondrashov return rc; 3209614219eSNikolai Kondrashov } 3219614219eSNikolai Kondrashov 3229614219eSNikolai Kondrashov /** 3232c3a88c6SNikolai Kondrashov * uclogic_params_get_le24() - get a 24-bit little-endian number from a 3242c3a88c6SNikolai Kondrashov * buffer. 3252c3a88c6SNikolai Kondrashov * 3262c3a88c6SNikolai Kondrashov * @p: The pointer to the number buffer. 3272c3a88c6SNikolai Kondrashov * 3282c3a88c6SNikolai Kondrashov * Returns: 3292c3a88c6SNikolai Kondrashov * The retrieved number 3302c3a88c6SNikolai Kondrashov */ 3312c3a88c6SNikolai Kondrashov static s32 uclogic_params_get_le24(const void *p) 3322c3a88c6SNikolai Kondrashov { 3332c3a88c6SNikolai Kondrashov const __u8 *b = p; 3342c3a88c6SNikolai Kondrashov return b[0] | (b[1] << 8UL) | (b[2] << 16UL); 3352c3a88c6SNikolai Kondrashov } 3362c3a88c6SNikolai Kondrashov 3372c3a88c6SNikolai Kondrashov /** 3382c3a88c6SNikolai Kondrashov * uclogic_params_pen_init_v2() - initialize tablet interface pen 3392c3a88c6SNikolai Kondrashov * input and retrieve its parameters from the device, using v2 protocol. 3402c3a88c6SNikolai Kondrashov * 3412c3a88c6SNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 342945d5dd5SNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not 343945d5dd5SNikolai Kondrashov * modified in case of error, or if parameters are not 344945d5dd5SNikolai Kondrashov * found. Cannot be NULL. 345945d5dd5SNikolai Kondrashov * @pfound: Location for a flag which is set to true if the 346945d5dd5SNikolai Kondrashov * parameters were found, and to false if not (e.g. 347945d5dd5SNikolai Kondrashov * device was incompatible). Not modified in case of 348945d5dd5SNikolai Kondrashov * error. Cannot be NULL. 349945d5dd5SNikolai Kondrashov * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw 350945d5dd5SNikolai Kondrashov * parameters, which could be used to identify the tablet 351945d5dd5SNikolai Kondrashov * to some extent. Should be freed with kfree after use. 352945d5dd5SNikolai Kondrashov * NULL, if not needed. Not modified in case of error. 353945d5dd5SNikolai Kondrashov * Only set if *pfound is set to true. 354945d5dd5SNikolai Kondrashov * @pparams_len: Location for the length of the retrieved raw 355945d5dd5SNikolai Kondrashov * parameters. NULL, if not needed. Not modified in case 356945d5dd5SNikolai Kondrashov * of error. Only set if *pfound is set to true. 357945d5dd5SNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize 358945d5dd5SNikolai Kondrashov * and get parameters from. Cannot be NULL. 3592c3a88c6SNikolai Kondrashov * 3602c3a88c6SNikolai Kondrashov * Returns: 3612c3a88c6SNikolai Kondrashov * Zero, if successful. A negative errno code on error. 3622c3a88c6SNikolai Kondrashov */ 3632c3a88c6SNikolai Kondrashov static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, 3642c3a88c6SNikolai Kondrashov bool *pfound, 365945d5dd5SNikolai Kondrashov __u8 **pparams_ptr, 366945d5dd5SNikolai Kondrashov size_t *pparams_len, 3672c3a88c6SNikolai Kondrashov struct hid_device *hdev) 3682c3a88c6SNikolai Kondrashov { 3692c3a88c6SNikolai Kondrashov int rc; 3702c3a88c6SNikolai Kondrashov bool found = false; 371945d5dd5SNikolai Kondrashov /* Buffer for (part of) the parameter string descriptor */ 3722c3a88c6SNikolai Kondrashov __u8 *buf = NULL; 373945d5dd5SNikolai Kondrashov /* Parameter string descriptor required length */ 374945d5dd5SNikolai Kondrashov const int params_len_min = 18; 375945d5dd5SNikolai Kondrashov /* Parameter string descriptor accepted length */ 376945d5dd5SNikolai Kondrashov const int params_len_max = 32; 377945d5dd5SNikolai Kondrashov /* Parameter string descriptor received length */ 378945d5dd5SNikolai Kondrashov int params_len; 379945d5dd5SNikolai Kondrashov size_t i; 3802c3a88c6SNikolai Kondrashov s32 resolution; 3812c3a88c6SNikolai Kondrashov /* Pen report descriptor template parameters */ 38276e645beSJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 3832c3a88c6SNikolai Kondrashov __u8 *desc_ptr = NULL; 3842c3a88c6SNikolai Kondrashov 3852c3a88c6SNikolai Kondrashov /* Check arguments */ 3862c3a88c6SNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 3872c3a88c6SNikolai Kondrashov rc = -EINVAL; 3882c3a88c6SNikolai Kondrashov goto cleanup; 3892c3a88c6SNikolai Kondrashov } 3902c3a88c6SNikolai Kondrashov 3912c3a88c6SNikolai Kondrashov /* 3922c3a88c6SNikolai Kondrashov * Read string descriptor containing pen input parameters. 3932c3a88c6SNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 3942c3a88c6SNikolai Kondrashov * the Windows driver traffic. 3952c3a88c6SNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 3962c3a88c6SNikolai Kondrashov */ 397945d5dd5SNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max); 3982c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 3992c3a88c6SNikolai Kondrashov hid_dbg(hdev, 4002c3a88c6SNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 4012c3a88c6SNikolai Kondrashov goto finish; 4022c3a88c6SNikolai Kondrashov } else if (rc < 0) { 4032c3a88c6SNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 4042c3a88c6SNikolai Kondrashov goto cleanup; 405945d5dd5SNikolai Kondrashov } else if (rc < params_len_min) { 4062c3a88c6SNikolai Kondrashov hid_dbg(hdev, 407945d5dd5SNikolai Kondrashov "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n", 408945d5dd5SNikolai Kondrashov rc, params_len_min); 4092c3a88c6SNikolai Kondrashov goto finish; 410945d5dd5SNikolai Kondrashov } 411945d5dd5SNikolai Kondrashov 412945d5dd5SNikolai Kondrashov params_len = rc; 413945d5dd5SNikolai Kondrashov 4142c3a88c6SNikolai Kondrashov /* 4152c3a88c6SNikolai Kondrashov * Check it's not just a catch-all UTF-16LE-encoded ASCII 4162c3a88c6SNikolai Kondrashov * string (such as the model name) some tablets put into all 4172c3a88c6SNikolai Kondrashov * unknown string descriptors. 4182c3a88c6SNikolai Kondrashov */ 4192c3a88c6SNikolai Kondrashov for (i = 2; 420945d5dd5SNikolai Kondrashov i < params_len && 4212c3a88c6SNikolai Kondrashov (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); 4222c3a88c6SNikolai Kondrashov i += 2); 423945d5dd5SNikolai Kondrashov if (i >= params_len) { 4242c3a88c6SNikolai Kondrashov hid_dbg(hdev, 4252c3a88c6SNikolai Kondrashov "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); 4262c3a88c6SNikolai Kondrashov goto finish; 4272c3a88c6SNikolai Kondrashov } 4282c3a88c6SNikolai Kondrashov 4292c3a88c6SNikolai Kondrashov /* 4302c3a88c6SNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 4312c3a88c6SNikolai Kondrashov */ 4322c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 4332c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 2); 4342c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 4352c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 5); 4362c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 4372c3a88c6SNikolai Kondrashov get_unaligned_le16(buf + 8); 4382c3a88c6SNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 4392c3a88c6SNikolai Kondrashov if (resolution == 0) { 4402c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 4412c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 4422c3a88c6SNikolai Kondrashov } else { 4432c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 4442c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 4452c3a88c6SNikolai Kondrashov resolution; 4462c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 4472c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 4482c3a88c6SNikolai Kondrashov resolution; 4492c3a88c6SNikolai Kondrashov } 4502c3a88c6SNikolai Kondrashov 4512c3a88c6SNikolai Kondrashov /* 4522c3a88c6SNikolai Kondrashov * Generate pen report descriptor 4532c3a88c6SNikolai Kondrashov */ 4542c3a88c6SNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 455a985de58SNikolai Kondrashov uclogic_rdesc_v2_pen_template_arr, 456a985de58SNikolai Kondrashov uclogic_rdesc_v2_pen_template_size, 4572c3a88c6SNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 4582c3a88c6SNikolai Kondrashov if (desc_ptr == NULL) { 4592c3a88c6SNikolai Kondrashov rc = -ENOMEM; 4602c3a88c6SNikolai Kondrashov goto cleanup; 4612c3a88c6SNikolai Kondrashov } 4622c3a88c6SNikolai Kondrashov 4632c3a88c6SNikolai Kondrashov /* 4642c3a88c6SNikolai Kondrashov * Fill-in the parameters 4652c3a88c6SNikolai Kondrashov */ 4662c3a88c6SNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 4672c3a88c6SNikolai Kondrashov pen->desc_ptr = desc_ptr; 4682c3a88c6SNikolai Kondrashov desc_ptr = NULL; 469a985de58SNikolai Kondrashov pen->desc_size = uclogic_rdesc_v2_pen_template_size; 470a985de58SNikolai Kondrashov pen->id = UCLOGIC_RDESC_V2_PEN_ID; 4712c3a88c6SNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; 4722c3a88c6SNikolai Kondrashov pen->fragmented_hires = true; 4731324c5acSNikolai Kondrashov pen->tilt_y_flipped = true; 4742c3a88c6SNikolai Kondrashov found = true; 475945d5dd5SNikolai Kondrashov if (pparams_ptr != NULL) { 476945d5dd5SNikolai Kondrashov *pparams_ptr = buf; 477945d5dd5SNikolai Kondrashov buf = NULL; 478945d5dd5SNikolai Kondrashov } 479945d5dd5SNikolai Kondrashov if (pparams_len != NULL) 480945d5dd5SNikolai Kondrashov *pparams_len = params_len; 481945d5dd5SNikolai Kondrashov 4822c3a88c6SNikolai Kondrashov finish: 4832c3a88c6SNikolai Kondrashov *pfound = found; 4842c3a88c6SNikolai Kondrashov rc = 0; 4852c3a88c6SNikolai Kondrashov cleanup: 4862c3a88c6SNikolai Kondrashov kfree(desc_ptr); 4872c3a88c6SNikolai Kondrashov kfree(buf); 4882c3a88c6SNikolai Kondrashov return rc; 4892c3a88c6SNikolai Kondrashov } 4902c3a88c6SNikolai Kondrashov 4912c3a88c6SNikolai Kondrashov /** 4929614219eSNikolai Kondrashov * uclogic_params_frame_cleanup - free resources used by struct 4939614219eSNikolai Kondrashov * uclogic_params_frame (tablet interface's frame controls input parameters). 4949614219eSNikolai Kondrashov * Can be called repeatedly. 4959614219eSNikolai Kondrashov * 4969614219eSNikolai Kondrashov * @frame: Frame controls input parameters to cleanup. Cannot be NULL. 4979614219eSNikolai Kondrashov */ 4989614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) 4999614219eSNikolai Kondrashov { 5009614219eSNikolai Kondrashov kfree(frame->desc_ptr); 5019614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 5029614219eSNikolai Kondrashov } 5039614219eSNikolai Kondrashov 5049614219eSNikolai Kondrashov /** 5059614219eSNikolai Kondrashov * uclogic_params_frame_init_with_desc() - initialize tablet's frame control 5069614219eSNikolai Kondrashov * parameters with a static report descriptor. 5079614219eSNikolai Kondrashov * 5089614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 5099614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 5109614219eSNikolai Kondrashov * of error. Cannot be NULL. 5119614219eSNikolai Kondrashov * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. 5129614219eSNikolai Kondrashov * @desc_size: Report descriptor size. 5139614219eSNikolai Kondrashov * @id: Report ID used for frame reports, if they should be tweaked, 5149614219eSNikolai Kondrashov * zero if not. 5159614219eSNikolai Kondrashov * 5169614219eSNikolai Kondrashov * Returns: 5179614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 5189614219eSNikolai Kondrashov */ 5199614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc( 5209614219eSNikolai Kondrashov struct uclogic_params_frame *frame, 5219614219eSNikolai Kondrashov const __u8 *desc_ptr, 5229614219eSNikolai Kondrashov size_t desc_size, 5239614219eSNikolai Kondrashov unsigned int id) 5249614219eSNikolai Kondrashov { 5259614219eSNikolai Kondrashov __u8 *copy_desc_ptr; 5269614219eSNikolai Kondrashov 5279614219eSNikolai Kondrashov if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) 5289614219eSNikolai Kondrashov return -EINVAL; 5299614219eSNikolai Kondrashov 5309614219eSNikolai Kondrashov copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 5319614219eSNikolai Kondrashov if (copy_desc_ptr == NULL) 5329614219eSNikolai Kondrashov return -ENOMEM; 5339614219eSNikolai Kondrashov 5349614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 5359614219eSNikolai Kondrashov frame->desc_ptr = copy_desc_ptr; 5369614219eSNikolai Kondrashov frame->desc_size = desc_size; 5379614219eSNikolai Kondrashov frame->id = id; 5389614219eSNikolai Kondrashov return 0; 5399614219eSNikolai Kondrashov } 5409614219eSNikolai Kondrashov 5419614219eSNikolai Kondrashov /** 5422e28f3e0SNikolai Kondrashov * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame 5432e28f3e0SNikolai Kondrashov * controls. 5449614219eSNikolai Kondrashov * 5459614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 5469614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 5479614219eSNikolai Kondrashov * of error, or if parameters are not found. Cannot be NULL. 5489614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 5499614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 5509614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 5519614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 5529614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 5539614219eSNikolai Kondrashov * 5549614219eSNikolai Kondrashov * Returns: 5559614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 5569614219eSNikolai Kondrashov */ 5572e28f3e0SNikolai Kondrashov static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame, 5589614219eSNikolai Kondrashov bool *pfound, 5599614219eSNikolai Kondrashov struct hid_device *hdev) 5609614219eSNikolai Kondrashov { 5619614219eSNikolai Kondrashov int rc; 5629614219eSNikolai Kondrashov bool found = false; 563aa320fdbSJosé Expósito struct usb_device *usb_dev; 5649614219eSNikolai Kondrashov char *str_buf = NULL; 5659614219eSNikolai Kondrashov const size_t str_len = 16; 5669614219eSNikolai Kondrashov 5679614219eSNikolai Kondrashov /* Check arguments */ 5689614219eSNikolai Kondrashov if (frame == NULL || pfound == NULL || hdev == NULL) { 5699614219eSNikolai Kondrashov rc = -EINVAL; 5709614219eSNikolai Kondrashov goto cleanup; 5719614219eSNikolai Kondrashov } 5729614219eSNikolai Kondrashov 573aa320fdbSJosé Expósito usb_dev = hid_to_usb_dev(hdev); 574aa320fdbSJosé Expósito 5759614219eSNikolai Kondrashov /* 5769614219eSNikolai Kondrashov * Enable generic button mode 5779614219eSNikolai Kondrashov */ 5789614219eSNikolai Kondrashov str_buf = kzalloc(str_len, GFP_KERNEL); 5799614219eSNikolai Kondrashov if (str_buf == NULL) { 5809614219eSNikolai Kondrashov rc = -ENOMEM; 5819614219eSNikolai Kondrashov goto cleanup; 5829614219eSNikolai Kondrashov } 5839614219eSNikolai Kondrashov 5849614219eSNikolai Kondrashov rc = usb_string(usb_dev, 123, str_buf, str_len); 5859614219eSNikolai Kondrashov if (rc == -EPIPE) { 5869614219eSNikolai Kondrashov hid_dbg(hdev, 5879614219eSNikolai Kondrashov "generic button -enabling string descriptor not found\n"); 5889614219eSNikolai Kondrashov } else if (rc < 0) { 5899614219eSNikolai Kondrashov goto cleanup; 5909614219eSNikolai Kondrashov } else if (strncmp(str_buf, "HK On", rc) != 0) { 5919614219eSNikolai Kondrashov hid_dbg(hdev, 5929614219eSNikolai Kondrashov "invalid response to enabling generic buttons: \"%s\"\n", 5939614219eSNikolai Kondrashov str_buf); 5949614219eSNikolai Kondrashov } else { 5959614219eSNikolai Kondrashov hid_dbg(hdev, "generic buttons enabled\n"); 5969614219eSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 5979614219eSNikolai Kondrashov frame, 598a985de58SNikolai Kondrashov uclogic_rdesc_v1_frame_arr, 599a985de58SNikolai Kondrashov uclogic_rdesc_v1_frame_size, 600a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID); 6019614219eSNikolai Kondrashov if (rc != 0) 6029614219eSNikolai Kondrashov goto cleanup; 6039614219eSNikolai Kondrashov found = true; 6049614219eSNikolai Kondrashov } 6059614219eSNikolai Kondrashov 6069614219eSNikolai Kondrashov *pfound = found; 6079614219eSNikolai Kondrashov rc = 0; 6089614219eSNikolai Kondrashov cleanup: 6099614219eSNikolai Kondrashov kfree(str_buf); 6109614219eSNikolai Kondrashov return rc; 6119614219eSNikolai Kondrashov } 6129614219eSNikolai Kondrashov 6139614219eSNikolai Kondrashov /** 6149614219eSNikolai Kondrashov * uclogic_params_cleanup - free resources used by struct uclogic_params 6159614219eSNikolai Kondrashov * (tablet interface's parameters). 6169614219eSNikolai Kondrashov * Can be called repeatedly. 6179614219eSNikolai Kondrashov * 6189614219eSNikolai Kondrashov * @params: Input parameters to cleanup. Cannot be NULL. 6199614219eSNikolai Kondrashov */ 6209614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params) 6219614219eSNikolai Kondrashov { 6229614219eSNikolai Kondrashov if (!params->invalid) { 623337fa051SNikolai Kondrashov size_t i; 6249614219eSNikolai Kondrashov kfree(params->desc_ptr); 6259614219eSNikolai Kondrashov uclogic_params_pen_cleanup(¶ms->pen); 626337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) 627337fa051SNikolai Kondrashov uclogic_params_frame_cleanup(¶ms->frame_list[i]); 628337fa051SNikolai Kondrashov 6299614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 6309614219eSNikolai Kondrashov } 6319614219eSNikolai Kondrashov } 6329614219eSNikolai Kondrashov 6339614219eSNikolai Kondrashov /** 6345abb5445SLee Jones * uclogic_params_get_desc() - Get a replacement report descriptor for a 6355abb5445SLee Jones * tablet's interface. 6369614219eSNikolai Kondrashov * 6379614219eSNikolai Kondrashov * @params: The parameters of a tablet interface to get report 6389614219eSNikolai Kondrashov * descriptor for. Cannot be NULL. 6399614219eSNikolai Kondrashov * @pdesc: Location for the resulting, kmalloc-allocated report 6409614219eSNikolai Kondrashov * descriptor pointer, or for NULL, if there's no replacement 6419614219eSNikolai Kondrashov * report descriptor. Not modified in case of error. Cannot be 6429614219eSNikolai Kondrashov * NULL. 6439614219eSNikolai Kondrashov * @psize: Location for the resulting report descriptor size, not set if 6449614219eSNikolai Kondrashov * there's no replacement report descriptor. Not modified in case 6459614219eSNikolai Kondrashov * of error. Cannot be NULL. 6469614219eSNikolai Kondrashov * 6479614219eSNikolai Kondrashov * Returns: 6489614219eSNikolai Kondrashov * Zero, if successful. 6499614219eSNikolai Kondrashov * -EINVAL, if invalid arguments are supplied. 6509614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 6519614219eSNikolai Kondrashov */ 6529614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params, 6539614219eSNikolai Kondrashov __u8 **pdesc, 6549614219eSNikolai Kondrashov unsigned int *psize) 6559614219eSNikolai Kondrashov { 656337fa051SNikolai Kondrashov int rc = -ENOMEM; 657337fa051SNikolai Kondrashov bool present = false; 658337fa051SNikolai Kondrashov unsigned int size = 0; 6599614219eSNikolai Kondrashov __u8 *desc = NULL; 660337fa051SNikolai Kondrashov size_t i; 6619614219eSNikolai Kondrashov 6629614219eSNikolai Kondrashov /* Check arguments */ 6639614219eSNikolai Kondrashov if (params == NULL || pdesc == NULL || psize == NULL) 6649614219eSNikolai Kondrashov return -EINVAL; 6659614219eSNikolai Kondrashov 666337fa051SNikolai Kondrashov /* Concatenate descriptors */ 667337fa051SNikolai Kondrashov #define ADD_DESC(_desc_ptr, _desc_size) \ 668337fa051SNikolai Kondrashov do { \ 669337fa051SNikolai Kondrashov unsigned int new_size; \ 670337fa051SNikolai Kondrashov __u8 *new_desc; \ 671337fa051SNikolai Kondrashov if ((_desc_ptr) == NULL) { \ 672337fa051SNikolai Kondrashov break; \ 673337fa051SNikolai Kondrashov } \ 674337fa051SNikolai Kondrashov new_size = size + (_desc_size); \ 675337fa051SNikolai Kondrashov new_desc = krealloc(desc, new_size, GFP_KERNEL); \ 676337fa051SNikolai Kondrashov if (new_desc == NULL) { \ 677337fa051SNikolai Kondrashov goto cleanup; \ 678337fa051SNikolai Kondrashov } \ 679337fa051SNikolai Kondrashov memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \ 680337fa051SNikolai Kondrashov desc = new_desc; \ 681337fa051SNikolai Kondrashov size = new_size; \ 682337fa051SNikolai Kondrashov present = true; \ 683337fa051SNikolai Kondrashov } while (0) 6849614219eSNikolai Kondrashov 685337fa051SNikolai Kondrashov ADD_DESC(params->desc_ptr, params->desc_size); 686337fa051SNikolai Kondrashov ADD_DESC(params->pen.desc_ptr, params->pen.desc_size); 687337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 688337fa051SNikolai Kondrashov ADD_DESC(params->frame_list[i].desc_ptr, 689337fa051SNikolai Kondrashov params->frame_list[i].desc_size); 6909614219eSNikolai Kondrashov } 6919614219eSNikolai Kondrashov 692337fa051SNikolai Kondrashov #undef ADD_DESC 6939614219eSNikolai Kondrashov 694337fa051SNikolai Kondrashov if (present) { 6959614219eSNikolai Kondrashov *pdesc = desc; 696337fa051SNikolai Kondrashov *psize = size; 697337fa051SNikolai Kondrashov desc = NULL; 698337fa051SNikolai Kondrashov } 699337fa051SNikolai Kondrashov rc = 0; 700337fa051SNikolai Kondrashov cleanup: 701337fa051SNikolai Kondrashov kfree(desc); 702337fa051SNikolai Kondrashov return rc; 7039614219eSNikolai Kondrashov } 7049614219eSNikolai Kondrashov 7059614219eSNikolai Kondrashov /** 7069614219eSNikolai Kondrashov * uclogic_params_init_invalid() - initialize tablet interface parameters, 7079614219eSNikolai Kondrashov * specifying the interface is invalid. 7089614219eSNikolai Kondrashov * 7099614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7109614219eSNikolai Kondrashov * uclogic_params_cleanup()). Cannot be NULL. 7119614219eSNikolai Kondrashov */ 7129614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params) 7139614219eSNikolai Kondrashov { 7149614219eSNikolai Kondrashov params->invalid = true; 7159614219eSNikolai Kondrashov } 7169614219eSNikolai Kondrashov 7179614219eSNikolai Kondrashov /** 7189614219eSNikolai Kondrashov * uclogic_params_init_with_opt_desc() - initialize tablet interface 7199614219eSNikolai Kondrashov * parameters with an optional replacement report descriptor. Only modify 7209614219eSNikolai Kondrashov * report descriptor, if the original report descriptor matches the expected 7219614219eSNikolai Kondrashov * size. 7229614219eSNikolai Kondrashov * 7239614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7249614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of 7259614219eSNikolai Kondrashov * error. Cannot be NULL. 7269614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface create the 7279614219eSNikolai Kondrashov * parameters for. Cannot be NULL. 7289614219eSNikolai Kondrashov * @orig_desc_size: Expected size of the original report descriptor to 7299614219eSNikolai Kondrashov * be replaced. 7309614219eSNikolai Kondrashov * @desc_ptr: Pointer to the replacement report descriptor. 7319614219eSNikolai Kondrashov * Can be NULL, if desc_size is zero. 7329614219eSNikolai Kondrashov * @desc_size: Size of the replacement report descriptor. 7339614219eSNikolai Kondrashov * 7349614219eSNikolai Kondrashov * Returns: 7359614219eSNikolai Kondrashov * Zero, if successful. -EINVAL if an invalid argument was passed. 7369614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 7379614219eSNikolai Kondrashov */ 7389614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, 7399614219eSNikolai Kondrashov struct hid_device *hdev, 7409614219eSNikolai Kondrashov unsigned int orig_desc_size, 7419614219eSNikolai Kondrashov __u8 *desc_ptr, 7429614219eSNikolai Kondrashov unsigned int desc_size) 7439614219eSNikolai Kondrashov { 7449614219eSNikolai Kondrashov __u8 *desc_copy_ptr = NULL; 7459614219eSNikolai Kondrashov unsigned int desc_copy_size; 7469614219eSNikolai Kondrashov int rc; 7479614219eSNikolai Kondrashov 7489614219eSNikolai Kondrashov /* Check arguments */ 7499614219eSNikolai Kondrashov if (params == NULL || hdev == NULL || 7509614219eSNikolai Kondrashov (desc_ptr == NULL && desc_size != 0)) { 7519614219eSNikolai Kondrashov rc = -EINVAL; 7529614219eSNikolai Kondrashov goto cleanup; 7539614219eSNikolai Kondrashov } 7549614219eSNikolai Kondrashov 7559614219eSNikolai Kondrashov /* Replace report descriptor, if it matches */ 7569614219eSNikolai Kondrashov if (hdev->dev_rsize == orig_desc_size) { 7579614219eSNikolai Kondrashov hid_dbg(hdev, 7589614219eSNikolai Kondrashov "device report descriptor matches the expected size, replacing\n"); 7599614219eSNikolai Kondrashov desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 7609614219eSNikolai Kondrashov if (desc_copy_ptr == NULL) { 7619614219eSNikolai Kondrashov rc = -ENOMEM; 7629614219eSNikolai Kondrashov goto cleanup; 7639614219eSNikolai Kondrashov } 7649614219eSNikolai Kondrashov desc_copy_size = desc_size; 7659614219eSNikolai Kondrashov } else { 7669614219eSNikolai Kondrashov hid_dbg(hdev, 7679614219eSNikolai Kondrashov "device report descriptor doesn't match the expected size (%u != %u), preserving\n", 7689614219eSNikolai Kondrashov hdev->dev_rsize, orig_desc_size); 7699614219eSNikolai Kondrashov desc_copy_ptr = NULL; 7709614219eSNikolai Kondrashov desc_copy_size = 0; 7719614219eSNikolai Kondrashov } 7729614219eSNikolai Kondrashov 7739614219eSNikolai Kondrashov /* Output parameters */ 7749614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 7759614219eSNikolai Kondrashov params->desc_ptr = desc_copy_ptr; 7769614219eSNikolai Kondrashov desc_copy_ptr = NULL; 7779614219eSNikolai Kondrashov params->desc_size = desc_copy_size; 7789614219eSNikolai Kondrashov 7799614219eSNikolai Kondrashov rc = 0; 7809614219eSNikolai Kondrashov cleanup: 7819614219eSNikolai Kondrashov kfree(desc_copy_ptr); 7829614219eSNikolai Kondrashov return rc; 7839614219eSNikolai Kondrashov } 7849614219eSNikolai Kondrashov 7859614219eSNikolai Kondrashov /** 7865abb5445SLee Jones * uclogic_params_huion_init() - initialize a Huion tablet interface and discover 7879614219eSNikolai Kondrashov * its parameters. 7889614219eSNikolai Kondrashov * 7899614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 7909614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 7919614219eSNikolai Kondrashov * Cannot be NULL. 7929614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 7939614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 7949614219eSNikolai Kondrashov * 7959614219eSNikolai Kondrashov * Returns: 7969614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 7979614219eSNikolai Kondrashov */ 7989614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params, 7999614219eSNikolai Kondrashov struct hid_device *hdev) 8009614219eSNikolai Kondrashov { 8019614219eSNikolai Kondrashov int rc; 802ff6b548aSJosé Expósito struct usb_device *udev; 803ff6b548aSJosé Expósito struct usb_interface *iface; 804ff6b548aSJosé Expósito __u8 bInterfaceNumber; 8059614219eSNikolai Kondrashov bool found; 8069614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 8079614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 8082c3a88c6SNikolai Kondrashov static const char transition_ver[] = "HUION_T153_160607"; 8092c3a88c6SNikolai Kondrashov char *ver_ptr = NULL; 8102c3a88c6SNikolai Kondrashov const size_t ver_len = sizeof(transition_ver) + 1; 811118dfdeaSNikolai Kondrashov __u8 *params_ptr = NULL; 812118dfdeaSNikolai Kondrashov size_t params_len = 0; 813118dfdeaSNikolai Kondrashov /* Parameters string descriptor of a model with touch ring (HS610) */ 814118dfdeaSNikolai Kondrashov const __u8 touch_ring_model_params_buf[] = { 815118dfdeaSNikolai Kondrashov 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, 816118dfdeaSNikolai Kondrashov 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, 817118dfdeaSNikolai Kondrashov 0x04, 0x3C, 0x3E 818118dfdeaSNikolai Kondrashov }; 8199614219eSNikolai Kondrashov 8209614219eSNikolai Kondrashov /* Check arguments */ 8219614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 8229614219eSNikolai Kondrashov rc = -EINVAL; 8239614219eSNikolai Kondrashov goto cleanup; 8249614219eSNikolai Kondrashov } 8259614219eSNikolai Kondrashov 826ff6b548aSJosé Expósito udev = hid_to_usb_dev(hdev); 827ff6b548aSJosé Expósito iface = to_usb_interface(hdev->dev.parent); 828ff6b548aSJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 829ff6b548aSJosé Expósito 830d64a6e44SNikolai Kondrashov /* If it's a custom keyboard interface */ 831d64a6e44SNikolai Kondrashov if (bInterfaceNumber == 1) { 8324c60bc7dSNikolai Kondrashov /* Keep everything intact, but mark pen usage invalid */ 8334c60bc7dSNikolai Kondrashov p.pen.usage_invalid = true; 834d64a6e44SNikolai Kondrashov goto output; 835d64a6e44SNikolai Kondrashov /* Else, if it's not a pen interface */ 836d64a6e44SNikolai Kondrashov } else if (bInterfaceNumber != 0) { 837606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 8389614219eSNikolai Kondrashov goto output; 8399614219eSNikolai Kondrashov } 8409614219eSNikolai Kondrashov 8412c3a88c6SNikolai Kondrashov /* Try to get firmware version */ 8422c3a88c6SNikolai Kondrashov ver_ptr = kzalloc(ver_len, GFP_KERNEL); 8432c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 8442c3a88c6SNikolai Kondrashov rc = -ENOMEM; 8452c3a88c6SNikolai Kondrashov goto cleanup; 8462c3a88c6SNikolai Kondrashov } 8472c3a88c6SNikolai Kondrashov rc = usb_string(udev, 201, ver_ptr, ver_len); 8482c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 8492c3a88c6SNikolai Kondrashov *ver_ptr = '\0'; 8502c3a88c6SNikolai Kondrashov } else if (rc < 0) { 8512c3a88c6SNikolai Kondrashov hid_err(hdev, 8522c3a88c6SNikolai Kondrashov "failed retrieving Huion firmware version: %d\n", rc); 8532c3a88c6SNikolai Kondrashov goto cleanup; 8542c3a88c6SNikolai Kondrashov } 8552c3a88c6SNikolai Kondrashov 8562c3a88c6SNikolai Kondrashov /* If this is a transition firmware */ 8572c3a88c6SNikolai Kondrashov if (strcmp(ver_ptr, transition_ver) == 0) { 8582c3a88c6SNikolai Kondrashov hid_dbg(hdev, 8592c3a88c6SNikolai Kondrashov "transition firmware detected, not probing pen v2 parameters\n"); 8602c3a88c6SNikolai Kondrashov } else { 8612c3a88c6SNikolai Kondrashov /* Try to probe v2 pen parameters */ 862945d5dd5SNikolai Kondrashov rc = uclogic_params_pen_init_v2(&p.pen, &found, 863118dfdeaSNikolai Kondrashov ¶ms_ptr, ¶ms_len, 864118dfdeaSNikolai Kondrashov hdev); 8652c3a88c6SNikolai Kondrashov if (rc != 0) { 8662c3a88c6SNikolai Kondrashov hid_err(hdev, 8672c3a88c6SNikolai Kondrashov "failed probing pen v2 parameters: %d\n", rc); 8682c3a88c6SNikolai Kondrashov goto cleanup; 8692c3a88c6SNikolai Kondrashov } else if (found) { 8702c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters found\n"); 871c3e6e59aSNikolai Kondrashov /* Create v2 frame button parameters */ 8722c3a88c6SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 873337fa051SNikolai Kondrashov &p.frame_list[0], 874c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_arr, 875c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_size, 876c3e6e59aSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); 8772c3a88c6SNikolai Kondrashov if (rc != 0) { 8782c3a88c6SNikolai Kondrashov hid_err(hdev, 879c3e6e59aSNikolai Kondrashov "failed creating v2 frame button parameters: %d\n", 8802c3a88c6SNikolai Kondrashov rc); 8812c3a88c6SNikolai Kondrashov goto cleanup; 8822c3a88c6SNikolai Kondrashov } 883c3e6e59aSNikolai Kondrashov 884118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 885118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 886118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].id = 887118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; 888118dfdeaSNikolai Kondrashov 889118dfdeaSNikolai Kondrashov /* If this is the model with touch ring */ 890118dfdeaSNikolai Kondrashov if (params_ptr != NULL && 891118dfdeaSNikolai Kondrashov params_len == sizeof(touch_ring_model_params_buf) && 892118dfdeaSNikolai Kondrashov memcmp(params_ptr, touch_ring_model_params_buf, 893118dfdeaSNikolai Kondrashov params_len) == 0) { 894118dfdeaSNikolai Kondrashov /* Create touch ring parameters */ 895c3e6e59aSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 896c3e6e59aSNikolai Kondrashov &p.frame_list[1], 897c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_arr, 898c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_size, 899caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 900c3e6e59aSNikolai Kondrashov if (rc != 0) { 901c3e6e59aSNikolai Kondrashov hid_err(hdev, 902c3e6e59aSNikolai Kondrashov "failed creating v2 frame touch ring parameters: %d\n", 903c3e6e59aSNikolai Kondrashov rc); 904c3e6e59aSNikolai Kondrashov goto cleanup; 905c3e6e59aSNikolai Kondrashov } 906c3e6e59aSNikolai Kondrashov p.frame_list[1].suffix = "Touch Ring"; 907c3e6e59aSNikolai Kondrashov p.frame_list[1].dev_id_byte = 908caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 909caf7e934SNikolai Kondrashov p.frame_list[1].touch_byte = 5; 910caf7e934SNikolai Kondrashov p.frame_list[1].touch_max = 12; 911fbc08b4eSNikolai Kondrashov p.frame_list[1].touch_flip_at = 7; 912118dfdeaSNikolai Kondrashov } else { 913118dfdeaSNikolai Kondrashov /* Create touch strip parameters */ 914118dfdeaSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 915118dfdeaSNikolai Kondrashov &p.frame_list[1], 916118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_arr, 917118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_size, 918118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 919118dfdeaSNikolai Kondrashov if (rc != 0) { 920118dfdeaSNikolai Kondrashov hid_err(hdev, 921118dfdeaSNikolai Kondrashov "failed creating v2 frame touch strip parameters: %d\n", 922118dfdeaSNikolai Kondrashov rc); 923118dfdeaSNikolai Kondrashov goto cleanup; 924118dfdeaSNikolai Kondrashov } 925118dfdeaSNikolai Kondrashov p.frame_list[1].suffix = "Touch Strip"; 926118dfdeaSNikolai Kondrashov p.frame_list[1].dev_id_byte = 927118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 928118dfdeaSNikolai Kondrashov p.frame_list[1].touch_byte = 5; 929118dfdeaSNikolai Kondrashov p.frame_list[1].touch_max = 8; 930118dfdeaSNikolai Kondrashov } 931118dfdeaSNikolai Kondrashov 932118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 933118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].value = 0xf0; 934118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].id = 935118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; 936c3e6e59aSNikolai Kondrashov 9376facd076SNikolai Kondrashov /* Create v2 frame dial parameters */ 9386facd076SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 9396facd076SNikolai Kondrashov &p.frame_list[2], 9406facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_arr, 9416facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_size, 9426facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID); 9436facd076SNikolai Kondrashov if (rc != 0) { 9446facd076SNikolai Kondrashov hid_err(hdev, 9456facd076SNikolai Kondrashov "failed creating v2 frame dial parameters: %d\n", 9466facd076SNikolai Kondrashov rc); 9476facd076SNikolai Kondrashov goto cleanup; 9486facd076SNikolai Kondrashov } 9496facd076SNikolai Kondrashov p.frame_list[2].suffix = "Dial"; 9506facd076SNikolai Kondrashov p.frame_list[2].dev_id_byte = 9516facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; 9526facd076SNikolai Kondrashov p.frame_list[2].bitmap_dial_byte = 5; 9536facd076SNikolai Kondrashov 954118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 9556facd076SNikolai Kondrashov p.pen.subreport_list[2].value = 0xf1; 9566facd076SNikolai Kondrashov p.pen.subreport_list[2].id = 9576facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID; 958118dfdeaSNikolai Kondrashov 9592c3a88c6SNikolai Kondrashov goto output; 9602c3a88c6SNikolai Kondrashov } 9612c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters not found\n"); 9622c3a88c6SNikolai Kondrashov } 9632c3a88c6SNikolai Kondrashov 964eecb5b84SNikolai Kondrashov /* Try to probe v1 pen parameters */ 965eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 9669614219eSNikolai Kondrashov if (rc != 0) { 9679614219eSNikolai Kondrashov hid_err(hdev, 968eecb5b84SNikolai Kondrashov "failed probing pen v1 parameters: %d\n", rc); 9699614219eSNikolai Kondrashov goto cleanup; 9709614219eSNikolai Kondrashov } else if (found) { 971eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters found\n"); 9722e28f3e0SNikolai Kondrashov /* Try to probe v1 frame */ 973337fa051SNikolai Kondrashov rc = uclogic_params_frame_init_v1(&p.frame_list[0], 9749614219eSNikolai Kondrashov &found, hdev); 9759614219eSNikolai Kondrashov if (rc != 0) { 9762e28f3e0SNikolai Kondrashov hid_err(hdev, "v1 frame probing failed: %d\n", rc); 9779614219eSNikolai Kondrashov goto cleanup; 9789614219eSNikolai Kondrashov } 9792e28f3e0SNikolai Kondrashov hid_dbg(hdev, "frame v1 parameters%s found\n", 9809614219eSNikolai Kondrashov (found ? "" : " not")); 9819614219eSNikolai Kondrashov if (found) { 9828b013098SNikolai Kondrashov /* Link frame button subreports from pen reports */ 983e6be956fSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 9848b013098SNikolai Kondrashov p.pen.subreport_list[0].id = 985a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID; 9869614219eSNikolai Kondrashov } 9879614219eSNikolai Kondrashov goto output; 9889614219eSNikolai Kondrashov } 989eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters not found\n"); 9909614219eSNikolai Kondrashov 9919614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 9929614219eSNikolai Kondrashov 9939614219eSNikolai Kondrashov output: 9949614219eSNikolai Kondrashov /* Output parameters */ 9959614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 9969614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 9979614219eSNikolai Kondrashov rc = 0; 9989614219eSNikolai Kondrashov cleanup: 999118dfdeaSNikolai Kondrashov kfree(params_ptr); 10002c3a88c6SNikolai Kondrashov kfree(ver_ptr); 10019614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 10029614219eSNikolai Kondrashov return rc; 10039614219eSNikolai Kondrashov } 10049614219eSNikolai Kondrashov 10059614219eSNikolai Kondrashov /** 1006*0cb1fc09SJosé Expósito * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or 1007*0cb1fc09SJosé Expósito * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data. 1008*0cb1fc09SJosé Expósito * 1009*0cb1fc09SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 1010*0cb1fc09SJosé Expósito * parameters from. Cannot be NULL. 1011*0cb1fc09SJosé Expósito * @magic_arr: The magic data that should be sent to probe the interface. 1012*0cb1fc09SJosé Expósito * Cannot be NULL. 1013*0cb1fc09SJosé Expósito * @magic_size: Size of the magic data. 1014*0cb1fc09SJosé Expósito * @endpoint: Endpoint where the magic data should be sent. 1015*0cb1fc09SJosé Expósito * 1016*0cb1fc09SJosé Expósito * Returns: 1017*0cb1fc09SJosé Expósito * Zero, if successful. A negative errno code on error. 1018*0cb1fc09SJosé Expósito */ 1019*0cb1fc09SJosé Expósito static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr, 1020*0cb1fc09SJosé Expósito int magic_size, int endpoint) 1021*0cb1fc09SJosé Expósito { 1022*0cb1fc09SJosé Expósito struct usb_device *udev; 1023*0cb1fc09SJosé Expósito unsigned int pipe = 0; 1024*0cb1fc09SJosé Expósito int sent; 1025*0cb1fc09SJosé Expósito u8 *buf = NULL; 1026*0cb1fc09SJosé Expósito int rc = 0; 1027*0cb1fc09SJosé Expósito 1028*0cb1fc09SJosé Expósito if (!hdev || !magic_arr) { 1029*0cb1fc09SJosé Expósito rc = -EINVAL; 1030*0cb1fc09SJosé Expósito goto cleanup; 1031*0cb1fc09SJosé Expósito } 1032*0cb1fc09SJosé Expósito 1033*0cb1fc09SJosé Expósito buf = kmemdup(magic_arr, magic_size, GFP_KERNEL); 1034*0cb1fc09SJosé Expósito if (!buf) { 1035*0cb1fc09SJosé Expósito rc = -ENOMEM; 1036*0cb1fc09SJosé Expósito goto cleanup; 1037*0cb1fc09SJosé Expósito } 1038*0cb1fc09SJosé Expósito 1039*0cb1fc09SJosé Expósito udev = hid_to_usb_dev(hdev); 1040*0cb1fc09SJosé Expósito pipe = usb_sndintpipe(udev, endpoint); 1041*0cb1fc09SJosé Expósito 1042*0cb1fc09SJosé Expósito rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000); 1043*0cb1fc09SJosé Expósito if (rc || sent != magic_size) { 1044*0cb1fc09SJosé Expósito hid_err(hdev, "Interface probing failed: %d\n", rc); 1045*0cb1fc09SJosé Expósito rc = -1; 1046*0cb1fc09SJosé Expósito goto cleanup; 1047*0cb1fc09SJosé Expósito } 1048*0cb1fc09SJosé Expósito 1049*0cb1fc09SJosé Expósito rc = 0; 1050*0cb1fc09SJosé Expósito cleanup: 1051*0cb1fc09SJosé Expósito kfree(buf); 1052*0cb1fc09SJosé Expósito return rc; 1053*0cb1fc09SJosé Expósito } 1054*0cb1fc09SJosé Expósito 1055*0cb1fc09SJosé Expósito /** 1056*0cb1fc09SJosé Expósito * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by 1057*0cb1fc09SJosé Expósito * discovering their parameters. 1058*0cb1fc09SJosé Expósito * 1059*0cb1fc09SJosé Expósito * These tables, internally designed as v2 to differentiate them from older 1060*0cb1fc09SJosé Expósito * models, expect a payload of magic data in orther to be switched to the fully 1061*0cb1fc09SJosé Expósito * functional mode and expose their parameters in a similar way to the 1062*0cb1fc09SJosé Expósito * information present in uclogic_params_pen_init_v1() but with some 1063*0cb1fc09SJosé Expósito * differences. 1064*0cb1fc09SJosé Expósito * 1065*0cb1fc09SJosé Expósito * @params: Parameters to fill in (to be cleaned with 1066*0cb1fc09SJosé Expósito * uclogic_params_cleanup()). Not modified in case of error. 1067*0cb1fc09SJosé Expósito * Cannot be NULL. 1068*0cb1fc09SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 1069*0cb1fc09SJosé Expósito * parameters from. Cannot be NULL. 1070*0cb1fc09SJosé Expósito * 1071*0cb1fc09SJosé Expósito * Returns: 1072*0cb1fc09SJosé Expósito * Zero, if successful. A negative errno code on error. 1073*0cb1fc09SJosé Expósito */ 1074*0cb1fc09SJosé Expósito static int uclogic_params_ugee_v2_init(struct uclogic_params *params, 1075*0cb1fc09SJosé Expósito struct hid_device *hdev) 1076*0cb1fc09SJosé Expósito { 1077*0cb1fc09SJosé Expósito int rc = 0; 1078*0cb1fc09SJosé Expósito struct usb_interface *iface; 1079*0cb1fc09SJosé Expósito __u8 bInterfaceNumber; 1080*0cb1fc09SJosé Expósito const int str_desc_len = 12; 1081*0cb1fc09SJosé Expósito __u8 *str_desc = NULL; 1082*0cb1fc09SJosé Expósito __u8 *rdesc_pen = NULL; 1083*0cb1fc09SJosé Expósito __u8 *rdesc_frame = NULL; 1084*0cb1fc09SJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 1085*0cb1fc09SJosé Expósito s32 resolution; 1086*0cb1fc09SJosé Expósito __u8 magic_arr[] = { 1087*0cb1fc09SJosé Expósito 0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 1088*0cb1fc09SJosé Expósito }; 1089*0cb1fc09SJosé Expósito /* The resulting parameters (noop) */ 1090*0cb1fc09SJosé Expósito struct uclogic_params p = {0, }; 1091*0cb1fc09SJosé Expósito 1092*0cb1fc09SJosé Expósito if (!params || !hdev) { 1093*0cb1fc09SJosé Expósito rc = -EINVAL; 1094*0cb1fc09SJosé Expósito goto cleanup; 1095*0cb1fc09SJosé Expósito } 1096*0cb1fc09SJosé Expósito 1097*0cb1fc09SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 1098*0cb1fc09SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1099*0cb1fc09SJosé Expósito if (bInterfaceNumber != 2) { 1100*0cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 1101*0cb1fc09SJosé Expósito goto output; 1102*0cb1fc09SJosé Expósito } 1103*0cb1fc09SJosé Expósito 1104*0cb1fc09SJosé Expósito /* 1105*0cb1fc09SJosé Expósito * Initialize the interface by sending magic data. 1106*0cb1fc09SJosé Expósito * The specific data was discovered by sniffing the Windows driver 1107*0cb1fc09SJosé Expósito * traffic. 1108*0cb1fc09SJosé Expósito */ 1109*0cb1fc09SJosé Expósito rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03); 1110*0cb1fc09SJosé Expósito if (rc) { 1111*0cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 1112*0cb1fc09SJosé Expósito goto output; 1113*0cb1fc09SJosé Expósito } 1114*0cb1fc09SJosé Expósito 1115*0cb1fc09SJosé Expósito /* 1116*0cb1fc09SJosé Expósito * Read the string descriptor containing pen and frame parameters. 1117*0cb1fc09SJosé Expósito * The specific string descriptor and data were discovered by sniffing 1118*0cb1fc09SJosé Expósito * the Windows driver traffic. 1119*0cb1fc09SJosé Expósito */ 1120*0cb1fc09SJosé Expósito rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); 1121*0cb1fc09SJosé Expósito if (rc != str_desc_len) { 1122*0cb1fc09SJosé Expósito hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); 1123*0cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 1124*0cb1fc09SJosé Expósito goto output; 1125*0cb1fc09SJosé Expósito } 1126*0cb1fc09SJosé Expósito 1127*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 1128*0cb1fc09SJosé Expósito get_unaligned_le16(str_desc + 2); 1129*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 1130*0cb1fc09SJosé Expósito get_unaligned_le16(str_desc + 4); 1131*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = str_desc[6]; 1132*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 1133*0cb1fc09SJosé Expósito get_unaligned_le16(str_desc + 8); 1134*0cb1fc09SJosé Expósito resolution = get_unaligned_le16(str_desc + 10); 1135*0cb1fc09SJosé Expósito if (resolution == 0) { 1136*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 1137*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 1138*0cb1fc09SJosé Expósito } else { 1139*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 1140*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 1141*0cb1fc09SJosé Expósito resolution; 1142*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 1143*0cb1fc09SJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 1144*0cb1fc09SJosé Expósito resolution; 1145*0cb1fc09SJosé Expósito } 1146*0cb1fc09SJosé Expósito kfree(str_desc); 1147*0cb1fc09SJosé Expósito str_desc = NULL; 1148*0cb1fc09SJosé Expósito 1149*0cb1fc09SJosé Expósito /* Initialize the pen interface */ 1150*0cb1fc09SJosé Expósito rdesc_pen = uclogic_rdesc_template_apply( 1151*0cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_pen_template_arr, 1152*0cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_pen_template_size, 1153*0cb1fc09SJosé Expósito desc_params, ARRAY_SIZE(desc_params)); 1154*0cb1fc09SJosé Expósito if (!rdesc_pen) { 1155*0cb1fc09SJosé Expósito rc = -ENOMEM; 1156*0cb1fc09SJosé Expósito goto cleanup; 1157*0cb1fc09SJosé Expósito } 1158*0cb1fc09SJosé Expósito 1159*0cb1fc09SJosé Expósito p.pen.desc_ptr = rdesc_pen; 1160*0cb1fc09SJosé Expósito p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; 1161*0cb1fc09SJosé Expósito p.pen.id = 0x02; 1162*0cb1fc09SJosé Expósito p.pen.subreport_list[0].value = 0xf0; 1163*0cb1fc09SJosé Expósito p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; 1164*0cb1fc09SJosé Expósito 1165*0cb1fc09SJosé Expósito /* Initialize the frame interface */ 1166*0cb1fc09SJosé Expósito rdesc_frame = uclogic_rdesc_template_apply( 1167*0cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_arr, 1168*0cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_size, 1169*0cb1fc09SJosé Expósito desc_params, ARRAY_SIZE(desc_params)); 1170*0cb1fc09SJosé Expósito if (!rdesc_frame) { 1171*0cb1fc09SJosé Expósito rc = -ENOMEM; 1172*0cb1fc09SJosé Expósito goto cleanup; 1173*0cb1fc09SJosé Expósito } 1174*0cb1fc09SJosé Expósito 1175*0cb1fc09SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p.frame_list[0], 1176*0cb1fc09SJosé Expósito rdesc_frame, 1177*0cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_size, 1178*0cb1fc09SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 1179*0cb1fc09SJosé Expósito kfree(rdesc_frame); 1180*0cb1fc09SJosé Expósito if (rc) { 1181*0cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 1182*0cb1fc09SJosé Expósito goto output; 1183*0cb1fc09SJosé Expósito } 1184*0cb1fc09SJosé Expósito 1185*0cb1fc09SJosé Expósito output: 1186*0cb1fc09SJosé Expósito /* Output parameters */ 1187*0cb1fc09SJosé Expósito memcpy(params, &p, sizeof(*params)); 1188*0cb1fc09SJosé Expósito memset(&p, 0, sizeof(p)); 1189*0cb1fc09SJosé Expósito rc = 0; 1190*0cb1fc09SJosé Expósito cleanup: 1191*0cb1fc09SJosé Expósito kfree(str_desc); 1192*0cb1fc09SJosé Expósito uclogic_params_cleanup(&p); 1193*0cb1fc09SJosé Expósito return rc; 1194*0cb1fc09SJosé Expósito } 1195*0cb1fc09SJosé Expósito 1196*0cb1fc09SJosé Expósito /** 11979614219eSNikolai Kondrashov * uclogic_params_init() - initialize a tablet interface and discover its 11989614219eSNikolai Kondrashov * parameters. 11999614219eSNikolai Kondrashov * 12009614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 12019614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 12029614219eSNikolai Kondrashov * Cannot be NULL. 12039614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 12048547b778SNikolai Kondrashov * parameters from. Cannot be NULL. Must be using the USB low-level 12058547b778SNikolai Kondrashov * driver, i.e. be an actual USB tablet. 12069614219eSNikolai Kondrashov * 12079614219eSNikolai Kondrashov * Returns: 12089614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 12099614219eSNikolai Kondrashov */ 12109614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params, 12119614219eSNikolai Kondrashov struct hid_device *hdev) 12129614219eSNikolai Kondrashov { 12139614219eSNikolai Kondrashov int rc; 1214f364c571SJosé Expósito struct usb_device *udev; 1215f364c571SJosé Expósito __u8 bNumInterfaces; 1216f364c571SJosé Expósito struct usb_interface *iface; 1217f364c571SJosé Expósito __u8 bInterfaceNumber; 12189614219eSNikolai Kondrashov bool found; 12199614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 12209614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 12219614219eSNikolai Kondrashov 12229614219eSNikolai Kondrashov /* Check arguments */ 1223f83baa0cSGreg Kroah-Hartman if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { 12249614219eSNikolai Kondrashov rc = -EINVAL; 12259614219eSNikolai Kondrashov goto cleanup; 12269614219eSNikolai Kondrashov } 12279614219eSNikolai Kondrashov 1228f364c571SJosé Expósito udev = hid_to_usb_dev(hdev); 1229f364c571SJosé Expósito bNumInterfaces = udev->config->desc.bNumInterfaces; 1230f364c571SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 1231f364c571SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1232f364c571SJosé Expósito 12339614219eSNikolai Kondrashov /* 12349614219eSNikolai Kondrashov * Set replacement report descriptor if the original matches the 12359614219eSNikolai Kondrashov * specified size. Otherwise keep interface unchanged. 12369614219eSNikolai Kondrashov */ 12379614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 12389614219eSNikolai Kondrashov uclogic_params_init_with_opt_desc( \ 12399614219eSNikolai Kondrashov &p, hdev, \ 12409614219eSNikolai Kondrashov UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 12419614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_arr, \ 12429614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_size) 12439614219eSNikolai Kondrashov 12449614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \ 12459614219eSNikolai Kondrashov (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 12469614219eSNikolai Kondrashov 12479614219eSNikolai Kondrashov /* 12489614219eSNikolai Kondrashov * Handle specific interfaces for specific tablets. 12499614219eSNikolai Kondrashov * 12509614219eSNikolai Kondrashov * Observe the following logic: 12519614219eSNikolai Kondrashov * 12529614219eSNikolai Kondrashov * If the interface is recognized as producing certain useful input: 12539614219eSNikolai Kondrashov * Mark interface as valid. 12549614219eSNikolai Kondrashov * Output interface parameters. 12559614219eSNikolai Kondrashov * Else, if the interface is recognized as *not* producing any useful 12569614219eSNikolai Kondrashov * input: 12579614219eSNikolai Kondrashov * Mark interface as invalid. 12589614219eSNikolai Kondrashov * Else: 12599614219eSNikolai Kondrashov * Mark interface as valid. 12609614219eSNikolai Kondrashov * Output noop parameters. 12619614219eSNikolai Kondrashov * 12629614219eSNikolai Kondrashov * Rule of thumb: it is better to disable a broken interface than let 12639614219eSNikolai Kondrashov * it spew garbage input. 12649614219eSNikolai Kondrashov */ 12659614219eSNikolai Kondrashov 12669614219eSNikolai Kondrashov switch (VID_PID(hdev->vendor, hdev->product)) { 12679614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 12689614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 12699614219eSNikolai Kondrashov rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 12709614219eSNikolai Kondrashov if (rc != 0) 12719614219eSNikolai Kondrashov goto cleanup; 12729614219eSNikolai Kondrashov break; 12739614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 12749614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 12759614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 12769614219eSNikolai Kondrashov if (rc != 0) 12779614219eSNikolai Kondrashov goto cleanup; 12789614219eSNikolai Kondrashov break; 12799614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 12809614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 12819c17f735SNikolai Kondrashov if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { 12829c17f735SNikolai Kondrashov if (bInterfaceNumber == 0) { 12839c17f735SNikolai Kondrashov /* Try to probe v1 pen parameters */ 12849c17f735SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, 12859c17f735SNikolai Kondrashov &found, hdev); 12869c17f735SNikolai Kondrashov if (rc != 0) { 12879c17f735SNikolai Kondrashov hid_err(hdev, 12889c17f735SNikolai Kondrashov "pen probing failed: %d\n", 12899c17f735SNikolai Kondrashov rc); 12909c17f735SNikolai Kondrashov goto cleanup; 12919c17f735SNikolai Kondrashov } 12929c17f735SNikolai Kondrashov if (!found) { 12939c17f735SNikolai Kondrashov hid_warn(hdev, 12949c17f735SNikolai Kondrashov "pen parameters not found"); 12959c17f735SNikolai Kondrashov } 12969c17f735SNikolai Kondrashov } else { 12979c17f735SNikolai Kondrashov uclogic_params_init_invalid(&p); 12989c17f735SNikolai Kondrashov } 12999c17f735SNikolai Kondrashov } else { 13009614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 13019614219eSNikolai Kondrashov if (rc != 0) 13029614219eSNikolai Kondrashov goto cleanup; 13039c17f735SNikolai Kondrashov } 13049614219eSNikolai Kondrashov break; 13059614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13069614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 13079614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 13089614219eSNikolai Kondrashov if (rc != 0) 13099614219eSNikolai Kondrashov goto cleanup; 13109614219eSNikolai Kondrashov break; 13119614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13129614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 13139614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 13149614219eSNikolai Kondrashov if (rc != 0) 13159614219eSNikolai Kondrashov goto cleanup; 13169614219eSNikolai Kondrashov break; 13179614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13189614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 13199614219eSNikolai Kondrashov switch (bInterfaceNumber) { 13209614219eSNikolai Kondrashov case 0: 13219614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 13229614219eSNikolai Kondrashov if (rc != 0) 13239614219eSNikolai Kondrashov goto cleanup; 13249614219eSNikolai Kondrashov break; 13259614219eSNikolai Kondrashov case 1: 13269614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 13279614219eSNikolai Kondrashov if (rc != 0) 13289614219eSNikolai Kondrashov goto cleanup; 13299614219eSNikolai Kondrashov break; 13309614219eSNikolai Kondrashov case 2: 13319614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 13329614219eSNikolai Kondrashov if (rc != 0) 13339614219eSNikolai Kondrashov goto cleanup; 13349614219eSNikolai Kondrashov break; 13359614219eSNikolai Kondrashov } 13369614219eSNikolai Kondrashov break; 13379614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13389614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 13399614219eSNikolai Kondrashov /* 13409614219eSNikolai Kondrashov * If it is not a three-interface version, which is known to 13419614219eSNikolai Kondrashov * respond to initialization. 13429614219eSNikolai Kondrashov */ 13439614219eSNikolai Kondrashov if (bNumInterfaces != 3) { 13449614219eSNikolai Kondrashov switch (bInterfaceNumber) { 13459614219eSNikolai Kondrashov case 0: 13469614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG0, 13479614219eSNikolai Kondrashov twha60_fixed0); 13489614219eSNikolai Kondrashov if (rc != 0) 13499614219eSNikolai Kondrashov goto cleanup; 13509614219eSNikolai Kondrashov break; 13519614219eSNikolai Kondrashov case 1: 13529614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG1, 13539614219eSNikolai Kondrashov twha60_fixed1); 13549614219eSNikolai Kondrashov if (rc != 0) 13559614219eSNikolai Kondrashov goto cleanup; 13569614219eSNikolai Kondrashov break; 13579614219eSNikolai Kondrashov } 13589614219eSNikolai Kondrashov break; 13599614219eSNikolai Kondrashov } 1360df561f66SGustavo A. R. Silva fallthrough; 13619614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_HUION, 13629614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 1363315ffcc9SKyle Godbey case VID_PID(USB_VENDOR_ID_HUION, 136485e86071SNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET2): 13659614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13669614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 13679614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13689614219eSNikolai Kondrashov USB_DEVICE_ID_YIYNOVA_TABLET): 13699614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13709614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 13719614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13729614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 13739614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13749614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 13750c15efe9SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 13760c15efe9SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 13779614219eSNikolai Kondrashov rc = uclogic_params_huion_init(&p, hdev); 13789614219eSNikolai Kondrashov if (rc != 0) 13799614219eSNikolai Kondrashov goto cleanup; 13809614219eSNikolai Kondrashov break; 13819614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGTIZER, 13829614219eSNikolai Kondrashov USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 1383022fc531SMartijn van de Streek case VID_PID(USB_VENDOR_ID_UGTIZER, 1384022fc531SMartijn van de Streek USB_DEVICE_ID_UGTIZER_TABLET_GT5040): 1385c3e5a67cSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1386c3e5a67cSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): 1387492a9e9aSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1388492a9e9aSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 138988bb346dSWang Xuerui case VID_PID(USB_VENDOR_ID_UGEE, 139061b1db5aSRoman Romanenko USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06): 139161b1db5aSRoman Romanenko case VID_PID(USB_VENDOR_ID_UGEE, 139288bb346dSWang Xuerui USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): 13939614219eSNikolai Kondrashov /* If this is the pen interface */ 13949614219eSNikolai Kondrashov if (bInterfaceNumber == 1) { 1395eecb5b84SNikolai Kondrashov /* Probe v1 pen parameters */ 1396eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 13979614219eSNikolai Kondrashov if (rc != 0) { 13989614219eSNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 13999614219eSNikolai Kondrashov goto cleanup; 14009614219eSNikolai Kondrashov } 14019614219eSNikolai Kondrashov if (!found) { 14029614219eSNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 14039614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 14049614219eSNikolai Kondrashov } 14059614219eSNikolai Kondrashov } else { 1406606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 14079614219eSNikolai Kondrashov } 14089614219eSNikolai Kondrashov break; 14091ee7c685SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 141008367be1SNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): 141108367be1SNikolai Kondrashov /* If this is the pen and frame interface */ 141208367be1SNikolai Kondrashov if (bInterfaceNumber == 1) { 141308367be1SNikolai Kondrashov /* Probe v1 pen parameters */ 141408367be1SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 141508367be1SNikolai Kondrashov if (rc != 0) { 141608367be1SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 141708367be1SNikolai Kondrashov goto cleanup; 141808367be1SNikolai Kondrashov } 141908367be1SNikolai Kondrashov /* Initialize frame parameters */ 142008367be1SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1421337fa051SNikolai Kondrashov &p.frame_list[0], 142208367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_arr, 142308367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_size, 142408367be1SNikolai Kondrashov 0); 142508367be1SNikolai Kondrashov if (rc != 0) 142608367be1SNikolai Kondrashov goto cleanup; 142708367be1SNikolai Kondrashov } else { 1428606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 142908367be1SNikolai Kondrashov } 143008367be1SNikolai Kondrashov break; 1431*0cb1fc09SJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 1432*0cb1fc09SJosé Expósito USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): 1433*0cb1fc09SJosé Expósito rc = uclogic_params_ugee_v2_init(&p, hdev); 1434*0cb1fc09SJosé Expósito if (rc != 0) 1435*0cb1fc09SJosé Expósito goto cleanup; 1436*0cb1fc09SJosé Expósito break; 1437f7271b2aSCristian Klein case VID_PID(USB_VENDOR_ID_TRUST, 1438f7271b2aSCristian Klein USB_DEVICE_ID_TRUST_PANORA_TABLET): 143908367be1SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1440e902ed93SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_G5): 1441e902ed93SNikolai Kondrashov /* Ignore non-pen interfaces */ 1442e902ed93SNikolai Kondrashov if (bInterfaceNumber != 1) { 1443e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1444e902ed93SNikolai Kondrashov break; 1445e902ed93SNikolai Kondrashov } 1446e902ed93SNikolai Kondrashov 1447e902ed93SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 1448e902ed93SNikolai Kondrashov if (rc != 0) { 1449e902ed93SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 1450e902ed93SNikolai Kondrashov goto cleanup; 1451e902ed93SNikolai Kondrashov } else if (found) { 1452e902ed93SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1453337fa051SNikolai Kondrashov &p.frame_list[0], 1454e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_arr, 1455e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_size, 1456e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_ID); 1457e902ed93SNikolai Kondrashov if (rc != 0) { 1458e902ed93SNikolai Kondrashov hid_err(hdev, 14592e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 1460e902ed93SNikolai Kondrashov rc); 1461e902ed93SNikolai Kondrashov goto cleanup; 1462e902ed93SNikolai Kondrashov } 1463337fa051SNikolai Kondrashov p.frame_list[0].re_lsb = 1464e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; 1465337fa051SNikolai Kondrashov p.frame_list[0].dev_id_byte = 1466e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; 1467e902ed93SNikolai Kondrashov } else { 1468e902ed93SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 1469e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1470e902ed93SNikolai Kondrashov } 1471e902ed93SNikolai Kondrashov 1472e902ed93SNikolai Kondrashov break; 1473e902ed93SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 14741ee7c685SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_EX07S): 14751ee7c685SNikolai Kondrashov /* Ignore non-pen interfaces */ 14761ee7c685SNikolai Kondrashov if (bInterfaceNumber != 1) { 14771ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 14781ee7c685SNikolai Kondrashov break; 14791ee7c685SNikolai Kondrashov } 14801ee7c685SNikolai Kondrashov 14811ee7c685SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 14821ee7c685SNikolai Kondrashov if (rc != 0) { 14831ee7c685SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 14841ee7c685SNikolai Kondrashov goto cleanup; 14851ee7c685SNikolai Kondrashov } else if (found) { 14861ee7c685SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1487337fa051SNikolai Kondrashov &p.frame_list[0], 14882e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_arr, 14892e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_size, 14901ee7c685SNikolai Kondrashov 0); 14911ee7c685SNikolai Kondrashov if (rc != 0) { 14921ee7c685SNikolai Kondrashov hid_err(hdev, 14932e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 14941ee7c685SNikolai Kondrashov rc); 14951ee7c685SNikolai Kondrashov goto cleanup; 14961ee7c685SNikolai Kondrashov } 14971ee7c685SNikolai Kondrashov } else { 14981ee7c685SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 14991ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 15001ee7c685SNikolai Kondrashov } 15011ee7c685SNikolai Kondrashov 15021ee7c685SNikolai Kondrashov break; 15039614219eSNikolai Kondrashov } 15049614219eSNikolai Kondrashov 15059614219eSNikolai Kondrashov #undef VID_PID 15069614219eSNikolai Kondrashov #undef WITH_OPT_DESC 15079614219eSNikolai Kondrashov 15089614219eSNikolai Kondrashov /* Output parameters */ 15099614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 15109614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 15119614219eSNikolai Kondrashov rc = 0; 15129614219eSNikolai Kondrashov cleanup: 15139614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 15149614219eSNikolai Kondrashov return rc; 15159614219eSNikolai Kondrashov } 1516