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 /** 619a251d657SJosé Expósito * uclogic_params_cleanup_event_hooks - free resources used by the list of raw 620a251d657SJosé Expósito * event hooks. 621a251d657SJosé Expósito * Can be called repeatedly. 622a251d657SJosé Expósito * 623a251d657SJosé Expósito * @params: Input parameters to cleanup. Cannot be NULL. 624a251d657SJosé Expósito */ 625a251d657SJosé Expósito static void uclogic_params_cleanup_event_hooks(struct uclogic_params *params) 626a251d657SJosé Expósito { 627a251d657SJosé Expósito struct uclogic_raw_event_hook *curr, *n; 628a251d657SJosé Expósito 629a251d657SJosé Expósito if (!params || !params->event_hooks) 630a251d657SJosé Expósito return; 631a251d657SJosé Expósito 632a251d657SJosé Expósito list_for_each_entry_safe(curr, n, ¶ms->event_hooks->list, list) { 633a251d657SJosé Expósito cancel_work_sync(&curr->work); 634a251d657SJosé Expósito list_del(&curr->list); 635a251d657SJosé Expósito kfree(curr->event); 636a251d657SJosé Expósito kfree(curr); 637a251d657SJosé Expósito } 638a251d657SJosé Expósito 639a251d657SJosé Expósito kfree(params->event_hooks); 640a251d657SJosé Expósito params->event_hooks = NULL; 641a251d657SJosé Expósito } 642a251d657SJosé Expósito 643a251d657SJosé Expósito /** 6449614219eSNikolai Kondrashov * uclogic_params_cleanup - free resources used by struct uclogic_params 6459614219eSNikolai Kondrashov * (tablet interface's parameters). 6469614219eSNikolai Kondrashov * Can be called repeatedly. 6479614219eSNikolai Kondrashov * 6489614219eSNikolai Kondrashov * @params: Input parameters to cleanup. Cannot be NULL. 6499614219eSNikolai Kondrashov */ 6509614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params) 6519614219eSNikolai Kondrashov { 6529614219eSNikolai Kondrashov if (!params->invalid) { 653337fa051SNikolai Kondrashov size_t i; 6549614219eSNikolai Kondrashov kfree(params->desc_ptr); 6559614219eSNikolai Kondrashov uclogic_params_pen_cleanup(¶ms->pen); 656337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) 657337fa051SNikolai Kondrashov uclogic_params_frame_cleanup(¶ms->frame_list[i]); 658337fa051SNikolai Kondrashov 659a251d657SJosé Expósito uclogic_params_cleanup_event_hooks(params); 6609614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 6619614219eSNikolai Kondrashov } 6629614219eSNikolai Kondrashov } 6639614219eSNikolai Kondrashov 6649614219eSNikolai Kondrashov /** 6655abb5445SLee Jones * uclogic_params_get_desc() - Get a replacement report descriptor for a 6665abb5445SLee Jones * tablet's interface. 6679614219eSNikolai Kondrashov * 6689614219eSNikolai Kondrashov * @params: The parameters of a tablet interface to get report 6699614219eSNikolai Kondrashov * descriptor for. Cannot be NULL. 6709614219eSNikolai Kondrashov * @pdesc: Location for the resulting, kmalloc-allocated report 6719614219eSNikolai Kondrashov * descriptor pointer, or for NULL, if there's no replacement 6729614219eSNikolai Kondrashov * report descriptor. Not modified in case of error. Cannot be 6739614219eSNikolai Kondrashov * NULL. 6749614219eSNikolai Kondrashov * @psize: Location for the resulting report descriptor size, not set if 6759614219eSNikolai Kondrashov * there's no replacement report descriptor. Not modified in case 6769614219eSNikolai Kondrashov * of error. Cannot be NULL. 6779614219eSNikolai Kondrashov * 6789614219eSNikolai Kondrashov * Returns: 6799614219eSNikolai Kondrashov * Zero, if successful. 6809614219eSNikolai Kondrashov * -EINVAL, if invalid arguments are supplied. 6819614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 6829614219eSNikolai Kondrashov */ 6839614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params, 6849614219eSNikolai Kondrashov __u8 **pdesc, 6859614219eSNikolai Kondrashov unsigned int *psize) 6869614219eSNikolai Kondrashov { 687337fa051SNikolai Kondrashov int rc = -ENOMEM; 688337fa051SNikolai Kondrashov bool present = false; 689337fa051SNikolai Kondrashov unsigned int size = 0; 6909614219eSNikolai Kondrashov __u8 *desc = NULL; 691337fa051SNikolai Kondrashov size_t i; 6929614219eSNikolai Kondrashov 6939614219eSNikolai Kondrashov /* Check arguments */ 6949614219eSNikolai Kondrashov if (params == NULL || pdesc == NULL || psize == NULL) 6959614219eSNikolai Kondrashov return -EINVAL; 6969614219eSNikolai Kondrashov 697337fa051SNikolai Kondrashov /* Concatenate descriptors */ 698337fa051SNikolai Kondrashov #define ADD_DESC(_desc_ptr, _desc_size) \ 699337fa051SNikolai Kondrashov do { \ 700337fa051SNikolai Kondrashov unsigned int new_size; \ 701337fa051SNikolai Kondrashov __u8 *new_desc; \ 702337fa051SNikolai Kondrashov if ((_desc_ptr) == NULL) { \ 703337fa051SNikolai Kondrashov break; \ 704337fa051SNikolai Kondrashov } \ 705337fa051SNikolai Kondrashov new_size = size + (_desc_size); \ 706337fa051SNikolai Kondrashov new_desc = krealloc(desc, new_size, GFP_KERNEL); \ 707337fa051SNikolai Kondrashov if (new_desc == NULL) { \ 708337fa051SNikolai Kondrashov goto cleanup; \ 709337fa051SNikolai Kondrashov } \ 710337fa051SNikolai Kondrashov memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \ 711337fa051SNikolai Kondrashov desc = new_desc; \ 712337fa051SNikolai Kondrashov size = new_size; \ 713337fa051SNikolai Kondrashov present = true; \ 714337fa051SNikolai Kondrashov } while (0) 7159614219eSNikolai Kondrashov 716337fa051SNikolai Kondrashov ADD_DESC(params->desc_ptr, params->desc_size); 717337fa051SNikolai Kondrashov ADD_DESC(params->pen.desc_ptr, params->pen.desc_size); 718337fa051SNikolai Kondrashov for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) { 719337fa051SNikolai Kondrashov ADD_DESC(params->frame_list[i].desc_ptr, 720337fa051SNikolai Kondrashov params->frame_list[i].desc_size); 7219614219eSNikolai Kondrashov } 7229614219eSNikolai Kondrashov 723337fa051SNikolai Kondrashov #undef ADD_DESC 7249614219eSNikolai Kondrashov 725337fa051SNikolai Kondrashov if (present) { 7269614219eSNikolai Kondrashov *pdesc = desc; 727337fa051SNikolai Kondrashov *psize = size; 728337fa051SNikolai Kondrashov desc = NULL; 729337fa051SNikolai Kondrashov } 730337fa051SNikolai Kondrashov rc = 0; 731337fa051SNikolai Kondrashov cleanup: 732337fa051SNikolai Kondrashov kfree(desc); 733337fa051SNikolai Kondrashov return rc; 7349614219eSNikolai Kondrashov } 7359614219eSNikolai Kondrashov 7369614219eSNikolai Kondrashov /** 7379614219eSNikolai Kondrashov * uclogic_params_init_invalid() - initialize tablet interface parameters, 7389614219eSNikolai Kondrashov * specifying the interface is invalid. 7399614219eSNikolai Kondrashov * 7409614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7419614219eSNikolai Kondrashov * uclogic_params_cleanup()). Cannot be NULL. 7429614219eSNikolai Kondrashov */ 7439614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params) 7449614219eSNikolai Kondrashov { 7459614219eSNikolai Kondrashov params->invalid = true; 7469614219eSNikolai Kondrashov } 7479614219eSNikolai Kondrashov 7489614219eSNikolai Kondrashov /** 7499614219eSNikolai Kondrashov * uclogic_params_init_with_opt_desc() - initialize tablet interface 7509614219eSNikolai Kondrashov * parameters with an optional replacement report descriptor. Only modify 7519614219eSNikolai Kondrashov * report descriptor, if the original report descriptor matches the expected 7529614219eSNikolai Kondrashov * size. 7539614219eSNikolai Kondrashov * 7549614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 7559614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of 7569614219eSNikolai Kondrashov * error. Cannot be NULL. 7579614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface create the 7589614219eSNikolai Kondrashov * parameters for. Cannot be NULL. 7599614219eSNikolai Kondrashov * @orig_desc_size: Expected size of the original report descriptor to 7609614219eSNikolai Kondrashov * be replaced. 7619614219eSNikolai Kondrashov * @desc_ptr: Pointer to the replacement report descriptor. 7629614219eSNikolai Kondrashov * Can be NULL, if desc_size is zero. 7639614219eSNikolai Kondrashov * @desc_size: Size of the replacement report descriptor. 7649614219eSNikolai Kondrashov * 7659614219eSNikolai Kondrashov * Returns: 7669614219eSNikolai Kondrashov * Zero, if successful. -EINVAL if an invalid argument was passed. 7679614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 7689614219eSNikolai Kondrashov */ 7699614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, 7709614219eSNikolai Kondrashov struct hid_device *hdev, 7719614219eSNikolai Kondrashov unsigned int orig_desc_size, 7729614219eSNikolai Kondrashov __u8 *desc_ptr, 7739614219eSNikolai Kondrashov unsigned int desc_size) 7749614219eSNikolai Kondrashov { 7759614219eSNikolai Kondrashov __u8 *desc_copy_ptr = NULL; 7769614219eSNikolai Kondrashov unsigned int desc_copy_size; 7779614219eSNikolai Kondrashov int rc; 7789614219eSNikolai Kondrashov 7799614219eSNikolai Kondrashov /* Check arguments */ 7809614219eSNikolai Kondrashov if (params == NULL || hdev == NULL || 7819614219eSNikolai Kondrashov (desc_ptr == NULL && desc_size != 0)) { 7829614219eSNikolai Kondrashov rc = -EINVAL; 7839614219eSNikolai Kondrashov goto cleanup; 7849614219eSNikolai Kondrashov } 7859614219eSNikolai Kondrashov 7869614219eSNikolai Kondrashov /* Replace report descriptor, if it matches */ 7879614219eSNikolai Kondrashov if (hdev->dev_rsize == orig_desc_size) { 7889614219eSNikolai Kondrashov hid_dbg(hdev, 7899614219eSNikolai Kondrashov "device report descriptor matches the expected size, replacing\n"); 7909614219eSNikolai Kondrashov desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 7919614219eSNikolai Kondrashov if (desc_copy_ptr == NULL) { 7929614219eSNikolai Kondrashov rc = -ENOMEM; 7939614219eSNikolai Kondrashov goto cleanup; 7949614219eSNikolai Kondrashov } 7959614219eSNikolai Kondrashov desc_copy_size = desc_size; 7969614219eSNikolai Kondrashov } else { 7979614219eSNikolai Kondrashov hid_dbg(hdev, 7989614219eSNikolai Kondrashov "device report descriptor doesn't match the expected size (%u != %u), preserving\n", 7999614219eSNikolai Kondrashov hdev->dev_rsize, orig_desc_size); 8009614219eSNikolai Kondrashov desc_copy_ptr = NULL; 8019614219eSNikolai Kondrashov desc_copy_size = 0; 8029614219eSNikolai Kondrashov } 8039614219eSNikolai Kondrashov 8049614219eSNikolai Kondrashov /* Output parameters */ 8059614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 8069614219eSNikolai Kondrashov params->desc_ptr = desc_copy_ptr; 8079614219eSNikolai Kondrashov desc_copy_ptr = NULL; 8089614219eSNikolai Kondrashov params->desc_size = desc_copy_size; 8099614219eSNikolai Kondrashov 8109614219eSNikolai Kondrashov rc = 0; 8119614219eSNikolai Kondrashov cleanup: 8129614219eSNikolai Kondrashov kfree(desc_copy_ptr); 8139614219eSNikolai Kondrashov return rc; 8149614219eSNikolai Kondrashov } 8159614219eSNikolai Kondrashov 8169614219eSNikolai Kondrashov /** 8175abb5445SLee Jones * uclogic_params_huion_init() - initialize a Huion tablet interface and discover 8189614219eSNikolai Kondrashov * its parameters. 8199614219eSNikolai Kondrashov * 8209614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 8219614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 8229614219eSNikolai Kondrashov * Cannot be NULL. 8239614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 8249614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 8259614219eSNikolai Kondrashov * 8269614219eSNikolai Kondrashov * Returns: 8279614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 8289614219eSNikolai Kondrashov */ 8299614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params, 8309614219eSNikolai Kondrashov struct hid_device *hdev) 8319614219eSNikolai Kondrashov { 8329614219eSNikolai Kondrashov int rc; 833ff6b548aSJosé Expósito struct usb_device *udev; 834ff6b548aSJosé Expósito struct usb_interface *iface; 835ff6b548aSJosé Expósito __u8 bInterfaceNumber; 8369614219eSNikolai Kondrashov bool found; 8379614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 8389614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 8392c3a88c6SNikolai Kondrashov static const char transition_ver[] = "HUION_T153_160607"; 8402c3a88c6SNikolai Kondrashov char *ver_ptr = NULL; 8412c3a88c6SNikolai Kondrashov const size_t ver_len = sizeof(transition_ver) + 1; 842118dfdeaSNikolai Kondrashov __u8 *params_ptr = NULL; 843118dfdeaSNikolai Kondrashov size_t params_len = 0; 844118dfdeaSNikolai Kondrashov /* Parameters string descriptor of a model with touch ring (HS610) */ 845118dfdeaSNikolai Kondrashov const __u8 touch_ring_model_params_buf[] = { 846118dfdeaSNikolai Kondrashov 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, 847118dfdeaSNikolai Kondrashov 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, 848118dfdeaSNikolai Kondrashov 0x04, 0x3C, 0x3E 849118dfdeaSNikolai Kondrashov }; 8509614219eSNikolai Kondrashov 8519614219eSNikolai Kondrashov /* Check arguments */ 8529614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 8539614219eSNikolai Kondrashov rc = -EINVAL; 8549614219eSNikolai Kondrashov goto cleanup; 8559614219eSNikolai Kondrashov } 8569614219eSNikolai Kondrashov 857ff6b548aSJosé Expósito udev = hid_to_usb_dev(hdev); 858ff6b548aSJosé Expósito iface = to_usb_interface(hdev->dev.parent); 859ff6b548aSJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 860ff6b548aSJosé Expósito 861d64a6e44SNikolai Kondrashov /* If it's a custom keyboard interface */ 862d64a6e44SNikolai Kondrashov if (bInterfaceNumber == 1) { 8634c60bc7dSNikolai Kondrashov /* Keep everything intact, but mark pen usage invalid */ 8644c60bc7dSNikolai Kondrashov p.pen.usage_invalid = true; 865d64a6e44SNikolai Kondrashov goto output; 866d64a6e44SNikolai Kondrashov /* Else, if it's not a pen interface */ 867d64a6e44SNikolai Kondrashov } else if (bInterfaceNumber != 0) { 868606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 8699614219eSNikolai Kondrashov goto output; 8709614219eSNikolai Kondrashov } 8719614219eSNikolai Kondrashov 8722c3a88c6SNikolai Kondrashov /* Try to get firmware version */ 8732c3a88c6SNikolai Kondrashov ver_ptr = kzalloc(ver_len, GFP_KERNEL); 8742c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 8752c3a88c6SNikolai Kondrashov rc = -ENOMEM; 8762c3a88c6SNikolai Kondrashov goto cleanup; 8772c3a88c6SNikolai Kondrashov } 8782c3a88c6SNikolai Kondrashov rc = usb_string(udev, 201, ver_ptr, ver_len); 8792c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 8802c3a88c6SNikolai Kondrashov *ver_ptr = '\0'; 8812c3a88c6SNikolai Kondrashov } else if (rc < 0) { 8822c3a88c6SNikolai Kondrashov hid_err(hdev, 8832c3a88c6SNikolai Kondrashov "failed retrieving Huion firmware version: %d\n", rc); 8842c3a88c6SNikolai Kondrashov goto cleanup; 8852c3a88c6SNikolai Kondrashov } 8862c3a88c6SNikolai Kondrashov 8872c3a88c6SNikolai Kondrashov /* If this is a transition firmware */ 8882c3a88c6SNikolai Kondrashov if (strcmp(ver_ptr, transition_ver) == 0) { 8892c3a88c6SNikolai Kondrashov hid_dbg(hdev, 8902c3a88c6SNikolai Kondrashov "transition firmware detected, not probing pen v2 parameters\n"); 8912c3a88c6SNikolai Kondrashov } else { 8922c3a88c6SNikolai Kondrashov /* Try to probe v2 pen parameters */ 893945d5dd5SNikolai Kondrashov rc = uclogic_params_pen_init_v2(&p.pen, &found, 894118dfdeaSNikolai Kondrashov ¶ms_ptr, ¶ms_len, 895118dfdeaSNikolai Kondrashov hdev); 8962c3a88c6SNikolai Kondrashov if (rc != 0) { 8972c3a88c6SNikolai Kondrashov hid_err(hdev, 8982c3a88c6SNikolai Kondrashov "failed probing pen v2 parameters: %d\n", rc); 8992c3a88c6SNikolai Kondrashov goto cleanup; 9002c3a88c6SNikolai Kondrashov } else if (found) { 9012c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters found\n"); 902c3e6e59aSNikolai Kondrashov /* Create v2 frame button parameters */ 9032c3a88c6SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 904337fa051SNikolai Kondrashov &p.frame_list[0], 905c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_arr, 906c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_buttons_size, 907c3e6e59aSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID); 9082c3a88c6SNikolai Kondrashov if (rc != 0) { 9092c3a88c6SNikolai Kondrashov hid_err(hdev, 910c3e6e59aSNikolai Kondrashov "failed creating v2 frame button parameters: %d\n", 9112c3a88c6SNikolai Kondrashov rc); 9122c3a88c6SNikolai Kondrashov goto cleanup; 9132c3a88c6SNikolai Kondrashov } 914c3e6e59aSNikolai Kondrashov 915118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 916118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 917118dfdeaSNikolai Kondrashov p.pen.subreport_list[0].id = 918118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID; 919118dfdeaSNikolai Kondrashov 920118dfdeaSNikolai Kondrashov /* If this is the model with touch ring */ 921118dfdeaSNikolai Kondrashov if (params_ptr != NULL && 922118dfdeaSNikolai Kondrashov params_len == sizeof(touch_ring_model_params_buf) && 923118dfdeaSNikolai Kondrashov memcmp(params_ptr, touch_ring_model_params_buf, 924118dfdeaSNikolai Kondrashov params_len) == 0) { 925118dfdeaSNikolai Kondrashov /* Create touch ring parameters */ 926c3e6e59aSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 927c3e6e59aSNikolai Kondrashov &p.frame_list[1], 928c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_arr, 929c3e6e59aSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_ring_size, 930caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 931c3e6e59aSNikolai Kondrashov if (rc != 0) { 932c3e6e59aSNikolai Kondrashov hid_err(hdev, 933c3e6e59aSNikolai Kondrashov "failed creating v2 frame touch ring parameters: %d\n", 934c3e6e59aSNikolai Kondrashov rc); 935c3e6e59aSNikolai Kondrashov goto cleanup; 936c3e6e59aSNikolai Kondrashov } 937c3e6e59aSNikolai Kondrashov p.frame_list[1].suffix = "Touch Ring"; 938c3e6e59aSNikolai Kondrashov p.frame_list[1].dev_id_byte = 939caf7e934SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 940caf7e934SNikolai Kondrashov p.frame_list[1].touch_byte = 5; 941caf7e934SNikolai Kondrashov p.frame_list[1].touch_max = 12; 942fbc08b4eSNikolai Kondrashov p.frame_list[1].touch_flip_at = 7; 943118dfdeaSNikolai Kondrashov } else { 944118dfdeaSNikolai Kondrashov /* Create touch strip parameters */ 945118dfdeaSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 946118dfdeaSNikolai Kondrashov &p.frame_list[1], 947118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_arr, 948118dfdeaSNikolai Kondrashov uclogic_rdesc_v2_frame_touch_strip_size, 949118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID); 950118dfdeaSNikolai Kondrashov if (rc != 0) { 951118dfdeaSNikolai Kondrashov hid_err(hdev, 952118dfdeaSNikolai Kondrashov "failed creating v2 frame touch strip parameters: %d\n", 953118dfdeaSNikolai Kondrashov rc); 954118dfdeaSNikolai Kondrashov goto cleanup; 955118dfdeaSNikolai Kondrashov } 956118dfdeaSNikolai Kondrashov p.frame_list[1].suffix = "Touch Strip"; 957118dfdeaSNikolai Kondrashov p.frame_list[1].dev_id_byte = 958118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE; 959118dfdeaSNikolai Kondrashov p.frame_list[1].touch_byte = 5; 960118dfdeaSNikolai Kondrashov p.frame_list[1].touch_max = 8; 961118dfdeaSNikolai Kondrashov } 962118dfdeaSNikolai Kondrashov 963118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 964118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].value = 0xf0; 965118dfdeaSNikolai Kondrashov p.pen.subreport_list[1].id = 966118dfdeaSNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_TOUCH_ID; 967c3e6e59aSNikolai Kondrashov 9686facd076SNikolai Kondrashov /* Create v2 frame dial parameters */ 9696facd076SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 9706facd076SNikolai Kondrashov &p.frame_list[2], 9716facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_arr, 9726facd076SNikolai Kondrashov uclogic_rdesc_v2_frame_dial_size, 9736facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID); 9746facd076SNikolai Kondrashov if (rc != 0) { 9756facd076SNikolai Kondrashov hid_err(hdev, 9766facd076SNikolai Kondrashov "failed creating v2 frame dial parameters: %d\n", 9776facd076SNikolai Kondrashov rc); 9786facd076SNikolai Kondrashov goto cleanup; 9796facd076SNikolai Kondrashov } 9806facd076SNikolai Kondrashov p.frame_list[2].suffix = "Dial"; 9816facd076SNikolai Kondrashov p.frame_list[2].dev_id_byte = 9826facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE; 9836facd076SNikolai Kondrashov p.frame_list[2].bitmap_dial_byte = 5; 9846facd076SNikolai Kondrashov 985118dfdeaSNikolai Kondrashov /* Link from pen sub-report */ 9866facd076SNikolai Kondrashov p.pen.subreport_list[2].value = 0xf1; 9876facd076SNikolai Kondrashov p.pen.subreport_list[2].id = 9886facd076SNikolai Kondrashov UCLOGIC_RDESC_V2_FRAME_DIAL_ID; 989118dfdeaSNikolai Kondrashov 9902c3a88c6SNikolai Kondrashov goto output; 9912c3a88c6SNikolai Kondrashov } 9922c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters not found\n"); 9932c3a88c6SNikolai Kondrashov } 9942c3a88c6SNikolai Kondrashov 995eecb5b84SNikolai Kondrashov /* Try to probe v1 pen parameters */ 996eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 9979614219eSNikolai Kondrashov if (rc != 0) { 9989614219eSNikolai Kondrashov hid_err(hdev, 999eecb5b84SNikolai Kondrashov "failed probing pen v1 parameters: %d\n", rc); 10009614219eSNikolai Kondrashov goto cleanup; 10019614219eSNikolai Kondrashov } else if (found) { 1002eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters found\n"); 10032e28f3e0SNikolai Kondrashov /* Try to probe v1 frame */ 1004337fa051SNikolai Kondrashov rc = uclogic_params_frame_init_v1(&p.frame_list[0], 10059614219eSNikolai Kondrashov &found, hdev); 10069614219eSNikolai Kondrashov if (rc != 0) { 10072e28f3e0SNikolai Kondrashov hid_err(hdev, "v1 frame probing failed: %d\n", rc); 10089614219eSNikolai Kondrashov goto cleanup; 10099614219eSNikolai Kondrashov } 10102e28f3e0SNikolai Kondrashov hid_dbg(hdev, "frame v1 parameters%s found\n", 10119614219eSNikolai Kondrashov (found ? "" : " not")); 10129614219eSNikolai Kondrashov if (found) { 10138b013098SNikolai Kondrashov /* Link frame button subreports from pen reports */ 1014e6be956fSNikolai Kondrashov p.pen.subreport_list[0].value = 0xe0; 10158b013098SNikolai Kondrashov p.pen.subreport_list[0].id = 1016a985de58SNikolai Kondrashov UCLOGIC_RDESC_V1_FRAME_ID; 10179614219eSNikolai Kondrashov } 10189614219eSNikolai Kondrashov goto output; 10199614219eSNikolai Kondrashov } 1020eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters not found\n"); 10219614219eSNikolai Kondrashov 10229614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 10239614219eSNikolai Kondrashov 10249614219eSNikolai Kondrashov output: 10259614219eSNikolai Kondrashov /* Output parameters */ 10269614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 10279614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 10289614219eSNikolai Kondrashov rc = 0; 10299614219eSNikolai Kondrashov cleanup: 1030118dfdeaSNikolai Kondrashov kfree(params_ptr); 10312c3a88c6SNikolai Kondrashov kfree(ver_ptr); 10329614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 10339614219eSNikolai Kondrashov return rc; 10349614219eSNikolai Kondrashov } 10359614219eSNikolai Kondrashov 10369614219eSNikolai Kondrashov /** 10370cb1fc09SJosé Expósito * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or 10380cb1fc09SJosé Expósito * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data. 10390cb1fc09SJosé Expósito * 10400cb1fc09SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 10410cb1fc09SJosé Expósito * parameters from. Cannot be NULL. 10420cb1fc09SJosé Expósito * @magic_arr: The magic data that should be sent to probe the interface. 10430cb1fc09SJosé Expósito * Cannot be NULL. 10440cb1fc09SJosé Expósito * @magic_size: Size of the magic data. 10450cb1fc09SJosé Expósito * @endpoint: Endpoint where the magic data should be sent. 10460cb1fc09SJosé Expósito * 10470cb1fc09SJosé Expósito * Returns: 10480cb1fc09SJosé Expósito * Zero, if successful. A negative errno code on error. 10490cb1fc09SJosé Expósito */ 1050bd85c131SJosé Expósito static int uclogic_probe_interface(struct hid_device *hdev, const u8 *magic_arr, 1051bd85c131SJosé Expósito size_t magic_size, int endpoint) 10520cb1fc09SJosé Expósito { 10530cb1fc09SJosé Expósito struct usb_device *udev; 10540cb1fc09SJosé Expósito unsigned int pipe = 0; 10550cb1fc09SJosé Expósito int sent; 10560cb1fc09SJosé Expósito u8 *buf = NULL; 10570cb1fc09SJosé Expósito int rc = 0; 10580cb1fc09SJosé Expósito 10590cb1fc09SJosé Expósito if (!hdev || !magic_arr) { 10600cb1fc09SJosé Expósito rc = -EINVAL; 10610cb1fc09SJosé Expósito goto cleanup; 10620cb1fc09SJosé Expósito } 10630cb1fc09SJosé Expósito 10640cb1fc09SJosé Expósito buf = kmemdup(magic_arr, magic_size, GFP_KERNEL); 10650cb1fc09SJosé Expósito if (!buf) { 10660cb1fc09SJosé Expósito rc = -ENOMEM; 10670cb1fc09SJosé Expósito goto cleanup; 10680cb1fc09SJosé Expósito } 10690cb1fc09SJosé Expósito 10700cb1fc09SJosé Expósito udev = hid_to_usb_dev(hdev); 10710cb1fc09SJosé Expósito pipe = usb_sndintpipe(udev, endpoint); 10720cb1fc09SJosé Expósito 10730cb1fc09SJosé Expósito rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000); 10740cb1fc09SJosé Expósito if (rc || sent != magic_size) { 10750cb1fc09SJosé Expósito hid_err(hdev, "Interface probing failed: %d\n", rc); 10760cb1fc09SJosé Expósito rc = -1; 10770cb1fc09SJosé Expósito goto cleanup; 10780cb1fc09SJosé Expósito } 10790cb1fc09SJosé Expósito 10800cb1fc09SJosé Expósito rc = 0; 10810cb1fc09SJosé Expósito cleanup: 10820cb1fc09SJosé Expósito kfree(buf); 10830cb1fc09SJosé Expósito return rc; 10840cb1fc09SJosé Expósito } 10850cb1fc09SJosé Expósito 10860cb1fc09SJosé Expósito /** 1087a64cbf3cSJosé Expósito * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing 1088a64cbf3cSJosé Expósito * pen and frame parameters returned by UGEE v2 devices. 1089a64cbf3cSJosé Expósito * 1090a64cbf3cSJosé Expósito * @str_desc: String descriptor, cannot be NULL. 1091a64cbf3cSJosé Expósito * @str_desc_size: Size of the string descriptor. 1092a64cbf3cSJosé Expósito * @desc_params: Output description params list. 1093a64cbf3cSJosé Expósito * @desc_params_size: Size of the output description params list. 1094a092986fSJosé Expósito * @frame_type: Output frame type. 1095a64cbf3cSJosé Expósito * 1096a64cbf3cSJosé Expósito * Returns: 1097a64cbf3cSJosé Expósito * Zero, if successful. A negative errno code on error. 1098a64cbf3cSJosé Expósito */ 1099a64cbf3cSJosé Expósito static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc, 1100a64cbf3cSJosé Expósito size_t str_desc_size, 1101a64cbf3cSJosé Expósito s32 *desc_params, 1102a092986fSJosé Expósito size_t desc_params_size, 1103a092986fSJosé Expósito enum uclogic_params_frame_type *frame_type) 1104a64cbf3cSJosé Expósito { 1105a64cbf3cSJosé Expósito s32 pen_x_lm, pen_y_lm; 1106a64cbf3cSJosé Expósito s32 pen_x_pm, pen_y_pm; 1107a64cbf3cSJosé Expósito s32 pen_pressure_lm; 1108a64cbf3cSJosé Expósito s32 frame_num_buttons; 1109a64cbf3cSJosé Expósito s32 resolution; 1110a64cbf3cSJosé Expósito 1111a64cbf3cSJosé Expósito /* Minimum descriptor length required, maximum seen so far is 14 */ 1112a64cbf3cSJosé Expósito const int min_str_desc_size = 12; 1113a64cbf3cSJosé Expósito 1114a64cbf3cSJosé Expósito if (!str_desc || str_desc_size < min_str_desc_size) 1115a64cbf3cSJosé Expósito return -EINVAL; 1116a64cbf3cSJosé Expósito 1117a64cbf3cSJosé Expósito if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 1118a64cbf3cSJosé Expósito return -EINVAL; 1119a64cbf3cSJosé Expósito 1120a64cbf3cSJosé Expósito pen_x_lm = get_unaligned_le16(str_desc + 2); 1121a64cbf3cSJosé Expósito pen_y_lm = get_unaligned_le16(str_desc + 4); 1122a64cbf3cSJosé Expósito frame_num_buttons = str_desc[6]; 1123a092986fSJosé Expósito *frame_type = str_desc[7]; 1124a64cbf3cSJosé Expósito pen_pressure_lm = get_unaligned_le16(str_desc + 8); 1125a64cbf3cSJosé Expósito 1126a64cbf3cSJosé Expósito resolution = get_unaligned_le16(str_desc + 10); 1127a64cbf3cSJosé Expósito if (resolution == 0) { 1128a64cbf3cSJosé Expósito pen_x_pm = 0; 1129a64cbf3cSJosé Expósito pen_y_pm = 0; 1130a64cbf3cSJosé Expósito } else { 1131a64cbf3cSJosé Expósito pen_x_pm = pen_x_lm * 1000 / resolution; 1132a64cbf3cSJosé Expósito pen_y_pm = pen_y_lm * 1000 / resolution; 1133a64cbf3cSJosé Expósito } 1134a64cbf3cSJosé Expósito 1135a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm; 1136a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm; 1137a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm; 1138a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm; 1139a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm; 1140a64cbf3cSJosé Expósito desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons; 1141a64cbf3cSJosé Expósito 1142a64cbf3cSJosé Expósito return 0; 1143a64cbf3cSJosé Expósito } 1144a64cbf3cSJosé Expósito 1145a64cbf3cSJosé Expósito /** 114686402296SJosé Expósito * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with 114786402296SJosé Expósito * buttons. 114886402296SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 114986402296SJosé Expósito * @desc_params: Device description params list. 115086402296SJosé Expósito * @desc_params_size: Size of the description params list. 115186402296SJosé Expósito * 115286402296SJosé Expósito * Returns: 115386402296SJosé Expósito * Zero, if successful. A negative errno code on error. 115486402296SJosé Expósito */ 115586402296SJosé Expósito static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p, 115686402296SJosé Expósito const s32 *desc_params, 115786402296SJosé Expósito size_t desc_params_size) 115886402296SJosé Expósito { 115986402296SJosé Expósito __u8 *rdesc_frame = NULL; 116086402296SJosé Expósito int rc = 0; 116186402296SJosé Expósito 116286402296SJosé Expósito if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 116386402296SJosé Expósito return -EINVAL; 116486402296SJosé Expósito 116586402296SJosé Expósito rdesc_frame = uclogic_rdesc_template_apply( 116686402296SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_arr, 116786402296SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_size, 116886402296SJosé Expósito desc_params, UCLOGIC_RDESC_PH_ID_NUM); 116986402296SJosé Expósito if (!rdesc_frame) 117086402296SJosé Expósito return -ENOMEM; 117186402296SJosé Expósito 117286402296SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], 117386402296SJosé Expósito rdesc_frame, 117486402296SJosé Expósito uclogic_rdesc_ugee_v2_frame_btn_template_size, 117586402296SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 117686402296SJosé Expósito kfree(rdesc_frame); 117786402296SJosé Expósito return rc; 117886402296SJosé Expósito } 117986402296SJosé Expósito 118086402296SJosé Expósito /** 1181b67439d7SJosé Expósito * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a 1182b67439d7SJosé Expósito * bitmap dial. 1183b67439d7SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1184b67439d7SJosé Expósito * @desc_params: Device description params list. 1185b67439d7SJosé Expósito * @desc_params_size: Size of the description params list. 1186b67439d7SJosé Expósito * 1187b67439d7SJosé Expósito * Returns: 1188b67439d7SJosé Expósito * Zero, if successful. A negative errno code on error. 1189b67439d7SJosé Expósito */ 1190b67439d7SJosé Expósito static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p, 1191b67439d7SJosé Expósito const s32 *desc_params, 1192b67439d7SJosé Expósito size_t desc_params_size) 1193b67439d7SJosé Expósito { 1194b67439d7SJosé Expósito __u8 *rdesc_frame = NULL; 1195b67439d7SJosé Expósito int rc = 0; 1196b67439d7SJosé Expósito 1197b67439d7SJosé Expósito if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM) 1198b67439d7SJosé Expósito return -EINVAL; 1199b67439d7SJosé Expósito 1200b67439d7SJosé Expósito rdesc_frame = uclogic_rdesc_template_apply( 1201b67439d7SJosé Expósito uclogic_rdesc_ugee_v2_frame_dial_template_arr, 1202b67439d7SJosé Expósito uclogic_rdesc_ugee_v2_frame_dial_template_size, 1203b67439d7SJosé Expósito desc_params, UCLOGIC_RDESC_PH_ID_NUM); 1204b67439d7SJosé Expósito if (!rdesc_frame) 1205b67439d7SJosé Expósito return -ENOMEM; 1206b67439d7SJosé Expósito 1207b67439d7SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[0], 1208b67439d7SJosé Expósito rdesc_frame, 1209b67439d7SJosé Expósito uclogic_rdesc_ugee_v2_frame_dial_template_size, 1210b67439d7SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 1211b67439d7SJosé Expósito kfree(rdesc_frame); 1212b67439d7SJosé Expósito if (rc) 1213b67439d7SJosé Expósito return rc; 1214b67439d7SJosé Expósito 1215b67439d7SJosé Expósito p->frame_list[0].bitmap_dial_byte = 7; 1216b67439d7SJosé Expósito return 0; 1217b67439d7SJosé Expósito } 1218b67439d7SJosé Expósito 1219b67439d7SJosé Expósito /** 1220387dcab7SJosé Expósito * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a 1221387dcab7SJosé Expósito * mouse. 1222387dcab7SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1223387dcab7SJosé Expósito * 1224387dcab7SJosé Expósito * Returns: 1225387dcab7SJosé Expósito * Zero, if successful. A negative errno code on error. 1226387dcab7SJosé Expósito */ 1227387dcab7SJosé Expósito static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p) 1228387dcab7SJosé Expósito { 1229387dcab7SJosé Expósito int rc = 0; 1230387dcab7SJosé Expósito 1231387dcab7SJosé Expósito if (!p) 1232387dcab7SJosé Expósito return -EINVAL; 1233387dcab7SJosé Expósito 1234387dcab7SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], 1235387dcab7SJosé Expósito uclogic_rdesc_ugee_v2_frame_mouse_template_arr, 1236387dcab7SJosé Expósito uclogic_rdesc_ugee_v2_frame_mouse_template_size, 1237387dcab7SJosé Expósito UCLOGIC_RDESC_V1_FRAME_ID); 1238387dcab7SJosé Expósito return rc; 1239387dcab7SJosé Expósito } 1240387dcab7SJosé Expósito 1241387dcab7SJosé Expósito /** 1242f9ce4db0SJosé Expósito * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has 1243f9ce4db0SJosé Expósito * battery or not. 1244f9ce4db0SJosé Expósito * @hdev: The HID device of the tablet interface. 1245f9ce4db0SJosé Expósito * 1246f9ce4db0SJosé Expósito * Returns: 1247f9ce4db0SJosé Expósito * True if the device has battery, false otherwise. 1248f9ce4db0SJosé Expósito */ 1249f9ce4db0SJosé Expósito static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev) 1250f9ce4db0SJosé Expósito { 1251f60c377fSJosé Expósito struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 1252f60c377fSJosé Expósito 1253f60c377fSJosé Expósito if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK) 1254f60c377fSJosé Expósito return true; 1255f60c377fSJosé Expósito 1256f9ce4db0SJosé Expósito /* The XP-PEN Deco LW vendor, product and version are identical to the 1257f9ce4db0SJosé Expósito * Deco L. The only difference reported by their firmware is the product 1258f9ce4db0SJosé Expósito * name. Add a quirk to support battery reporting on the wireless 1259f9ce4db0SJosé Expósito * version. 1260f9ce4db0SJosé Expósito */ 1261f9ce4db0SJosé Expósito if (hdev->vendor == USB_VENDOR_ID_UGEE && 1262f9ce4db0SJosé Expósito hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) { 1263f9ce4db0SJosé Expósito struct usb_device *udev = hid_to_usb_dev(hdev); 1264f9ce4db0SJosé Expósito 1265f9ce4db0SJosé Expósito if (strstarts(udev->product, "Deco LW")) 1266f9ce4db0SJosé Expósito return true; 1267f9ce4db0SJosé Expósito } 1268f9ce4db0SJosé Expósito 1269f9ce4db0SJosé Expósito return false; 1270f9ce4db0SJosé Expósito } 1271f9ce4db0SJosé Expósito 1272f9ce4db0SJosé Expósito /** 1273f9ce4db0SJosé Expósito * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting. 1274f9ce4db0SJosé Expósito * @hdev: The HID device of the tablet interface, cannot be NULL. 1275f9ce4db0SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1276f9ce4db0SJosé Expósito * 1277f9ce4db0SJosé Expósito * Returns: 1278f9ce4db0SJosé Expósito * Zero, if successful. A negative errno code on error. 1279f9ce4db0SJosé Expósito */ 1280f9ce4db0SJosé Expósito static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev, 1281f9ce4db0SJosé Expósito struct uclogic_params *p) 1282f9ce4db0SJosé Expósito { 1283f9ce4db0SJosé Expósito int rc = 0; 1284f9ce4db0SJosé Expósito 1285f9ce4db0SJosé Expósito if (!hdev || !p) 1286f9ce4db0SJosé Expósito return -EINVAL; 1287f9ce4db0SJosé Expósito 1288f9ce4db0SJosé Expósito /* Some tablets contain invalid characters in hdev->uniq, throwing a 1289f9ce4db0SJosé Expósito * "hwmon: '<name>' is not a valid name attribute, please fix" error. 1290f9ce4db0SJosé Expósito * Use the device vendor and product IDs instead. 1291f9ce4db0SJosé Expósito */ 1292f9ce4db0SJosé Expósito snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor, 1293f9ce4db0SJosé Expósito hdev->product); 1294f9ce4db0SJosé Expósito 1295f9ce4db0SJosé Expósito rc = uclogic_params_frame_init_with_desc(&p->frame_list[1], 1296f9ce4db0SJosé Expósito uclogic_rdesc_ugee_v2_battery_template_arr, 1297f9ce4db0SJosé Expósito uclogic_rdesc_ugee_v2_battery_template_size, 1298f9ce4db0SJosé Expósito UCLOGIC_RDESC_UGEE_V2_BATTERY_ID); 1299f9ce4db0SJosé Expósito if (rc) 1300f9ce4db0SJosé Expósito return rc; 1301f9ce4db0SJosé Expósito 1302f9ce4db0SJosé Expósito p->frame_list[1].suffix = "Battery"; 1303f9ce4db0SJosé Expósito p->pen.subreport_list[1].value = 0xf2; 1304f9ce4db0SJosé Expósito p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID; 1305f9ce4db0SJosé Expósito 1306f9ce4db0SJosé Expósito return rc; 1307f9ce4db0SJosé Expósito } 1308f9ce4db0SJosé Expósito 1309f9ce4db0SJosé Expósito /** 1310a251d657SJosé Expósito * uclogic_params_ugee_v2_reconnect_work() - When a wireless tablet looses 1311a251d657SJosé Expósito * connection to the USB dongle and reconnects, either because of its physical 1312a251d657SJosé Expósito * distance or because it was switches off and on using the frame's switch, 1313a251d657SJosé Expósito * uclogic_probe_interface() needs to be called again to enable the tablet. 1314a251d657SJosé Expósito * 1315a251d657SJosé Expósito * @work: The work that triggered this function. 1316a251d657SJosé Expósito */ 1317a251d657SJosé Expósito static void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work) 1318a251d657SJosé Expósito { 1319a251d657SJosé Expósito struct uclogic_raw_event_hook *event_hook; 1320a251d657SJosé Expósito 1321a251d657SJosé Expósito event_hook = container_of(work, struct uclogic_raw_event_hook, work); 1322a251d657SJosé Expósito uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr, 1323a251d657SJosé Expósito uclogic_ugee_v2_probe_size, 1324a251d657SJosé Expósito uclogic_ugee_v2_probe_endpoint); 1325a251d657SJosé Expósito } 1326a251d657SJosé Expósito 1327a251d657SJosé Expósito /** 1328a251d657SJosé Expósito * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events 1329a251d657SJosé Expósito * to be hooked for UGEE v2 devices. 1330a251d657SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 1331a251d657SJosé Expósito * parameters from. 1332a251d657SJosé Expósito * @p: Parameters to fill in, cannot be NULL. 1333a251d657SJosé Expósito * 1334a251d657SJosé Expósito * Returns: 1335a251d657SJosé Expósito * Zero, if successful. A negative errno code on error. 1336a251d657SJosé Expósito */ 1337a251d657SJosé Expósito static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev, 1338a251d657SJosé Expósito struct uclogic_params *p) 1339a251d657SJosé Expósito { 1340a251d657SJosé Expósito struct uclogic_raw_event_hook *event_hook; 1341a251d657SJosé Expósito __u8 reconnect_event[] = { 1342a251d657SJosé Expósito /* Event received on wireless tablet reconnection */ 1343a251d657SJosé Expósito 0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 1344a251d657SJosé Expósito }; 1345a251d657SJosé Expósito 1346a251d657SJosé Expósito if (!p) 1347a251d657SJosé Expósito return -EINVAL; 1348a251d657SJosé Expósito 1349a251d657SJosé Expósito /* The reconnection event is only received if the tablet has battery */ 1350a251d657SJosé Expósito if (!uclogic_params_ugee_v2_has_battery(hdev)) 1351a251d657SJosé Expósito return 0; 1352a251d657SJosé Expósito 1353a251d657SJosé Expósito p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL); 1354a251d657SJosé Expósito if (!p->event_hooks) 1355a251d657SJosé Expósito return -ENOMEM; 1356a251d657SJosé Expósito 1357a251d657SJosé Expósito INIT_LIST_HEAD(&p->event_hooks->list); 1358a251d657SJosé Expósito 1359a251d657SJosé Expósito event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL); 1360a251d657SJosé Expósito if (!event_hook) 1361a251d657SJosé Expósito return -ENOMEM; 1362a251d657SJosé Expósito 1363a251d657SJosé Expósito INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work); 1364a251d657SJosé Expósito event_hook->hdev = hdev; 1365a251d657SJosé Expósito event_hook->size = ARRAY_SIZE(reconnect_event); 1366a251d657SJosé Expósito event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL); 1367a251d657SJosé Expósito if (!event_hook->event) 1368a251d657SJosé Expósito return -ENOMEM; 1369a251d657SJosé Expósito 1370a251d657SJosé Expósito list_add_tail(&event_hook->list, &p->event_hooks->list); 1371a251d657SJosé Expósito 1372a251d657SJosé Expósito return 0; 1373a251d657SJosé Expósito } 1374a251d657SJosé Expósito 1375a251d657SJosé Expósito /** 13760cb1fc09SJosé Expósito * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by 13770cb1fc09SJosé Expósito * discovering their parameters. 13780cb1fc09SJosé Expósito * 13790cb1fc09SJosé Expósito * These tables, internally designed as v2 to differentiate them from older 13800cb1fc09SJosé Expósito * models, expect a payload of magic data in orther to be switched to the fully 13810cb1fc09SJosé Expósito * functional mode and expose their parameters in a similar way to the 13820cb1fc09SJosé Expósito * information present in uclogic_params_pen_init_v1() but with some 13830cb1fc09SJosé Expósito * differences. 13840cb1fc09SJosé Expósito * 13850cb1fc09SJosé Expósito * @params: Parameters to fill in (to be cleaned with 13860cb1fc09SJosé Expósito * uclogic_params_cleanup()). Not modified in case of error. 13870cb1fc09SJosé Expósito * Cannot be NULL. 13880cb1fc09SJosé Expósito * @hdev: The HID device of the tablet interface to initialize and get 13890cb1fc09SJosé Expósito * parameters from. Cannot be NULL. 13900cb1fc09SJosé Expósito * 13910cb1fc09SJosé Expósito * Returns: 13920cb1fc09SJosé Expósito * Zero, if successful. A negative errno code on error. 13930cb1fc09SJosé Expósito */ 13940cb1fc09SJosé Expósito static int uclogic_params_ugee_v2_init(struct uclogic_params *params, 13950cb1fc09SJosé Expósito struct hid_device *hdev) 13960cb1fc09SJosé Expósito { 13970cb1fc09SJosé Expósito int rc = 0; 139814b71e6aSJosé Expósito struct uclogic_drvdata *drvdata; 13990cb1fc09SJosé Expósito struct usb_interface *iface; 14000cb1fc09SJosé Expósito __u8 bInterfaceNumber; 14010cb1fc09SJosé Expósito const int str_desc_len = 12; 14020cb1fc09SJosé Expósito __u8 *str_desc = NULL; 14030cb1fc09SJosé Expósito __u8 *rdesc_pen = NULL; 14040cb1fc09SJosé Expósito s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; 1405a092986fSJosé Expósito enum uclogic_params_frame_type frame_type; 14060cb1fc09SJosé Expósito /* The resulting parameters (noop) */ 14070cb1fc09SJosé Expósito struct uclogic_params p = {0, }; 14080cb1fc09SJosé Expósito 14090cb1fc09SJosé Expósito if (!params || !hdev) { 14100cb1fc09SJosé Expósito rc = -EINVAL; 14110cb1fc09SJosé Expósito goto cleanup; 14120cb1fc09SJosé Expósito } 14130cb1fc09SJosé Expósito 141414b71e6aSJosé Expósito drvdata = hid_get_drvdata(hdev); 14150cb1fc09SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 14160cb1fc09SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1417387dcab7SJosé Expósito 1418387dcab7SJosé Expósito if (bInterfaceNumber == 0) { 1419387dcab7SJosé Expósito rc = uclogic_params_ugee_v2_init_frame_mouse(&p); 1420387dcab7SJosé Expósito if (rc) 1421387dcab7SJosé Expósito goto cleanup; 1422387dcab7SJosé Expósito 1423387dcab7SJosé Expósito goto output; 1424387dcab7SJosé Expósito } 1425387dcab7SJosé Expósito 14260cb1fc09SJosé Expósito if (bInterfaceNumber != 2) { 14270cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 14280cb1fc09SJosé Expósito goto output; 14290cb1fc09SJosé Expósito } 14300cb1fc09SJosé Expósito 14310cb1fc09SJosé Expósito /* 14320cb1fc09SJosé Expósito * Initialize the interface by sending magic data. 14330cb1fc09SJosé Expósito * The specific data was discovered by sniffing the Windows driver 14340cb1fc09SJosé Expósito * traffic. 14350cb1fc09SJosé Expósito */ 1436bd85c131SJosé Expósito rc = uclogic_probe_interface(hdev, uclogic_ugee_v2_probe_arr, 1437bd85c131SJosé Expósito uclogic_ugee_v2_probe_size, 1438bd85c131SJosé Expósito uclogic_ugee_v2_probe_endpoint); 14390cb1fc09SJosé Expósito if (rc) { 14400cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 14410cb1fc09SJosé Expósito goto output; 14420cb1fc09SJosé Expósito } 14430cb1fc09SJosé Expósito 14440cb1fc09SJosé Expósito /* 14450cb1fc09SJosé Expósito * Read the string descriptor containing pen and frame parameters. 14460cb1fc09SJosé Expósito * The specific string descriptor and data were discovered by sniffing 14470cb1fc09SJosé Expósito * the Windows driver traffic. 14480cb1fc09SJosé Expósito */ 14490cb1fc09SJosé Expósito rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); 14500cb1fc09SJosé Expósito if (rc != str_desc_len) { 14510cb1fc09SJosé Expósito hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); 14520cb1fc09SJosé Expósito uclogic_params_init_invalid(&p); 14530cb1fc09SJosé Expósito goto output; 14540cb1fc09SJosé Expósito } 14550cb1fc09SJosé Expósito 1456a64cbf3cSJosé Expósito rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len, 1457a64cbf3cSJosé Expósito desc_params, 1458a092986fSJosé Expósito ARRAY_SIZE(desc_params), 1459a092986fSJosé Expósito &frame_type); 1460a64cbf3cSJosé Expósito if (rc) 1461a64cbf3cSJosé Expósito goto cleanup; 1462a64cbf3cSJosé Expósito 14630cb1fc09SJosé Expósito kfree(str_desc); 14640cb1fc09SJosé Expósito str_desc = NULL; 14650cb1fc09SJosé Expósito 14660cb1fc09SJosé Expósito /* Initialize the pen interface */ 14670cb1fc09SJosé Expósito rdesc_pen = uclogic_rdesc_template_apply( 14680cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_pen_template_arr, 14690cb1fc09SJosé Expósito uclogic_rdesc_ugee_v2_pen_template_size, 14700cb1fc09SJosé Expósito desc_params, ARRAY_SIZE(desc_params)); 14710cb1fc09SJosé Expósito if (!rdesc_pen) { 14720cb1fc09SJosé Expósito rc = -ENOMEM; 14730cb1fc09SJosé Expósito goto cleanup; 14740cb1fc09SJosé Expósito } 14750cb1fc09SJosé Expósito 14760cb1fc09SJosé Expósito p.pen.desc_ptr = rdesc_pen; 14770cb1fc09SJosé Expósito p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; 14780cb1fc09SJosé Expósito p.pen.id = 0x02; 14790cb1fc09SJosé Expósito p.pen.subreport_list[0].value = 0xf0; 14800cb1fc09SJosé Expósito p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; 14810cb1fc09SJosé Expósito 14820cb1fc09SJosé Expósito /* Initialize the frame interface */ 148314b71e6aSJosé Expósito if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK) 148414b71e6aSJosé Expósito frame_type = UCLOGIC_PARAMS_FRAME_MOUSE; 148514b71e6aSJosé Expósito 1486a092986fSJosé Expósito switch (frame_type) { 1487b67439d7SJosé Expósito case UCLOGIC_PARAMS_FRAME_DIAL: 1488b67439d7SJosé Expósito case UCLOGIC_PARAMS_FRAME_MOUSE: 1489b67439d7SJosé Expósito rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params, 1490b67439d7SJosé Expósito ARRAY_SIZE(desc_params)); 1491b67439d7SJosé Expósito break; 1492a092986fSJosé Expósito case UCLOGIC_PARAMS_FRAME_BUTTONS: 1493a092986fSJosé Expósito default: 149486402296SJosé Expósito rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params, 149586402296SJosé Expósito ARRAY_SIZE(desc_params)); 1496a092986fSJosé Expósito break; 1497a092986fSJosé Expósito } 1498a092986fSJosé Expósito 149986402296SJosé Expósito if (rc) 15000cb1fc09SJosé Expósito goto cleanup; 15010cb1fc09SJosé Expósito 1502f9ce4db0SJosé Expósito /* Initialize the battery interface*/ 1503f9ce4db0SJosé Expósito if (uclogic_params_ugee_v2_has_battery(hdev)) { 1504f9ce4db0SJosé Expósito rc = uclogic_params_ugee_v2_init_battery(hdev, &p); 1505f9ce4db0SJosé Expósito if (rc) { 1506f9ce4db0SJosé Expósito hid_err(hdev, "error initializing battery: %d\n", rc); 1507f9ce4db0SJosé Expósito goto cleanup; 1508f9ce4db0SJosé Expósito } 1509f9ce4db0SJosé Expósito } 1510f9ce4db0SJosé Expósito 1511a251d657SJosé Expósito /* Create a list of raw events to be ignored */ 1512a251d657SJosé Expósito rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p); 1513a251d657SJosé Expósito if (rc) { 1514a251d657SJosé Expósito hid_err(hdev, "error initializing event hook list: %d\n", rc); 1515a251d657SJosé Expósito goto cleanup; 1516a251d657SJosé Expósito } 1517a251d657SJosé Expósito 15180cb1fc09SJosé Expósito output: 15190cb1fc09SJosé Expósito /* Output parameters */ 15200cb1fc09SJosé Expósito memcpy(params, &p, sizeof(*params)); 15210cb1fc09SJosé Expósito memset(&p, 0, sizeof(p)); 15220cb1fc09SJosé Expósito rc = 0; 15230cb1fc09SJosé Expósito cleanup: 15240cb1fc09SJosé Expósito kfree(str_desc); 15250cb1fc09SJosé Expósito uclogic_params_cleanup(&p); 15260cb1fc09SJosé Expósito return rc; 15270cb1fc09SJosé Expósito } 15280cb1fc09SJosé Expósito 15290cb1fc09SJosé Expósito /** 15309614219eSNikolai Kondrashov * uclogic_params_init() - initialize a tablet interface and discover its 15319614219eSNikolai Kondrashov * parameters. 15329614219eSNikolai Kondrashov * 15339614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 15349614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 15359614219eSNikolai Kondrashov * Cannot be NULL. 15369614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 15378547b778SNikolai Kondrashov * parameters from. Cannot be NULL. Must be using the USB low-level 15388547b778SNikolai Kondrashov * driver, i.e. be an actual USB tablet. 15399614219eSNikolai Kondrashov * 15409614219eSNikolai Kondrashov * Returns: 15419614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 15429614219eSNikolai Kondrashov */ 15439614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params, 15449614219eSNikolai Kondrashov struct hid_device *hdev) 15459614219eSNikolai Kondrashov { 15469614219eSNikolai Kondrashov int rc; 1547f364c571SJosé Expósito struct usb_device *udev; 1548f364c571SJosé Expósito __u8 bNumInterfaces; 1549f364c571SJosé Expósito struct usb_interface *iface; 1550f364c571SJosé Expósito __u8 bInterfaceNumber; 15519614219eSNikolai Kondrashov bool found; 15529614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 15539614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 15549614219eSNikolai Kondrashov 15559614219eSNikolai Kondrashov /* Check arguments */ 1556f83baa0cSGreg Kroah-Hartman if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) { 15579614219eSNikolai Kondrashov rc = -EINVAL; 15589614219eSNikolai Kondrashov goto cleanup; 15599614219eSNikolai Kondrashov } 15609614219eSNikolai Kondrashov 1561f364c571SJosé Expósito udev = hid_to_usb_dev(hdev); 1562f364c571SJosé Expósito bNumInterfaces = udev->config->desc.bNumInterfaces; 1563f364c571SJosé Expósito iface = to_usb_interface(hdev->dev.parent); 1564f364c571SJosé Expósito bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 1565f364c571SJosé Expósito 15669614219eSNikolai Kondrashov /* 15679614219eSNikolai Kondrashov * Set replacement report descriptor if the original matches the 15689614219eSNikolai Kondrashov * specified size. Otherwise keep interface unchanged. 15699614219eSNikolai Kondrashov */ 15709614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 15719614219eSNikolai Kondrashov uclogic_params_init_with_opt_desc( \ 15729614219eSNikolai Kondrashov &p, hdev, \ 15739614219eSNikolai Kondrashov UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 15749614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_arr, \ 15759614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_size) 15769614219eSNikolai Kondrashov 15779614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \ 15789614219eSNikolai Kondrashov (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 15799614219eSNikolai Kondrashov 15809614219eSNikolai Kondrashov /* 15819614219eSNikolai Kondrashov * Handle specific interfaces for specific tablets. 15829614219eSNikolai Kondrashov * 15839614219eSNikolai Kondrashov * Observe the following logic: 15849614219eSNikolai Kondrashov * 15859614219eSNikolai Kondrashov * If the interface is recognized as producing certain useful input: 15869614219eSNikolai Kondrashov * Mark interface as valid. 15879614219eSNikolai Kondrashov * Output interface parameters. 15889614219eSNikolai Kondrashov * Else, if the interface is recognized as *not* producing any useful 15899614219eSNikolai Kondrashov * input: 15909614219eSNikolai Kondrashov * Mark interface as invalid. 15919614219eSNikolai Kondrashov * Else: 15929614219eSNikolai Kondrashov * Mark interface as valid. 15939614219eSNikolai Kondrashov * Output noop parameters. 15949614219eSNikolai Kondrashov * 15959614219eSNikolai Kondrashov * Rule of thumb: it is better to disable a broken interface than let 15969614219eSNikolai Kondrashov * it spew garbage input. 15979614219eSNikolai Kondrashov */ 15989614219eSNikolai Kondrashov 15999614219eSNikolai Kondrashov switch (VID_PID(hdev->vendor, hdev->product)) { 16009614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16019614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 16029614219eSNikolai Kondrashov rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 16039614219eSNikolai Kondrashov if (rc != 0) 16049614219eSNikolai Kondrashov goto cleanup; 16059614219eSNikolai Kondrashov break; 16069614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16079614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 16089614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 16099614219eSNikolai Kondrashov if (rc != 0) 16109614219eSNikolai Kondrashov goto cleanup; 16119614219eSNikolai Kondrashov break; 16129614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16139614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 16149c17f735SNikolai Kondrashov if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) { 16159c17f735SNikolai Kondrashov if (bInterfaceNumber == 0) { 16169c17f735SNikolai Kondrashov /* Try to probe v1 pen parameters */ 16179c17f735SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, 16189c17f735SNikolai Kondrashov &found, hdev); 16199c17f735SNikolai Kondrashov if (rc != 0) { 16209c17f735SNikolai Kondrashov hid_err(hdev, 16219c17f735SNikolai Kondrashov "pen probing failed: %d\n", 16229c17f735SNikolai Kondrashov rc); 16239c17f735SNikolai Kondrashov goto cleanup; 16249c17f735SNikolai Kondrashov } 16259c17f735SNikolai Kondrashov if (!found) { 16269c17f735SNikolai Kondrashov hid_warn(hdev, 16279c17f735SNikolai Kondrashov "pen parameters not found"); 16289c17f735SNikolai Kondrashov } 16299c17f735SNikolai Kondrashov } else { 16309c17f735SNikolai Kondrashov uclogic_params_init_invalid(&p); 16319c17f735SNikolai Kondrashov } 16329c17f735SNikolai Kondrashov } else { 16339614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 16349614219eSNikolai Kondrashov if (rc != 0) 16359614219eSNikolai Kondrashov goto cleanup; 16369c17f735SNikolai Kondrashov } 16379614219eSNikolai Kondrashov break; 16389614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16399614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 16409614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 16419614219eSNikolai Kondrashov if (rc != 0) 16429614219eSNikolai Kondrashov goto cleanup; 16439614219eSNikolai Kondrashov break; 16449614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16459614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 16469614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 16479614219eSNikolai Kondrashov if (rc != 0) 16489614219eSNikolai Kondrashov goto cleanup; 16499614219eSNikolai Kondrashov break; 16509614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16519614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 16529614219eSNikolai Kondrashov switch (bInterfaceNumber) { 16539614219eSNikolai Kondrashov case 0: 16549614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 16559614219eSNikolai Kondrashov if (rc != 0) 16569614219eSNikolai Kondrashov goto cleanup; 16579614219eSNikolai Kondrashov break; 16589614219eSNikolai Kondrashov case 1: 16599614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 16609614219eSNikolai Kondrashov if (rc != 0) 16619614219eSNikolai Kondrashov goto cleanup; 16629614219eSNikolai Kondrashov break; 16639614219eSNikolai Kondrashov case 2: 16649614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 16659614219eSNikolai Kondrashov if (rc != 0) 16669614219eSNikolai Kondrashov goto cleanup; 16679614219eSNikolai Kondrashov break; 16689614219eSNikolai Kondrashov } 16699614219eSNikolai Kondrashov break; 16709614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16719614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 16729614219eSNikolai Kondrashov /* 16739614219eSNikolai Kondrashov * If it is not a three-interface version, which is known to 16749614219eSNikolai Kondrashov * respond to initialization. 16759614219eSNikolai Kondrashov */ 16769614219eSNikolai Kondrashov if (bNumInterfaces != 3) { 16779614219eSNikolai Kondrashov switch (bInterfaceNumber) { 16789614219eSNikolai Kondrashov case 0: 16799614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG0, 16809614219eSNikolai Kondrashov twha60_fixed0); 16819614219eSNikolai Kondrashov if (rc != 0) 16829614219eSNikolai Kondrashov goto cleanup; 16839614219eSNikolai Kondrashov break; 16849614219eSNikolai Kondrashov case 1: 16859614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG1, 16869614219eSNikolai Kondrashov twha60_fixed1); 16879614219eSNikolai Kondrashov if (rc != 0) 16889614219eSNikolai Kondrashov goto cleanup; 16899614219eSNikolai Kondrashov break; 16909614219eSNikolai Kondrashov } 16919614219eSNikolai Kondrashov break; 16929614219eSNikolai Kondrashov } 1693df561f66SGustavo A. R. Silva fallthrough; 16949614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_HUION, 16959614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 1696315ffcc9SKyle Godbey case VID_PID(USB_VENDOR_ID_HUION, 169785e86071SNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET2): 16989614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 16999614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 17009614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 17019614219eSNikolai Kondrashov USB_DEVICE_ID_YIYNOVA_TABLET): 17029614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 17039614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 17049614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 17059614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 17069614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 17079614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 17080c15efe9SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 17090c15efe9SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 17109614219eSNikolai Kondrashov rc = uclogic_params_huion_init(&p, hdev); 17119614219eSNikolai Kondrashov if (rc != 0) 17129614219eSNikolai Kondrashov goto cleanup; 17139614219eSNikolai Kondrashov break; 17149614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGTIZER, 17159614219eSNikolai Kondrashov USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 1716022fc531SMartijn van de Streek case VID_PID(USB_VENDOR_ID_UGTIZER, 1717022fc531SMartijn van de Streek USB_DEVICE_ID_UGTIZER_TABLET_GT5040): 1718c3e5a67cSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1719c3e5a67cSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540): 1720492a9e9aSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1721492a9e9aSNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640): 172288bb346dSWang Xuerui case VID_PID(USB_VENDOR_ID_UGEE, 172361b1db5aSRoman Romanenko USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06): 172461b1db5aSRoman Romanenko case VID_PID(USB_VENDOR_ID_UGEE, 172588bb346dSWang Xuerui USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720): 17269614219eSNikolai Kondrashov /* If this is the pen interface */ 17279614219eSNikolai Kondrashov if (bInterfaceNumber == 1) { 1728eecb5b84SNikolai Kondrashov /* Probe v1 pen parameters */ 1729eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 17309614219eSNikolai Kondrashov if (rc != 0) { 17319614219eSNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 17329614219eSNikolai Kondrashov goto cleanup; 17339614219eSNikolai Kondrashov } 17349614219eSNikolai Kondrashov if (!found) { 17359614219eSNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 17369614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 17379614219eSNikolai Kondrashov } 17389614219eSNikolai Kondrashov } else { 1739606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 17409614219eSNikolai Kondrashov } 17419614219eSNikolai Kondrashov break; 17421ee7c685SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 174308367be1SNikolai Kondrashov USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01): 174408367be1SNikolai Kondrashov /* If this is the pen and frame interface */ 174508367be1SNikolai Kondrashov if (bInterfaceNumber == 1) { 174608367be1SNikolai Kondrashov /* Probe v1 pen parameters */ 174708367be1SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 174808367be1SNikolai Kondrashov if (rc != 0) { 174908367be1SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 175008367be1SNikolai Kondrashov goto cleanup; 175108367be1SNikolai Kondrashov } 175208367be1SNikolai Kondrashov /* Initialize frame parameters */ 175308367be1SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1754337fa051SNikolai Kondrashov &p.frame_list[0], 175508367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_arr, 175608367be1SNikolai Kondrashov uclogic_rdesc_xppen_deco01_frame_size, 175708367be1SNikolai Kondrashov 0); 175808367be1SNikolai Kondrashov if (rc != 0) 175908367be1SNikolai Kondrashov goto cleanup; 176008367be1SNikolai Kondrashov } else { 1761606dadc1SNikolai Kondrashov uclogic_params_init_invalid(&p); 176208367be1SNikolai Kondrashov } 176308367be1SNikolai Kondrashov break; 17640cb1fc09SJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 17657495fb7eSJosé Expósito USB_DEVICE_ID_UGEE_PARBLO_A610_PRO): 17667495fb7eSJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 17670cb1fc09SJosé Expósito USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): 176893b40b3eSJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 176993b40b3eSJosé Expósito USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S): 1770*7744ca57SJosé Expósito case VID_PID(USB_VENDOR_ID_UGEE, 1771*7744ca57SJosé Expósito USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW): 17720cb1fc09SJosé Expósito rc = uclogic_params_ugee_v2_init(&p, hdev); 17730cb1fc09SJosé Expósito if (rc != 0) 17740cb1fc09SJosé Expósito goto cleanup; 17750cb1fc09SJosé Expósito break; 1776f7271b2aSCristian Klein case VID_PID(USB_VENDOR_ID_TRUST, 1777f7271b2aSCristian Klein USB_DEVICE_ID_TRUST_PANORA_TABLET): 177808367be1SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 1779e902ed93SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_G5): 1780e902ed93SNikolai Kondrashov /* Ignore non-pen interfaces */ 1781e902ed93SNikolai Kondrashov if (bInterfaceNumber != 1) { 1782e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1783e902ed93SNikolai Kondrashov break; 1784e902ed93SNikolai Kondrashov } 1785e902ed93SNikolai Kondrashov 1786e902ed93SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 1787e902ed93SNikolai Kondrashov if (rc != 0) { 1788e902ed93SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 1789e902ed93SNikolai Kondrashov goto cleanup; 1790e902ed93SNikolai Kondrashov } else if (found) { 1791e902ed93SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1792337fa051SNikolai Kondrashov &p.frame_list[0], 1793e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_arr, 1794e902ed93SNikolai Kondrashov uclogic_rdesc_ugee_g5_frame_size, 1795e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_ID); 1796e902ed93SNikolai Kondrashov if (rc != 0) { 1797e902ed93SNikolai Kondrashov hid_err(hdev, 17982e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 1799e902ed93SNikolai Kondrashov rc); 1800e902ed93SNikolai Kondrashov goto cleanup; 1801e902ed93SNikolai Kondrashov } 1802337fa051SNikolai Kondrashov p.frame_list[0].re_lsb = 1803e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB; 1804337fa051SNikolai Kondrashov p.frame_list[0].dev_id_byte = 1805e902ed93SNikolai Kondrashov UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE; 1806e902ed93SNikolai Kondrashov } else { 1807e902ed93SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 1808e902ed93SNikolai Kondrashov uclogic_params_init_invalid(&p); 1809e902ed93SNikolai Kondrashov } 1810e902ed93SNikolai Kondrashov 1811e902ed93SNikolai Kondrashov break; 1812e902ed93SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 18131ee7c685SNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_EX07S): 18141ee7c685SNikolai Kondrashov /* Ignore non-pen interfaces */ 18151ee7c685SNikolai Kondrashov if (bInterfaceNumber != 1) { 18161ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 18171ee7c685SNikolai Kondrashov break; 18181ee7c685SNikolai Kondrashov } 18191ee7c685SNikolai Kondrashov 18201ee7c685SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 18211ee7c685SNikolai Kondrashov if (rc != 0) { 18221ee7c685SNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 18231ee7c685SNikolai Kondrashov goto cleanup; 18241ee7c685SNikolai Kondrashov } else if (found) { 18251ee7c685SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 1826337fa051SNikolai Kondrashov &p.frame_list[0], 18272e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_arr, 18282e28f3e0SNikolai Kondrashov uclogic_rdesc_ugee_ex07_frame_size, 18291ee7c685SNikolai Kondrashov 0); 18301ee7c685SNikolai Kondrashov if (rc != 0) { 18311ee7c685SNikolai Kondrashov hid_err(hdev, 18322e28f3e0SNikolai Kondrashov "failed creating frame parameters: %d\n", 18331ee7c685SNikolai Kondrashov rc); 18341ee7c685SNikolai Kondrashov goto cleanup; 18351ee7c685SNikolai Kondrashov } 18361ee7c685SNikolai Kondrashov } else { 18371ee7c685SNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 18381ee7c685SNikolai Kondrashov uclogic_params_init_invalid(&p); 18391ee7c685SNikolai Kondrashov } 18401ee7c685SNikolai Kondrashov 18411ee7c685SNikolai Kondrashov break; 18429614219eSNikolai Kondrashov } 18439614219eSNikolai Kondrashov 18449614219eSNikolai Kondrashov #undef VID_PID 18459614219eSNikolai Kondrashov #undef WITH_OPT_DESC 18469614219eSNikolai Kondrashov 18479614219eSNikolai Kondrashov /* Output parameters */ 18489614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 18499614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 18509614219eSNikolai Kondrashov rc = 0; 18519614219eSNikolai Kondrashov cleanup: 18529614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 18539614219eSNikolai Kondrashov return rc; 18549614219eSNikolai Kondrashov } 1855a64cbf3cSJosé Expósito 1856a64cbf3cSJosé Expósito #ifdef CONFIG_HID_KUNIT_TEST 1857a64cbf3cSJosé Expósito #include "hid-uclogic-params-test.c" 1858a64cbf3cSJosé Expósito #endif 1859