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> 21f9ce4db0SJosé Expósito #include <linux/string.h> 229614219eSNikolai Kondrashov #include <asm/unaligned.h> 239614219eSNikolai Kondrashov 249614219eSNikolai Kondrashov /** 255abb5445SLee Jones * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type 265abb5445SLee Jones * to a string. 279614219eSNikolai Kondrashov * @inrange: The in-range reporting type to convert. 289614219eSNikolai Kondrashov * 29d5e649a5SBagas Sanjaya * Return: 30d5e649a5SBagas Sanjaya * * The string representing the type, or 31d5e649a5SBagas Sanjaya * * %NULL if the type is unknown. 329614219eSNikolai Kondrashov */ 33a228809fSNikolai Kondrashov static const char *uclogic_params_pen_inrange_to_str( 349614219eSNikolai Kondrashov enum uclogic_params_pen_inrange inrange) 359614219eSNikolai Kondrashov { 369614219eSNikolai Kondrashov switch (inrange) { 379614219eSNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: 389614219eSNikolai Kondrashov return "normal"; 399614219eSNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: 409614219eSNikolai Kondrashov return "inverted"; 4101309e29SNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_NONE: 4201309e29SNikolai Kondrashov return "none"; 439614219eSNikolai Kondrashov default: 449614219eSNikolai Kondrashov return NULL; 459614219eSNikolai Kondrashov } 469614219eSNikolai Kondrashov } 479614219eSNikolai Kondrashov 489614219eSNikolai Kondrashov /** 49d5e649a5SBagas Sanjaya * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters 50a228809fSNikolai Kondrashov * @hdev: The HID device the pen parameters describe. 51a228809fSNikolai Kondrashov * @pen: The pen parameters to dump. 52d5e649a5SBagas Sanjaya * 53d5e649a5SBagas Sanjaya * Dump tablet interface pen parameters with hid_dbg(). The dump is indented 54d5e649a5SBagas Sanjaya * with a tab. 55a228809fSNikolai Kondrashov */ 56a228809fSNikolai Kondrashov static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, 57a228809fSNikolai Kondrashov const struct uclogic_params_pen *pen) 58a228809fSNikolai Kondrashov { 59a228809fSNikolai Kondrashov size_t i; 60a228809fSNikolai Kondrashov 61a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.usage_invalid = %s\n", 62a228809fSNikolai Kondrashov (pen->usage_invalid ? "true" : "false")); 63a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr); 64a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size); 65a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.id = %u\n", pen->id); 66a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.subreport_list = {\n"); 67a228809fSNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) { 68a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n", 69a228809fSNikolai Kondrashov pen->subreport_list[i].value, 70a228809fSNikolai Kondrashov pen->subreport_list[i].id, 71a228809fSNikolai Kondrashov i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : ""); 72a228809fSNikolai Kondrashov } 73a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}\n"); 74a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.inrange = %s\n", 75a228809fSNikolai Kondrashov uclogic_params_pen_inrange_to_str(pen->inrange)); 76a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.fragmented_hires = %s\n", 77a228809fSNikolai Kondrashov (pen->fragmented_hires ? "true" : "false")); 78a228809fSNikolai Kondrashov hid_dbg(hdev, "\t.tilt_y_flipped = %s\n", 79a228809fSNikolai Kondrashov (pen->tilt_y_flipped ? "true" : "false")); 80a228809fSNikolai Kondrashov } 81a228809fSNikolai Kondrashov 82a228809fSNikolai Kondrashov /** 83d5e649a5SBagas Sanjaya * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters 84a228809fSNikolai Kondrashov * @hdev: The HID device the pen parameters describe. 85a228809fSNikolai Kondrashov * @frame: The frame parameters to dump. 86d5e649a5SBagas Sanjaya * 87d5e649a5SBagas Sanjaya * Dump tablet interface frame parameters with hid_dbg(). The dump is 88d5e649a5SBagas Sanjaya * indented with two tabs. 89a228809fSNikolai Kondrashov */ 90a228809fSNikolai Kondrashov static void uclogic_params_frame_hid_dbg( 91a228809fSNikolai Kondrashov const struct hid_device *hdev, 92a228809fSNikolai Kondrashov const struct uclogic_params_frame *frame) 93a228809fSNikolai Kondrashov { 94a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr); 95a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size); 96a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.id = %u\n", frame->id); 97a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix); 98a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb); 99a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte); 100caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte); 101caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max); 102caf7e934SNikolai Kondrashov hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n", 103caf7e934SNikolai Kondrashov frame->touch_flip_at); 104a228809fSNikolai Kondrashov hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n", 105a228809fSNikolai Kondrashov frame->bitmap_dial_byte); 106a228809fSNikolai Kondrashov } 107a228809fSNikolai Kondrashov 108a228809fSNikolai Kondrashov /** 109d5e649a5SBagas Sanjaya * uclogic_params_hid_dbg() - Dump tablet interface parameters 110a228809fSNikolai Kondrashov * @hdev: The HID device the parameters describe. 111a228809fSNikolai Kondrashov * @params: The parameters to dump. 112d5e649a5SBagas Sanjaya * 113d5e649a5SBagas Sanjaya * Dump tablet interface parameters with hid_dbg(). 114a228809fSNikolai Kondrashov */ 115a228809fSNikolai Kondrashov void uclogic_params_hid_dbg(const struct hid_device *hdev, 116a228809fSNikolai Kondrashov const struct uclogic_params *params) 117a228809fSNikolai Kondrashov { 118a228809fSNikolai Kondrashov size_t i; 119a228809fSNikolai Kondrashov 120a228809fSNikolai Kondrashov hid_dbg(hdev, ".invalid = %s\n", 121a228809fSNikolai Kondrashov params->invalid ? "true" : "false"); 122a228809fSNikolai Kondrashov hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr); 123a228809fSNikolai Kondrashov hid_dbg(hdev, ".desc_size = %u\n", params->desc_size); 124a228809fSNikolai Kondrashov hid_dbg(hdev, ".pen = {\n"); 125a228809fSNikolai Kondrashov uclogic_params_pen_hid_dbg(hdev, ¶ms->pen); 126a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}\n"); 127a228809fSNikolai Kondrashov hid_dbg(hdev, ".frame_list = {\n"); 128a228809fSNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 129a228809fSNikolai Kondrashov hid_dbg(hdev, "\t{\n"); 130a228809fSNikolai Kondrashov uclogic_params_frame_hid_dbg(hdev, ¶ms->frame_list[i]); 131a228809fSNikolai Kondrashov hid_dbg(hdev, "\t}%s\n", 132a228809fSNikolai Kondrashov i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : ""); 133a228809fSNikolai Kondrashov } 134a228809fSNikolai Kondrashov hid_dbg(hdev, "}\n"); 135a228809fSNikolai Kondrashov } 136a228809fSNikolai Kondrashov 137a228809fSNikolai Kondrashov /** 1389614219eSNikolai Kondrashov * uclogic_params_get_str_desc - retrieve a string descriptor from a HID 1399614219eSNikolai Kondrashov * device interface, putting it into a kmalloc-allocated buffer as is, without 1409614219eSNikolai Kondrashov * character encoding conversion. 1419614219eSNikolai Kondrashov * 1429614219eSNikolai Kondrashov * @pbuf: Location for the kmalloc-allocated buffer pointer containing 1439614219eSNikolai Kondrashov * the retrieved descriptor. Not modified in case of error. 1449614219eSNikolai Kondrashov * Can be NULL to have retrieved descriptor discarded. 1459614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to retrieve the string 1469614219eSNikolai Kondrashov * descriptor from. Cannot be NULL. 1479614219eSNikolai Kondrashov * @idx: Index of the string descriptor to request from the device. 1489614219eSNikolai Kondrashov * @len: Length of the buffer to allocate and the data to retrieve. 1499614219eSNikolai Kondrashov * 1509614219eSNikolai Kondrashov * Returns: 1519614219eSNikolai Kondrashov * number of bytes retrieved (<= len), 1529614219eSNikolai Kondrashov * -EPIPE, if the descriptor was not found, or 1539614219eSNikolai Kondrashov * another negative errno code in case of other error. 1549614219eSNikolai Kondrashov */ 1559614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, 1569614219eSNikolai Kondrashov __u8 idx, size_t len) 1579614219eSNikolai Kondrashov { 1589614219eSNikolai Kondrashov int rc; 1590a94131dSJosé Expósito struct usb_device *udev; 1609614219eSNikolai Kondrashov __u8 *buf = NULL; 1619614219eSNikolai Kondrashov 1629614219eSNikolai Kondrashov /* Check arguments */ 1639614219eSNikolai Kondrashov if (hdev == NULL) { 1649614219eSNikolai Kondrashov rc = -EINVAL; 1659614219eSNikolai Kondrashov goto cleanup; 1669614219eSNikolai Kondrashov } 1679614219eSNikolai Kondrashov 1680a94131dSJosé Expósito udev = hid_to_usb_dev(hdev); 1690a94131dSJosé Expósito 1709614219eSNikolai Kondrashov buf = kmalloc(len, GFP_KERNEL); 1719614219eSNikolai Kondrashov if (buf == NULL) { 1729614219eSNikolai Kondrashov rc = -ENOMEM; 1739614219eSNikolai Kondrashov goto cleanup; 1749614219eSNikolai Kondrashov } 1759614219eSNikolai Kondrashov 1769614219eSNikolai Kondrashov rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1779614219eSNikolai Kondrashov USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, 1789614219eSNikolai Kondrashov (USB_DT_STRING << 8) + idx, 1799614219eSNikolai Kondrashov 0x0409, buf, len, 1809614219eSNikolai Kondrashov USB_CTRL_GET_TIMEOUT); 1819614219eSNikolai Kondrashov if (rc == -EPIPE) { 1829614219eSNikolai Kondrashov hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); 1839614219eSNikolai Kondrashov goto cleanup; 1849614219eSNikolai Kondrashov } else if (rc < 0) { 1859614219eSNikolai Kondrashov hid_err(hdev, 186a876e7e2STom Rix "failed retrieving string descriptor #%u: %d\n", 1879614219eSNikolai Kondrashov idx, rc); 1889614219eSNikolai Kondrashov goto cleanup; 1899614219eSNikolai Kondrashov } 1909614219eSNikolai Kondrashov 1919614219eSNikolai Kondrashov if (pbuf != NULL) { 1929614219eSNikolai Kondrashov *pbuf = buf; 1939614219eSNikolai Kondrashov buf = NULL; 1949614219eSNikolai Kondrashov } 1959614219eSNikolai Kondrashov 1969614219eSNikolai Kondrashov cleanup: 1979614219eSNikolai Kondrashov kfree(buf); 1989614219eSNikolai Kondrashov return rc; 1999614219eSNikolai Kondrashov } 2009614219eSNikolai Kondrashov 2019614219eSNikolai Kondrashov /** 2029614219eSNikolai Kondrashov * uclogic_params_pen_cleanup - free resources used by struct 2039614219eSNikolai Kondrashov * uclogic_params_pen (tablet interface's pen input parameters). 2049614219eSNikolai Kondrashov * Can be called repeatedly. 2059614219eSNikolai Kondrashov * 2069614219eSNikolai Kondrashov * @pen: Pen input parameters to cleanup. Cannot be NULL. 2079614219eSNikolai Kondrashov */ 2089614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) 2099614219eSNikolai Kondrashov { 2109614219eSNikolai Kondrashov kfree(pen->desc_ptr); 2119614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 2129614219eSNikolai Kondrashov } 2139614219eSNikolai Kondrashov 2149614219eSNikolai Kondrashov /** 215eecb5b84SNikolai Kondrashov * uclogic_params_pen_init_v1() - initialize tablet interface pen 216eecb5b84SNikolai Kondrashov * input and retrieve its parameters from the device, using v1 protocol. 2179614219eSNikolai Kondrashov * 2189614219eSNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 2199614219eSNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not modified in 2209614219eSNikolai Kondrashov * case of error, or if parameters are not found. Cannot be NULL. 2219614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 2229614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 2239614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 2249614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 2259614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 2269614219eSNikolai Kondrashov * 2279614219eSNikolai Kondrashov * Returns: 2289614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 2299614219eSNikolai Kondrashov */ 230eecb5b84SNikolai Kondrashov static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, 2319614219eSNikolai Kondrashov bool *pfound, 2329614219eSNikolai Kondrashov struct hid_device *hdev) 2339614219eSNikolai Kondrashov { 2349614219eSNikolai Kondrashov int rc; 2359614219eSNikolai Kondrashov bool found = false; 2369614219eSNikolai Kondrashov /* Buffer for (part of) the string descriptor */ 2379614219eSNikolai Kondrashov __u8 *buf = NULL; 2389614219eSNikolai Kondrashov /* Minimum descriptor length required, maximum seen so far is 18 */ 2399614219eSNikolai Kondrashov const int len = 12; 2409614219eSNikolai Kondrashov s32 resolution; 2419614219eSNikolai Kondrashov /* Pen report descriptor template parameters */ 24276e645beSJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 2439614219eSNikolai Kondrashov __u8 *desc_ptr = NULL; 2449614219eSNikolai Kondrashov 2459614219eSNikolai Kondrashov /* Check arguments */ 2469614219eSNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 2479614219eSNikolai Kondrashov rc = -EINVAL; 2489614219eSNikolai Kondrashov goto cleanup; 2499614219eSNikolai Kondrashov } 2509614219eSNikolai Kondrashov 2519614219eSNikolai Kondrashov /* 2529614219eSNikolai Kondrashov * Read string descriptor containing pen input parameters. 2539614219eSNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 2549614219eSNikolai Kondrashov * the Windows driver traffic. 2559614219eSNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 2569614219eSNikolai Kondrashov */ 2579614219eSNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); 2589614219eSNikolai Kondrashov if (rc == -EPIPE) { 2599614219eSNikolai Kondrashov hid_dbg(hdev, 2609614219eSNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 2619614219eSNikolai Kondrashov goto finish; 2629614219eSNikolai Kondrashov } else if (rc < 0) { 2639614219eSNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 2649614219eSNikolai Kondrashov goto cleanup; 2659614219eSNikolai Kondrashov } else if (rc != len) { 2669614219eSNikolai Kondrashov hid_dbg(hdev, 2679614219eSNikolai Kondrashov "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", 2689614219eSNikolai Kondrashov rc, len); 2699614219eSNikolai Kondrashov goto finish; 2709614219eSNikolai Kondrashov } 2719614219eSNikolai Kondrashov 2729614219eSNikolai Kondrashov /* 2739614219eSNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 2749614219eSNikolai Kondrashov */ 2759614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 2769614219eSNikolai Kondrashov get_unaligned_le16(buf + 2); 2779614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 2789614219eSNikolai Kondrashov get_unaligned_le16(buf + 4); 2799614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 2809614219eSNikolai Kondrashov get_unaligned_le16(buf + 8); 2819614219eSNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 2829614219eSNikolai Kondrashov if (resolution == 0) { 2839614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 2849614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 2859614219eSNikolai Kondrashov } else { 2869614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 2879614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 2889614219eSNikolai Kondrashov resolution; 2899614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 2909614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 2919614219eSNikolai Kondrashov resolution; 2929614219eSNikolai Kondrashov } 2939614219eSNikolai Kondrashov kfree(buf); 2949614219eSNikolai Kondrashov buf = NULL; 2959614219eSNikolai Kondrashov 2969614219eSNikolai Kondrashov /* 2979614219eSNikolai Kondrashov * Generate pen report descriptor 2989614219eSNikolai Kondrashov */ 2999614219eSNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 300a985de58SNikolai Kondrashov uclogic_rdesc_v1_pen_template_arr, 301a985de58SNikolai Kondrashov uclogic_rdesc_v1_pen_template_size, 3029614219eSNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 3039614219eSNikolai Kondrashov if (desc_ptr == NULL) { 3049614219eSNikolai Kondrashov rc = -ENOMEM; 3059614219eSNikolai Kondrashov goto cleanup; 3069614219eSNikolai Kondrashov } 3079614219eSNikolai Kondrashov 3089614219eSNikolai Kondrashov /* 3099614219eSNikolai Kondrashov * Fill-in the parameters 3109614219eSNikolai Kondrashov */ 3119614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 3129614219eSNikolai Kondrashov pen->desc_ptr = desc_ptr; 3139614219eSNikolai Kondrashov desc_ptr = NULL; 314a985de58SNikolai Kondrashov pen->desc_size = uclogic_rdesc_v1_pen_template_size; 315a985de58SNikolai Kondrashov pen->id = UCLOGIC_RDESC_V1_PEN_ID; 3169614219eSNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; 3179614219eSNikolai Kondrashov found = true; 3189614219eSNikolai Kondrashov finish: 3199614219eSNikolai Kondrashov *pfound = found; 3209614219eSNikolai Kondrashov rc = 0; 3219614219eSNikolai Kondrashov cleanup: 3229614219eSNikolai Kondrashov kfree(desc_ptr); 3239614219eSNikolai Kondrashov kfree(buf); 3249614219eSNikolai Kondrashov return rc; 3259614219eSNikolai Kondrashov } 3269614219eSNikolai Kondrashov 3279614219eSNikolai Kondrashov /** 3282c3a88c6SNikolai Kondrashov * uclogic_params_get_le24() - get a 24-bit little-endian number from a 3292c3a88c6SNikolai Kondrashov * buffer. 3302c3a88c6SNikolai Kondrashov * 3312c3a88c6SNikolai Kondrashov * @p: The pointer to the number buffer. 3322c3a88c6SNikolai Kondrashov * 3332c3a88c6SNikolai Kondrashov * Returns: 3342c3a88c6SNikolai Kondrashov * The retrieved number 3352c3a88c6SNikolai Kondrashov */ 3362c3a88c6SNikolai Kondrashov static s32 uclogic_params_get_le24(const void *p) 3372c3a88c6SNikolai Kondrashov { 3382c3a88c6SNikolai Kondrashov const __u8 *b = p; 3392c3a88c6SNikolai Kondrashov return b[0] | (b[1] << 8UL) | (b[2] << 16UL); 3402c3a88c6SNikolai Kondrashov } 3412c3a88c6SNikolai Kondrashov 3422c3a88c6SNikolai Kondrashov /** 3432c3a88c6SNikolai Kondrashov * uclogic_params_pen_init_v2() - initialize tablet interface pen 3442c3a88c6SNikolai Kondrashov * input and retrieve its parameters from the device, using v2 protocol. 3452c3a88c6SNikolai Kondrashov * 3462c3a88c6SNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 347945d5dd5SNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not 348945d5dd5SNikolai Kondrashov * modified in case of error, or if parameters are not 349945d5dd5SNikolai Kondrashov * found. Cannot be NULL. 350945d5dd5SNikolai Kondrashov * @pfound: Location for a flag which is set to true if the 351945d5dd5SNikolai Kondrashov * parameters were found, and to false if not (e.g. 352945d5dd5SNikolai Kondrashov * device was incompatible). Not modified in case of 353945d5dd5SNikolai Kondrashov * error. Cannot be NULL. 354945d5dd5SNikolai Kondrashov * @pparams_ptr: Location for a kmalloc'ed pointer to the retrieved raw 355945d5dd5SNikolai Kondrashov * parameters, which could be used to identify the tablet 356945d5dd5SNikolai Kondrashov * to some extent. Should be freed with kfree after use. 357945d5dd5SNikolai Kondrashov * NULL, if not needed. Not modified in case of error. 358945d5dd5SNikolai Kondrashov * Only set if *pfound is set to true. 359945d5dd5SNikolai Kondrashov * @pparams_len: Location for the length of the retrieved raw 360945d5dd5SNikolai Kondrashov * parameters. NULL, if not needed. Not modified in case 361945d5dd5SNikolai Kondrashov * of error. Only set if *pfound is set to true. 362945d5dd5SNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize 363945d5dd5SNikolai Kondrashov * and get parameters from. Cannot be NULL. 3642c3a88c6SNikolai Kondrashov * 3652c3a88c6SNikolai Kondrashov * Returns: 3662c3a88c6SNikolai Kondrashov * Zero, if successful. A negative errno code on error. 3672c3a88c6SNikolai Kondrashov */ 3682c3a88c6SNikolai Kondrashov static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, 3692c3a88c6SNikolai Kondrashov bool *pfound, 370945d5dd5SNikolai Kondrashov __u8 **pparams_ptr, 371945d5dd5SNikolai Kondrashov size_t *pparams_len, 3722c3a88c6SNikolai Kondrashov struct hid_device *hdev) 3732c3a88c6SNikolai Kondrashov { 3742c3a88c6SNikolai Kondrashov int rc; 3752c3a88c6SNikolai Kondrashov bool found = false; 376945d5dd5SNikolai Kondrashov /* Buffer for (part of) the parameter string descriptor */ 3772c3a88c6SNikolai Kondrashov __u8 *buf = NULL; 378945d5dd5SNikolai Kondrashov /* Parameter string descriptor required length */ 379945d5dd5SNikolai Kondrashov const int params_len_min = 18; 380945d5dd5SNikolai Kondrashov /* Parameter string descriptor accepted length */ 381945d5dd5SNikolai Kondrashov const int params_len_max = 32; 382945d5dd5SNikolai Kondrashov /* Parameter string descriptor received length */ 383945d5dd5SNikolai Kondrashov int params_len; 384945d5dd5SNikolai Kondrashov size_t i; 3852c3a88c6SNikolai Kondrashov s32 resolution; 3862c3a88c6SNikolai Kondrashov /* Pen report descriptor template parameters */ 38776e645beSJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 3882c3a88c6SNikolai Kondrashov __u8 *desc_ptr = NULL; 3892c3a88c6SNikolai Kondrashov 3902c3a88c6SNikolai Kondrashov /* Check arguments */ 3912c3a88c6SNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 3922c3a88c6SNikolai Kondrashov rc = -EINVAL; 3932c3a88c6SNikolai Kondrashov goto cleanup; 3942c3a88c6SNikolai Kondrashov } 3952c3a88c6SNikolai Kondrashov 3962c3a88c6SNikolai Kondrashov /* 3972c3a88c6SNikolai Kondrashov * Read string descriptor containing pen input parameters. 3982c3a88c6SNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 3992c3a88c6SNikolai Kondrashov * the Windows driver traffic. 4002c3a88c6SNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 4012c3a88c6SNikolai Kondrashov */ 402945d5dd5SNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max); 4032c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 4042c3a88c6SNikolai Kondrashov hid_dbg(hdev, 4052c3a88c6SNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 4062c3a88c6SNikolai Kondrashov goto finish; 4072c3a88c6SNikolai Kondrashov } else if (rc < 0) { 4082c3a88c6SNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 4092c3a88c6SNikolai Kondrashov goto cleanup; 410945d5dd5SNikolai Kondrashov } else if (rc < params_len_min) { 4112c3a88c6SNikolai Kondrashov hid_dbg(hdev, 412945d5dd5SNikolai Kondrashov "string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n", 413945d5dd5SNikolai Kondrashov rc, params_len_min); 4142c3a88c6SNikolai Kondrashov goto finish; 415945d5dd5SNikolai Kondrashov } 416945d5dd5SNikolai Kondrashov 417945d5dd5SNikolai Kondrashov params_len = rc; 418945d5dd5SNikolai Kondrashov 4192c3a88c6SNikolai Kondrashov /* 4202c3a88c6SNikolai Kondrashov * Check it's not just a catch-all UTF-16LE-encoded ASCII 4212c3a88c6SNikolai Kondrashov * string (such as the model name) some tablets put into all 4222c3a88c6SNikolai Kondrashov * unknown string descriptors. 4232c3a88c6SNikolai Kondrashov */ 4242c3a88c6SNikolai Kondrashov for (i = 2; 425945d5dd5SNikolai Kondrashov i < params_len && 4262c3a88c6SNikolai Kondrashov (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); 4272c3a88c6SNikolai Kondrashov i += 2); 428945d5dd5SNikolai Kondrashov if (i >= params_len) { 4292c3a88c6SNikolai Kondrashov hid_dbg(hdev, 4302c3a88c6SNikolai Kondrashov "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); 4312c3a88c6SNikolai Kondrashov goto finish; 4322c3a88c6SNikolai Kondrashov } 4332c3a88c6SNikolai Kondrashov 4342c3a88c6SNikolai Kondrashov /* 4352c3a88c6SNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 4362c3a88c6SNikolai Kondrashov */ 4372c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 4382c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 2); 4392c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 4402c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 5); 4412c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 4422c3a88c6SNikolai Kondrashov get_unaligned_le16(buf + 8); 4432c3a88c6SNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 4442c3a88c6SNikolai Kondrashov if (resolution == 0) { 4452c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 4462c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 4472c3a88c6SNikolai Kondrashov } else { 4482c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 4492c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 4502c3a88c6SNikolai Kondrashov resolution; 4512c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 4522c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 4532c3a88c6SNikolai Kondrashov resolution; 4542c3a88c6SNikolai Kondrashov } 4552c3a88c6SNikolai Kondrashov 4562c3a88c6SNikolai Kondrashov /* 4572c3a88c6SNikolai Kondrashov * Generate pen report descriptor 4582c3a88c6SNikolai Kondrashov */ 4592c3a88c6SNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 460a985de58SNikolai Kondrashov uclogic_rdesc_v2_pen_template_arr, 461a985de58SNikolai Kondrashov uclogic_rdesc_v2_pen_template_size, 4622c3a88c6SNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 4632c3a88c6SNikolai Kondrashov if (desc_ptr == NULL) { 4642c3a88c6SNikolai Kondrashov rc = -ENOMEM; 4652c3a88c6SNikolai Kondrashov goto cleanup; 4662c3a88c6SNikolai Kondrashov } 4672c3a88c6SNikolai Kondrashov 4682c3a88c6SNikolai Kondrashov /* 4692c3a88c6SNikolai Kondrashov * Fill-in the parameters 4702c3a88c6SNikolai Kondrashov */ 4712c3a88c6SNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 4722c3a88c6SNikolai Kondrashov pen->desc_ptr = desc_ptr; 4732c3a88c6SNikolai Kondrashov desc_ptr = NULL; 474a985de58SNikolai Kondrashov pen->desc_size = uclogic_rdesc_v2_pen_template_size; 475a985de58SNikolai Kondrashov pen->id = UCLOGIC_RDESC_V2_PEN_ID; 4762c3a88c6SNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; 4772c3a88c6SNikolai Kondrashov pen->fragmented_hires = true; 4781324c5acSNikolai Kondrashov pen->tilt_y_flipped = true; 4792c3a88c6SNikolai Kondrashov found = true; 480945d5dd5SNikolai Kondrashov if (pparams_ptr != NULL) { 481945d5dd5SNikolai Kondrashov *pparams_ptr = buf; 482945d5dd5SNikolai Kondrashov buf = NULL; 483945d5dd5SNikolai Kondrashov } 484945d5dd5SNikolai Kondrashov if (pparams_len != NULL) 485945d5dd5SNikolai Kondrashov *pparams_len = params_len; 486945d5dd5SNikolai Kondrashov 4872c3a88c6SNikolai Kondrashov finish: 4882c3a88c6SNikolai Kondrashov *pfound = found; 4892c3a88c6SNikolai Kondrashov rc = 0; 4902c3a88c6SNikolai Kondrashov cleanup: 4912c3a88c6SNikolai Kondrashov kfree(desc_ptr); 4922c3a88c6SNikolai Kondrashov kfree(buf); 4932c3a88c6SNikolai Kondrashov return rc; 4942c3a88c6SNikolai Kondrashov } 4952c3a88c6SNikolai Kondrashov 4962c3a88c6SNikolai Kondrashov /** 4979614219eSNikolai Kondrashov * uclogic_params_frame_cleanup - free resources used by struct 4989614219eSNikolai Kondrashov * uclogic_params_frame (tablet interface's frame controls input parameters). 4999614219eSNikolai Kondrashov * Can be called repeatedly. 5009614219eSNikolai Kondrashov * 5019614219eSNikolai Kondrashov * @frame: Frame controls input parameters to cleanup. Cannot be NULL. 5029614219eSNikolai Kondrashov */ 5039614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) 5049614219eSNikolai Kondrashov { 5059614219eSNikolai Kondrashov kfree(frame->desc_ptr); 5069614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 5079614219eSNikolai Kondrashov } 5089614219eSNikolai Kondrashov 5099614219eSNikolai Kondrashov /** 5109614219eSNikolai Kondrashov * uclogic_params_frame_init_with_desc() - initialize tablet's frame control 5119614219eSNikolai Kondrashov * parameters with a static report descriptor. 5129614219eSNikolai Kondrashov * 5139614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 5149614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 5159614219eSNikolai Kondrashov * of error. Cannot be NULL. 5169614219eSNikolai Kondrashov * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. 5179614219eSNikolai Kondrashov * @desc_size: Report descriptor size. 5189614219eSNikolai Kondrashov * @id: Report ID used for frame reports, if they should be tweaked, 5199614219eSNikolai Kondrashov * zero if not. 5209614219eSNikolai Kondrashov * 5219614219eSNikolai Kondrashov * Returns: 5229614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 5239614219eSNikolai Kondrashov */ 5249614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc( 5259614219eSNikolai Kondrashov struct uclogic_params_frame *frame, 5269614219eSNikolai Kondrashov const __u8 *desc_ptr, 5279614219eSNikolai Kondrashov size_t desc_size, 5289614219eSNikolai Kondrashov unsigned int id) 5299614219eSNikolai Kondrashov { 5309614219eSNikolai Kondrashov __u8 *copy_desc_ptr; 5319614219eSNikolai Kondrashov 5329614219eSNikolai Kondrashov if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) 5339614219eSNikolai Kondrashov return -EINVAL; 5349614219eSNikolai Kondrashov 5359614219eSNikolai Kondrashov copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 5369614219eSNikolai Kondrashov if (copy_desc_ptr == NULL) 5379614219eSNikolai Kondrashov return -ENOMEM; 5389614219eSNikolai Kondrashov 5399614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 5409614219eSNikolai Kondrashov frame->desc_ptr = copy_desc_ptr; 5419614219eSNikolai Kondrashov frame->desc_size = desc_size; 5429614219eSNikolai Kondrashov frame->id = id; 5439614219eSNikolai Kondrashov return 0; 5449614219eSNikolai Kondrashov } 5459614219eSNikolai Kondrashov 5469614219eSNikolai Kondrashov /** 5472e28f3e0SNikolai Kondrashov * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame 5482e28f3e0SNikolai Kondrashov * controls. 5499614219eSNikolai Kondrashov * 5509614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 5519614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 5529614219eSNikolai Kondrashov * of error, or if parameters are not found. Cannot be NULL. 5539614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 5549614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 5559614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 5569614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 5579614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 5589614219eSNikolai Kondrashov * 5599614219eSNikolai Kondrashov * Returns: 5609614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 5619614219eSNikolai Kondrashov */ 5622e28f3e0SNikolai Kondrashov static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame, 5639614219eSNikolai Kondrashov bool *pfound, 5649614219eSNikolai Kondrashov struct hid_device *hdev) 5659614219eSNikolai Kondrashov { 5669614219eSNikolai Kondrashov int rc; 5679614219eSNikolai Kondrashov bool found = false; 568aa320fdbSJosé Expósito struct usb_device *usb_dev; 5699614219eSNikolai Kondrashov char *str_buf = NULL; 5709614219eSNikolai Kondrashov const size_t str_len = 16; 5719614219eSNikolai Kondrashov 5729614219eSNikolai Kondrashov /* Check arguments */ 5739614219eSNikolai Kondrashov if (frame == NULL || pfound == NULL || hdev == NULL) { 5749614219eSNikolai Kondrashov rc = -EINVAL; 5759614219eSNikolai Kondrashov goto cleanup; 5769614219eSNikolai Kondrashov } 5779614219eSNikolai Kondrashov 578aa320fdbSJosé Expósito usb_dev = hid_to_usb_dev(hdev); 579aa320fdbSJosé Expósito 5809614219eSNikolai Kondrashov /* 5819614219eSNikolai Kondrashov * Enable generic button mode 5829614219eSNikolai Kondrashov */ 5839614219eSNikolai Kondrashov str_buf = kzalloc(str_len, GFP_KERNEL); 5849614219eSNikolai Kondrashov if (str_buf == NULL) { 5859614219eSNikolai Kondrashov rc = -ENOMEM; 5869614219eSNikolai Kondrashov goto cleanup; 5879614219eSNikolai Kondrashov } 5889614219eSNikolai Kondrashov 5899614219eSNikolai Kondrashov rc = usb_string(usb_dev, 123, str_buf, str_len); 5909614219eSNikolai Kondrashov if (rc == -EPIPE) { 5919614219eSNikolai Kondrashov hid_dbg(hdev, 5929614219eSNikolai Kondrashov "generic button -enabling string descriptor not found\n"); 5939614219eSNikolai Kondrashov } else if (rc < 0) { 5949614219eSNikolai Kondrashov goto cleanup; 5959614219eSNikolai Kondrashov } else if (strncmp(str_buf, "HK On", rc) != 0) { 5969614219eSNikolai Kondrashov hid_dbg(hdev, 5979614219eSNikolai Kondrashov "invalid response to enabling generic buttons: \"%s\"\n", 5989614219eSNikolai Kondrashov str_buf); 5999614219eSNikolai Kondrashov } else { 6009614219eSNikolai Kondrashov hid_dbg(hdev, "generic buttons enabled\n"); 6019614219eSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 6029614219eSNikolai Kondrashov frame, 603a985de58SNikolai Kondrashov uclogic_rdesc_v1_frame_arr, 604a985de58SNikolai Kondrashov uclogic_rdesc_v1_frame_size, 605a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID); 6069614219eSNikolai Kondrashov if (rc != 0) 6079614219eSNikolai Kondrashov goto cleanup; 6089614219eSNikolai Kondrashov found = true; 6099614219eSNikolai Kondrashov } 6109614219eSNikolai Kondrashov 6119614219eSNikolai Kondrashov *pfound = found; 6129614219eSNikolai Kondrashov rc = 0; 6139614219eSNikolai Kondrashov cleanup: 6149614219eSNikolai Kondrashov kfree(str_buf); 6159614219eSNikolai Kondrashov return rc; 6169614219eSNikolai Kondrashov } 6179614219eSNikolai Kondrashov 6189614219eSNikolai Kondrashov /** 6199614219eSNikolai Kondrashov * uclogic_params_cleanup - free resources used by struct uclogic_params 6209614219eSNikolai Kondrashov * (tablet interface's parameters). 6219614219eSNikolai Kondrashov * Can be called repeatedly. 6229614219eSNikolai Kondrashov * 6239614219eSNikolai Kondrashov * @params: Input parameters to cleanup. Cannot be NULL. 6249614219eSNikolai Kondrashov */ 6259614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params) 6269614219eSNikolai Kondrashov { 6279614219eSNikolai Kondrashov if (!params->invalid) { 628337fa051SNikolai Kondrashov size_t i; 6299614219eSNikolai Kondrashov kfree(params->desc_ptr); 6309614219eSNikolai Kondrashov uclogic_params_pen_cleanup(¶ms->pen); 631337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) 632337fa051SNikolai Kondrashov uclogic_params_frame_cleanup(¶ms->frame_list[i]); 633337fa051SNikolai Kondrashov 6349614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 6359614219eSNikolai Kondrashov } 6369614219eSNikolai Kondrashov } 6379614219eSNikolai Kondrashov 6389614219eSNikolai Kondrashov /** 6395abb5445SLee Jones * uclogic_params_get_desc() - Get a replacement report descriptor for a 6405abb5445SLee Jones * tablet's interface. 6419614219eSNikolai Kondrashov * 6429614219eSNikolai Kondrashov * @params: The parameters of a tablet interface to get report 6439614219eSNikolai Kondrashov * descriptor for. Cannot be NULL. 6449614219eSNikolai Kondrashov * @pdesc: Location for the resulting, kmalloc-allocated report 6459614219eSNikolai Kondrashov * descriptor pointer, or for NULL, if there's no replacement 6469614219eSNikolai Kondrashov * report descriptor. Not modified in case of error. Cannot be 6479614219eSNikolai Kondrashov * NULL. 6489614219eSNikolai Kondrashov * @psize: Location for the resulting report descriptor size, not set if 6499614219eSNikolai Kondrashov * there's no replacement report descriptor. Not modified in case 6509614219eSNikolai Kondrashov * of error. Cannot be NULL. 6519614219eSNikolai Kondrashov * 6529614219eSNikolai Kondrashov * Returns: 6539614219eSNikolai Kondrashov * Zero, if successful. 6549614219eSNikolai Kondrashov * -EINVAL, if invalid arguments are supplied. 6559614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 6569614219eSNikolai Kondrashov */ 6579614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params, 6589614219eSNikolai Kondrashov __u8 **pdesc, 6599614219eSNikolai Kondrashov unsigned int *psize) 6609614219eSNikolai Kondrashov { 661337fa051SNikolai Kondrashov int rc = -ENOMEM; 662337fa051SNikolai Kondrashov bool present = false; 663337fa051SNikolai Kondrashov unsigned int size = 0; 6649614219eSNikolai Kondrashov __u8 *desc = NULL; 665337fa051SNikolai Kondrashov size_t i; 6669614219eSNikolai Kondrashov 6679614219eSNikolai Kondrashov /* Check arguments */ 6689614219eSNikolai Kondrashov if (params == NULL || pdesc == NULL || psize == NULL) 6699614219eSNikolai Kondrashov return -EINVAL; 6709614219eSNikolai Kondrashov 671337fa051SNikolai Kondrashov /* Concatenate descriptors */ 672337fa051SNikolai Kondrashov #define ADD_DESC(_desc_ptr, _desc_size) \ 673337fa051SNikolai Kondrashov do { \ 674337fa051SNikolai Kondrashov unsigned int new_size; \ 675337fa051SNikolai Kondrashov __u8 *new_desc; \ 676337fa051SNikolai Kondrashov if ((_desc_ptr) == NULL) { \ 677337fa051SNikolai Kondrashov break; \ 678337fa051SNikolai Kondrashov } \ 679337fa051SNikolai Kondrashov new_size = size + (_desc_size); \ 680337fa051SNikolai Kondrashov new_desc = krealloc(desc, new_size, GFP_KERNEL); \ 681337fa051SNikolai Kondrashov if (new_desc == NULL) { \ 682337fa051SNikolai Kondrashov goto cleanup; \ 683337fa051SNikolai Kondrashov } \ 684337fa051SNikolai Kondrashov memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \ 685337fa051SNikolai Kondrashov desc = new_desc; \ 686337fa051SNikolai Kondrashov size = new_size; \ 687337fa051SNikolai Kondrashov present = true; \ 688337fa051SNikolai Kondrashov } while (0) 6899614219eSNikolai Kondrashov 690337fa051SNikolai Kondrashov ADD_DESC(params->desc_ptr, params->desc_size); 691337fa051SNikolai Kondrashov ADD_DESC(params->pen.desc_ptr, params->pen.desc_size); 692337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 693337fa051SNikolai Kondrashov ADD_DESC(params->frame_list[i].desc_ptr, 694337fa051SNikolai Kondrashov params->frame_list[i].desc_size); 6959614219eSNikolai Kondrashov } 6969614219eSNikolai Kondrashov 697337fa051SNikolai Kondrashov #undef ADD_DESC 6989614219eSNikolai Kondrashov 699337fa051SNikolai Kondrashov if (present) { 7009614219eSNikolai Kondrashov *pdesc = desc; 701337fa051SNikolai Kondrashov *psize = size; 702337fa051SNikolai Kondrashov desc = NULL; 703337fa051SNikolai Kondrashov } 704337fa051SNikolai Kondrashov rc = 0; 705337fa051SNikolai Kondrashov cleanup: 706337fa051SNikolai Kondrashov kfree(desc); 707337fa051SNikolai Kondrashov return rc; 7089614219eSNikolai Kondrashov } 7099614219eSNikolai Kondrashov 7109614219eSNikolai Kondrashov /** 7119614219eSNikolai Kondrashov * uclogic_params_init_invalid() - initialize tablet interface parameters, 7129614219eSNikolai Kondrashov * specifying the interface is invalid. 7139614219eSNikolai Kondrashov * 7149614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7159614219eSNikolai Kondrashov * uclogic_params_cleanup()). Cannot be NULL. 7169614219eSNikolai Kondrashov */ 7179614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params) 7189614219eSNikolai Kondrashov { 7199614219eSNikolai Kondrashov params->invalid = true; 7209614219eSNikolai Kondrashov } 7219614219eSNikolai Kondrashov 7229614219eSNikolai Kondrashov /** 7239614219eSNikolai Kondrashov * uclogic_params_init_with_opt_desc() - initialize tablet interface 7249614219eSNikolai Kondrashov * parameters with an optional replacement report descriptor. Only modify 7259614219eSNikolai Kondrashov * report descriptor, if the original report descriptor matches the expected 7269614219eSNikolai Kondrashov * size. 7279614219eSNikolai Kondrashov * 7289614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7299614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of 7309614219eSNikolai Kondrashov * error. Cannot be NULL. 7319614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface create the 7329614219eSNikolai Kondrashov * parameters for. Cannot be NULL. 7339614219eSNikolai Kondrashov * @orig_desc_size: Expected size of the original report descriptor to 7349614219eSNikolai Kondrashov * be replaced. 7359614219eSNikolai Kondrashov * @desc_ptr: Pointer to the replacement report descriptor. 7369614219eSNikolai Kondrashov * Can be NULL, if desc_size is zero. 7379614219eSNikolai Kondrashov * @desc_size: Size of the replacement report descriptor. 7389614219eSNikolai Kondrashov * 7399614219eSNikolai Kondrashov * Returns: 7409614219eSNikolai Kondrashov * Zero, if successful. -EINVAL if an invalid argument was passed. 7419614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 7429614219eSNikolai Kondrashov */ 7439614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, 7449614219eSNikolai Kondrashov struct hid_device *hdev, 7459614219eSNikolai Kondrashov unsigned int orig_desc_size, 7469614219eSNikolai Kondrashov __u8 *desc_ptr, 7479614219eSNikolai Kondrashov unsigned int desc_size) 7489614219eSNikolai Kondrashov { 7499614219eSNikolai Kondrashov __u8 *desc_copy_ptr = NULL; 7509614219eSNikolai Kondrashov unsigned int desc_copy_size; 7519614219eSNikolai Kondrashov int rc; 7529614219eSNikolai Kondrashov 7539614219eSNikolai Kondrashov /* Check arguments */ 7549614219eSNikolai Kondrashov if (params == NULL || hdev == NULL || 7559614219eSNikolai Kondrashov (desc_ptr == NULL && desc_size != 0)) { 7569614219eSNikolai Kondrashov rc = -EINVAL; 7579614219eSNikolai Kondrashov goto cleanup; 7589614219eSNikolai Kondrashov } 7599614219eSNikolai Kondrashov 7609614219eSNikolai Kondrashov /* Replace report descriptor, if it matches */ 7619614219eSNikolai Kondrashov if (hdev->dev_rsize == orig_desc_size) { 7629614219eSNikolai Kondrashov hid_dbg(hdev, 7639614219eSNikolai Kondrashov "device report descriptor matches the expected size, replacing\n"); 7649614219eSNikolai Kondrashov desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 7659614219eSNikolai Kondrashov if (desc_copy_ptr == NULL) { 7669614219eSNikolai Kondrashov rc = -ENOMEM; 7679614219eSNikolai Kondrashov goto cleanup; 7689614219eSNikolai Kondrashov } 7699614219eSNikolai Kondrashov desc_copy_size = desc_size; 7709614219eSNikolai Kondrashov } else { 7719614219eSNikolai Kondrashov hid_dbg(hdev, 7729614219eSNikolai Kondrashov "device report descriptor doesn't match the expected size (%u != %u), preserving\n", 7739614219eSNikolai Kondrashov hdev->dev_rsize, orig_desc_size); 7749614219eSNikolai Kondrashov desc_copy_ptr = NULL; 7759614219eSNikolai Kondrashov desc_copy_size = 0; 7769614219eSNikolai Kondrashov } 7779614219eSNikolai Kondrashov 7789614219eSNikolai Kondrashov /* Output parameters */ 7799614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 7809614219eSNikolai Kondrashov params->desc_ptr = desc_copy_ptr; 7819614219eSNikolai Kondrashov desc_copy_ptr = NULL; 7829614219eSNikolai Kondrashov params->desc_size = desc_copy_size; 7839614219eSNikolai Kondrashov 7849614219eSNikolai Kondrashov rc = 0; 7859614219eSNikolai Kondrashov cleanup: 7869614219eSNikolai Kondrashov kfree(desc_copy_ptr); 7879614219eSNikolai Kondrashov return rc; 7889614219eSNikolai Kondrashov } 7899614219eSNikolai Kondrashov 7909614219eSNikolai Kondrashov /** 7915abb5445SLee Jones * uclogic_params_huion_init() - initialize a Huion tablet interface and discover 7929614219eSNikolai Kondrashov * its parameters. 7939614219eSNikolai Kondrashov * 7949614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 7959614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 7969614219eSNikolai Kondrashov * Cannot be NULL. 7979614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 7989614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 7999614219eSNikolai Kondrashov * 8009614219eSNikolai Kondrashov * Returns: 8019614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 8029614219eSNikolai Kondrashov */ 8039614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params, 8049614219eSNikolai Kondrashov struct hid_device *hdev) 8059614219eSNikolai Kondrashov { 8069614219eSNikolai Kondrashov int rc; 807ff6b548aSJosé Expósito struct usb_device *udev; 808ff6b548aSJosé Expósito struct usb_interface *iface; 809ff6b548aSJosé Expósito __u8 bInterfaceNumber; 8109614219eSNikolai Kondrashov bool found; 8119614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 8129614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 8132c3a88c6SNikolai Kondrashov static const char transition_ver[] = "HUION_T153_160607"; 8142c3a88c6SNikolai Kondrashov char *ver_ptr = NULL; 8152c3a88c6SNikolai Kondrashov const size_t ver_len = sizeof(transition_ver) + 1; 816118dfdeaSNikolai Kondrashov __u8 *params_ptr = NULL; 817118dfdeaSNikolai Kondrashov size_t params_len = 0; 818118dfdeaSNikolai Kondrashov /* Parameters string descriptor of a model with touch ring (HS610) */ 819118dfdeaSNikolai Kondrashov const __u8 touch_ring_model_params_buf[] = { 820118dfdeaSNikolai Kondrashov 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, 821118dfdeaSNikolai Kondrashov 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, 822118dfdeaSNikolai Kondrashov 0x04, 0x3C, 0x3E 823118dfdeaSNikolai Kondrashov }; 8249614219eSNikolai Kondrashov 8259614219eSNikolai Kondrashov /* Check arguments */ 8269614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 8279614219eSNikolai Kondrashov rc = -EINVAL; 8289614219eSNikolai Kondrashov goto cleanup; 8299614219eSNikolai Kondrashov } 8309614219eSNikolai Kondrashov 831ff6b548aSJosé Expósito udev = hid_to_usb_dev(hdev); 832ff6b548aSJosé Expósito iface = to_usb_interface(hdev->dev.parent); 833ff6b548aSJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 834ff6b548aSJosé Expósito 835d64a6e44SNikolai Kondrashov /* If it's a custom keyboard interface */ 836d64a6e44SNikolai Kondrashov if (bInterfaceNumber == 1) { 8374c60bc7dSNikolai Kondrashov /* Keep everything intact, but mark pen usage invalid */ 8384c60bc7dSNikolai Kondrashov p.pen.usage_invalid = true; 839d64a6e44SNikolai Kondrashov goto output; 840d64a6e44SNikolai Kondrashov /* Else, if it's not a pen interface */ 841d64a6e44SNikolai Kondrashov } else if (bInterfaceNumber != 0) { 842606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 8439614219eSNikolai Kondrashov goto output; 8449614219eSNikolai Kondrashov } 8459614219eSNikolai Kondrashov 8462c3a88c6SNikolai Kondrashov /* Try to get firmware version */ 8472c3a88c6SNikolai Kondrashov ver_ptr = kzalloc(ver_len, GFP_KERNEL); 8482c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 8492c3a88c6SNikolai Kondrashov rc = -ENOMEM; 8502c3a88c6SNikolai Kondrashov goto cleanup; 8512c3a88c6SNikolai Kondrashov } 8522c3a88c6SNikolai Kondrashov rc = usb_string(udev, 201, ver_ptr, ver_len); 8532c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 8542c3a88c6SNikolai Kondrashov *ver_ptr = '\0'; 8552c3a88c6SNikolai Kondrashov } else if (rc < 0) { 8562c3a88c6SNikolai Kondrashov hid_err(hdev, 8572c3a88c6SNikolai Kondrashov "failed retrieving Huion firmware version: %d\n", rc); 8582c3a88c6SNikolai Kondrashov goto cleanup; 8592c3a88c6SNikolai Kondrashov } 8602c3a88c6SNikolai Kondrashov 8612c3a88c6SNikolai Kondrashov /* If this is a transition firmware */ 8622c3a88c6SNikolai Kondrashov if (strcmp(ver_ptr, transition_ver) == 0) { 8632c3a88c6SNikolai Kondrashov hid_dbg(hdev, 8642c3a88c6SNikolai Kondrashov "transition firmware detected, not probing pen v2 parameters\n"); 8652c3a88c6SNikolai Kondrashov } else { 8662c3a88c6SNikolai Kondrashov /* Try to probe v2 pen parameters */ 867945d5dd5SNikolai Kondrashov rc = uclogic_params_pen_init_v2(&p.pen, &found, 868118dfdeaSNikolai Kondrashov ¶ms_ptr, ¶ms_len, 869118dfdeaSNikolai Kondrashov hdev); 8702c3a88c6SNikolai Kondrashov if (rc != 0) { 8712c3a88c6SNikolai Kondrashov hid_err(hdev, 8722c3a88c6SNikolai Kondrashov "failed probing pen v2 parameters: %d\n", rc); 8732c3a88c6SNikolai Kondrashov goto cleanup; 8742c3a88c6SNikolai Kondrashov } else if (found) { 8752c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters found\n"); 876c3e6e59aSNikolai Kondrashov /* Create v2 frame button parameters */ 8772c3a88c6SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 878337fa051SNikolai Kondrashov &p.frame_list[0], 879c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_arr, 880c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_size, 881c3e6e59aSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); 8822c3a88c6SNikolai Kondrashov if (rc != 0) { 8832c3a88c6SNikolai Kondrashov hid_err(hdev, 884c3e6e59aSNikolai Kondrashov "failed creating v2 frame button parameters: %d\n", 8852c3a88c6SNikolai Kondrashov rc); 8862c3a88c6SNikolai Kondrashov goto cleanup; 8872c3a88c6SNikolai Kondrashov } 888c3e6e59aSNikolai Kondrashov 889118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 890118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 891118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].id = 892118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; 893118dfdeaSNikolai Kondrashov 894118dfdeaSNikolai Kondrashov /* If this is the model with touch ring */ 895118dfdeaSNikolai Kondrashov if (params_ptr != NULL && 896118dfdeaSNikolai Kondrashov params_len == sizeof(touch_ring_model_params_buf) && 897118dfdeaSNikolai Kondrashov memcmp(params_ptr, touch_ring_model_params_buf, 898118dfdeaSNikolai Kondrashov params_len) == 0) { 899118dfdeaSNikolai Kondrashov /* Create touch ring parameters */ 900c3e6e59aSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 901c3e6e59aSNikolai Kondrashov &p.frame_list[1], 902c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_arr, 903c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_size, 904caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 905c3e6e59aSNikolai Kondrashov if (rc != 0) { 906c3e6e59aSNikolai Kondrashov hid_err(hdev, 907c3e6e59aSNikolai Kondrashov "failed creating v2 frame touch ring parameters: %d\n", 908c3e6e59aSNikolai Kondrashov rc); 909c3e6e59aSNikolai Kondrashov goto cleanup; 910c3e6e59aSNikolai Kondrashov } 911c3e6e59aSNikolai Kondrashov p.frame_list[1].suffix = "Touch Ring"; 912c3e6e59aSNikolai Kondrashov p.frame_list[1].dev_id_byte = 913caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 914caf7e934SNikolai Kondrashov p.frame_list[1].touch_byte = 5; 915caf7e934SNikolai Kondrashov p.frame_list[1].touch_max = 12; 916fbc08b4eSNikolai Kondrashov p.frame_list[1].touch_flip_at = 7; 917118dfdeaSNikolai Kondrashov } else { 918118dfdeaSNikolai Kondrashov /* Create touch strip parameters */ 919118dfdeaSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 920118dfdeaSNikolai Kondrashov &p.frame_list[1], 921118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_arr, 922118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_size, 923118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 924118dfdeaSNikolai Kondrashov if (rc != 0) { 925118dfdeaSNikolai Kondrashov hid_err(hdev, 926118dfdeaSNikolai Kondrashov "failed creating v2 frame touch strip parameters: %d\n", 927118dfdeaSNikolai Kondrashov rc); 928118dfdeaSNikolai Kondrashov goto cleanup; 929118dfdeaSNikolai Kondrashov } 930118dfdeaSNikolai Kondrashov p.frame_list[1].suffix = "Touch Strip"; 931118dfdeaSNikolai Kondrashov p.frame_list[1].dev_id_byte = 932118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 933118dfdeaSNikolai Kondrashov p.frame_list[1].touch_byte = 5; 934118dfdeaSNikolai Kondrashov p.frame_list[1].touch_max = 8; 935118dfdeaSNikolai Kondrashov } 936118dfdeaSNikolai Kondrashov 937118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 938118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].value = 0xf0; 939118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].id = 940118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; 941c3e6e59aSNikolai Kondrashov 9426facd076SNikolai Kondrashov /* Create v2 frame dial parameters */ 9436facd076SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 9446facd076SNikolai Kondrashov &p.frame_list[2], 9456facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_arr, 9466facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_size, 9476facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID); 9486facd076SNikolai Kondrashov if (rc != 0) { 9496facd076SNikolai Kondrashov hid_err(hdev, 9506facd076SNikolai Kondrashov "failed creating v2 frame dial parameters: %d\n", 9516facd076SNikolai Kondrashov rc); 9526facd076SNikolai Kondrashov goto cleanup; 9536facd076SNikolai Kondrashov } 9546facd076SNikolai Kondrashov p.frame_list[2].suffix = "Dial"; 9556facd076SNikolai Kondrashov p.frame_list[2].dev_id_byte = 9566facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; 9576facd076SNikolai Kondrashov p.frame_list[2].bitmap_dial_byte = 5; 9586facd076SNikolai Kondrashov 959118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 9606facd076SNikolai Kondrashov p.pen.subreport_list[2].value = 0xf1; 9616facd076SNikolai Kondrashov p.pen.subreport_list[2].id = 9626facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID; 963118dfdeaSNikolai Kondrashov 9642c3a88c6SNikolai Kondrashov goto output; 9652c3a88c6SNikolai Kondrashov } 9662c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters not found\n"); 9672c3a88c6SNikolai Kondrashov } 9682c3a88c6SNikolai Kondrashov 969eecb5b84SNikolai Kondrashov /* Try to probe v1 pen parameters */ 970eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 9719614219eSNikolai Kondrashov if (rc != 0) { 9729614219eSNikolai Kondrashov hid_err(hdev, 973eecb5b84SNikolai Kondrashov "failed probing pen v1 parameters: %d\n", rc); 9749614219eSNikolai Kondrashov goto cleanup; 9759614219eSNikolai Kondrashov } else if (found) { 976eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters found\n"); 9772e28f3e0SNikolai Kondrashov /* Try to probe v1 frame */ 978337fa051SNikolai Kondrashov rc = uclogic_params_frame_init_v1(&p.frame_list[0], 9799614219eSNikolai Kondrashov &found, hdev); 9809614219eSNikolai Kondrashov if (rc != 0) { 9812e28f3e0SNikolai Kondrashov hid_err(hdev, "v1 frame probing failed: %d\n", rc); 9829614219eSNikolai Kondrashov goto cleanup; 9839614219eSNikolai Kondrashov } 9842e28f3e0SNikolai Kondrashov hid_dbg(hdev, "frame v1 parameters%s found\n", 9859614219eSNikolai Kondrashov (found ? "" : " not")); 9869614219eSNikolai Kondrashov if (found) { 9878b013098SNikolai Kondrashov /* Link frame button subreports from pen reports */ 988e6be956fSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 9898b013098SNikolai Kondrashov p.pen.subreport_list[0].id = 990a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID; 9919614219eSNikolai Kondrashov } 9929614219eSNikolai Kondrashov goto output; 9939614219eSNikolai Kondrashov } 994eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters not found\n"); 9959614219eSNikolai Kondrashov 9969614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 9979614219eSNikolai Kondrashov 9989614219eSNikolai Kondrashov output: 9999614219eSNikolai Kondrashov /* Output parameters */ 10009614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 10019614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 10029614219eSNikolai Kondrashov rc = 0; 10039614219eSNikolai Kondrashov cleanup: 1004118dfdeaSNikolai Kondrashov kfree(params_ptr); 10052c3a88c6SNikolai Kondrashov kfree(ver_ptr); 10069614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 10079614219eSNikolai Kondrashov return rc; 10089614219eSNikolai Kondrashov } 10099614219eSNikolai Kondrashov 10109614219eSNikolai Kondrashov /** 10110cb1fc09SJosé Expósito * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or 10120cb1fc09SJosé Expósito * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data. 10130cb1fc09SJosé Expósito * 10140cb1fc09SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 10150cb1fc09SJosé Expósito * parameters from. Cannot be NULL. 10160cb1fc09SJosé Expósito * @magic_arr: The magic data that should be sent to probe the interface. 10170cb1fc09SJosé Expósito * Cannot be NULL. 10180cb1fc09SJosé Expósito * @magic_size: Size of the magic data. 10190cb1fc09SJosé Expósito * @endpoint: Endpoint where the magic data should be sent. 10200cb1fc09SJosé Expósito * 10210cb1fc09SJosé Expósito * Returns: 10220cb1fc09SJosé Expósito * Zero, if successful. A negative errno code on error. 10230cb1fc09SJosé Expósito */ 10240cb1fc09SJosé Expósito static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr, 10250cb1fc09SJosé Expósito int magic_size, int endpoint) 10260cb1fc09SJosé Expósito { 10270cb1fc09SJosé Expósito struct usb_device *udev; 10280cb1fc09SJosé Expósito unsigned int pipe = 0; 10290cb1fc09SJosé Expósito int sent; 10300cb1fc09SJosé Expósito u8 *buf = NULL; 10310cb1fc09SJosé Expósito int rc = 0; 10320cb1fc09SJosé Expósito 10330cb1fc09SJosé Expósito if (!hdev || !magic_arr) { 10340cb1fc09SJosé Expósito rc = -EINVAL; 10350cb1fc09SJosé Expósito goto cleanup; 10360cb1fc09SJosé Expósito } 10370cb1fc09SJosé Expósito 10380cb1fc09SJosé Expósito buf = kmemdup(magic_arr, magic_size, GFP_KERNEL); 10390cb1fc09SJosé Expósito if (!buf) { 10400cb1fc09SJosé Expósito rc = -ENOMEM; 10410cb1fc09SJosé Expósito goto cleanup; 10420cb1fc09SJosé Expósito } 10430cb1fc09SJosé Expósito 10440cb1fc09SJosé Expósito udev = hid_to_usb_dev(hdev); 10450cb1fc09SJosé Expósito pipe = usb_sndintpipe(udev, endpoint); 10460cb1fc09SJosé Expósito 10470cb1fc09SJosé Expósito rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000); 10480cb1fc09SJosé Expósito if (rc || sent != magic_size) { 10490cb1fc09SJosé Expósito hid_err(hdev, "Interface probing failed: %d\n", rc); 10500cb1fc09SJosé Expósito rc = -1; 10510cb1fc09SJosé Expósito goto cleanup; 10520cb1fc09SJosé Expósito } 10530cb1fc09SJosé Expósito 10540cb1fc09SJosé Expósito rc = 0; 10550cb1fc09SJosé Expósito cleanup: 10560cb1fc09SJosé Expósito kfree(buf); 10570cb1fc09SJosé Expósito return rc; 10580cb1fc09SJosé Expósito } 10590cb1fc09SJosé Expósito 10600cb1fc09SJosé Expósito /** 1061a64cbf3cSJosé Expósito * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing 1062a64cbf3cSJosé Expósito * pen and frame parameters returned by UGEE v2 devices. 1063a64cbf3cSJosé Expósito * 1064a64cbf3cSJosé Expósito * @str_desc: String descriptor, cannot be NULL. 1065a64cbf3cSJosé Expósito * @str_desc_size: Size of the string descriptor. 1066a64cbf3cSJosé Expósito * @desc_params: Output description params list. 1067a64cbf3cSJosé Expósito * @desc_params_size: Size of the output description params list. 1068a092986fSJosé Expósito * @frame_type: Output frame type. 1069a64cbf3cSJosé Expósito * 1070a64cbf3cSJosé Expósito * Returns: 1071a64cbf3cSJosé Expósito * Zero, if successful. A negative errno code on error. 1072a64cbf3cSJosé Expósito */ 1073a64cbf3cSJosé Expósito static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc, 1074a64cbf3cSJosé Expósito size_t str_desc_size, 1075a64cbf3cSJosé Expósito s32 *desc_params, 1076a092986fSJosé Expósito size_t desc_params_size, 1077a092986fSJosé Expósito enum uclogic_params_frame_type *frame_type) 1078a64cbf3cSJosé Expósito { 1079a64cbf3cSJosé Expósito s32 pen_x_lm, pen_y_lm; 1080a64cbf3cSJosé Expósito s32 pen_x_pm, pen_y_pm; 1081a64cbf3cSJosé Expósito s32 pen_pressure_lm; 1082a64cbf3cSJosé Expósito s32 frame_num_buttons; 1083a64cbf3cSJosé Expósito s32 resolution; 1084a64cbf3cSJosé Expósito 1085a64cbf3cSJosé Expósito /* Minimum descriptor length required, maximum seen so far is 14 */ 1086a64cbf3cSJosé Expósito const int min_str_desc_size = 12; 1087a64cbf3cSJosé Expósito 1088a64cbf3cSJosé Expósito if (!str_desc || str_desc_size < min_str_desc_size) 1089a64cbf3cSJosé Expósito return -EINVAL; 1090a64cbf3cSJosé Expósito 1091a64cbf3cSJosé Expósito if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 1092a64cbf3cSJosé Expósito return -EINVAL; 1093a64cbf3cSJosé Expósito 1094a64cbf3cSJosé Expósito pen_x_lm = get_unaligned_le16(str_desc + 2); 1095a64cbf3cSJosé Expósito pen_y_lm = get_unaligned_le16(str_desc + 4); 1096a64cbf3cSJosé Expósito frame_num_buttons = str_desc[6]; 1097a092986fSJosé Expósito *frame_type = str_desc[7]; 1098a64cbf3cSJosé Expósito pen_pressure_lm = get_unaligned_le16(str_desc + 8); 1099a64cbf3cSJosé Expósito 1100a64cbf3cSJosé Expósito resolution = get_unaligned_le16(str_desc + 10); 1101a64cbf3cSJosé Expósito if (resolution == 0) { 1102a64cbf3cSJosé Expósito pen_x_pm = 0; 1103a64cbf3cSJosé Expósito pen_y_pm = 0; 1104a64cbf3cSJosé Expósito } else { 1105a64cbf3cSJosé Expósito pen_x_pm = pen_x_lm * 1000 / resolution; 1106a64cbf3cSJosé Expósito pen_y_pm = pen_y_lm * 1000 / resolution; 1107a64cbf3cSJosé Expósito } 1108a64cbf3cSJosé Expósito 1109a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm; 1110a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm; 1111a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm; 1112a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm; 1113a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm; 1114a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons; 1115a64cbf3cSJosé Expósito 1116a64cbf3cSJosé Expósito return 0; 1117a64cbf3cSJosé Expósito } 1118a64cbf3cSJosé Expósito 1119a64cbf3cSJosé Expósito /** 112086402296SJosé Expósito * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with 112186402296SJosé Expósito * buttons. 112286402296SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 112386402296SJosé Expósito * @desc_params: Device description params list. 112486402296SJosé Expósito * @desc_params_size: Size of the description params list. 112586402296SJosé Expósito * 112686402296SJosé Expósito * Returns: 112786402296SJosé Expósito * Zero, if successful. A negative errno code on error. 112886402296SJosé Expósito */ 112986402296SJosé Expósito static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p, 113086402296SJosé Expósito const s32 *desc_params, 113186402296SJosé Expósito size_t desc_params_size) 113286402296SJosé Expósito { 113386402296SJosé Expósito __u8 *rdesc_frame = NULL; 113486402296SJosé Expósito int rc = 0; 113586402296SJosé Expósito 113686402296SJosé Expósito if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 113786402296SJosé Expósito return -EINVAL; 113886402296SJosé Expósito 113986402296SJosé Expósito rdesc_frame = uclogic_rdesc_template_apply( 114086402296SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_arr, 114186402296SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_size, 114286402296SJosé Expósito desc_params, UCLOGIC_RDESC_PH_ID_NUM); 114386402296SJosé Expósito if (!rdesc_frame) 114486402296SJosé Expósito return -ENOMEM; 114586402296SJosé Expósito 114686402296SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], 114786402296SJosé Expósito rdesc_frame, 114886402296SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_size, 114986402296SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 115086402296SJosé Expósito kfree(rdesc_frame); 115186402296SJosé Expósito return rc; 115286402296SJosé Expósito } 115386402296SJosé Expósito 115486402296SJosé Expósito /** 1155b67439d7SJosé Expósito * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a 1156b67439d7SJosé Expósito * bitmap dial. 1157b67439d7SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1158b67439d7SJosé Expósito * @desc_params: Device description params list. 1159b67439d7SJosé Expósito * @desc_params_size: Size of the description params list. 1160b67439d7SJosé Expósito * 1161b67439d7SJosé Expósito * Returns: 1162b67439d7SJosé Expósito * Zero, if successful. A negative errno code on error. 1163b67439d7SJosé Expósito */ 1164b67439d7SJosé Expósito static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p, 1165b67439d7SJosé Expósito const s32 *desc_params, 1166b67439d7SJosé Expósito size_t desc_params_size) 1167b67439d7SJosé Expósito { 1168b67439d7SJosé Expósito __u8 *rdesc_frame = NULL; 1169b67439d7SJosé Expósito int rc = 0; 1170b67439d7SJosé Expósito 1171b67439d7SJosé Expósito if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 1172b67439d7SJosé Expósito return -EINVAL; 1173b67439d7SJosé Expósito 1174b67439d7SJosé Expósito rdesc_frame = uclogic_rdesc_template_apply( 1175b67439d7SJosé Expósito uclogic_rdesc_ugee_v2_frame_dial_template_arr, 1176b67439d7SJosé Expósito uclogic_rdesc_ugee_v2_frame_dial_template_size, 1177b67439d7SJosé Expósito desc_params, UCLOGIC_RDESC_PH_ID_NUM); 1178b67439d7SJosé Expósito if (!rdesc_frame) 1179b67439d7SJosé Expósito return -ENOMEM; 1180b67439d7SJosé Expósito 1181b67439d7SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], 1182b67439d7SJosé Expósito rdesc_frame, 1183b67439d7SJosé Expósito uclogic_rdesc_ugee_v2_frame_dial_template_size, 1184b67439d7SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 1185b67439d7SJosé Expósito kfree(rdesc_frame); 1186b67439d7SJosé Expósito if (rc) 1187b67439d7SJosé Expósito return rc; 1188b67439d7SJosé Expósito 1189b67439d7SJosé Expósito p->frame_list[0].bitmap_dial_byte = 7; 1190b67439d7SJosé Expósito return 0; 1191b67439d7SJosé Expósito } 1192b67439d7SJosé Expósito 1193b67439d7SJosé Expósito /** 1194387dcab7SJosé Expósito * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a 1195387dcab7SJosé Expósito * mouse. 1196387dcab7SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1197387dcab7SJosé Expósito * 1198387dcab7SJosé Expósito * Returns: 1199387dcab7SJosé Expósito * Zero, if successful. A negative errno code on error. 1200387dcab7SJosé Expósito */ 1201387dcab7SJosé Expósito static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p) 1202387dcab7SJosé Expósito { 1203387dcab7SJosé Expósito int rc = 0; 1204387dcab7SJosé Expósito 1205387dcab7SJosé Expósito if (!p) 1206387dcab7SJosé Expósito return -EINVAL; 1207387dcab7SJosé Expósito 1208387dcab7SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], 1209387dcab7SJosé Expósito uclogic_rdesc_ugee_v2_frame_mouse_template_arr, 1210387dcab7SJosé Expósito uclogic_rdesc_ugee_v2_frame_mouse_template_size, 1211387dcab7SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 1212387dcab7SJosé Expósito return rc; 1213387dcab7SJosé Expósito } 1214387dcab7SJosé Expósito 1215387dcab7SJosé Expósito /** 1216f9ce4db0SJosé Expósito * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has 1217f9ce4db0SJosé Expósito * battery or not. 1218f9ce4db0SJosé Expósito * @hdev: The HID device of the tablet interface. 1219f9ce4db0SJosé Expósito * 1220f9ce4db0SJosé Expósito * Returns: 1221f9ce4db0SJosé Expósito * True if the device has battery, false otherwise. 1222f9ce4db0SJosé Expósito */ 1223f9ce4db0SJosé Expósito static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev) 1224f9ce4db0SJosé Expósito { 1225*f60c377fSJosé Expósito struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 1226*f60c377fSJosé Expósito 1227*f60c377fSJosé Expósito if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK) 1228*f60c377fSJosé Expósito return true; 1229*f60c377fSJosé Expósito 1230f9ce4db0SJosé Expósito /* The XP-PEN Deco LW vendor, product and version are identical to the 1231f9ce4db0SJosé Expósito * Deco L. The only difference reported by their firmware is the product 1232f9ce4db0SJosé Expósito * name. Add a quirk to support battery reporting on the wireless 1233f9ce4db0SJosé Expósito * version. 1234f9ce4db0SJosé Expósito */ 1235f9ce4db0SJosé Expósito if (hdev->vendor == USB_VENDOR_ID_UGEE && 1236f9ce4db0SJosé Expósito hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) { 1237f9ce4db0SJosé Expósito struct usb_device *udev = hid_to_usb_dev(hdev); 1238f9ce4db0SJosé Expósito 1239f9ce4db0SJosé Expósito if (strstarts(udev->product, "Deco LW")) 1240f9ce4db0SJosé Expósito return true; 1241f9ce4db0SJosé Expósito } 1242f9ce4db0SJosé Expósito 1243f9ce4db0SJosé Expósito return false; 1244f9ce4db0SJosé Expósito } 1245f9ce4db0SJosé Expósito 1246f9ce4db0SJosé Expósito /** 1247f9ce4db0SJosé Expósito * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting. 1248f9ce4db0SJosé Expósito * @hdev: The HID device of the tablet interface, cannot be NULL. 1249f9ce4db0SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1250f9ce4db0SJosé Expósito * 1251f9ce4db0SJosé Expósito * Returns: 1252f9ce4db0SJosé Expósito * Zero, if successful. A negative errno code on error. 1253f9ce4db0SJosé Expósito */ 1254f9ce4db0SJosé Expósito static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev, 1255f9ce4db0SJosé Expósito struct uclogic_params *p) 1256f9ce4db0SJosé Expósito { 1257f9ce4db0SJosé Expósito int rc = 0; 1258f9ce4db0SJosé Expósito 1259f9ce4db0SJosé Expósito if (!hdev || !p) 1260f9ce4db0SJosé Expósito return -EINVAL; 1261f9ce4db0SJosé Expósito 1262f9ce4db0SJosé Expósito /* Some tablets contain invalid characters in hdev->uniq, throwing a 1263f9ce4db0SJosé Expósito * "hwmon: '<name>' is not a valid name attribute, please fix" error. 1264f9ce4db0SJosé Expósito * Use the device vendor and product IDs instead. 1265f9ce4db0SJosé Expósito */ 1266f9ce4db0SJosé Expósito snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor, 1267f9ce4db0SJosé Expósito hdev->product); 1268f9ce4db0SJosé Expósito 1269f9ce4db0SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], 1270f9ce4db0SJosé Expósito uclogic_rdesc_ugee_v2_battery_template_arr, 1271f9ce4db0SJosé Expósito uclogic_rdesc_ugee_v2_battery_template_size, 1272f9ce4db0SJosé Expósito UCLOGIC_RDESC_UGEE_V2_BATTERY_ID); 1273f9ce4db0SJosé Expósito if (rc) 1274f9ce4db0SJosé Expósito return rc; 1275f9ce4db0SJosé Expósito 1276f9ce4db0SJosé Expósito p->frame_list[1].suffix = "Battery"; 1277f9ce4db0SJosé Expósito p->pen.subreport_list[1].value = 0xf2; 1278f9ce4db0SJosé Expósito p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID; 1279f9ce4db0SJosé Expósito 1280f9ce4db0SJosé Expósito return rc; 1281f9ce4db0SJosé Expósito } 1282f9ce4db0SJosé Expósito 1283f9ce4db0SJosé Expósito /** 12840cb1fc09SJosé Expósito * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by 12850cb1fc09SJosé Expósito * discovering their parameters. 12860cb1fc09SJosé Expósito * 12870cb1fc09SJosé Expósito * These tables, internally designed as v2 to differentiate them from older 12880cb1fc09SJosé Expósito * models, expect a payload of magic data in orther to be switched to the fully 12890cb1fc09SJosé Expósito * functional mode and expose their parameters in a similar way to the 12900cb1fc09SJosé Expósito * information present in uclogic_params_pen_init_v1() but with some 12910cb1fc09SJosé Expósito * differences. 12920cb1fc09SJosé Expósito * 12930cb1fc09SJosé Expósito * @params: Parameters to fill in (to be cleaned with 12940cb1fc09SJosé Expósito * uclogic_params_cleanup()). Not modified in case of error. 12950cb1fc09SJosé Expósito * Cannot be NULL. 12960cb1fc09SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 12970cb1fc09SJosé Expósito * parameters from. Cannot be NULL. 12980cb1fc09SJosé Expósito * 12990cb1fc09SJosé Expósito * Returns: 13000cb1fc09SJosé Expósito * Zero, if successful. A negative errno code on error. 13010cb1fc09SJosé Expósito */ 13020cb1fc09SJosé Expósito static int uclogic_params_ugee_v2_init(struct uclogic_params *params, 13030cb1fc09SJosé Expósito struct hid_device *hdev) 13040cb1fc09SJosé Expósito { 13050cb1fc09SJosé Expósito int rc = 0; 130614b71e6aSJosé Expósito struct uclogic_drvdata *drvdata; 13070cb1fc09SJosé Expósito struct usb_interface *iface; 13080cb1fc09SJosé Expósito __u8 bInterfaceNumber; 13090cb1fc09SJosé Expósito const int str_desc_len = 12; 13100cb1fc09SJosé Expósito __u8 *str_desc = NULL; 13110cb1fc09SJosé Expósito __u8 *rdesc_pen = NULL; 13120cb1fc09SJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 1313a092986fSJosé Expósito enum uclogic_params_frame_type frame_type; 13140cb1fc09SJosé Expósito __u8 magic_arr[] = { 13150cb1fc09SJosé Expósito 0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 13160cb1fc09SJosé Expósito }; 13170cb1fc09SJosé Expósito /* The resulting parameters (noop) */ 13180cb1fc09SJosé Expósito struct uclogic_params p = {0, }; 13190cb1fc09SJosé Expósito 13200cb1fc09SJosé Expósito if (!params || !hdev) { 13210cb1fc09SJosé Expósito rc = -EINVAL; 13220cb1fc09SJosé Expósito goto cleanup; 13230cb1fc09SJosé Expósito } 13240cb1fc09SJosé Expósito 132514b71e6aSJosé Expósito drvdata = hid_get_drvdata(hdev); 13260cb1fc09SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 13270cb1fc09SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1328387dcab7SJosé Expósito 1329387dcab7SJosé Expósito if (bInterfaceNumber == 0) { 1330387dcab7SJosé Expósito rc = uclogic_params_ugee_v2_init_frame_mouse(&p); 1331387dcab7SJosé Expósito if (rc) 1332387dcab7SJosé Expósito goto cleanup; 1333387dcab7SJosé Expósito 1334387dcab7SJosé Expósito goto output; 1335387dcab7SJosé Expósito } 1336387dcab7SJosé Expósito 13370cb1fc09SJosé Expósito if (bInterfaceNumber != 2) { 13380cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 13390cb1fc09SJosé Expósito goto output; 13400cb1fc09SJosé Expósito } 13410cb1fc09SJosé Expósito 13420cb1fc09SJosé Expósito /* 13430cb1fc09SJosé Expósito * Initialize the interface by sending magic data. 13440cb1fc09SJosé Expósito * The specific data was discovered by sniffing the Windows driver 13450cb1fc09SJosé Expósito * traffic. 13460cb1fc09SJosé Expósito */ 13470cb1fc09SJosé Expósito rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03); 13480cb1fc09SJosé Expósito if (rc) { 13490cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 13500cb1fc09SJosé Expósito goto output; 13510cb1fc09SJosé Expósito } 13520cb1fc09SJosé Expósito 13530cb1fc09SJosé Expósito /* 13540cb1fc09SJosé Expósito * Read the string descriptor containing pen and frame parameters. 13550cb1fc09SJosé Expósito * The specific string descriptor and data were discovered by sniffing 13560cb1fc09SJosé Expósito * the Windows driver traffic. 13570cb1fc09SJosé Expósito */ 13580cb1fc09SJosé Expósito rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); 13590cb1fc09SJosé Expósito if (rc != str_desc_len) { 13600cb1fc09SJosé Expósito hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); 13610cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 13620cb1fc09SJosé Expósito goto output; 13630cb1fc09SJosé Expósito } 13640cb1fc09SJosé Expósito 1365a64cbf3cSJosé Expósito rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len, 1366a64cbf3cSJosé Expósito desc_params, 1367a092986fSJosé Expósito ARRAY_SIZE(desc_params), 1368a092986fSJosé Expósito &frame_type); 1369a64cbf3cSJosé Expósito if (rc) 1370a64cbf3cSJosé Expósito goto cleanup; 1371a64cbf3cSJosé Expósito 13720cb1fc09SJosé Expósito kfree(str_desc); 13730cb1fc09SJosé Expósito str_desc = NULL; 13740cb1fc09SJosé Expósito 13750cb1fc09SJosé Expósito /* Initialize the pen interface */ 13760cb1fc09SJosé Expósito rdesc_pen = uclogic_rdesc_template_apply( 13770cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_pen_template_arr, 13780cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_pen_template_size, 13790cb1fc09SJosé Expósito desc_params, ARRAY_SIZE(desc_params)); 13800cb1fc09SJosé Expósito if (!rdesc_pen) { 13810cb1fc09SJosé Expósito rc = -ENOMEM; 13820cb1fc09SJosé Expósito goto cleanup; 13830cb1fc09SJosé Expósito } 13840cb1fc09SJosé Expósito 13850cb1fc09SJosé Expósito p.pen.desc_ptr = rdesc_pen; 13860cb1fc09SJosé Expósito p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; 13870cb1fc09SJosé Expósito p.pen.id = 0x02; 13880cb1fc09SJosé Expósito p.pen.subreport_list[0].value = 0xf0; 13890cb1fc09SJosé Expósito p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; 13900cb1fc09SJosé Expósito 13910cb1fc09SJosé Expósito /* Initialize the frame interface */ 139214b71e6aSJosé Expósito if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK) 139314b71e6aSJosé Expósito frame_type = UCLOGIC_PARAMS_FRAME_MOUSE; 139414b71e6aSJosé Expósito 1395a092986fSJosé Expósito switch (frame_type) { 1396b67439d7SJosé Expósito case UCLOGIC_PARAMS_FRAME_DIAL: 1397b67439d7SJosé Expósito case UCLOGIC_PARAMS_FRAME_MOUSE: 1398b67439d7SJosé Expósito rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params, 1399b67439d7SJosé Expósito ARRAY_SIZE(desc_params)); 1400b67439d7SJosé Expósito break; 1401a092986fSJosé Expósito case UCLOGIC_PARAMS_FRAME_BUTTONS: 1402a092986fSJosé Expósito default: 140386402296SJosé Expósito rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params, 140486402296SJosé Expósito ARRAY_SIZE(desc_params)); 1405a092986fSJosé Expósito break; 1406a092986fSJosé Expósito } 1407a092986fSJosé Expósito 140886402296SJosé Expósito if (rc) 14090cb1fc09SJosé Expósito goto cleanup; 14100cb1fc09SJosé Expósito 1411f9ce4db0SJosé Expósito /* Initialize the battery interface*/ 1412f9ce4db0SJosé Expósito if (uclogic_params_ugee_v2_has_battery(hdev)) { 1413f9ce4db0SJosé Expósito rc = uclogic_params_ugee_v2_init_battery(hdev, &p); 1414f9ce4db0SJosé Expósito if (rc) { 1415f9ce4db0SJosé Expósito hid_err(hdev, "error initializing battery: %d\n", rc); 1416f9ce4db0SJosé Expósito goto cleanup; 1417f9ce4db0SJosé Expósito } 1418f9ce4db0SJosé Expósito } 1419f9ce4db0SJosé Expósito 14200cb1fc09SJosé Expósito output: 14210cb1fc09SJosé Expósito /* Output parameters */ 14220cb1fc09SJosé Expósito memcpy(params, &p, sizeof(*params)); 14230cb1fc09SJosé Expósito memset(&p, 0, sizeof(p)); 14240cb1fc09SJosé Expósito rc = 0; 14250cb1fc09SJosé Expósito cleanup: 14260cb1fc09SJosé Expósito kfree(str_desc); 14270cb1fc09SJosé Expósito uclogic_params_cleanup(&p); 14280cb1fc09SJosé Expósito return rc; 14290cb1fc09SJosé Expósito } 14300cb1fc09SJosé Expósito 14310cb1fc09SJosé Expósito /** 14329614219eSNikolai Kondrashov * uclogic_params_init() - initialize a tablet interface and discover its 14339614219eSNikolai Kondrashov * parameters. 14349614219eSNikolai Kondrashov * 14359614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 14369614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 14379614219eSNikolai Kondrashov * Cannot be NULL. 14389614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 14398547b778SNikolai Kondrashov * parameters from. Cannot be NULL. Must be using the USB low-level 14408547b778SNikolai Kondrashov * driver, i.e. be an actual USB tablet. 14419614219eSNikolai Kondrashov * 14429614219eSNikolai Kondrashov * Returns: 14439614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 14449614219eSNikolai Kondrashov */ 14459614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params, 14469614219eSNikolai Kondrashov struct hid_device *hdev) 14479614219eSNikolai Kondrashov { 14489614219eSNikolai Kondrashov int rc; 1449f364c571SJosé Expósito struct usb_device *udev; 1450f364c571SJosé Expósito __u8 bNumInterfaces; 1451f364c571SJosé Expósito struct usb_interface *iface; 1452f364c571SJosé Expósito __u8 bInterfaceNumber; 14539614219eSNikolai Kondrashov bool found; 14549614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 14559614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 14569614219eSNikolai Kondrashov 14579614219eSNikolai Kondrashov /* Check arguments */ 1458f83baa0cSGreg Kroah-Hartman if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { 14599614219eSNikolai Kondrashov rc = -EINVAL; 14609614219eSNikolai Kondrashov goto cleanup; 14619614219eSNikolai Kondrashov } 14629614219eSNikolai Kondrashov 1463f364c571SJosé Expósito udev = hid_to_usb_dev(hdev); 1464f364c571SJosé Expósito bNumInterfaces = udev->config->desc.bNumInterfaces; 1465f364c571SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 1466f364c571SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1467f364c571SJosé Expósito 14689614219eSNikolai Kondrashov /* 14699614219eSNikolai Kondrashov * Set replacement report descriptor if the original matches the 14709614219eSNikolai Kondrashov * specified size. Otherwise keep interface unchanged. 14719614219eSNikolai Kondrashov */ 14729614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 14739614219eSNikolai Kondrashov uclogic_params_init_with_opt_desc( \ 14749614219eSNikolai Kondrashov &p, hdev, \ 14759614219eSNikolai Kondrashov UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 14769614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_arr, \ 14779614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_size) 14789614219eSNikolai Kondrashov 14799614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \ 14809614219eSNikolai Kondrashov (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 14819614219eSNikolai Kondrashov 14829614219eSNikolai Kondrashov /* 14839614219eSNikolai Kondrashov * Handle specific interfaces for specific tablets. 14849614219eSNikolai Kondrashov * 14859614219eSNikolai Kondrashov * Observe the following logic: 14869614219eSNikolai Kondrashov * 14879614219eSNikolai Kondrashov * If the interface is recognized as producing certain useful input: 14889614219eSNikolai Kondrashov * Mark interface as valid. 14899614219eSNikolai Kondrashov * Output interface parameters. 14909614219eSNikolai Kondrashov * Else, if the interface is recognized as *not* producing any useful 14919614219eSNikolai Kondrashov * input: 14929614219eSNikolai Kondrashov * Mark interface as invalid. 14939614219eSNikolai Kondrashov * Else: 14949614219eSNikolai Kondrashov * Mark interface as valid. 14959614219eSNikolai Kondrashov * Output noop parameters. 14969614219eSNikolai Kondrashov * 14979614219eSNikolai Kondrashov * Rule of thumb: it is better to disable a broken interface than let 14989614219eSNikolai Kondrashov * it spew garbage input. 14999614219eSNikolai Kondrashov */ 15009614219eSNikolai Kondrashov 15019614219eSNikolai Kondrashov switch (VID_PID(hdev->vendor, hdev->product)) { 15029614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15039614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 15049614219eSNikolai Kondrashov rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 15059614219eSNikolai Kondrashov if (rc != 0) 15069614219eSNikolai Kondrashov goto cleanup; 15079614219eSNikolai Kondrashov break; 15089614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15099614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 15109614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 15119614219eSNikolai Kondrashov if (rc != 0) 15129614219eSNikolai Kondrashov goto cleanup; 15139614219eSNikolai Kondrashov break; 15149614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15159614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 15169c17f735SNikolai Kondrashov if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { 15179c17f735SNikolai Kondrashov if (bInterfaceNumber == 0) { 15189c17f735SNikolai Kondrashov /* Try to probe v1 pen parameters */ 15199c17f735SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, 15209c17f735SNikolai Kondrashov &found, hdev); 15219c17f735SNikolai Kondrashov if (rc != 0) { 15229c17f735SNikolai Kondrashov hid_err(hdev, 15239c17f735SNikolai Kondrashov "pen probing failed: %d\n", 15249c17f735SNikolai Kondrashov rc); 15259c17f735SNikolai Kondrashov goto cleanup; 15269c17f735SNikolai Kondrashov } 15279c17f735SNikolai Kondrashov if (!found) { 15289c17f735SNikolai Kondrashov hid_warn(hdev, 15299c17f735SNikolai Kondrashov "pen parameters not found"); 15309c17f735SNikolai Kondrashov } 15319c17f735SNikolai Kondrashov } else { 15329c17f735SNikolai Kondrashov uclogic_params_init_invalid(&p); 15339c17f735SNikolai Kondrashov } 15349c17f735SNikolai Kondrashov } else { 15359614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 15369614219eSNikolai Kondrashov if (rc != 0) 15379614219eSNikolai Kondrashov goto cleanup; 15389c17f735SNikolai Kondrashov } 15399614219eSNikolai Kondrashov break; 15409614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15419614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 15429614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 15439614219eSNikolai Kondrashov if (rc != 0) 15449614219eSNikolai Kondrashov goto cleanup; 15459614219eSNikolai Kondrashov break; 15469614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15479614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 15489614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 15499614219eSNikolai Kondrashov if (rc != 0) 15509614219eSNikolai Kondrashov goto cleanup; 15519614219eSNikolai Kondrashov break; 15529614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15539614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 15549614219eSNikolai Kondrashov switch (bInterfaceNumber) { 15559614219eSNikolai Kondrashov case 0: 15569614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 15579614219eSNikolai Kondrashov if (rc != 0) 15589614219eSNikolai Kondrashov goto cleanup; 15599614219eSNikolai Kondrashov break; 15609614219eSNikolai Kondrashov case 1: 15619614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 15629614219eSNikolai Kondrashov if (rc != 0) 15639614219eSNikolai Kondrashov goto cleanup; 15649614219eSNikolai Kondrashov break; 15659614219eSNikolai Kondrashov case 2: 15669614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 15679614219eSNikolai Kondrashov if (rc != 0) 15689614219eSNikolai Kondrashov goto cleanup; 15699614219eSNikolai Kondrashov break; 15709614219eSNikolai Kondrashov } 15719614219eSNikolai Kondrashov break; 15729614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 15739614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 15749614219eSNikolai Kondrashov /* 15759614219eSNikolai Kondrashov * If it is not a three-interface version, which is known to 15769614219eSNikolai Kondrashov * respond to initialization. 15779614219eSNikolai Kondrashov */ 15789614219eSNikolai Kondrashov if (bNumInterfaces != 3) { 15799614219eSNikolai Kondrashov switch (bInterfaceNumber) { 15809614219eSNikolai Kondrashov case 0: 15819614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG0, 15829614219eSNikolai Kondrashov twha60_fixed0); 15839614219eSNikolai Kondrashov if (rc != 0) 15849614219eSNikolai Kondrashov goto cleanup; 15859614219eSNikolai Kondrashov break; 15869614219eSNikolai Kondrashov case 1: 15879614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG1, 15889614219eSNikolai Kondrashov twha60_fixed1); 15899614219eSNikolai Kondrashov if (rc != 0) 15909614219eSNikolai Kondrashov goto cleanup; 15919614219eSNikolai Kondrashov break; 15929614219eSNikolai Kondrashov } 15939614219eSNikolai Kondrashov break; 15949614219eSNikolai Kondrashov } 1595df561f66SGustavo A. R. Silva fallthrough; 15969614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_HUION, 15979614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 1598315ffcc9SKyle Godbey case VID_PID(USB_VENDOR_ID_HUION, 159985e86071SNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET2): 16009614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16019614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 16029614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16039614219eSNikolai Kondrashov USB_DEVICE_ID_YIYNOVA_TABLET): 16049614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16059614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 16069614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16079614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 16089614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16099614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 16100c15efe9SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16110c15efe9SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 16129614219eSNikolai Kondrashov rc = uclogic_params_huion_init(&p, hdev); 16139614219eSNikolai Kondrashov if (rc != 0) 16149614219eSNikolai Kondrashov goto cleanup; 16159614219eSNikolai Kondrashov break; 16169614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGTIZER, 16179614219eSNikolai Kondrashov USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 1618022fc531SMartijn van de Streek case VID_PID(USB_VENDOR_ID_UGTIZER, 1619022fc531SMartijn van de Streek USB_DEVICE_ID_UGTIZER_TABLET_GT5040): 1620c3e5a67cSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1621c3e5a67cSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): 1622492a9e9aSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1623492a9e9aSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 162488bb346dSWang Xuerui case VID_PID(USB_VENDOR_ID_UGEE, 162561b1db5aSRoman Romanenko USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06): 162661b1db5aSRoman Romanenko case VID_PID(USB_VENDOR_ID_UGEE, 162788bb346dSWang Xuerui USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): 16289614219eSNikolai Kondrashov /* If this is the pen interface */ 16299614219eSNikolai Kondrashov if (bInterfaceNumber == 1) { 1630eecb5b84SNikolai Kondrashov /* Probe v1 pen parameters */ 1631eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 16329614219eSNikolai Kondrashov if (rc != 0) { 16339614219eSNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 16349614219eSNikolai Kondrashov goto cleanup; 16359614219eSNikolai Kondrashov } 16369614219eSNikolai Kondrashov if (!found) { 16379614219eSNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 16389614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 16399614219eSNikolai Kondrashov } 16409614219eSNikolai Kondrashov } else { 1641606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 16429614219eSNikolai Kondrashov } 16439614219eSNikolai Kondrashov break; 16441ee7c685SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 164508367be1SNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): 164608367be1SNikolai Kondrashov /* If this is the pen and frame interface */ 164708367be1SNikolai Kondrashov if (bInterfaceNumber == 1) { 164808367be1SNikolai Kondrashov /* Probe v1 pen parameters */ 164908367be1SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 165008367be1SNikolai Kondrashov if (rc != 0) { 165108367be1SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 165208367be1SNikolai Kondrashov goto cleanup; 165308367be1SNikolai Kondrashov } 165408367be1SNikolai Kondrashov /* Initialize frame parameters */ 165508367be1SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1656337fa051SNikolai Kondrashov &p.frame_list[0], 165708367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_arr, 165808367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_size, 165908367be1SNikolai Kondrashov 0); 166008367be1SNikolai Kondrashov if (rc != 0) 166108367be1SNikolai Kondrashov goto cleanup; 166208367be1SNikolai Kondrashov } else { 1663606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 166408367be1SNikolai Kondrashov } 166508367be1SNikolai Kondrashov break; 16660cb1fc09SJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 16677495fb7eSJosé Expósito USB_DEVICE_ID_UGEE_PARBLO_A610_PRO): 16687495fb7eSJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 16690cb1fc09SJosé Expósito USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): 167093b40b3eSJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 167193b40b3eSJosé Expósito USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S): 16720cb1fc09SJosé Expósito rc = uclogic_params_ugee_v2_init(&p, hdev); 16730cb1fc09SJosé Expósito if (rc != 0) 16740cb1fc09SJosé Expósito goto cleanup; 16750cb1fc09SJosé Expósito break; 1676f7271b2aSCristian Klein case VID_PID(USB_VENDOR_ID_TRUST, 1677f7271b2aSCristian Klein USB_DEVICE_ID_TRUST_PANORA_TABLET): 167808367be1SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1679e902ed93SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_G5): 1680e902ed93SNikolai Kondrashov /* Ignore non-pen interfaces */ 1681e902ed93SNikolai Kondrashov if (bInterfaceNumber != 1) { 1682e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1683e902ed93SNikolai Kondrashov break; 1684e902ed93SNikolai Kondrashov } 1685e902ed93SNikolai Kondrashov 1686e902ed93SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 1687e902ed93SNikolai Kondrashov if (rc != 0) { 1688e902ed93SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 1689e902ed93SNikolai Kondrashov goto cleanup; 1690e902ed93SNikolai Kondrashov } else if (found) { 1691e902ed93SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1692337fa051SNikolai Kondrashov &p.frame_list[0], 1693e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_arr, 1694e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_size, 1695e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_ID); 1696e902ed93SNikolai Kondrashov if (rc != 0) { 1697e902ed93SNikolai Kondrashov hid_err(hdev, 16982e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 1699e902ed93SNikolai Kondrashov rc); 1700e902ed93SNikolai Kondrashov goto cleanup; 1701e902ed93SNikolai Kondrashov } 1702337fa051SNikolai Kondrashov p.frame_list[0].re_lsb = 1703e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; 1704337fa051SNikolai Kondrashov p.frame_list[0].dev_id_byte = 1705e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; 1706e902ed93SNikolai Kondrashov } else { 1707e902ed93SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 1708e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1709e902ed93SNikolai Kondrashov } 1710e902ed93SNikolai Kondrashov 1711e902ed93SNikolai Kondrashov break; 1712e902ed93SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 17131ee7c685SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_EX07S): 17141ee7c685SNikolai Kondrashov /* Ignore non-pen interfaces */ 17151ee7c685SNikolai Kondrashov if (bInterfaceNumber != 1) { 17161ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 17171ee7c685SNikolai Kondrashov break; 17181ee7c685SNikolai Kondrashov } 17191ee7c685SNikolai Kondrashov 17201ee7c685SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 17211ee7c685SNikolai Kondrashov if (rc != 0) { 17221ee7c685SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 17231ee7c685SNikolai Kondrashov goto cleanup; 17241ee7c685SNikolai Kondrashov } else if (found) { 17251ee7c685SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1726337fa051SNikolai Kondrashov &p.frame_list[0], 17272e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_arr, 17282e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_size, 17291ee7c685SNikolai Kondrashov 0); 17301ee7c685SNikolai Kondrashov if (rc != 0) { 17311ee7c685SNikolai Kondrashov hid_err(hdev, 17322e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 17331ee7c685SNikolai Kondrashov rc); 17341ee7c685SNikolai Kondrashov goto cleanup; 17351ee7c685SNikolai Kondrashov } 17361ee7c685SNikolai Kondrashov } else { 17371ee7c685SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 17381ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 17391ee7c685SNikolai Kondrashov } 17401ee7c685SNikolai Kondrashov 17411ee7c685SNikolai Kondrashov break; 17429614219eSNikolai Kondrashov } 17439614219eSNikolai Kondrashov 17449614219eSNikolai Kondrashov #undef VID_PID 17459614219eSNikolai Kondrashov #undef WITH_OPT_DESC 17469614219eSNikolai Kondrashov 17479614219eSNikolai Kondrashov /* Output parameters */ 17489614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 17499614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 17509614219eSNikolai Kondrashov rc = 0; 17519614219eSNikolai Kondrashov cleanup: 17529614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 17539614219eSNikolai Kondrashov return rc; 17549614219eSNikolai Kondrashov } 1755a64cbf3cSJosé Expósito 1756a64cbf3cSJosé Expósito #ifdef CONFIG_HID_KUNIT_TEST 1757a64cbf3cSJosé Expósito #include "hid-uclogic-params-test.c" 1758a64cbf3cSJosé Expósito #endif 1759