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 * @inrange: The in-range reporting type to convert. 279614219eSNikolai Kondrashov * 28*d5e649a5SBagas Sanjaya * Return: 29*d5e649a5SBagas Sanjaya * * The string representing the type, or 30*d5e649a5SBagas Sanjaya * * %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 /** 48*d5e649a5SBagas Sanjaya * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters 49a228809fSNikolai Kondrashov * @hdev: The HID device the pen parameters describe. 50a228809fSNikolai Kondrashov * @pen: The pen parameters to dump. 51*d5e649a5SBagas Sanjaya * 52*d5e649a5SBagas Sanjaya * Dump tablet interface pen parameters with hid_dbg(). The dump is indented 53*d5e649a5SBagas Sanjaya * with a tab. 54a228809fSNikolai Kondrashov */ 55a228809fSNikolai Kondrashov static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, 56a228809fSNikolai Kondrashov const struct uclogic_params_pen *pen) 57a228809fSNikolai Kondrashov { 58a228809fSNikolai Kondrashov size_t i; 59a228809fSNikolai Kondrashov 60a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.usage_invalid = %s\n", 61a228809fSNikolai Kondrashov (pen->usage_invalid ? "true" : "false")); 62a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr); 63a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size); 64a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.id = %u\n", pen->id); 65a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.subreport_list = {\n"); 66a228809fSNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) { 67a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n", 68a228809fSNikolai Kondrashov pen->subreport_list[i].value, 69a228809fSNikolai Kondrashov pen->subreport_list[i].id, 70a228809fSNikolai Kondrashov i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : ""); 71a228809fSNikolai Kondrashov } 72a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}\n"); 73a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.inrange = %s\n", 74a228809fSNikolai Kondrashov uclogic_params_pen_inrange_to_str(pen->inrange)); 75a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.fragmented_hires = %s\n", 76a228809fSNikolai Kondrashov (pen->fragmented_hires ? "true" : "false")); 77a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.tilt_y_flipped = %s\n", 78a228809fSNikolai Kondrashov (pen->tilt_y_flipped ? "true" : "false")); 79a228809fSNikolai Kondrashov } 80a228809fSNikolai Kondrashov 81a228809fSNikolai Kondrashov /** 82*d5e649a5SBagas Sanjaya * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters 83a228809fSNikolai Kondrashov * @hdev: The HID device the pen parameters describe. 84a228809fSNikolai Kondrashov * @frame: The frame parameters to dump. 85*d5e649a5SBagas Sanjaya * 86*d5e649a5SBagas Sanjaya * Dump tablet interface frame parameters with hid_dbg(). The dump is 87*d5e649a5SBagas Sanjaya * indented with two tabs. 88a228809fSNikolai Kondrashov */ 89a228809fSNikolai Kondrashov static void uclogic_params_frame_hid_dbg( 90a228809fSNikolai Kondrashov const struct hid_device *hdev, 91a228809fSNikolai Kondrashov const struct uclogic_params_frame *frame) 92a228809fSNikolai Kondrashov { 93a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr); 94a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size); 95a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.id = %u\n", frame->id); 96a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix); 97a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb); 98a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte); 99caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte); 100caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max); 101caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n", 102caf7e934SNikolai Kondrashov frame->touch_flip_at); 103a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n", 104a228809fSNikolai Kondrashov frame->bitmap_dial_byte); 105a228809fSNikolai Kondrashov } 106a228809fSNikolai Kondrashov 107a228809fSNikolai Kondrashov /** 108*d5e649a5SBagas Sanjaya * uclogic_params_hid_dbg() - Dump tablet interface parameters 109a228809fSNikolai Kondrashov * @hdev: The HID device the parameters describe. 110a228809fSNikolai Kondrashov * @params: The parameters to dump. 111*d5e649a5SBagas Sanjaya * 112*d5e649a5SBagas Sanjaya * Dump tablet interface parameters with hid_dbg(). 113a228809fSNikolai Kondrashov */ 114a228809fSNikolai Kondrashov void uclogic_params_hid_dbg(const struct hid_device *hdev, 115a228809fSNikolai Kondrashov const struct uclogic_params *params) 116a228809fSNikolai Kondrashov { 117a228809fSNikolai Kondrashov size_t i; 118a228809fSNikolai Kondrashov 119a228809fSNikolai Kondrashov hid_dbg(hdev, ".invalid = %s\n", 120a228809fSNikolai Kondrashov params->invalid ? "true" : "false"); 121a228809fSNikolai Kondrashov hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr); 122a228809fSNikolai Kondrashov hid_dbg(hdev, ".desc_size = %u\n", params->desc_size); 123a228809fSNikolai Kondrashov hid_dbg(hdev, ".pen = {\n"); 124a228809fSNikolai Kondrashov uclogic_params_pen_hid_dbg(hdev, ¶ms->pen); 125a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}\n"); 126a228809fSNikolai Kondrashov hid_dbg(hdev, ".frame_list = {\n"); 127a228809fSNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 128a228809fSNikolai Kondrashov hid_dbg(hdev, "\t{\n"); 129a228809fSNikolai Kondrashov uclogic_params_frame_hid_dbg(hdev, ¶ms->frame_list[i]); 130a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}%s\n", 131a228809fSNikolai Kondrashov i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : ""); 132a228809fSNikolai Kondrashov } 133a228809fSNikolai Kondrashov hid_dbg(hdev, "}\n"); 134a228809fSNikolai Kondrashov } 135a228809fSNikolai Kondrashov 136a228809fSNikolai Kondrashov /** 1379614219eSNikolai Kondrashov * uclogic_params_get_str_desc - retrieve a string descriptor from a HID 1389614219eSNikolai Kondrashov * device interface, putting it into a kmalloc-allocated buffer as is, without 1399614219eSNikolai Kondrashov * character encoding conversion. 1409614219eSNikolai Kondrashov * 1419614219eSNikolai Kondrashov * @pbuf: Location for the kmalloc-allocated buffer pointer containing 1429614219eSNikolai Kondrashov * the retrieved descriptor. Not modified in case of error. 1439614219eSNikolai Kondrashov * Can be NULL to have retrieved descriptor discarded. 1449614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to retrieve the string 1459614219eSNikolai Kondrashov * descriptor from. Cannot be NULL. 1469614219eSNikolai Kondrashov * @idx: Index of the string descriptor to request from the device. 1479614219eSNikolai Kondrashov * @len: Length of the buffer to allocate and the data to retrieve. 1489614219eSNikolai Kondrashov * 1499614219eSNikolai Kondrashov * Returns: 1509614219eSNikolai Kondrashov * number of bytes retrieved (<= len), 1519614219eSNikolai Kondrashov * -EPIPE, if the descriptor was not found, or 1529614219eSNikolai Kondrashov * another negative errno code in case of other error. 1539614219eSNikolai Kondrashov */ 1549614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, 1559614219eSNikolai Kondrashov __u8 idx, size_t len) 1569614219eSNikolai Kondrashov { 1579614219eSNikolai Kondrashov int rc; 1580a94131dSJosé Expósito struct usb_device *udev; 1599614219eSNikolai Kondrashov __u8 *buf = NULL; 1609614219eSNikolai Kondrashov 1619614219eSNikolai Kondrashov /* Check arguments */ 1629614219eSNikolai Kondrashov if (hdev == NULL) { 1639614219eSNikolai Kondrashov rc = -EINVAL; 1649614219eSNikolai Kondrashov goto cleanup; 1659614219eSNikolai Kondrashov } 1669614219eSNikolai Kondrashov 1670a94131dSJosé Expósito udev = hid_to_usb_dev(hdev); 1680a94131dSJosé Expósito 1699614219eSNikolai Kondrashov buf = kmalloc(len, GFP_KERNEL); 1709614219eSNikolai Kondrashov if (buf == NULL) { 1719614219eSNikolai Kondrashov rc = -ENOMEM; 1729614219eSNikolai Kondrashov goto cleanup; 1739614219eSNikolai Kondrashov } 1749614219eSNikolai Kondrashov 1759614219eSNikolai Kondrashov rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1769614219eSNikolai Kondrashov USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, 1779614219eSNikolai Kondrashov (USB_DT_STRING << 8) + idx, 1789614219eSNikolai Kondrashov 0x0409, buf, len, 1799614219eSNikolai Kondrashov USB_CTRL_GET_TIMEOUT); 1809614219eSNikolai Kondrashov if (rc == -EPIPE) { 1819614219eSNikolai Kondrashov hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); 1829614219eSNikolai Kondrashov goto cleanup; 1839614219eSNikolai Kondrashov } else if (rc < 0) { 1849614219eSNikolai Kondrashov hid_err(hdev, 185a876e7e2STom Rix "failed retrieving string descriptor #%u: %d\n", 1869614219eSNikolai Kondrashov idx, rc); 1879614219eSNikolai Kondrashov goto cleanup; 1889614219eSNikolai Kondrashov } 1899614219eSNikolai Kondrashov 1909614219eSNikolai Kondrashov if (pbuf != NULL) { 1919614219eSNikolai Kondrashov *pbuf = buf; 1929614219eSNikolai Kondrashov buf = NULL; 1939614219eSNikolai Kondrashov } 1949614219eSNikolai Kondrashov 1959614219eSNikolai Kondrashov cleanup: 1969614219eSNikolai Kondrashov kfree(buf); 1979614219eSNikolai Kondrashov return rc; 1989614219eSNikolai Kondrashov } 1999614219eSNikolai Kondrashov 2009614219eSNikolai Kondrashov /** 2019614219eSNikolai Kondrashov * uclogic_params_pen_cleanup - free resources used by struct 2029614219eSNikolai Kondrashov * uclogic_params_pen (tablet interface's pen input parameters). 2039614219eSNikolai Kondrashov * Can be called repeatedly. 2049614219eSNikolai Kondrashov * 2059614219eSNikolai Kondrashov * @pen: Pen input parameters to cleanup. Cannot be NULL. 2069614219eSNikolai Kondrashov */ 2079614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) 2089614219eSNikolai Kondrashov { 2099614219eSNikolai Kondrashov kfree(pen->desc_ptr); 2109614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 2119614219eSNikolai Kondrashov } 2129614219eSNikolai Kondrashov 2139614219eSNikolai Kondrashov /** 214eecb5b84SNikolai Kondrashov * uclogic_params_pen_init_v1() - initialize tablet interface pen 215eecb5b84SNikolai Kondrashov * input and retrieve its parameters from the device, using v1 protocol. 2169614219eSNikolai Kondrashov * 2179614219eSNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 2189614219eSNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not modified in 2199614219eSNikolai Kondrashov * case of error, or if parameters are not found. Cannot be NULL. 2209614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 2219614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 2229614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 2239614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 2249614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 2259614219eSNikolai Kondrashov * 2269614219eSNikolai Kondrashov * Returns: 2279614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 2289614219eSNikolai Kondrashov */ 229eecb5b84SNikolai Kondrashov static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, 2309614219eSNikolai Kondrashov bool *pfound, 2319614219eSNikolai Kondrashov struct hid_device *hdev) 2329614219eSNikolai Kondrashov { 2339614219eSNikolai Kondrashov int rc; 2349614219eSNikolai Kondrashov bool found = false; 2359614219eSNikolai Kondrashov /* Buffer for (part of) the string descriptor */ 2369614219eSNikolai Kondrashov __u8 *buf = NULL; 2379614219eSNikolai Kondrashov /* Minimum descriptor length required, maximum seen so far is 18 */ 2389614219eSNikolai Kondrashov const int len = 12; 2399614219eSNikolai Kondrashov s32 resolution; 2409614219eSNikolai Kondrashov /* Pen report descriptor template parameters */ 2419614219eSNikolai Kondrashov s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; 2429614219eSNikolai Kondrashov __u8 *desc_ptr = NULL; 2439614219eSNikolai Kondrashov 2449614219eSNikolai Kondrashov /* Check arguments */ 2459614219eSNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 2469614219eSNikolai Kondrashov rc = -EINVAL; 2479614219eSNikolai Kondrashov goto cleanup; 2489614219eSNikolai Kondrashov } 2499614219eSNikolai Kondrashov 2509614219eSNikolai Kondrashov /* 2519614219eSNikolai Kondrashov * Read string descriptor containing pen input parameters. 2529614219eSNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 2539614219eSNikolai Kondrashov * the Windows driver traffic. 2549614219eSNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 2559614219eSNikolai Kondrashov */ 2569614219eSNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); 2579614219eSNikolai Kondrashov if (rc == -EPIPE) { 2589614219eSNikolai Kondrashov hid_dbg(hdev, 2599614219eSNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 2609614219eSNikolai Kondrashov goto finish; 2619614219eSNikolai Kondrashov } else if (rc < 0) { 2629614219eSNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 2639614219eSNikolai Kondrashov goto cleanup; 2649614219eSNikolai Kondrashov } else if (rc != len) { 2659614219eSNikolai Kondrashov hid_dbg(hdev, 2669614219eSNikolai Kondrashov "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", 2679614219eSNikolai Kondrashov rc, len); 2689614219eSNikolai Kondrashov goto finish; 2699614219eSNikolai Kondrashov } 2709614219eSNikolai Kondrashov 2719614219eSNikolai Kondrashov /* 2729614219eSNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 2739614219eSNikolai Kondrashov */ 2749614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 2759614219eSNikolai Kondrashov get_unaligned_le16(buf + 2); 2769614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 2779614219eSNikolai Kondrashov get_unaligned_le16(buf + 4); 2789614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 2799614219eSNikolai Kondrashov get_unaligned_le16(buf + 8); 2809614219eSNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 2819614219eSNikolai Kondrashov if (resolution == 0) { 2829614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 2839614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 2849614219eSNikolai Kondrashov } else { 2859614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 2869614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 2879614219eSNikolai Kondrashov resolution; 2889614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 2899614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 2909614219eSNikolai Kondrashov resolution; 2919614219eSNikolai Kondrashov } 2929614219eSNikolai Kondrashov kfree(buf); 2939614219eSNikolai Kondrashov buf = NULL; 2949614219eSNikolai Kondrashov 2959614219eSNikolai Kondrashov /* 2969614219eSNikolai Kondrashov * Generate pen report descriptor 2979614219eSNikolai Kondrashov */ 2989614219eSNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 299a985de58SNikolai Kondrashov uclogic_rdesc_v1_pen_template_arr, 300a985de58SNikolai Kondrashov uclogic_rdesc_v1_pen_template_size, 3019614219eSNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 3029614219eSNikolai Kondrashov if (desc_ptr == NULL) { 3039614219eSNikolai Kondrashov rc = -ENOMEM; 3049614219eSNikolai Kondrashov goto cleanup; 3059614219eSNikolai Kondrashov } 3069614219eSNikolai Kondrashov 3079614219eSNikolai Kondrashov /* 3089614219eSNikolai Kondrashov * Fill-in the parameters 3099614219eSNikolai Kondrashov */ 3109614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 3119614219eSNikolai Kondrashov pen->desc_ptr = desc_ptr; 3129614219eSNikolai Kondrashov desc_ptr = NULL; 313a985de58SNikolai Kondrashov pen->desc_size = uclogic_rdesc_v1_pen_template_size; 314a985de58SNikolai Kondrashov pen->id = UCLOGIC_RDESC_V1_PEN_ID; 3159614219eSNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; 3169614219eSNikolai Kondrashov found = true; 3179614219eSNikolai Kondrashov finish: 3189614219eSNikolai Kondrashov *pfound = found; 3199614219eSNikolai Kondrashov rc = 0; 3209614219eSNikolai Kondrashov cleanup: 3219614219eSNikolai Kondrashov kfree(desc_ptr); 3229614219eSNikolai Kondrashov kfree(buf); 3239614219eSNikolai Kondrashov return rc; 3249614219eSNikolai Kondrashov } 3259614219eSNikolai Kondrashov 3269614219eSNikolai Kondrashov /** 3272c3a88c6SNikolai Kondrashov * uclogic_params_get_le24() - get a 24-bit little-endian number from a 3282c3a88c6SNikolai Kondrashov * buffer. 3292c3a88c6SNikolai Kondrashov * 3302c3a88c6SNikolai Kondrashov * @p: The pointer to the number buffer. 3312c3a88c6SNikolai Kondrashov * 3322c3a88c6SNikolai Kondrashov * Returns: 3332c3a88c6SNikolai Kondrashov * The retrieved number 3342c3a88c6SNikolai Kondrashov */ 3352c3a88c6SNikolai Kondrashov static s32 uclogic_params_get_le24(const void *p) 3362c3a88c6SNikolai Kondrashov { 3372c3a88c6SNikolai Kondrashov const __u8 *b = p; 3382c3a88c6SNikolai Kondrashov return b[0] | (b[1] << 8UL) | (b[2] << 16UL); 3392c3a88c6SNikolai Kondrashov } 3402c3a88c6SNikolai Kondrashov 3412c3a88c6SNikolai Kondrashov /** 3422c3a88c6SNikolai Kondrashov * uclogic_params_pen_init_v2() - initialize tablet interface pen 3432c3a88c6SNikolai Kondrashov * input and retrieve its parameters from the device, using v2 protocol. 3442c3a88c6SNikolai Kondrashov * 3452c3a88c6SNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 346945d5dd5SNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not 347945d5dd5SNikolai Kondrashov * modified in case of error, or if parameters are not 348945d5dd5SNikolai Kondrashov * found. Cannot be NULL. 349945d5dd5SNikolai Kondrashov * @pfound: Location for a flag which is set to true if the 350945d5dd5SNikolai Kondrashov * parameters were found, and to false if not (e.g. 351945d5dd5SNikolai Kondrashov * device was incompatible). Not modified in case of 352945d5dd5SNikolai Kondrashov * error. Cannot be NULL. 353945d5dd5SNikolai Kondrashov * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw 354945d5dd5SNikolai Kondrashov * parameters, which could be used to identify the tablet 355945d5dd5SNikolai Kondrashov * to some extent. Should be freed with kfree after use. 356945d5dd5SNikolai Kondrashov * NULL, if not needed. Not modified in case of error. 357945d5dd5SNikolai Kondrashov * Only set if *pfound is set to true. 358945d5dd5SNikolai Kondrashov * @pparams_len: Location for the length of the retrieved raw 359945d5dd5SNikolai Kondrashov * parameters. NULL, if not needed. Not modified in case 360945d5dd5SNikolai Kondrashov * of error. Only set if *pfound is set to true. 361945d5dd5SNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize 362945d5dd5SNikolai Kondrashov * and get parameters from. Cannot be NULL. 3632c3a88c6SNikolai Kondrashov * 3642c3a88c6SNikolai Kondrashov * Returns: 3652c3a88c6SNikolai Kondrashov * Zero, if successful. A negative errno code on error. 3662c3a88c6SNikolai Kondrashov */ 3672c3a88c6SNikolai Kondrashov static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, 3682c3a88c6SNikolai Kondrashov bool *pfound, 369945d5dd5SNikolai Kondrashov __u8 **pparams_ptr, 370945d5dd5SNikolai Kondrashov size_t *pparams_len, 3712c3a88c6SNikolai Kondrashov struct hid_device *hdev) 3722c3a88c6SNikolai Kondrashov { 3732c3a88c6SNikolai Kondrashov int rc; 3742c3a88c6SNikolai Kondrashov bool found = false; 375945d5dd5SNikolai Kondrashov /* Buffer for (part of) the parameter string descriptor */ 3762c3a88c6SNikolai Kondrashov __u8 *buf = NULL; 377945d5dd5SNikolai Kondrashov /* Parameter string descriptor required length */ 378945d5dd5SNikolai Kondrashov const int params_len_min = 18; 379945d5dd5SNikolai Kondrashov /* Parameter string descriptor accepted length */ 380945d5dd5SNikolai Kondrashov const int params_len_max = 32; 381945d5dd5SNikolai Kondrashov /* Parameter string descriptor received length */ 382945d5dd5SNikolai Kondrashov int params_len; 383945d5dd5SNikolai Kondrashov size_t i; 3842c3a88c6SNikolai Kondrashov s32 resolution; 3852c3a88c6SNikolai Kondrashov /* Pen report descriptor template parameters */ 3862c3a88c6SNikolai Kondrashov s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; 3872c3a88c6SNikolai Kondrashov __u8 *desc_ptr = NULL; 3882c3a88c6SNikolai Kondrashov 3892c3a88c6SNikolai Kondrashov /* Check arguments */ 3902c3a88c6SNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 3912c3a88c6SNikolai Kondrashov rc = -EINVAL; 3922c3a88c6SNikolai Kondrashov goto cleanup; 3932c3a88c6SNikolai Kondrashov } 3942c3a88c6SNikolai Kondrashov 3952c3a88c6SNikolai Kondrashov /* 3962c3a88c6SNikolai Kondrashov * Read string descriptor containing pen input parameters. 3972c3a88c6SNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 3982c3a88c6SNikolai Kondrashov * the Windows driver traffic. 3992c3a88c6SNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 4002c3a88c6SNikolai Kondrashov */ 401945d5dd5SNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max); 4022c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 4032c3a88c6SNikolai Kondrashov hid_dbg(hdev, 4042c3a88c6SNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 4052c3a88c6SNikolai Kondrashov goto finish; 4062c3a88c6SNikolai Kondrashov } else if (rc < 0) { 4072c3a88c6SNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 4082c3a88c6SNikolai Kondrashov goto cleanup; 409945d5dd5SNikolai Kondrashov } else if (rc < params_len_min) { 4102c3a88c6SNikolai Kondrashov hid_dbg(hdev, 411945d5dd5SNikolai Kondrashov "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n", 412945d5dd5SNikolai Kondrashov rc, params_len_min); 4132c3a88c6SNikolai Kondrashov goto finish; 414945d5dd5SNikolai Kondrashov } 415945d5dd5SNikolai Kondrashov 416945d5dd5SNikolai Kondrashov params_len = rc; 417945d5dd5SNikolai Kondrashov 4182c3a88c6SNikolai Kondrashov /* 4192c3a88c6SNikolai Kondrashov * Check it's not just a catch-all UTF-16LE-encoded ASCII 4202c3a88c6SNikolai Kondrashov * string (such as the model name) some tablets put into all 4212c3a88c6SNikolai Kondrashov * unknown string descriptors. 4222c3a88c6SNikolai Kondrashov */ 4232c3a88c6SNikolai Kondrashov for (i = 2; 424945d5dd5SNikolai Kondrashov i < params_len && 4252c3a88c6SNikolai Kondrashov (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); 4262c3a88c6SNikolai Kondrashov i += 2); 427945d5dd5SNikolai Kondrashov if (i >= params_len) { 4282c3a88c6SNikolai Kondrashov hid_dbg(hdev, 4292c3a88c6SNikolai Kondrashov "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); 4302c3a88c6SNikolai Kondrashov goto finish; 4312c3a88c6SNikolai Kondrashov } 4322c3a88c6SNikolai Kondrashov 4332c3a88c6SNikolai Kondrashov /* 4342c3a88c6SNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 4352c3a88c6SNikolai Kondrashov */ 4362c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 4372c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 2); 4382c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 4392c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 5); 4402c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 4412c3a88c6SNikolai Kondrashov get_unaligned_le16(buf + 8); 4422c3a88c6SNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 4432c3a88c6SNikolai Kondrashov if (resolution == 0) { 4442c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 4452c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 4462c3a88c6SNikolai Kondrashov } else { 4472c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 4482c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 4492c3a88c6SNikolai Kondrashov resolution; 4502c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 4512c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 4522c3a88c6SNikolai Kondrashov resolution; 4532c3a88c6SNikolai Kondrashov } 4542c3a88c6SNikolai Kondrashov 4552c3a88c6SNikolai Kondrashov /* 4562c3a88c6SNikolai Kondrashov * Generate pen report descriptor 4572c3a88c6SNikolai Kondrashov */ 4582c3a88c6SNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 459a985de58SNikolai Kondrashov uclogic_rdesc_v2_pen_template_arr, 460a985de58SNikolai Kondrashov uclogic_rdesc_v2_pen_template_size, 4612c3a88c6SNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 4622c3a88c6SNikolai Kondrashov if (desc_ptr == NULL) { 4632c3a88c6SNikolai Kondrashov rc = -ENOMEM; 4642c3a88c6SNikolai Kondrashov goto cleanup; 4652c3a88c6SNikolai Kondrashov } 4662c3a88c6SNikolai Kondrashov 4672c3a88c6SNikolai Kondrashov /* 4682c3a88c6SNikolai Kondrashov * Fill-in the parameters 4692c3a88c6SNikolai Kondrashov */ 4702c3a88c6SNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 4712c3a88c6SNikolai Kondrashov pen->desc_ptr = desc_ptr; 4722c3a88c6SNikolai Kondrashov desc_ptr = NULL; 473a985de58SNikolai Kondrashov pen->desc_size = uclogic_rdesc_v2_pen_template_size; 474a985de58SNikolai Kondrashov pen->id = UCLOGIC_RDESC_V2_PEN_ID; 4752c3a88c6SNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; 4762c3a88c6SNikolai Kondrashov pen->fragmented_hires = true; 4771324c5acSNikolai Kondrashov pen->tilt_y_flipped = true; 4782c3a88c6SNikolai Kondrashov found = true; 479945d5dd5SNikolai Kondrashov if (pparams_ptr != NULL) { 480945d5dd5SNikolai Kondrashov *pparams_ptr = buf; 481945d5dd5SNikolai Kondrashov buf = NULL; 482945d5dd5SNikolai Kondrashov } 483945d5dd5SNikolai Kondrashov if (pparams_len != NULL) 484945d5dd5SNikolai Kondrashov *pparams_len = params_len; 485945d5dd5SNikolai Kondrashov 4862c3a88c6SNikolai Kondrashov finish: 4872c3a88c6SNikolai Kondrashov *pfound = found; 4882c3a88c6SNikolai Kondrashov rc = 0; 4892c3a88c6SNikolai Kondrashov cleanup: 4902c3a88c6SNikolai Kondrashov kfree(desc_ptr); 4912c3a88c6SNikolai Kondrashov kfree(buf); 4922c3a88c6SNikolai Kondrashov return rc; 4932c3a88c6SNikolai Kondrashov } 4942c3a88c6SNikolai Kondrashov 4952c3a88c6SNikolai Kondrashov /** 4969614219eSNikolai Kondrashov * uclogic_params_frame_cleanup - free resources used by struct 4979614219eSNikolai Kondrashov * uclogic_params_frame (tablet interface's frame controls input parameters). 4989614219eSNikolai Kondrashov * Can be called repeatedly. 4999614219eSNikolai Kondrashov * 5009614219eSNikolai Kondrashov * @frame: Frame controls input parameters to cleanup. Cannot be NULL. 5019614219eSNikolai Kondrashov */ 5029614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) 5039614219eSNikolai Kondrashov { 5049614219eSNikolai Kondrashov kfree(frame->desc_ptr); 5059614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 5069614219eSNikolai Kondrashov } 5079614219eSNikolai Kondrashov 5089614219eSNikolai Kondrashov /** 5099614219eSNikolai Kondrashov * uclogic_params_frame_init_with_desc() - initialize tablet's frame control 5109614219eSNikolai Kondrashov * parameters with a static report descriptor. 5119614219eSNikolai Kondrashov * 5129614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 5139614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 5149614219eSNikolai Kondrashov * of error. Cannot be NULL. 5159614219eSNikolai Kondrashov * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. 5169614219eSNikolai Kondrashov * @desc_size: Report descriptor size. 5179614219eSNikolai Kondrashov * @id: Report ID used for frame reports, if they should be tweaked, 5189614219eSNikolai Kondrashov * zero if not. 5199614219eSNikolai Kondrashov * 5209614219eSNikolai Kondrashov * Returns: 5219614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 5229614219eSNikolai Kondrashov */ 5239614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc( 5249614219eSNikolai Kondrashov struct uclogic_params_frame *frame, 5259614219eSNikolai Kondrashov const __u8 *desc_ptr, 5269614219eSNikolai Kondrashov size_t desc_size, 5279614219eSNikolai Kondrashov unsigned int id) 5289614219eSNikolai Kondrashov { 5299614219eSNikolai Kondrashov __u8 *copy_desc_ptr; 5309614219eSNikolai Kondrashov 5319614219eSNikolai Kondrashov if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) 5329614219eSNikolai Kondrashov return -EINVAL; 5339614219eSNikolai Kondrashov 5349614219eSNikolai Kondrashov copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 5359614219eSNikolai Kondrashov if (copy_desc_ptr == NULL) 5369614219eSNikolai Kondrashov return -ENOMEM; 5379614219eSNikolai Kondrashov 5389614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 5399614219eSNikolai Kondrashov frame->desc_ptr = copy_desc_ptr; 5409614219eSNikolai Kondrashov frame->desc_size = desc_size; 5419614219eSNikolai Kondrashov frame->id = id; 5429614219eSNikolai Kondrashov return 0; 5439614219eSNikolai Kondrashov } 5449614219eSNikolai Kondrashov 5459614219eSNikolai Kondrashov /** 5462e28f3e0SNikolai Kondrashov * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame 5472e28f3e0SNikolai Kondrashov * controls. 5489614219eSNikolai Kondrashov * 5499614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 5509614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 5519614219eSNikolai Kondrashov * of error, or if parameters are not found. Cannot be NULL. 5529614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 5539614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 5549614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 5559614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 5569614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 5579614219eSNikolai Kondrashov * 5589614219eSNikolai Kondrashov * Returns: 5599614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 5609614219eSNikolai Kondrashov */ 5612e28f3e0SNikolai Kondrashov static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame, 5629614219eSNikolai Kondrashov bool *pfound, 5639614219eSNikolai Kondrashov struct hid_device *hdev) 5649614219eSNikolai Kondrashov { 5659614219eSNikolai Kondrashov int rc; 5669614219eSNikolai Kondrashov bool found = false; 567aa320fdbSJosé Expósito struct usb_device *usb_dev; 5689614219eSNikolai Kondrashov char *str_buf = NULL; 5699614219eSNikolai Kondrashov const size_t str_len = 16; 5709614219eSNikolai Kondrashov 5719614219eSNikolai Kondrashov /* Check arguments */ 5729614219eSNikolai Kondrashov if (frame == NULL || pfound == NULL || hdev == NULL) { 5739614219eSNikolai Kondrashov rc = -EINVAL; 5749614219eSNikolai Kondrashov goto cleanup; 5759614219eSNikolai Kondrashov } 5769614219eSNikolai Kondrashov 577aa320fdbSJosé Expósito usb_dev = hid_to_usb_dev(hdev); 578aa320fdbSJosé Expósito 5799614219eSNikolai Kondrashov /* 5809614219eSNikolai Kondrashov * Enable generic button mode 5819614219eSNikolai Kondrashov */ 5829614219eSNikolai Kondrashov str_buf = kzalloc(str_len, GFP_KERNEL); 5839614219eSNikolai Kondrashov if (str_buf == NULL) { 5849614219eSNikolai Kondrashov rc = -ENOMEM; 5859614219eSNikolai Kondrashov goto cleanup; 5869614219eSNikolai Kondrashov } 5879614219eSNikolai Kondrashov 5889614219eSNikolai Kondrashov rc = usb_string(usb_dev, 123, str_buf, str_len); 5899614219eSNikolai Kondrashov if (rc == -EPIPE) { 5909614219eSNikolai Kondrashov hid_dbg(hdev, 5919614219eSNikolai Kondrashov "generic button -enabling string descriptor not found\n"); 5929614219eSNikolai Kondrashov } else if (rc < 0) { 5939614219eSNikolai Kondrashov goto cleanup; 5949614219eSNikolai Kondrashov } else if (strncmp(str_buf, "HK On", rc) != 0) { 5959614219eSNikolai Kondrashov hid_dbg(hdev, 5969614219eSNikolai Kondrashov "invalid response to enabling generic buttons: \"%s\"\n", 5979614219eSNikolai Kondrashov str_buf); 5989614219eSNikolai Kondrashov } else { 5999614219eSNikolai Kondrashov hid_dbg(hdev, "generic buttons enabled\n"); 6009614219eSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 6019614219eSNikolai Kondrashov frame, 602a985de58SNikolai Kondrashov uclogic_rdesc_v1_frame_arr, 603a985de58SNikolai Kondrashov uclogic_rdesc_v1_frame_size, 604a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID); 6059614219eSNikolai Kondrashov if (rc != 0) 6069614219eSNikolai Kondrashov goto cleanup; 6079614219eSNikolai Kondrashov found = true; 6089614219eSNikolai Kondrashov } 6099614219eSNikolai Kondrashov 6109614219eSNikolai Kondrashov *pfound = found; 6119614219eSNikolai Kondrashov rc = 0; 6129614219eSNikolai Kondrashov cleanup: 6139614219eSNikolai Kondrashov kfree(str_buf); 6149614219eSNikolai Kondrashov return rc; 6159614219eSNikolai Kondrashov } 6169614219eSNikolai Kondrashov 6179614219eSNikolai Kondrashov /** 6189614219eSNikolai Kondrashov * uclogic_params_cleanup - free resources used by struct uclogic_params 6199614219eSNikolai Kondrashov * (tablet interface's parameters). 6209614219eSNikolai Kondrashov * Can be called repeatedly. 6219614219eSNikolai Kondrashov * 6229614219eSNikolai Kondrashov * @params: Input parameters to cleanup. Cannot be NULL. 6239614219eSNikolai Kondrashov */ 6249614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params) 6259614219eSNikolai Kondrashov { 6269614219eSNikolai Kondrashov if (!params->invalid) { 627337fa051SNikolai Kondrashov size_t i; 6289614219eSNikolai Kondrashov kfree(params->desc_ptr); 6299614219eSNikolai Kondrashov uclogic_params_pen_cleanup(¶ms->pen); 630337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) 631337fa051SNikolai Kondrashov uclogic_params_frame_cleanup(¶ms->frame_list[i]); 632337fa051SNikolai Kondrashov 6339614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 6349614219eSNikolai Kondrashov } 6359614219eSNikolai Kondrashov } 6369614219eSNikolai Kondrashov 6379614219eSNikolai Kondrashov /** 6385abb5445SLee Jones * uclogic_params_get_desc() - Get a replacement report descriptor for a 6395abb5445SLee Jones * tablet's interface. 6409614219eSNikolai Kondrashov * 6419614219eSNikolai Kondrashov * @params: The parameters of a tablet interface to get report 6429614219eSNikolai Kondrashov * descriptor for. Cannot be NULL. 6439614219eSNikolai Kondrashov * @pdesc: Location for the resulting, kmalloc-allocated report 6449614219eSNikolai Kondrashov * descriptor pointer, or for NULL, if there's no replacement 6459614219eSNikolai Kondrashov * report descriptor. Not modified in case of error. Cannot be 6469614219eSNikolai Kondrashov * NULL. 6479614219eSNikolai Kondrashov * @psize: Location for the resulting report descriptor size, not set if 6489614219eSNikolai Kondrashov * there's no replacement report descriptor. Not modified in case 6499614219eSNikolai Kondrashov * of error. Cannot be NULL. 6509614219eSNikolai Kondrashov * 6519614219eSNikolai Kondrashov * Returns: 6529614219eSNikolai Kondrashov * Zero, if successful. 6539614219eSNikolai Kondrashov * -EINVAL, if invalid arguments are supplied. 6549614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 6559614219eSNikolai Kondrashov */ 6569614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params, 6579614219eSNikolai Kondrashov __u8 **pdesc, 6589614219eSNikolai Kondrashov unsigned int *psize) 6599614219eSNikolai Kondrashov { 660337fa051SNikolai Kondrashov int rc = -ENOMEM; 661337fa051SNikolai Kondrashov bool present = false; 662337fa051SNikolai Kondrashov unsigned int size = 0; 6639614219eSNikolai Kondrashov __u8 *desc = NULL; 664337fa051SNikolai Kondrashov size_t i; 6659614219eSNikolai Kondrashov 6669614219eSNikolai Kondrashov /* Check arguments */ 6679614219eSNikolai Kondrashov if (params == NULL || pdesc == NULL || psize == NULL) 6689614219eSNikolai Kondrashov return -EINVAL; 6699614219eSNikolai Kondrashov 670337fa051SNikolai Kondrashov /* Concatenate descriptors */ 671337fa051SNikolai Kondrashov #define ADD_DESC(_desc_ptr, _desc_size) \ 672337fa051SNikolai Kondrashov do { \ 673337fa051SNikolai Kondrashov unsigned int new_size; \ 674337fa051SNikolai Kondrashov __u8 *new_desc; \ 675337fa051SNikolai Kondrashov if ((_desc_ptr) == NULL) { \ 676337fa051SNikolai Kondrashov break; \ 677337fa051SNikolai Kondrashov } \ 678337fa051SNikolai Kondrashov new_size = size + (_desc_size); \ 679337fa051SNikolai Kondrashov new_desc = krealloc(desc, new_size, GFP_KERNEL); \ 680337fa051SNikolai Kondrashov if (new_desc == NULL) { \ 681337fa051SNikolai Kondrashov goto cleanup; \ 682337fa051SNikolai Kondrashov } \ 683337fa051SNikolai Kondrashov memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \ 684337fa051SNikolai Kondrashov desc = new_desc; \ 685337fa051SNikolai Kondrashov size = new_size; \ 686337fa051SNikolai Kondrashov present = true; \ 687337fa051SNikolai Kondrashov } while (0) 6889614219eSNikolai Kondrashov 689337fa051SNikolai Kondrashov ADD_DESC(params->desc_ptr, params->desc_size); 690337fa051SNikolai Kondrashov ADD_DESC(params->pen.desc_ptr, params->pen.desc_size); 691337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 692337fa051SNikolai Kondrashov ADD_DESC(params->frame_list[i].desc_ptr, 693337fa051SNikolai Kondrashov params->frame_list[i].desc_size); 6949614219eSNikolai Kondrashov } 6959614219eSNikolai Kondrashov 696337fa051SNikolai Kondrashov #undef ADD_DESC 6979614219eSNikolai Kondrashov 698337fa051SNikolai Kondrashov if (present) { 6999614219eSNikolai Kondrashov *pdesc = desc; 700337fa051SNikolai Kondrashov *psize = size; 701337fa051SNikolai Kondrashov desc = NULL; 702337fa051SNikolai Kondrashov } 703337fa051SNikolai Kondrashov rc = 0; 704337fa051SNikolai Kondrashov cleanup: 705337fa051SNikolai Kondrashov kfree(desc); 706337fa051SNikolai Kondrashov return rc; 7079614219eSNikolai Kondrashov } 7089614219eSNikolai Kondrashov 7099614219eSNikolai Kondrashov /** 7109614219eSNikolai Kondrashov * uclogic_params_init_invalid() - initialize tablet interface parameters, 7119614219eSNikolai Kondrashov * specifying the interface is invalid. 7129614219eSNikolai Kondrashov * 7139614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7149614219eSNikolai Kondrashov * uclogic_params_cleanup()). Cannot be NULL. 7159614219eSNikolai Kondrashov */ 7169614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params) 7179614219eSNikolai Kondrashov { 7189614219eSNikolai Kondrashov params->invalid = true; 7199614219eSNikolai Kondrashov } 7209614219eSNikolai Kondrashov 7219614219eSNikolai Kondrashov /** 7229614219eSNikolai Kondrashov * uclogic_params_init_with_opt_desc() - initialize tablet interface 7239614219eSNikolai Kondrashov * parameters with an optional replacement report descriptor. Only modify 7249614219eSNikolai Kondrashov * report descriptor, if the original report descriptor matches the expected 7259614219eSNikolai Kondrashov * size. 7269614219eSNikolai Kondrashov * 7279614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7289614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of 7299614219eSNikolai Kondrashov * error. Cannot be NULL. 7309614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface create the 7319614219eSNikolai Kondrashov * parameters for. Cannot be NULL. 7329614219eSNikolai Kondrashov * @orig_desc_size: Expected size of the original report descriptor to 7339614219eSNikolai Kondrashov * be replaced. 7349614219eSNikolai Kondrashov * @desc_ptr: Pointer to the replacement report descriptor. 7359614219eSNikolai Kondrashov * Can be NULL, if desc_size is zero. 7369614219eSNikolai Kondrashov * @desc_size: Size of the replacement report descriptor. 7379614219eSNikolai Kondrashov * 7389614219eSNikolai Kondrashov * Returns: 7399614219eSNikolai Kondrashov * Zero, if successful. -EINVAL if an invalid argument was passed. 7409614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 7419614219eSNikolai Kondrashov */ 7429614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, 7439614219eSNikolai Kondrashov struct hid_device *hdev, 7449614219eSNikolai Kondrashov unsigned int orig_desc_size, 7459614219eSNikolai Kondrashov __u8 *desc_ptr, 7469614219eSNikolai Kondrashov unsigned int desc_size) 7479614219eSNikolai Kondrashov { 7489614219eSNikolai Kondrashov __u8 *desc_copy_ptr = NULL; 7499614219eSNikolai Kondrashov unsigned int desc_copy_size; 7509614219eSNikolai Kondrashov int rc; 7519614219eSNikolai Kondrashov 7529614219eSNikolai Kondrashov /* Check arguments */ 7539614219eSNikolai Kondrashov if (params == NULL || hdev == NULL || 7549614219eSNikolai Kondrashov (desc_ptr == NULL && desc_size != 0)) { 7559614219eSNikolai Kondrashov rc = -EINVAL; 7569614219eSNikolai Kondrashov goto cleanup; 7579614219eSNikolai Kondrashov } 7589614219eSNikolai Kondrashov 7599614219eSNikolai Kondrashov /* Replace report descriptor, if it matches */ 7609614219eSNikolai Kondrashov if (hdev->dev_rsize == orig_desc_size) { 7619614219eSNikolai Kondrashov hid_dbg(hdev, 7629614219eSNikolai Kondrashov "device report descriptor matches the expected size, replacing\n"); 7639614219eSNikolai Kondrashov desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 7649614219eSNikolai Kondrashov if (desc_copy_ptr == NULL) { 7659614219eSNikolai Kondrashov rc = -ENOMEM; 7669614219eSNikolai Kondrashov goto cleanup; 7679614219eSNikolai Kondrashov } 7689614219eSNikolai Kondrashov desc_copy_size = desc_size; 7699614219eSNikolai Kondrashov } else { 7709614219eSNikolai Kondrashov hid_dbg(hdev, 7719614219eSNikolai Kondrashov "device report descriptor doesn't match the expected size (%u != %u), preserving\n", 7729614219eSNikolai Kondrashov hdev->dev_rsize, orig_desc_size); 7739614219eSNikolai Kondrashov desc_copy_ptr = NULL; 7749614219eSNikolai Kondrashov desc_copy_size = 0; 7759614219eSNikolai Kondrashov } 7769614219eSNikolai Kondrashov 7779614219eSNikolai Kondrashov /* Output parameters */ 7789614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 7799614219eSNikolai Kondrashov params->desc_ptr = desc_copy_ptr; 7809614219eSNikolai Kondrashov desc_copy_ptr = NULL; 7819614219eSNikolai Kondrashov params->desc_size = desc_copy_size; 7829614219eSNikolai Kondrashov 7839614219eSNikolai Kondrashov rc = 0; 7849614219eSNikolai Kondrashov cleanup: 7859614219eSNikolai Kondrashov kfree(desc_copy_ptr); 7869614219eSNikolai Kondrashov return rc; 7879614219eSNikolai Kondrashov } 7889614219eSNikolai Kondrashov 7899614219eSNikolai Kondrashov /** 7905abb5445SLee Jones * uclogic_params_huion_init() - initialize a Huion tablet interface and discover 7919614219eSNikolai Kondrashov * its parameters. 7929614219eSNikolai Kondrashov * 7939614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 7949614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 7959614219eSNikolai Kondrashov * Cannot be NULL. 7969614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 7979614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 7989614219eSNikolai Kondrashov * 7999614219eSNikolai Kondrashov * Returns: 8009614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 8019614219eSNikolai Kondrashov */ 8029614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params, 8039614219eSNikolai Kondrashov struct hid_device *hdev) 8049614219eSNikolai Kondrashov { 8059614219eSNikolai Kondrashov int rc; 806ff6b548aSJosé Expósito struct usb_device *udev; 807ff6b548aSJosé Expósito struct usb_interface *iface; 808ff6b548aSJosé Expósito __u8 bInterfaceNumber; 8099614219eSNikolai Kondrashov bool found; 8109614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 8119614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 8122c3a88c6SNikolai Kondrashov static const char transition_ver[] = "HUION_T153_160607"; 8132c3a88c6SNikolai Kondrashov char *ver_ptr = NULL; 8142c3a88c6SNikolai Kondrashov const size_t ver_len = sizeof(transition_ver) + 1; 815118dfdeaSNikolai Kondrashov __u8 *params_ptr = NULL; 816118dfdeaSNikolai Kondrashov size_t params_len = 0; 817118dfdeaSNikolai Kondrashov /* Parameters string descriptor of a model with touch ring (HS610) */ 818118dfdeaSNikolai Kondrashov const __u8 touch_ring_model_params_buf[] = { 819118dfdeaSNikolai Kondrashov 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, 820118dfdeaSNikolai Kondrashov 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, 821118dfdeaSNikolai Kondrashov 0x04, 0x3C, 0x3E 822118dfdeaSNikolai Kondrashov }; 8239614219eSNikolai Kondrashov 8249614219eSNikolai Kondrashov /* Check arguments */ 8259614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 8269614219eSNikolai Kondrashov rc = -EINVAL; 8279614219eSNikolai Kondrashov goto cleanup; 8289614219eSNikolai Kondrashov } 8299614219eSNikolai Kondrashov 830ff6b548aSJosé Expósito udev = hid_to_usb_dev(hdev); 831ff6b548aSJosé Expósito iface = to_usb_interface(hdev->dev.parent); 832ff6b548aSJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 833ff6b548aSJosé Expósito 834d64a6e44SNikolai Kondrashov /* If it's a custom keyboard interface */ 835d64a6e44SNikolai Kondrashov if (bInterfaceNumber == 1) { 8364c60bc7dSNikolai Kondrashov /* Keep everything intact, but mark pen usage invalid */ 8374c60bc7dSNikolai Kondrashov p.pen.usage_invalid = true; 838d64a6e44SNikolai Kondrashov goto output; 839d64a6e44SNikolai Kondrashov /* Else, if it's not a pen interface */ 840d64a6e44SNikolai Kondrashov } else if (bInterfaceNumber != 0) { 841606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 8429614219eSNikolai Kondrashov goto output; 8439614219eSNikolai Kondrashov } 8449614219eSNikolai Kondrashov 8452c3a88c6SNikolai Kondrashov /* Try to get firmware version */ 8462c3a88c6SNikolai Kondrashov ver_ptr = kzalloc(ver_len, GFP_KERNEL); 8472c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 8482c3a88c6SNikolai Kondrashov rc = -ENOMEM; 8492c3a88c6SNikolai Kondrashov goto cleanup; 8502c3a88c6SNikolai Kondrashov } 8512c3a88c6SNikolai Kondrashov rc = usb_string(udev, 201, ver_ptr, ver_len); 8522c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 8532c3a88c6SNikolai Kondrashov *ver_ptr = '\0'; 8542c3a88c6SNikolai Kondrashov } else if (rc < 0) { 8552c3a88c6SNikolai Kondrashov hid_err(hdev, 8562c3a88c6SNikolai Kondrashov "failed retrieving Huion firmware version: %d\n", rc); 8572c3a88c6SNikolai Kondrashov goto cleanup; 8582c3a88c6SNikolai Kondrashov } 8592c3a88c6SNikolai Kondrashov 8602c3a88c6SNikolai Kondrashov /* If this is a transition firmware */ 8612c3a88c6SNikolai Kondrashov if (strcmp(ver_ptr, transition_ver) == 0) { 8622c3a88c6SNikolai Kondrashov hid_dbg(hdev, 8632c3a88c6SNikolai Kondrashov "transition firmware detected, not probing pen v2 parameters\n"); 8642c3a88c6SNikolai Kondrashov } else { 8652c3a88c6SNikolai Kondrashov /* Try to probe v2 pen parameters */ 866945d5dd5SNikolai Kondrashov rc = uclogic_params_pen_init_v2(&p.pen, &found, 867118dfdeaSNikolai Kondrashov ¶ms_ptr, ¶ms_len, 868118dfdeaSNikolai Kondrashov hdev); 8692c3a88c6SNikolai Kondrashov if (rc != 0) { 8702c3a88c6SNikolai Kondrashov hid_err(hdev, 8712c3a88c6SNikolai Kondrashov "failed probing pen v2 parameters: %d\n", rc); 8722c3a88c6SNikolai Kondrashov goto cleanup; 8732c3a88c6SNikolai Kondrashov } else if (found) { 8742c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters found\n"); 875c3e6e59aSNikolai Kondrashov /* Create v2 frame button parameters */ 8762c3a88c6SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 877337fa051SNikolai Kondrashov &p.frame_list[0], 878c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_arr, 879c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_size, 880c3e6e59aSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); 8812c3a88c6SNikolai Kondrashov if (rc != 0) { 8822c3a88c6SNikolai Kondrashov hid_err(hdev, 883c3e6e59aSNikolai Kondrashov "failed creating v2 frame button parameters: %d\n", 8842c3a88c6SNikolai Kondrashov rc); 8852c3a88c6SNikolai Kondrashov goto cleanup; 8862c3a88c6SNikolai Kondrashov } 887c3e6e59aSNikolai Kondrashov 888118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 889118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 890118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].id = 891118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; 892118dfdeaSNikolai Kondrashov 893118dfdeaSNikolai Kondrashov /* If this is the model with touch ring */ 894118dfdeaSNikolai Kondrashov if (params_ptr != NULL && 895118dfdeaSNikolai Kondrashov params_len == sizeof(touch_ring_model_params_buf) && 896118dfdeaSNikolai Kondrashov memcmp(params_ptr, touch_ring_model_params_buf, 897118dfdeaSNikolai Kondrashov params_len) == 0) { 898118dfdeaSNikolai Kondrashov /* Create touch ring parameters */ 899c3e6e59aSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 900c3e6e59aSNikolai Kondrashov &p.frame_list[1], 901c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_arr, 902c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_size, 903caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 904c3e6e59aSNikolai Kondrashov if (rc != 0) { 905c3e6e59aSNikolai Kondrashov hid_err(hdev, 906c3e6e59aSNikolai Kondrashov "failed creating v2 frame touch ring parameters: %d\n", 907c3e6e59aSNikolai Kondrashov rc); 908c3e6e59aSNikolai Kondrashov goto cleanup; 909c3e6e59aSNikolai Kondrashov } 910c3e6e59aSNikolai Kondrashov p.frame_list[1].suffix = "Touch Ring"; 911c3e6e59aSNikolai Kondrashov p.frame_list[1].dev_id_byte = 912caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 913caf7e934SNikolai Kondrashov p.frame_list[1].touch_byte = 5; 914caf7e934SNikolai Kondrashov p.frame_list[1].touch_max = 12; 915fbc08b4eSNikolai Kondrashov p.frame_list[1].touch_flip_at = 7; 916118dfdeaSNikolai Kondrashov } else { 917118dfdeaSNikolai Kondrashov /* Create touch strip parameters */ 918118dfdeaSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 919118dfdeaSNikolai Kondrashov &p.frame_list[1], 920118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_arr, 921118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_size, 922118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 923118dfdeaSNikolai Kondrashov if (rc != 0) { 924118dfdeaSNikolai Kondrashov hid_err(hdev, 925118dfdeaSNikolai Kondrashov "failed creating v2 frame touch strip parameters: %d\n", 926118dfdeaSNikolai Kondrashov rc); 927118dfdeaSNikolai Kondrashov goto cleanup; 928118dfdeaSNikolai Kondrashov } 929118dfdeaSNikolai Kondrashov p.frame_list[1].suffix = "Touch Strip"; 930118dfdeaSNikolai Kondrashov p.frame_list[1].dev_id_byte = 931118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 932118dfdeaSNikolai Kondrashov p.frame_list[1].touch_byte = 5; 933118dfdeaSNikolai Kondrashov p.frame_list[1].touch_max = 8; 934118dfdeaSNikolai Kondrashov } 935118dfdeaSNikolai Kondrashov 936118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 937118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].value = 0xf0; 938118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].id = 939118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; 940c3e6e59aSNikolai Kondrashov 9416facd076SNikolai Kondrashov /* Create v2 frame dial parameters */ 9426facd076SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 9436facd076SNikolai Kondrashov &p.frame_list[2], 9446facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_arr, 9456facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_size, 9466facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID); 9476facd076SNikolai Kondrashov if (rc != 0) { 9486facd076SNikolai Kondrashov hid_err(hdev, 9496facd076SNikolai Kondrashov "failed creating v2 frame dial parameters: %d\n", 9506facd076SNikolai Kondrashov rc); 9516facd076SNikolai Kondrashov goto cleanup; 9526facd076SNikolai Kondrashov } 9536facd076SNikolai Kondrashov p.frame_list[2].suffix = "Dial"; 9546facd076SNikolai Kondrashov p.frame_list[2].dev_id_byte = 9556facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; 9566facd076SNikolai Kondrashov p.frame_list[2].bitmap_dial_byte = 5; 9576facd076SNikolai Kondrashov 958118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 9596facd076SNikolai Kondrashov p.pen.subreport_list[2].value = 0xf1; 9606facd076SNikolai Kondrashov p.pen.subreport_list[2].id = 9616facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID; 962118dfdeaSNikolai Kondrashov 9632c3a88c6SNikolai Kondrashov goto output; 9642c3a88c6SNikolai Kondrashov } 9652c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters not found\n"); 9662c3a88c6SNikolai Kondrashov } 9672c3a88c6SNikolai Kondrashov 968eecb5b84SNikolai Kondrashov /* Try to probe v1 pen parameters */ 969eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 9709614219eSNikolai Kondrashov if (rc != 0) { 9719614219eSNikolai Kondrashov hid_err(hdev, 972eecb5b84SNikolai Kondrashov "failed probing pen v1 parameters: %d\n", rc); 9739614219eSNikolai Kondrashov goto cleanup; 9749614219eSNikolai Kondrashov } else if (found) { 975eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters found\n"); 9762e28f3e0SNikolai Kondrashov /* Try to probe v1 frame */ 977337fa051SNikolai Kondrashov rc = uclogic_params_frame_init_v1(&p.frame_list[0], 9789614219eSNikolai Kondrashov &found, hdev); 9799614219eSNikolai Kondrashov if (rc != 0) { 9802e28f3e0SNikolai Kondrashov hid_err(hdev, "v1 frame probing failed: %d\n", rc); 9819614219eSNikolai Kondrashov goto cleanup; 9829614219eSNikolai Kondrashov } 9832e28f3e0SNikolai Kondrashov hid_dbg(hdev, "frame v1 parameters%s found\n", 9849614219eSNikolai Kondrashov (found ? "" : " not")); 9859614219eSNikolai Kondrashov if (found) { 9868b013098SNikolai Kondrashov /* Link frame button subreports from pen reports */ 987e6be956fSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 9888b013098SNikolai Kondrashov p.pen.subreport_list[0].id = 989a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID; 9909614219eSNikolai Kondrashov } 9919614219eSNikolai Kondrashov goto output; 9929614219eSNikolai Kondrashov } 993eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters not found\n"); 9949614219eSNikolai Kondrashov 9959614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 9969614219eSNikolai Kondrashov 9979614219eSNikolai Kondrashov output: 9989614219eSNikolai Kondrashov /* Output parameters */ 9999614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 10009614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 10019614219eSNikolai Kondrashov rc = 0; 10029614219eSNikolai Kondrashov cleanup: 1003118dfdeaSNikolai Kondrashov kfree(params_ptr); 10042c3a88c6SNikolai Kondrashov kfree(ver_ptr); 10059614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 10069614219eSNikolai Kondrashov return rc; 10079614219eSNikolai Kondrashov } 10089614219eSNikolai Kondrashov 10099614219eSNikolai Kondrashov /** 10109614219eSNikolai Kondrashov * uclogic_params_init() - initialize a tablet interface and discover its 10119614219eSNikolai Kondrashov * parameters. 10129614219eSNikolai Kondrashov * 10139614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 10149614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 10159614219eSNikolai Kondrashov * Cannot be NULL. 10169614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 10178547b778SNikolai Kondrashov * parameters from. Cannot be NULL. Must be using the USB low-level 10188547b778SNikolai Kondrashov * driver, i.e. be an actual USB tablet. 10199614219eSNikolai Kondrashov * 10209614219eSNikolai Kondrashov * Returns: 10219614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 10229614219eSNikolai Kondrashov */ 10239614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params, 10249614219eSNikolai Kondrashov struct hid_device *hdev) 10259614219eSNikolai Kondrashov { 10269614219eSNikolai Kondrashov int rc; 1027f364c571SJosé Expósito struct usb_device *udev; 1028f364c571SJosé Expósito __u8 bNumInterfaces; 1029f364c571SJosé Expósito struct usb_interface *iface; 1030f364c571SJosé Expósito __u8 bInterfaceNumber; 10319614219eSNikolai Kondrashov bool found; 10329614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 10339614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 10349614219eSNikolai Kondrashov 10359614219eSNikolai Kondrashov /* Check arguments */ 1036f83baa0cSGreg Kroah-Hartman if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { 10379614219eSNikolai Kondrashov rc = -EINVAL; 10389614219eSNikolai Kondrashov goto cleanup; 10399614219eSNikolai Kondrashov } 10409614219eSNikolai Kondrashov 1041f364c571SJosé Expósito udev = hid_to_usb_dev(hdev); 1042f364c571SJosé Expósito bNumInterfaces = udev->config->desc.bNumInterfaces; 1043f364c571SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 1044f364c571SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1045f364c571SJosé Expósito 10469614219eSNikolai Kondrashov /* 10479614219eSNikolai Kondrashov * Set replacement report descriptor if the original matches the 10489614219eSNikolai Kondrashov * specified size. Otherwise keep interface unchanged. 10499614219eSNikolai Kondrashov */ 10509614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 10519614219eSNikolai Kondrashov uclogic_params_init_with_opt_desc( \ 10529614219eSNikolai Kondrashov &p, hdev, \ 10539614219eSNikolai Kondrashov UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 10549614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_arr, \ 10559614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_size) 10569614219eSNikolai Kondrashov 10579614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \ 10589614219eSNikolai Kondrashov (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 10599614219eSNikolai Kondrashov 10609614219eSNikolai Kondrashov /* 10619614219eSNikolai Kondrashov * Handle specific interfaces for specific tablets. 10629614219eSNikolai Kondrashov * 10639614219eSNikolai Kondrashov * Observe the following logic: 10649614219eSNikolai Kondrashov * 10659614219eSNikolai Kondrashov * If the interface is recognized as producing certain useful input: 10669614219eSNikolai Kondrashov * Mark interface as valid. 10679614219eSNikolai Kondrashov * Output interface parameters. 10689614219eSNikolai Kondrashov * Else, if the interface is recognized as *not* producing any useful 10699614219eSNikolai Kondrashov * input: 10709614219eSNikolai Kondrashov * Mark interface as invalid. 10719614219eSNikolai Kondrashov * Else: 10729614219eSNikolai Kondrashov * Mark interface as valid. 10739614219eSNikolai Kondrashov * Output noop parameters. 10749614219eSNikolai Kondrashov * 10759614219eSNikolai Kondrashov * Rule of thumb: it is better to disable a broken interface than let 10769614219eSNikolai Kondrashov * it spew garbage input. 10779614219eSNikolai Kondrashov */ 10789614219eSNikolai Kondrashov 10799614219eSNikolai Kondrashov switch (VID_PID(hdev->vendor, hdev->product)) { 10809614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10819614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 10829614219eSNikolai Kondrashov rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 10839614219eSNikolai Kondrashov if (rc != 0) 10849614219eSNikolai Kondrashov goto cleanup; 10859614219eSNikolai Kondrashov break; 10869614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10879614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 10889614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 10899614219eSNikolai Kondrashov if (rc != 0) 10909614219eSNikolai Kondrashov goto cleanup; 10919614219eSNikolai Kondrashov break; 10929614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 10939614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 10949c17f735SNikolai Kondrashov if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { 10959c17f735SNikolai Kondrashov if (bInterfaceNumber == 0) { 10969c17f735SNikolai Kondrashov /* Try to probe v1 pen parameters */ 10979c17f735SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, 10989c17f735SNikolai Kondrashov &found, hdev); 10999c17f735SNikolai Kondrashov if (rc != 0) { 11009c17f735SNikolai Kondrashov hid_err(hdev, 11019c17f735SNikolai Kondrashov "pen probing failed: %d\n", 11029c17f735SNikolai Kondrashov rc); 11039c17f735SNikolai Kondrashov goto cleanup; 11049c17f735SNikolai Kondrashov } 11059c17f735SNikolai Kondrashov if (!found) { 11069c17f735SNikolai Kondrashov hid_warn(hdev, 11079c17f735SNikolai Kondrashov "pen parameters not found"); 11089c17f735SNikolai Kondrashov } 11099c17f735SNikolai Kondrashov } else { 11109c17f735SNikolai Kondrashov uclogic_params_init_invalid(&p); 11119c17f735SNikolai Kondrashov } 11129c17f735SNikolai Kondrashov } else { 11139614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 11149614219eSNikolai Kondrashov if (rc != 0) 11159614219eSNikolai Kondrashov goto cleanup; 11169c17f735SNikolai Kondrashov } 11179614219eSNikolai Kondrashov break; 11189614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11199614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 11209614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 11219614219eSNikolai Kondrashov if (rc != 0) 11229614219eSNikolai Kondrashov goto cleanup; 11239614219eSNikolai Kondrashov break; 11249614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11259614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 11269614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 11279614219eSNikolai Kondrashov if (rc != 0) 11289614219eSNikolai Kondrashov goto cleanup; 11299614219eSNikolai Kondrashov break; 11309614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11319614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 11329614219eSNikolai Kondrashov switch (bInterfaceNumber) { 11339614219eSNikolai Kondrashov case 0: 11349614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 11359614219eSNikolai Kondrashov if (rc != 0) 11369614219eSNikolai Kondrashov goto cleanup; 11379614219eSNikolai Kondrashov break; 11389614219eSNikolai Kondrashov case 1: 11399614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 11409614219eSNikolai Kondrashov if (rc != 0) 11419614219eSNikolai Kondrashov goto cleanup; 11429614219eSNikolai Kondrashov break; 11439614219eSNikolai Kondrashov case 2: 11449614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 11459614219eSNikolai Kondrashov if (rc != 0) 11469614219eSNikolai Kondrashov goto cleanup; 11479614219eSNikolai Kondrashov break; 11489614219eSNikolai Kondrashov } 11499614219eSNikolai Kondrashov break; 11509614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11519614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 11529614219eSNikolai Kondrashov /* 11539614219eSNikolai Kondrashov * If it is not a three-interface version, which is known to 11549614219eSNikolai Kondrashov * respond to initialization. 11559614219eSNikolai Kondrashov */ 11569614219eSNikolai Kondrashov if (bNumInterfaces != 3) { 11579614219eSNikolai Kondrashov switch (bInterfaceNumber) { 11589614219eSNikolai Kondrashov case 0: 11599614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG0, 11609614219eSNikolai Kondrashov twha60_fixed0); 11619614219eSNikolai Kondrashov if (rc != 0) 11629614219eSNikolai Kondrashov goto cleanup; 11639614219eSNikolai Kondrashov break; 11649614219eSNikolai Kondrashov case 1: 11659614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG1, 11669614219eSNikolai Kondrashov twha60_fixed1); 11679614219eSNikolai Kondrashov if (rc != 0) 11689614219eSNikolai Kondrashov goto cleanup; 11699614219eSNikolai Kondrashov break; 11709614219eSNikolai Kondrashov } 11719614219eSNikolai Kondrashov break; 11729614219eSNikolai Kondrashov } 1173df561f66SGustavo A. R. Silva fallthrough; 11749614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_HUION, 11759614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 1176315ffcc9SKyle Godbey case VID_PID(USB_VENDOR_ID_HUION, 117785e86071SNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET2): 11789614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11799614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 11809614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11819614219eSNikolai Kondrashov USB_DEVICE_ID_YIYNOVA_TABLET): 11829614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11839614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 11849614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11859614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 11869614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11879614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 11880c15efe9SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 11890c15efe9SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 11909614219eSNikolai Kondrashov rc = uclogic_params_huion_init(&p, hdev); 11919614219eSNikolai Kondrashov if (rc != 0) 11929614219eSNikolai Kondrashov goto cleanup; 11939614219eSNikolai Kondrashov break; 11949614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGTIZER, 11959614219eSNikolai Kondrashov USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 1196022fc531SMartijn van de Streek case VID_PID(USB_VENDOR_ID_UGTIZER, 1197022fc531SMartijn van de Streek USB_DEVICE_ID_UGTIZER_TABLET_GT5040): 1198c3e5a67cSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1199c3e5a67cSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): 1200492a9e9aSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1201492a9e9aSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 120288bb346dSWang Xuerui case VID_PID(USB_VENDOR_ID_UGEE, 120361b1db5aSRoman Romanenko USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06): 120461b1db5aSRoman Romanenko case VID_PID(USB_VENDOR_ID_UGEE, 120588bb346dSWang Xuerui USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): 12069614219eSNikolai Kondrashov /* If this is the pen interface */ 12079614219eSNikolai Kondrashov if (bInterfaceNumber == 1) { 1208eecb5b84SNikolai Kondrashov /* Probe v1 pen parameters */ 1209eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 12109614219eSNikolai Kondrashov if (rc != 0) { 12119614219eSNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 12129614219eSNikolai Kondrashov goto cleanup; 12139614219eSNikolai Kondrashov } 12149614219eSNikolai Kondrashov if (!found) { 12159614219eSNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 12169614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 12179614219eSNikolai Kondrashov } 12189614219eSNikolai Kondrashov } else { 1219606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 12209614219eSNikolai Kondrashov } 12219614219eSNikolai Kondrashov break; 12221ee7c685SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 122308367be1SNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): 122408367be1SNikolai Kondrashov /* If this is the pen and frame interface */ 122508367be1SNikolai Kondrashov if (bInterfaceNumber == 1) { 122608367be1SNikolai Kondrashov /* Probe v1 pen parameters */ 122708367be1SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 122808367be1SNikolai Kondrashov if (rc != 0) { 122908367be1SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 123008367be1SNikolai Kondrashov goto cleanup; 123108367be1SNikolai Kondrashov } 123208367be1SNikolai Kondrashov /* Initialize frame parameters */ 123308367be1SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1234337fa051SNikolai Kondrashov &p.frame_list[0], 123508367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_arr, 123608367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_size, 123708367be1SNikolai Kondrashov 0); 123808367be1SNikolai Kondrashov if (rc != 0) 123908367be1SNikolai Kondrashov goto cleanup; 124008367be1SNikolai Kondrashov } else { 1241606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 124208367be1SNikolai Kondrashov } 124308367be1SNikolai Kondrashov break; 1244f7271b2aSCristian Klein case VID_PID(USB_VENDOR_ID_TRUST, 1245f7271b2aSCristian Klein USB_DEVICE_ID_TRUST_PANORA_TABLET): 124608367be1SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1247e902ed93SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_G5): 1248e902ed93SNikolai Kondrashov /* Ignore non-pen interfaces */ 1249e902ed93SNikolai Kondrashov if (bInterfaceNumber != 1) { 1250e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1251e902ed93SNikolai Kondrashov break; 1252e902ed93SNikolai Kondrashov } 1253e902ed93SNikolai Kondrashov 1254e902ed93SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 1255e902ed93SNikolai Kondrashov if (rc != 0) { 1256e902ed93SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 1257e902ed93SNikolai Kondrashov goto cleanup; 1258e902ed93SNikolai Kondrashov } else if (found) { 1259e902ed93SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1260337fa051SNikolai Kondrashov &p.frame_list[0], 1261e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_arr, 1262e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_size, 1263e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_ID); 1264e902ed93SNikolai Kondrashov if (rc != 0) { 1265e902ed93SNikolai Kondrashov hid_err(hdev, 12662e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 1267e902ed93SNikolai Kondrashov rc); 1268e902ed93SNikolai Kondrashov goto cleanup; 1269e902ed93SNikolai Kondrashov } 1270337fa051SNikolai Kondrashov p.frame_list[0].re_lsb = 1271e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; 1272337fa051SNikolai Kondrashov p.frame_list[0].dev_id_byte = 1273e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; 1274e902ed93SNikolai Kondrashov } else { 1275e902ed93SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 1276e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1277e902ed93SNikolai Kondrashov } 1278e902ed93SNikolai Kondrashov 1279e902ed93SNikolai Kondrashov break; 1280e902ed93SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 12811ee7c685SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_EX07S): 12821ee7c685SNikolai Kondrashov /* Ignore non-pen interfaces */ 12831ee7c685SNikolai Kondrashov if (bInterfaceNumber != 1) { 12841ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 12851ee7c685SNikolai Kondrashov break; 12861ee7c685SNikolai Kondrashov } 12871ee7c685SNikolai Kondrashov 12881ee7c685SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 12891ee7c685SNikolai Kondrashov if (rc != 0) { 12901ee7c685SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 12911ee7c685SNikolai Kondrashov goto cleanup; 12921ee7c685SNikolai Kondrashov } else if (found) { 12931ee7c685SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1294337fa051SNikolai Kondrashov &p.frame_list[0], 12952e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_arr, 12962e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_size, 12971ee7c685SNikolai Kondrashov 0); 12981ee7c685SNikolai Kondrashov if (rc != 0) { 12991ee7c685SNikolai Kondrashov hid_err(hdev, 13002e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 13011ee7c685SNikolai Kondrashov rc); 13021ee7c685SNikolai Kondrashov goto cleanup; 13031ee7c685SNikolai Kondrashov } 13041ee7c685SNikolai Kondrashov } else { 13051ee7c685SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 13061ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 13071ee7c685SNikolai Kondrashov } 13081ee7c685SNikolai Kondrashov 13091ee7c685SNikolai Kondrashov break; 13109614219eSNikolai Kondrashov } 13119614219eSNikolai Kondrashov 13129614219eSNikolai Kondrashov #undef VID_PID 13139614219eSNikolai Kondrashov #undef WITH_OPT_DESC 13149614219eSNikolai Kondrashov 13159614219eSNikolai Kondrashov /* Output parameters */ 13169614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 13179614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 13189614219eSNikolai Kondrashov rc = 0; 13199614219eSNikolai Kondrashov cleanup: 13209614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 13219614219eSNikolai Kondrashov return rc; 13229614219eSNikolai Kondrashov } 1323