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 */ 2379614219eSNikolai Kondrashov s32 desc_params[UCLOGIC_RDESC_PEN_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 */ 3822c3a88c6SNikolai Kondrashov s32 desc_params[UCLOGIC_RDESC_PEN_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; 8119614219eSNikolai Kondrashov 8129614219eSNikolai Kondrashov /* Check arguments */ 8139614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 8149614219eSNikolai Kondrashov rc = -EINVAL; 8159614219eSNikolai Kondrashov goto cleanup; 8169614219eSNikolai Kondrashov } 8179614219eSNikolai Kondrashov 818ff6b548aSJosé Expósito udev = hid_to_usb_dev(hdev); 819ff6b548aSJosé Expósito iface = to_usb_interface(hdev->dev.parent); 820ff6b548aSJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 821ff6b548aSJosé Expósito 822d64a6e44SNikolai Kondrashov /* If it's a custom keyboard interface */ 823d64a6e44SNikolai Kondrashov if (bInterfaceNumber == 1) { 8244c60bc7dSNikolai Kondrashov /* Keep everything intact, but mark pen usage invalid */ 8254c60bc7dSNikolai Kondrashov p.pen.usage_invalid = true; 826d64a6e44SNikolai Kondrashov goto output; 827d64a6e44SNikolai Kondrashov /* Else, if it's not a pen interface */ 828d64a6e44SNikolai Kondrashov } else if (bInterfaceNumber != 0) { 829606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 8309614219eSNikolai Kondrashov goto output; 8319614219eSNikolai Kondrashov } 8329614219eSNikolai Kondrashov 8332c3a88c6SNikolai Kondrashov /* Try to get firmware version */ 8342c3a88c6SNikolai Kondrashov ver_ptr = kzalloc(ver_len, GFP_KERNEL); 8352c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 8362c3a88c6SNikolai Kondrashov rc = -ENOMEM; 8372c3a88c6SNikolai Kondrashov goto cleanup; 8382c3a88c6SNikolai Kondrashov } 8392c3a88c6SNikolai Kondrashov rc = usb_string(udev, 201, ver_ptr, ver_len); 8402c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 8412c3a88c6SNikolai Kondrashov *ver_ptr = '\0'; 8422c3a88c6SNikolai Kondrashov } else if (rc < 0) { 8432c3a88c6SNikolai Kondrashov hid_err(hdev, 8442c3a88c6SNikolai Kondrashov "failed retrieving Huion firmware version: %d\n", rc); 8452c3a88c6SNikolai Kondrashov goto cleanup; 8462c3a88c6SNikolai Kondrashov } 8472c3a88c6SNikolai Kondrashov 8482c3a88c6SNikolai Kondrashov /* If this is a transition firmware */ 8492c3a88c6SNikolai Kondrashov if (strcmp(ver_ptr, transition_ver) == 0) { 8502c3a88c6SNikolai Kondrashov hid_dbg(hdev, 8512c3a88c6SNikolai Kondrashov "transition firmware detected, not probing pen v2 parameters\n"); 8522c3a88c6SNikolai Kondrashov } else { 8532c3a88c6SNikolai Kondrashov /* Try to probe v2 pen parameters */ 854945d5dd5SNikolai Kondrashov rc = uclogic_params_pen_init_v2(&p.pen, &found, 855945d5dd5SNikolai Kondrashov NULL, NULL, hdev); 8562c3a88c6SNikolai Kondrashov if (rc != 0) { 8572c3a88c6SNikolai Kondrashov hid_err(hdev, 8582c3a88c6SNikolai Kondrashov "failed probing pen v2 parameters: %d\n", rc); 8592c3a88c6SNikolai Kondrashov goto cleanup; 8602c3a88c6SNikolai Kondrashov } else if (found) { 8612c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters found\n"); 862c3e6e59aSNikolai Kondrashov /* Create v2 frame button parameters */ 8632c3a88c6SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 864337fa051SNikolai Kondrashov &p.frame_list[0], 865c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_arr, 866c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_size, 867c3e6e59aSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); 8682c3a88c6SNikolai Kondrashov if (rc != 0) { 8692c3a88c6SNikolai Kondrashov hid_err(hdev, 870c3e6e59aSNikolai Kondrashov "failed creating v2 frame button parameters: %d\n", 8712c3a88c6SNikolai Kondrashov rc); 8722c3a88c6SNikolai Kondrashov goto cleanup; 8732c3a88c6SNikolai Kondrashov } 874c3e6e59aSNikolai Kondrashov 875c3e6e59aSNikolai Kondrashov /* Create v2 frame touch ring parameters */ 876c3e6e59aSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 877c3e6e59aSNikolai Kondrashov &p.frame_list[1], 878c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_arr, 879c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_size, 880caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 881c3e6e59aSNikolai Kondrashov if (rc != 0) { 882c3e6e59aSNikolai Kondrashov hid_err(hdev, 883c3e6e59aSNikolai Kondrashov "failed creating v2 frame touch ring parameters: %d\n", 884c3e6e59aSNikolai Kondrashov rc); 885c3e6e59aSNikolai Kondrashov goto cleanup; 886c3e6e59aSNikolai Kondrashov } 887c3e6e59aSNikolai Kondrashov p.frame_list[1].suffix = "Touch Ring"; 888c3e6e59aSNikolai Kondrashov p.frame_list[1].dev_id_byte = 889caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 890caf7e934SNikolai Kondrashov p.frame_list[1].touch_byte = 5; 891caf7e934SNikolai Kondrashov p.frame_list[1].touch_max = 12; 892*fbc08b4eSNikolai Kondrashov p.frame_list[1].touch_flip_at = 7; 893c3e6e59aSNikolai Kondrashov 8946facd076SNikolai Kondrashov /* Create v2 frame dial parameters */ 8956facd076SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 8966facd076SNikolai Kondrashov &p.frame_list[2], 8976facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_arr, 8986facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_size, 8996facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID); 9006facd076SNikolai Kondrashov if (rc != 0) { 9016facd076SNikolai Kondrashov hid_err(hdev, 9026facd076SNikolai Kondrashov "failed creating v2 frame dial parameters: %d\n", 9036facd076SNikolai Kondrashov rc); 9046facd076SNikolai Kondrashov goto cleanup; 9056facd076SNikolai Kondrashov } 9066facd076SNikolai Kondrashov p.frame_list[2].suffix = "Dial"; 9076facd076SNikolai Kondrashov p.frame_list[2].dev_id_byte = 9086facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; 9096facd076SNikolai Kondrashov p.frame_list[2].bitmap_dial_byte = 5; 9106facd076SNikolai Kondrashov 911c3e6e59aSNikolai Kondrashov /* 912c3e6e59aSNikolai Kondrashov * Link button and touch ring subreports from pen 913c3e6e59aSNikolai Kondrashov * reports 914c3e6e59aSNikolai Kondrashov */ 915e6be956fSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 9168b013098SNikolai Kondrashov p.pen.subreport_list[0].id = 917c3e6e59aSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; 918c3e6e59aSNikolai Kondrashov p.pen.subreport_list[1].value = 0xf0; 919c3e6e59aSNikolai Kondrashov p.pen.subreport_list[1].id = 920caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; 9216facd076SNikolai Kondrashov p.pen.subreport_list[2].value = 0xf1; 9226facd076SNikolai Kondrashov p.pen.subreport_list[2].id = 9236facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID; 9242c3a88c6SNikolai Kondrashov goto output; 9252c3a88c6SNikolai Kondrashov } 9262c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters not found\n"); 9272c3a88c6SNikolai Kondrashov } 9282c3a88c6SNikolai Kondrashov 929eecb5b84SNikolai Kondrashov /* Try to probe v1 pen parameters */ 930eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 9319614219eSNikolai Kondrashov if (rc != 0) { 9329614219eSNikolai Kondrashov hid_err(hdev, 933eecb5b84SNikolai Kondrashov "failed probing pen v1 parameters: %d\n", rc); 9349614219eSNikolai Kondrashov goto cleanup; 9359614219eSNikolai Kondrashov } else if (found) { 936eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters found\n"); 9372e28f3e0SNikolai Kondrashov /* Try to probe v1 frame */ 938337fa051SNikolai Kondrashov rc = uclogic_params_frame_init_v1(&p.frame_list[0], 9399614219eSNikolai Kondrashov &found, hdev); 9409614219eSNikolai Kondrashov if (rc != 0) { 9412e28f3e0SNikolai Kondrashov hid_err(hdev, "v1 frame probing failed: %d\n", rc); 9429614219eSNikolai Kondrashov goto cleanup; 9439614219eSNikolai Kondrashov } 9442e28f3e0SNikolai Kondrashov hid_dbg(hdev, "frame v1 parameters%s found\n", 9459614219eSNikolai Kondrashov (found ? "" : " not")); 9469614219eSNikolai Kondrashov if (found) { 9478b013098SNikolai Kondrashov /* Link frame button subreports from pen reports */ 948e6be956fSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 9498b013098SNikolai Kondrashov p.pen.subreport_list[0].id = 950a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID; 9519614219eSNikolai Kondrashov } 9529614219eSNikolai Kondrashov goto output; 9539614219eSNikolai Kondrashov } 954eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters not found\n"); 9559614219eSNikolai Kondrashov 9569614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 9579614219eSNikolai Kondrashov 9589614219eSNikolai Kondrashov output: 9599614219eSNikolai Kondrashov /* Output parameters */ 9609614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 9619614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 9629614219eSNikolai Kondrashov rc = 0; 9639614219eSNikolai Kondrashov cleanup: 9642c3a88c6SNikolai Kondrashov kfree(ver_ptr); 9659614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 9669614219eSNikolai Kondrashov return rc; 9679614219eSNikolai Kondrashov } 9689614219eSNikolai Kondrashov 9699614219eSNikolai Kondrashov /** 9709614219eSNikolai Kondrashov * uclogic_params_init() - initialize a tablet interface and discover its 9719614219eSNikolai Kondrashov * parameters. 9729614219eSNikolai Kondrashov * 9739614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 9749614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 9759614219eSNikolai Kondrashov * Cannot be NULL. 9769614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 9778547b778SNikolai Kondrashov * parameters from. Cannot be NULL. Must be using the USB low-level 9788547b778SNikolai Kondrashov * driver, i.e. be an actual USB tablet. 9799614219eSNikolai Kondrashov * 9809614219eSNikolai Kondrashov * Returns: 9819614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 9829614219eSNikolai Kondrashov */ 9839614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params, 9849614219eSNikolai Kondrashov struct hid_device *hdev) 9859614219eSNikolai Kondrashov { 9869614219eSNikolai Kondrashov int rc; 987f364c571SJosé Expósito struct usb_device *udev; 988f364c571SJosé Expósito __u8 bNumInterfaces; 989f364c571SJosé Expósito struct usb_interface *iface; 990f364c571SJosé Expósito __u8 bInterfaceNumber; 9919614219eSNikolai Kondrashov bool found; 9929614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 9939614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 9949614219eSNikolai Kondrashov 9959614219eSNikolai Kondrashov /* Check arguments */ 996f83baa0cSGreg Kroah-Hartman if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { 9979614219eSNikolai Kondrashov rc = -EINVAL; 9989614219eSNikolai Kondrashov goto cleanup; 9999614219eSNikolai Kondrashov } 10009614219eSNikolai Kondrashov 1001f364c571SJosé Expósito udev = hid_to_usb_dev(hdev); 1002f364c571SJosé Expósito bNumInterfaces = udev->config->desc.bNumInterfaces; 1003f364c571SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 1004f364c571SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1005f364c571SJosé Expósito 10069614219eSNikolai Kondrashov /* 10079614219eSNikolai Kondrashov * Set replacement report descriptor if the original matches the 10089614219eSNikolai Kondrashov * specified size. Otherwise keep interface unchanged. 10099614219eSNikolai Kondrashov */ 10109614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 10119614219eSNikolai Kondrashov uclogic_params_init_with_opt_desc( \ 10129614219eSNikolai Kondrashov &p, hdev, \ 10139614219eSNikolai Kondrashov UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 10149614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_arr, \ 10159614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_size) 10169614219eSNikolai Kondrashov 10179614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \ 10189614219eSNikolai Kondrashov (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 10199614219eSNikolai Kondrashov 10209614219eSNikolai Kondrashov /* 10219614219eSNikolai Kondrashov * Handle specific interfaces for specific tablets. 10229614219eSNikolai Kondrashov * 10239614219eSNikolai Kondrashov * Observe the following logic: 10249614219eSNikolai Kondrashov * 10259614219eSNikolai Kondrashov * If the interface is recognized as producing certain useful input: 10269614219eSNikolai Kondrashov * Mark interface as valid. 10279614219eSNikolai Kondrashov * Output interface parameters. 10289614219eSNikolai Kondrashov * Else, if the interface is recognized as *not* producing any useful 10299614219eSNikolai Kondrashov * input: 10309614219eSNikolai Kondrashov * Mark interface as invalid. 10319614219eSNikolai Kondrashov * Else: 10329614219eSNikolai Kondrashov * Mark interface as valid. 10339614219eSNikolai Kondrashov * Output noop parameters. 10349614219eSNikolai Kondrashov * 10359614219eSNikolai Kondrashov * Rule of thumb: it is better to disable a broken interface than let 10369614219eSNikolai Kondrashov * it spew garbage input. 10379614219eSNikolai Kondrashov */ 10389614219eSNikolai Kondrashov 10399614219eSNikolai Kondrashov switch (VID_PID(hdev->vendor, hdev->product)) { 10409614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10419614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 10429614219eSNikolai Kondrashov rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 10439614219eSNikolai Kondrashov if (rc != 0) 10449614219eSNikolai Kondrashov goto cleanup; 10459614219eSNikolai Kondrashov break; 10469614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10479614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 10489614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 10499614219eSNikolai Kondrashov if (rc != 0) 10509614219eSNikolai Kondrashov goto cleanup; 10519614219eSNikolai Kondrashov break; 10529614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10539614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 10549c17f735SNikolai Kondrashov if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { 10559c17f735SNikolai Kondrashov if (bInterfaceNumber == 0) { 10569c17f735SNikolai Kondrashov /* Try to probe v1 pen parameters */ 10579c17f735SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, 10589c17f735SNikolai Kondrashov &found, hdev); 10599c17f735SNikolai Kondrashov if (rc != 0) { 10609c17f735SNikolai Kondrashov hid_err(hdev, 10619c17f735SNikolai Kondrashov "pen probing failed: %d\n", 10629c17f735SNikolai Kondrashov rc); 10639c17f735SNikolai Kondrashov goto cleanup; 10649c17f735SNikolai Kondrashov } 10659c17f735SNikolai Kondrashov if (!found) { 10669c17f735SNikolai Kondrashov hid_warn(hdev, 10679c17f735SNikolai Kondrashov "pen parameters not found"); 10689c17f735SNikolai Kondrashov } 10699c17f735SNikolai Kondrashov } else { 10709c17f735SNikolai Kondrashov uclogic_params_init_invalid(&p); 10719c17f735SNikolai Kondrashov } 10729c17f735SNikolai Kondrashov } else { 10739614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 10749614219eSNikolai Kondrashov if (rc != 0) 10759614219eSNikolai Kondrashov goto cleanup; 10769c17f735SNikolai Kondrashov } 10779614219eSNikolai Kondrashov break; 10789614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10799614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 10809614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 10819614219eSNikolai Kondrashov if (rc != 0) 10829614219eSNikolai Kondrashov goto cleanup; 10839614219eSNikolai Kondrashov break; 10849614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10859614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 10869614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 10879614219eSNikolai Kondrashov if (rc != 0) 10889614219eSNikolai Kondrashov goto cleanup; 10899614219eSNikolai Kondrashov break; 10909614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10919614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 10929614219eSNikolai Kondrashov switch (bInterfaceNumber) { 10939614219eSNikolai Kondrashov case 0: 10949614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 10959614219eSNikolai Kondrashov if (rc != 0) 10969614219eSNikolai Kondrashov goto cleanup; 10979614219eSNikolai Kondrashov break; 10989614219eSNikolai Kondrashov case 1: 10999614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 11009614219eSNikolai Kondrashov if (rc != 0) 11019614219eSNikolai Kondrashov goto cleanup; 11029614219eSNikolai Kondrashov break; 11039614219eSNikolai Kondrashov case 2: 11049614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 11059614219eSNikolai Kondrashov if (rc != 0) 11069614219eSNikolai Kondrashov goto cleanup; 11079614219eSNikolai Kondrashov break; 11089614219eSNikolai Kondrashov } 11099614219eSNikolai Kondrashov break; 11109614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11119614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 11129614219eSNikolai Kondrashov /* 11139614219eSNikolai Kondrashov * If it is not a three-interface version, which is known to 11149614219eSNikolai Kondrashov * respond to initialization. 11159614219eSNikolai Kondrashov */ 11169614219eSNikolai Kondrashov if (bNumInterfaces != 3) { 11179614219eSNikolai Kondrashov switch (bInterfaceNumber) { 11189614219eSNikolai Kondrashov case 0: 11199614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG0, 11209614219eSNikolai Kondrashov twha60_fixed0); 11219614219eSNikolai Kondrashov if (rc != 0) 11229614219eSNikolai Kondrashov goto cleanup; 11239614219eSNikolai Kondrashov break; 11249614219eSNikolai Kondrashov case 1: 11259614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG1, 11269614219eSNikolai Kondrashov twha60_fixed1); 11279614219eSNikolai Kondrashov if (rc != 0) 11289614219eSNikolai Kondrashov goto cleanup; 11299614219eSNikolai Kondrashov break; 11309614219eSNikolai Kondrashov } 11319614219eSNikolai Kondrashov break; 11329614219eSNikolai Kondrashov } 1133df561f66SGustavo A. R. Silva fallthrough; 11349614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_HUION, 11359614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 1136315ffcc9SKyle Godbey case VID_PID(USB_VENDOR_ID_HUION, 113785e86071SNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET2): 11389614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11399614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 11409614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11419614219eSNikolai Kondrashov USB_DEVICE_ID_YIYNOVA_TABLET): 11429614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11439614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 11449614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11459614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 11469614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11479614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 11480c15efe9SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11490c15efe9SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 11509614219eSNikolai Kondrashov rc = uclogic_params_huion_init(&p, hdev); 11519614219eSNikolai Kondrashov if (rc != 0) 11529614219eSNikolai Kondrashov goto cleanup; 11539614219eSNikolai Kondrashov break; 11549614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGTIZER, 11559614219eSNikolai Kondrashov USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 1156022fc531SMartijn van de Streek case VID_PID(USB_VENDOR_ID_UGTIZER, 1157022fc531SMartijn van de Streek USB_DEVICE_ID_UGTIZER_TABLET_GT5040): 1158c3e5a67cSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1159c3e5a67cSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): 1160492a9e9aSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1161492a9e9aSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 116288bb346dSWang Xuerui case VID_PID(USB_VENDOR_ID_UGEE, 116388bb346dSWang Xuerui USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): 11649614219eSNikolai Kondrashov /* If this is the pen interface */ 11659614219eSNikolai Kondrashov if (bInterfaceNumber == 1) { 1166eecb5b84SNikolai Kondrashov /* Probe v1 pen parameters */ 1167eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 11689614219eSNikolai Kondrashov if (rc != 0) { 11699614219eSNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 11709614219eSNikolai Kondrashov goto cleanup; 11719614219eSNikolai Kondrashov } 11729614219eSNikolai Kondrashov if (!found) { 11739614219eSNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 11749614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 11759614219eSNikolai Kondrashov } 11769614219eSNikolai Kondrashov } else { 1177606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 11789614219eSNikolai Kondrashov } 11799614219eSNikolai Kondrashov break; 11801ee7c685SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 118108367be1SNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): 118208367be1SNikolai Kondrashov /* If this is the pen and frame interface */ 118308367be1SNikolai Kondrashov if (bInterfaceNumber == 1) { 118408367be1SNikolai Kondrashov /* Probe v1 pen parameters */ 118508367be1SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 118608367be1SNikolai Kondrashov if (rc != 0) { 118708367be1SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 118808367be1SNikolai Kondrashov goto cleanup; 118908367be1SNikolai Kondrashov } 119008367be1SNikolai Kondrashov /* Initialize frame parameters */ 119108367be1SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1192337fa051SNikolai Kondrashov &p.frame_list[0], 119308367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_arr, 119408367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_size, 119508367be1SNikolai Kondrashov 0); 119608367be1SNikolai Kondrashov if (rc != 0) 119708367be1SNikolai Kondrashov goto cleanup; 119808367be1SNikolai Kondrashov } else { 1199606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 120008367be1SNikolai Kondrashov } 120108367be1SNikolai Kondrashov break; 1202f7271b2aSCristian Klein case VID_PID(USB_VENDOR_ID_TRUST, 1203f7271b2aSCristian Klein USB_DEVICE_ID_TRUST_PANORA_TABLET): 120408367be1SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1205e902ed93SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_G5): 1206e902ed93SNikolai Kondrashov /* Ignore non-pen interfaces */ 1207e902ed93SNikolai Kondrashov if (bInterfaceNumber != 1) { 1208e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1209e902ed93SNikolai Kondrashov break; 1210e902ed93SNikolai Kondrashov } 1211e902ed93SNikolai Kondrashov 1212e902ed93SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 1213e902ed93SNikolai Kondrashov if (rc != 0) { 1214e902ed93SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 1215e902ed93SNikolai Kondrashov goto cleanup; 1216e902ed93SNikolai Kondrashov } else if (found) { 1217e902ed93SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1218337fa051SNikolai Kondrashov &p.frame_list[0], 1219e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_arr, 1220e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_size, 1221e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_ID); 1222e902ed93SNikolai Kondrashov if (rc != 0) { 1223e902ed93SNikolai Kondrashov hid_err(hdev, 12242e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 1225e902ed93SNikolai Kondrashov rc); 1226e902ed93SNikolai Kondrashov goto cleanup; 1227e902ed93SNikolai Kondrashov } 1228337fa051SNikolai Kondrashov p.frame_list[0].re_lsb = 1229e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; 1230337fa051SNikolai Kondrashov p.frame_list[0].dev_id_byte = 1231e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; 1232e902ed93SNikolai Kondrashov } else { 1233e902ed93SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 1234e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1235e902ed93SNikolai Kondrashov } 1236e902ed93SNikolai Kondrashov 1237e902ed93SNikolai Kondrashov break; 1238e902ed93SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 12391ee7c685SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_EX07S): 12401ee7c685SNikolai Kondrashov /* Ignore non-pen interfaces */ 12411ee7c685SNikolai Kondrashov if (bInterfaceNumber != 1) { 12421ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 12431ee7c685SNikolai Kondrashov break; 12441ee7c685SNikolai Kondrashov } 12451ee7c685SNikolai Kondrashov 12461ee7c685SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 12471ee7c685SNikolai Kondrashov if (rc != 0) { 12481ee7c685SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 12491ee7c685SNikolai Kondrashov goto cleanup; 12501ee7c685SNikolai Kondrashov } else if (found) { 12511ee7c685SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1252337fa051SNikolai Kondrashov &p.frame_list[0], 12532e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_arr, 12542e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_size, 12551ee7c685SNikolai Kondrashov 0); 12561ee7c685SNikolai Kondrashov if (rc != 0) { 12571ee7c685SNikolai Kondrashov hid_err(hdev, 12582e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 12591ee7c685SNikolai Kondrashov rc); 12601ee7c685SNikolai Kondrashov goto cleanup; 12611ee7c685SNikolai Kondrashov } 12621ee7c685SNikolai Kondrashov } else { 12631ee7c685SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 12641ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 12651ee7c685SNikolai Kondrashov } 12661ee7c685SNikolai Kondrashov 12671ee7c685SNikolai Kondrashov break; 12689614219eSNikolai Kondrashov } 12699614219eSNikolai Kondrashov 12709614219eSNikolai Kondrashov #undef VID_PID 12719614219eSNikolai Kondrashov #undef WITH_OPT_DESC 12729614219eSNikolai Kondrashov 12739614219eSNikolai Kondrashov /* Output parameters */ 12749614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 12759614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 12769614219eSNikolai Kondrashov rc = 0; 12779614219eSNikolai Kondrashov cleanup: 12789614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 12799614219eSNikolai Kondrashov return rc; 12809614219eSNikolai Kondrashov } 1281