19614219eSNikolai Kondrashov // SPDX-License-Identifier: GPL-2.0+ 29614219eSNikolai Kondrashov /* 39614219eSNikolai Kondrashov * HID driver for UC-Logic devices not fully compliant with HID standard 49614219eSNikolai Kondrashov * - tablet initialization and parameter retrieval 59614219eSNikolai Kondrashov * 69614219eSNikolai Kondrashov * Copyright (c) 2018 Nikolai Kondrashov 79614219eSNikolai Kondrashov */ 89614219eSNikolai Kondrashov 99614219eSNikolai Kondrashov /* 109614219eSNikolai Kondrashov * This program is free software; you can redistribute it and/or modify it 119614219eSNikolai Kondrashov * under the terms of the GNU General Public License as published by the Free 129614219eSNikolai Kondrashov * Software Foundation; either version 2 of the License, or (at your option) 139614219eSNikolai Kondrashov * any later version. 149614219eSNikolai Kondrashov */ 159614219eSNikolai Kondrashov 169614219eSNikolai Kondrashov #include "hid-uclogic-params.h" 179614219eSNikolai Kondrashov #include "hid-uclogic-rdesc.h" 189614219eSNikolai Kondrashov #include "usbhid/usbhid.h" 199614219eSNikolai Kondrashov #include "hid-ids.h" 209614219eSNikolai Kondrashov #include <linux/ctype.h> 219614219eSNikolai Kondrashov #include <asm/unaligned.h> 229614219eSNikolai Kondrashov 239614219eSNikolai Kondrashov /** 249614219eSNikolai Kondrashov * Convert a pen in-range reporting type to a string. 259614219eSNikolai Kondrashov * 269614219eSNikolai Kondrashov * @inrange: The in-range reporting type to convert. 279614219eSNikolai Kondrashov * 289614219eSNikolai Kondrashov * Returns: 299614219eSNikolai Kondrashov * The string representing the type, or NULL if the type is unknown. 309614219eSNikolai Kondrashov */ 319614219eSNikolai Kondrashov const char *uclogic_params_pen_inrange_to_str( 329614219eSNikolai Kondrashov enum uclogic_params_pen_inrange inrange) 339614219eSNikolai Kondrashov { 349614219eSNikolai Kondrashov switch (inrange) { 359614219eSNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL: 369614219eSNikolai Kondrashov return "normal"; 379614219eSNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED: 389614219eSNikolai Kondrashov return "inverted"; 3901309e29SNikolai Kondrashov case UCLOGIC_PARAMS_PEN_INRANGE_NONE: 4001309e29SNikolai Kondrashov return "none"; 419614219eSNikolai Kondrashov default: 429614219eSNikolai Kondrashov return NULL; 439614219eSNikolai Kondrashov } 449614219eSNikolai Kondrashov } 459614219eSNikolai Kondrashov 469614219eSNikolai Kondrashov /** 479614219eSNikolai Kondrashov * uclogic_params_get_str_desc - retrieve a string descriptor from a HID 489614219eSNikolai Kondrashov * device interface, putting it into a kmalloc-allocated buffer as is, without 499614219eSNikolai Kondrashov * character encoding conversion. 509614219eSNikolai Kondrashov * 519614219eSNikolai Kondrashov * @pbuf: Location for the kmalloc-allocated buffer pointer containing 529614219eSNikolai Kondrashov * the retrieved descriptor. Not modified in case of error. 539614219eSNikolai Kondrashov * Can be NULL to have retrieved descriptor discarded. 549614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to retrieve the string 559614219eSNikolai Kondrashov * descriptor from. Cannot be NULL. 569614219eSNikolai Kondrashov * @idx: Index of the string descriptor to request from the device. 579614219eSNikolai Kondrashov * @len: Length of the buffer to allocate and the data to retrieve. 589614219eSNikolai Kondrashov * 599614219eSNikolai Kondrashov * Returns: 609614219eSNikolai Kondrashov * number of bytes retrieved (<= len), 619614219eSNikolai Kondrashov * -EPIPE, if the descriptor was not found, or 629614219eSNikolai Kondrashov * another negative errno code in case of other error. 639614219eSNikolai Kondrashov */ 649614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev, 659614219eSNikolai Kondrashov __u8 idx, size_t len) 669614219eSNikolai Kondrashov { 679614219eSNikolai Kondrashov int rc; 689614219eSNikolai Kondrashov struct usb_device *udev = hid_to_usb_dev(hdev); 699614219eSNikolai Kondrashov __u8 *buf = NULL; 709614219eSNikolai Kondrashov 719614219eSNikolai Kondrashov /* Check arguments */ 729614219eSNikolai Kondrashov if (hdev == NULL) { 739614219eSNikolai Kondrashov rc = -EINVAL; 749614219eSNikolai Kondrashov goto cleanup; 759614219eSNikolai Kondrashov } 769614219eSNikolai Kondrashov 779614219eSNikolai Kondrashov buf = kmalloc(len, GFP_KERNEL); 789614219eSNikolai Kondrashov if (buf == NULL) { 799614219eSNikolai Kondrashov rc = -ENOMEM; 809614219eSNikolai Kondrashov goto cleanup; 819614219eSNikolai Kondrashov } 829614219eSNikolai Kondrashov 839614219eSNikolai Kondrashov rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 849614219eSNikolai Kondrashov USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, 859614219eSNikolai Kondrashov (USB_DT_STRING << 8) + idx, 869614219eSNikolai Kondrashov 0x0409, buf, len, 879614219eSNikolai Kondrashov USB_CTRL_GET_TIMEOUT); 889614219eSNikolai Kondrashov if (rc == -EPIPE) { 899614219eSNikolai Kondrashov hid_dbg(hdev, "string descriptor #%hhu not found\n", idx); 909614219eSNikolai Kondrashov goto cleanup; 919614219eSNikolai Kondrashov } else if (rc < 0) { 929614219eSNikolai Kondrashov hid_err(hdev, 939614219eSNikolai Kondrashov "failed retrieving string descriptor #%hhu: %d\n", 949614219eSNikolai Kondrashov idx, rc); 959614219eSNikolai Kondrashov goto cleanup; 969614219eSNikolai Kondrashov } 979614219eSNikolai Kondrashov 989614219eSNikolai Kondrashov if (pbuf != NULL) { 999614219eSNikolai Kondrashov *pbuf = buf; 1009614219eSNikolai Kondrashov buf = NULL; 1019614219eSNikolai Kondrashov } 1029614219eSNikolai Kondrashov 1039614219eSNikolai Kondrashov cleanup: 1049614219eSNikolai Kondrashov kfree(buf); 1059614219eSNikolai Kondrashov return rc; 1069614219eSNikolai Kondrashov } 1079614219eSNikolai Kondrashov 1089614219eSNikolai Kondrashov /** 1099614219eSNikolai Kondrashov * uclogic_params_pen_cleanup - free resources used by struct 1109614219eSNikolai Kondrashov * uclogic_params_pen (tablet interface's pen input parameters). 1119614219eSNikolai Kondrashov * Can be called repeatedly. 1129614219eSNikolai Kondrashov * 1139614219eSNikolai Kondrashov * @pen: Pen input parameters to cleanup. Cannot be NULL. 1149614219eSNikolai Kondrashov */ 1159614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen) 1169614219eSNikolai Kondrashov { 1179614219eSNikolai Kondrashov kfree(pen->desc_ptr); 1189614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 1199614219eSNikolai Kondrashov } 1209614219eSNikolai Kondrashov 1219614219eSNikolai Kondrashov /** 122eecb5b84SNikolai Kondrashov * uclogic_params_pen_init_v1() - initialize tablet interface pen 123eecb5b84SNikolai Kondrashov * input and retrieve its parameters from the device, using v1 protocol. 1249614219eSNikolai Kondrashov * 1259614219eSNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 1269614219eSNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not modified in 1279614219eSNikolai Kondrashov * case of error, or if parameters are not found. Cannot be NULL. 1289614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 1299614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 1309614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 1319614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 1329614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 1339614219eSNikolai Kondrashov * 1349614219eSNikolai Kondrashov * Returns: 1359614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 1369614219eSNikolai Kondrashov */ 137eecb5b84SNikolai Kondrashov static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, 1389614219eSNikolai Kondrashov bool *pfound, 1399614219eSNikolai Kondrashov struct hid_device *hdev) 1409614219eSNikolai Kondrashov { 1419614219eSNikolai Kondrashov int rc; 1429614219eSNikolai Kondrashov bool found = false; 1439614219eSNikolai Kondrashov /* Buffer for (part of) the string descriptor */ 1449614219eSNikolai Kondrashov __u8 *buf = NULL; 1459614219eSNikolai Kondrashov /* Minimum descriptor length required, maximum seen so far is 18 */ 1469614219eSNikolai Kondrashov const int len = 12; 1479614219eSNikolai Kondrashov s32 resolution; 1489614219eSNikolai Kondrashov /* Pen report descriptor template parameters */ 1499614219eSNikolai Kondrashov s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; 1509614219eSNikolai Kondrashov __u8 *desc_ptr = NULL; 1519614219eSNikolai Kondrashov 1529614219eSNikolai Kondrashov /* Check arguments */ 1539614219eSNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 1549614219eSNikolai Kondrashov rc = -EINVAL; 1559614219eSNikolai Kondrashov goto cleanup; 1569614219eSNikolai Kondrashov } 1579614219eSNikolai Kondrashov 1589614219eSNikolai Kondrashov /* 1599614219eSNikolai Kondrashov * Read string descriptor containing pen input parameters. 1609614219eSNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 1619614219eSNikolai Kondrashov * the Windows driver traffic. 1629614219eSNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 1639614219eSNikolai Kondrashov */ 1649614219eSNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 100, len); 1659614219eSNikolai Kondrashov if (rc == -EPIPE) { 1669614219eSNikolai Kondrashov hid_dbg(hdev, 1679614219eSNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 1689614219eSNikolai Kondrashov goto finish; 1699614219eSNikolai Kondrashov } else if (rc < 0) { 1709614219eSNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 1719614219eSNikolai Kondrashov goto cleanup; 1729614219eSNikolai Kondrashov } else if (rc != len) { 1739614219eSNikolai Kondrashov hid_dbg(hdev, 1749614219eSNikolai Kondrashov "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", 1759614219eSNikolai Kondrashov rc, len); 1769614219eSNikolai Kondrashov goto finish; 1779614219eSNikolai Kondrashov } 1789614219eSNikolai Kondrashov 1799614219eSNikolai Kondrashov /* 1809614219eSNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 1819614219eSNikolai Kondrashov */ 1829614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 1839614219eSNikolai Kondrashov get_unaligned_le16(buf + 2); 1849614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 1859614219eSNikolai Kondrashov get_unaligned_le16(buf + 4); 1869614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 1879614219eSNikolai Kondrashov get_unaligned_le16(buf + 8); 1889614219eSNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 1899614219eSNikolai Kondrashov if (resolution == 0) { 1909614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 1919614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 1929614219eSNikolai Kondrashov } else { 1939614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 1949614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 1959614219eSNikolai Kondrashov resolution; 1969614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 1979614219eSNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 1989614219eSNikolai Kondrashov resolution; 1999614219eSNikolai Kondrashov } 2009614219eSNikolai Kondrashov kfree(buf); 2019614219eSNikolai Kondrashov buf = NULL; 2029614219eSNikolai Kondrashov 2039614219eSNikolai Kondrashov /* 2049614219eSNikolai Kondrashov * Generate pen report descriptor 2059614219eSNikolai Kondrashov */ 2069614219eSNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 207eecb5b84SNikolai Kondrashov uclogic_rdesc_pen_v1_template_arr, 208eecb5b84SNikolai Kondrashov uclogic_rdesc_pen_v1_template_size, 2099614219eSNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 2109614219eSNikolai Kondrashov if (desc_ptr == NULL) { 2119614219eSNikolai Kondrashov rc = -ENOMEM; 2129614219eSNikolai Kondrashov goto cleanup; 2139614219eSNikolai Kondrashov } 2149614219eSNikolai Kondrashov 2159614219eSNikolai Kondrashov /* 2169614219eSNikolai Kondrashov * Fill-in the parameters 2179614219eSNikolai Kondrashov */ 2189614219eSNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 2199614219eSNikolai Kondrashov pen->desc_ptr = desc_ptr; 2209614219eSNikolai Kondrashov desc_ptr = NULL; 221eecb5b84SNikolai Kondrashov pen->desc_size = uclogic_rdesc_pen_v1_template_size; 222eecb5b84SNikolai Kondrashov pen->id = UCLOGIC_RDESC_PEN_V1_ID; 2239614219eSNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED; 2249614219eSNikolai Kondrashov found = true; 2259614219eSNikolai Kondrashov finish: 2269614219eSNikolai Kondrashov *pfound = found; 2279614219eSNikolai Kondrashov rc = 0; 2289614219eSNikolai Kondrashov cleanup: 2299614219eSNikolai Kondrashov kfree(desc_ptr); 2309614219eSNikolai Kondrashov kfree(buf); 2319614219eSNikolai Kondrashov return rc; 2329614219eSNikolai Kondrashov } 2339614219eSNikolai Kondrashov 2349614219eSNikolai Kondrashov /** 2352c3a88c6SNikolai Kondrashov * uclogic_params_get_le24() - get a 24-bit little-endian number from a 2362c3a88c6SNikolai Kondrashov * buffer. 2372c3a88c6SNikolai Kondrashov * 2382c3a88c6SNikolai Kondrashov * @p: The pointer to the number buffer. 2392c3a88c6SNikolai Kondrashov * 2402c3a88c6SNikolai Kondrashov * Returns: 2412c3a88c6SNikolai Kondrashov * The retrieved number 2422c3a88c6SNikolai Kondrashov */ 2432c3a88c6SNikolai Kondrashov static s32 uclogic_params_get_le24(const void *p) 2442c3a88c6SNikolai Kondrashov { 2452c3a88c6SNikolai Kondrashov const __u8 *b = p; 2462c3a88c6SNikolai Kondrashov return b[0] | (b[1] << 8UL) | (b[2] << 16UL); 2472c3a88c6SNikolai Kondrashov } 2482c3a88c6SNikolai Kondrashov 2492c3a88c6SNikolai Kondrashov /** 2502c3a88c6SNikolai Kondrashov * uclogic_params_pen_init_v2() - initialize tablet interface pen 2512c3a88c6SNikolai Kondrashov * input and retrieve its parameters from the device, using v2 protocol. 2522c3a88c6SNikolai Kondrashov * 2532c3a88c6SNikolai Kondrashov * @pen: Pointer to the pen parameters to initialize (to be 2542c3a88c6SNikolai Kondrashov * cleaned up with uclogic_params_pen_cleanup()). Not modified in 2552c3a88c6SNikolai Kondrashov * case of error, or if parameters are not found. Cannot be NULL. 2562c3a88c6SNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 2572c3a88c6SNikolai Kondrashov * were found, and to false if not (e.g. device was 2582c3a88c6SNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 2592c3a88c6SNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 2602c3a88c6SNikolai Kondrashov * parameters from. Cannot be NULL. 2612c3a88c6SNikolai Kondrashov * 2622c3a88c6SNikolai Kondrashov * Returns: 2632c3a88c6SNikolai Kondrashov * Zero, if successful. A negative errno code on error. 2642c3a88c6SNikolai Kondrashov */ 2652c3a88c6SNikolai Kondrashov static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, 2662c3a88c6SNikolai Kondrashov bool *pfound, 2672c3a88c6SNikolai Kondrashov struct hid_device *hdev) 2682c3a88c6SNikolai Kondrashov { 2692c3a88c6SNikolai Kondrashov int rc; 2702c3a88c6SNikolai Kondrashov bool found = false; 2712c3a88c6SNikolai Kondrashov /* Buffer for (part of) the string descriptor */ 2722c3a88c6SNikolai Kondrashov __u8 *buf = NULL; 2732c3a88c6SNikolai Kondrashov /* Descriptor length required */ 2742c3a88c6SNikolai Kondrashov const int len = 18; 2752c3a88c6SNikolai Kondrashov s32 resolution; 2762c3a88c6SNikolai Kondrashov /* Pen report descriptor template parameters */ 2772c3a88c6SNikolai Kondrashov s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; 2782c3a88c6SNikolai Kondrashov __u8 *desc_ptr = NULL; 2792c3a88c6SNikolai Kondrashov 2802c3a88c6SNikolai Kondrashov /* Check arguments */ 2812c3a88c6SNikolai Kondrashov if (pen == NULL || pfound == NULL || hdev == NULL) { 2822c3a88c6SNikolai Kondrashov rc = -EINVAL; 2832c3a88c6SNikolai Kondrashov goto cleanup; 2842c3a88c6SNikolai Kondrashov } 2852c3a88c6SNikolai Kondrashov 2862c3a88c6SNikolai Kondrashov /* 2872c3a88c6SNikolai Kondrashov * Read string descriptor containing pen input parameters. 2882c3a88c6SNikolai Kondrashov * The specific string descriptor and data were discovered by sniffing 2892c3a88c6SNikolai Kondrashov * the Windows driver traffic. 2902c3a88c6SNikolai Kondrashov * NOTE: This enables fully-functional tablet mode. 2912c3a88c6SNikolai Kondrashov */ 2922c3a88c6SNikolai Kondrashov rc = uclogic_params_get_str_desc(&buf, hdev, 200, len); 2932c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 2942c3a88c6SNikolai Kondrashov hid_dbg(hdev, 2952c3a88c6SNikolai Kondrashov "string descriptor with pen parameters not found, assuming not compatible\n"); 2962c3a88c6SNikolai Kondrashov goto finish; 2972c3a88c6SNikolai Kondrashov } else if (rc < 0) { 2982c3a88c6SNikolai Kondrashov hid_err(hdev, "failed retrieving pen parameters: %d\n", rc); 2992c3a88c6SNikolai Kondrashov goto cleanup; 3002c3a88c6SNikolai Kondrashov } else if (rc != len) { 3012c3a88c6SNikolai Kondrashov hid_dbg(hdev, 3022c3a88c6SNikolai Kondrashov "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n", 3032c3a88c6SNikolai Kondrashov rc, len); 3042c3a88c6SNikolai Kondrashov goto finish; 3052c3a88c6SNikolai Kondrashov } else { 3062c3a88c6SNikolai Kondrashov size_t i; 3072c3a88c6SNikolai Kondrashov /* 3082c3a88c6SNikolai Kondrashov * Check it's not just a catch-all UTF-16LE-encoded ASCII 3092c3a88c6SNikolai Kondrashov * string (such as the model name) some tablets put into all 3102c3a88c6SNikolai Kondrashov * unknown string descriptors. 3112c3a88c6SNikolai Kondrashov */ 3122c3a88c6SNikolai Kondrashov for (i = 2; 3132c3a88c6SNikolai Kondrashov i < len && 3142c3a88c6SNikolai Kondrashov (buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0); 3152c3a88c6SNikolai Kondrashov i += 2); 3162c3a88c6SNikolai Kondrashov if (i >= len) { 3172c3a88c6SNikolai Kondrashov hid_dbg(hdev, 3182c3a88c6SNikolai Kondrashov "string descriptor with pen parameters seems to contain only text, assuming not compatible\n"); 3192c3a88c6SNikolai Kondrashov goto finish; 3202c3a88c6SNikolai Kondrashov } 3212c3a88c6SNikolai Kondrashov } 3222c3a88c6SNikolai Kondrashov 3232c3a88c6SNikolai Kondrashov /* 3242c3a88c6SNikolai Kondrashov * Fill report descriptor parameters from the string descriptor 3252c3a88c6SNikolai Kondrashov */ 3262c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 3272c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 2); 3282c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 3292c3a88c6SNikolai Kondrashov uclogic_params_get_le24(buf + 5); 3302c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 3312c3a88c6SNikolai Kondrashov get_unaligned_le16(buf + 8); 3322c3a88c6SNikolai Kondrashov resolution = get_unaligned_le16(buf + 10); 3332c3a88c6SNikolai Kondrashov if (resolution == 0) { 3342c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; 3352c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; 3362c3a88c6SNikolai Kondrashov } else { 3372c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 3382c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / 3392c3a88c6SNikolai Kondrashov resolution; 3402c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 3412c3a88c6SNikolai Kondrashov desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / 3422c3a88c6SNikolai Kondrashov resolution; 3432c3a88c6SNikolai Kondrashov } 3442c3a88c6SNikolai Kondrashov kfree(buf); 3452c3a88c6SNikolai Kondrashov buf = NULL; 3462c3a88c6SNikolai Kondrashov 3472c3a88c6SNikolai Kondrashov /* 3482c3a88c6SNikolai Kondrashov * Generate pen report descriptor 3492c3a88c6SNikolai Kondrashov */ 3502c3a88c6SNikolai Kondrashov desc_ptr = uclogic_rdesc_template_apply( 3512c3a88c6SNikolai Kondrashov uclogic_rdesc_pen_v2_template_arr, 3522c3a88c6SNikolai Kondrashov uclogic_rdesc_pen_v2_template_size, 3532c3a88c6SNikolai Kondrashov desc_params, ARRAY_SIZE(desc_params)); 3542c3a88c6SNikolai Kondrashov if (desc_ptr == NULL) { 3552c3a88c6SNikolai Kondrashov rc = -ENOMEM; 3562c3a88c6SNikolai Kondrashov goto cleanup; 3572c3a88c6SNikolai Kondrashov } 3582c3a88c6SNikolai Kondrashov 3592c3a88c6SNikolai Kondrashov /* 3602c3a88c6SNikolai Kondrashov * Fill-in the parameters 3612c3a88c6SNikolai Kondrashov */ 3622c3a88c6SNikolai Kondrashov memset(pen, 0, sizeof(*pen)); 3632c3a88c6SNikolai Kondrashov pen->desc_ptr = desc_ptr; 3642c3a88c6SNikolai Kondrashov desc_ptr = NULL; 3652c3a88c6SNikolai Kondrashov pen->desc_size = uclogic_rdesc_pen_v2_template_size; 3662c3a88c6SNikolai Kondrashov pen->id = UCLOGIC_RDESC_PEN_V2_ID; 3672c3a88c6SNikolai Kondrashov pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE; 3682c3a88c6SNikolai Kondrashov pen->fragmented_hires = true; 3692c3a88c6SNikolai Kondrashov found = true; 3702c3a88c6SNikolai Kondrashov finish: 3712c3a88c6SNikolai Kondrashov *pfound = found; 3722c3a88c6SNikolai Kondrashov rc = 0; 3732c3a88c6SNikolai Kondrashov cleanup: 3742c3a88c6SNikolai Kondrashov kfree(desc_ptr); 3752c3a88c6SNikolai Kondrashov kfree(buf); 3762c3a88c6SNikolai Kondrashov return rc; 3772c3a88c6SNikolai Kondrashov } 3782c3a88c6SNikolai Kondrashov 3792c3a88c6SNikolai Kondrashov /** 3809614219eSNikolai Kondrashov * uclogic_params_frame_cleanup - free resources used by struct 3819614219eSNikolai Kondrashov * uclogic_params_frame (tablet interface's frame controls input parameters). 3829614219eSNikolai Kondrashov * Can be called repeatedly. 3839614219eSNikolai Kondrashov * 3849614219eSNikolai Kondrashov * @frame: Frame controls input parameters to cleanup. Cannot be NULL. 3859614219eSNikolai Kondrashov */ 3869614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame) 3879614219eSNikolai Kondrashov { 3889614219eSNikolai Kondrashov kfree(frame->desc_ptr); 3899614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 3909614219eSNikolai Kondrashov } 3919614219eSNikolai Kondrashov 3929614219eSNikolai Kondrashov /** 3939614219eSNikolai Kondrashov * uclogic_params_frame_init_with_desc() - initialize tablet's frame control 3949614219eSNikolai Kondrashov * parameters with a static report descriptor. 3959614219eSNikolai Kondrashov * 3969614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 3979614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 3989614219eSNikolai Kondrashov * of error. Cannot be NULL. 3999614219eSNikolai Kondrashov * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero. 4009614219eSNikolai Kondrashov * @desc_size: Report descriptor size. 4019614219eSNikolai Kondrashov * @id: Report ID used for frame reports, if they should be tweaked, 4029614219eSNikolai Kondrashov * zero if not. 4039614219eSNikolai Kondrashov * 4049614219eSNikolai Kondrashov * Returns: 4059614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 4069614219eSNikolai Kondrashov */ 4079614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc( 4089614219eSNikolai Kondrashov struct uclogic_params_frame *frame, 4099614219eSNikolai Kondrashov const __u8 *desc_ptr, 4109614219eSNikolai Kondrashov size_t desc_size, 4119614219eSNikolai Kondrashov unsigned int id) 4129614219eSNikolai Kondrashov { 4139614219eSNikolai Kondrashov __u8 *copy_desc_ptr; 4149614219eSNikolai Kondrashov 4159614219eSNikolai Kondrashov if (frame == NULL || (desc_ptr == NULL && desc_size != 0)) 4169614219eSNikolai Kondrashov return -EINVAL; 4179614219eSNikolai Kondrashov 4189614219eSNikolai Kondrashov copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 4199614219eSNikolai Kondrashov if (copy_desc_ptr == NULL) 4209614219eSNikolai Kondrashov return -ENOMEM; 4219614219eSNikolai Kondrashov 4229614219eSNikolai Kondrashov memset(frame, 0, sizeof(*frame)); 4239614219eSNikolai Kondrashov frame->desc_ptr = copy_desc_ptr; 4249614219eSNikolai Kondrashov frame->desc_size = desc_size; 4259614219eSNikolai Kondrashov frame->id = id; 4269614219eSNikolai Kondrashov return 0; 4279614219eSNikolai Kondrashov } 4289614219eSNikolai Kondrashov 4299614219eSNikolai Kondrashov /** 430eecb5b84SNikolai Kondrashov * uclogic_params_frame_init_v1_buttonpad() - initialize abstract buttonpad 431eecb5b84SNikolai Kondrashov * on a v1 tablet interface. 4329614219eSNikolai Kondrashov * 4339614219eSNikolai Kondrashov * @frame: Pointer to the frame parameters to initialize (to be cleaned 4349614219eSNikolai Kondrashov * up with uclogic_params_frame_cleanup()). Not modified in case 4359614219eSNikolai Kondrashov * of error, or if parameters are not found. Cannot be NULL. 4369614219eSNikolai Kondrashov * @pfound: Location for a flag which is set to true if the parameters 4379614219eSNikolai Kondrashov * were found, and to false if not (e.g. device was 4389614219eSNikolai Kondrashov * incompatible). Not modified in case of error. Cannot be NULL. 4399614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 4409614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 4419614219eSNikolai Kondrashov * 4429614219eSNikolai Kondrashov * Returns: 4439614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 4449614219eSNikolai Kondrashov */ 445eecb5b84SNikolai Kondrashov static int uclogic_params_frame_init_v1_buttonpad( 4469614219eSNikolai Kondrashov struct uclogic_params_frame *frame, 4479614219eSNikolai Kondrashov bool *pfound, 4489614219eSNikolai Kondrashov struct hid_device *hdev) 4499614219eSNikolai Kondrashov { 4509614219eSNikolai Kondrashov int rc; 4519614219eSNikolai Kondrashov bool found = false; 4529614219eSNikolai Kondrashov struct usb_device *usb_dev = hid_to_usb_dev(hdev); 4539614219eSNikolai Kondrashov char *str_buf = NULL; 4549614219eSNikolai Kondrashov const size_t str_len = 16; 4559614219eSNikolai Kondrashov 4569614219eSNikolai Kondrashov /* Check arguments */ 4579614219eSNikolai Kondrashov if (frame == NULL || pfound == NULL || hdev == NULL) { 4589614219eSNikolai Kondrashov rc = -EINVAL; 4599614219eSNikolai Kondrashov goto cleanup; 4609614219eSNikolai Kondrashov } 4619614219eSNikolai Kondrashov 4629614219eSNikolai Kondrashov /* 4639614219eSNikolai Kondrashov * Enable generic button mode 4649614219eSNikolai Kondrashov */ 4659614219eSNikolai Kondrashov str_buf = kzalloc(str_len, GFP_KERNEL); 4669614219eSNikolai Kondrashov if (str_buf == NULL) { 4679614219eSNikolai Kondrashov rc = -ENOMEM; 4689614219eSNikolai Kondrashov goto cleanup; 4699614219eSNikolai Kondrashov } 4709614219eSNikolai Kondrashov 4719614219eSNikolai Kondrashov rc = usb_string(usb_dev, 123, str_buf, str_len); 4729614219eSNikolai Kondrashov if (rc == -EPIPE) { 4739614219eSNikolai Kondrashov hid_dbg(hdev, 4749614219eSNikolai Kondrashov "generic button -enabling string descriptor not found\n"); 4759614219eSNikolai Kondrashov } else if (rc < 0) { 4769614219eSNikolai Kondrashov goto cleanup; 4779614219eSNikolai Kondrashov } else if (strncmp(str_buf, "HK On", rc) != 0) { 4789614219eSNikolai Kondrashov hid_dbg(hdev, 4799614219eSNikolai Kondrashov "invalid response to enabling generic buttons: \"%s\"\n", 4809614219eSNikolai Kondrashov str_buf); 4819614219eSNikolai Kondrashov } else { 4829614219eSNikolai Kondrashov hid_dbg(hdev, "generic buttons enabled\n"); 4839614219eSNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 4849614219eSNikolai Kondrashov frame, 485eecb5b84SNikolai Kondrashov uclogic_rdesc_buttonpad_v1_arr, 486eecb5b84SNikolai Kondrashov uclogic_rdesc_buttonpad_v1_size, 487eecb5b84SNikolai Kondrashov UCLOGIC_RDESC_BUTTONPAD_V1_ID); 4889614219eSNikolai Kondrashov if (rc != 0) 4899614219eSNikolai Kondrashov goto cleanup; 4909614219eSNikolai Kondrashov found = true; 4919614219eSNikolai Kondrashov } 4929614219eSNikolai Kondrashov 4939614219eSNikolai Kondrashov *pfound = found; 4949614219eSNikolai Kondrashov rc = 0; 4959614219eSNikolai Kondrashov cleanup: 4969614219eSNikolai Kondrashov kfree(str_buf); 4979614219eSNikolai Kondrashov return rc; 4989614219eSNikolai Kondrashov } 4999614219eSNikolai Kondrashov 5009614219eSNikolai Kondrashov /** 5019614219eSNikolai Kondrashov * uclogic_params_cleanup - free resources used by struct uclogic_params 5029614219eSNikolai Kondrashov * (tablet interface's parameters). 5039614219eSNikolai Kondrashov * Can be called repeatedly. 5049614219eSNikolai Kondrashov * 5059614219eSNikolai Kondrashov * @params: Input parameters to cleanup. Cannot be NULL. 5069614219eSNikolai Kondrashov */ 5079614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params) 5089614219eSNikolai Kondrashov { 5099614219eSNikolai Kondrashov if (!params->invalid) { 5109614219eSNikolai Kondrashov kfree(params->desc_ptr); 5119614219eSNikolai Kondrashov if (!params->pen_unused) 5129614219eSNikolai Kondrashov uclogic_params_pen_cleanup(¶ms->pen); 5139614219eSNikolai Kondrashov uclogic_params_frame_cleanup(¶ms->frame); 5149614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 5159614219eSNikolai Kondrashov } 5169614219eSNikolai Kondrashov } 5179614219eSNikolai Kondrashov 5189614219eSNikolai Kondrashov /** 5199614219eSNikolai Kondrashov * Get a replacement report descriptor for a tablet's interface. 5209614219eSNikolai Kondrashov * 5219614219eSNikolai Kondrashov * @params: The parameters of a tablet interface to get report 5229614219eSNikolai Kondrashov * descriptor for. Cannot be NULL. 5239614219eSNikolai Kondrashov * @pdesc: Location for the resulting, kmalloc-allocated report 5249614219eSNikolai Kondrashov * descriptor pointer, or for NULL, if there's no replacement 5259614219eSNikolai Kondrashov * report descriptor. Not modified in case of error. Cannot be 5269614219eSNikolai Kondrashov * NULL. 5279614219eSNikolai Kondrashov * @psize: Location for the resulting report descriptor size, not set if 5289614219eSNikolai Kondrashov * there's no replacement report descriptor. Not modified in case 5299614219eSNikolai Kondrashov * of error. Cannot be NULL. 5309614219eSNikolai Kondrashov * 5319614219eSNikolai Kondrashov * Returns: 5329614219eSNikolai Kondrashov * Zero, if successful. 5339614219eSNikolai Kondrashov * -EINVAL, if invalid arguments are supplied. 5349614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 5359614219eSNikolai Kondrashov */ 5369614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params, 5379614219eSNikolai Kondrashov __u8 **pdesc, 5389614219eSNikolai Kondrashov unsigned int *psize) 5399614219eSNikolai Kondrashov { 5409614219eSNikolai Kondrashov bool common_present; 5419614219eSNikolai Kondrashov bool pen_present; 5429614219eSNikolai Kondrashov bool frame_present; 5439614219eSNikolai Kondrashov unsigned int size; 5449614219eSNikolai Kondrashov __u8 *desc = NULL; 5459614219eSNikolai Kondrashov 5469614219eSNikolai Kondrashov /* Check arguments */ 5479614219eSNikolai Kondrashov if (params == NULL || pdesc == NULL || psize == NULL) 5489614219eSNikolai Kondrashov return -EINVAL; 5499614219eSNikolai Kondrashov 5509614219eSNikolai Kondrashov size = 0; 5519614219eSNikolai Kondrashov 5529614219eSNikolai Kondrashov common_present = (params->desc_ptr != NULL); 5539614219eSNikolai Kondrashov pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL); 5549614219eSNikolai Kondrashov frame_present = (params->frame.desc_ptr != NULL); 5559614219eSNikolai Kondrashov 5569614219eSNikolai Kondrashov if (common_present) 5579614219eSNikolai Kondrashov size += params->desc_size; 5589614219eSNikolai Kondrashov if (pen_present) 5599614219eSNikolai Kondrashov size += params->pen.desc_size; 5609614219eSNikolai Kondrashov if (frame_present) 5619614219eSNikolai Kondrashov size += params->frame.desc_size; 5629614219eSNikolai Kondrashov 5639614219eSNikolai Kondrashov if (common_present || pen_present || frame_present) { 5649614219eSNikolai Kondrashov __u8 *p; 5659614219eSNikolai Kondrashov 5669614219eSNikolai Kondrashov desc = kmalloc(size, GFP_KERNEL); 5679614219eSNikolai Kondrashov if (desc == NULL) 5689614219eSNikolai Kondrashov return -ENOMEM; 5699614219eSNikolai Kondrashov p = desc; 5709614219eSNikolai Kondrashov 5719614219eSNikolai Kondrashov if (common_present) { 5729614219eSNikolai Kondrashov memcpy(p, params->desc_ptr, 5739614219eSNikolai Kondrashov params->desc_size); 5749614219eSNikolai Kondrashov p += params->desc_size; 5759614219eSNikolai Kondrashov } 5769614219eSNikolai Kondrashov if (pen_present) { 5779614219eSNikolai Kondrashov memcpy(p, params->pen.desc_ptr, 5789614219eSNikolai Kondrashov params->pen.desc_size); 5799614219eSNikolai Kondrashov p += params->pen.desc_size; 5809614219eSNikolai Kondrashov } 5819614219eSNikolai Kondrashov if (frame_present) { 5829614219eSNikolai Kondrashov memcpy(p, params->frame.desc_ptr, 5839614219eSNikolai Kondrashov params->frame.desc_size); 5849614219eSNikolai Kondrashov p += params->frame.desc_size; 5859614219eSNikolai Kondrashov } 5869614219eSNikolai Kondrashov 5879614219eSNikolai Kondrashov WARN_ON(p != desc + size); 5889614219eSNikolai Kondrashov 5899614219eSNikolai Kondrashov *psize = size; 5909614219eSNikolai Kondrashov } 5919614219eSNikolai Kondrashov 5929614219eSNikolai Kondrashov *pdesc = desc; 5939614219eSNikolai Kondrashov return 0; 5949614219eSNikolai Kondrashov } 5959614219eSNikolai Kondrashov 5969614219eSNikolai Kondrashov /** 5979614219eSNikolai Kondrashov * uclogic_params_init_invalid() - initialize tablet interface parameters, 5989614219eSNikolai Kondrashov * specifying the interface is invalid. 5999614219eSNikolai Kondrashov * 6009614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 6019614219eSNikolai Kondrashov * uclogic_params_cleanup()). Cannot be NULL. 6029614219eSNikolai Kondrashov */ 6039614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params) 6049614219eSNikolai Kondrashov { 6059614219eSNikolai Kondrashov params->invalid = true; 6069614219eSNikolai Kondrashov } 6079614219eSNikolai Kondrashov 6089614219eSNikolai Kondrashov /** 6099614219eSNikolai Kondrashov * uclogic_params_init_with_opt_desc() - initialize tablet interface 6109614219eSNikolai Kondrashov * parameters with an optional replacement report descriptor. Only modify 6119614219eSNikolai Kondrashov * report descriptor, if the original report descriptor matches the expected 6129614219eSNikolai Kondrashov * size. 6139614219eSNikolai Kondrashov * 6149614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 6159614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of 6169614219eSNikolai Kondrashov * error. Cannot be NULL. 6179614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface create the 6189614219eSNikolai Kondrashov * parameters for. Cannot be NULL. 6199614219eSNikolai Kondrashov * @orig_desc_size: Expected size of the original report descriptor to 6209614219eSNikolai Kondrashov * be replaced. 6219614219eSNikolai Kondrashov * @desc_ptr: Pointer to the replacement report descriptor. 6229614219eSNikolai Kondrashov * Can be NULL, if desc_size is zero. 6239614219eSNikolai Kondrashov * @desc_size: Size of the replacement report descriptor. 6249614219eSNikolai Kondrashov * 6259614219eSNikolai Kondrashov * Returns: 6269614219eSNikolai Kondrashov * Zero, if successful. -EINVAL if an invalid argument was passed. 6279614219eSNikolai Kondrashov * -ENOMEM, if failed to allocate memory. 6289614219eSNikolai Kondrashov */ 6299614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, 6309614219eSNikolai Kondrashov struct hid_device *hdev, 6319614219eSNikolai Kondrashov unsigned int orig_desc_size, 6329614219eSNikolai Kondrashov __u8 *desc_ptr, 6339614219eSNikolai Kondrashov unsigned int desc_size) 6349614219eSNikolai Kondrashov { 6359614219eSNikolai Kondrashov __u8 *desc_copy_ptr = NULL; 6369614219eSNikolai Kondrashov unsigned int desc_copy_size; 6379614219eSNikolai Kondrashov int rc; 6389614219eSNikolai Kondrashov 6399614219eSNikolai Kondrashov /* Check arguments */ 6409614219eSNikolai Kondrashov if (params == NULL || hdev == NULL || 6419614219eSNikolai Kondrashov (desc_ptr == NULL && desc_size != 0)) { 6429614219eSNikolai Kondrashov rc = -EINVAL; 6439614219eSNikolai Kondrashov goto cleanup; 6449614219eSNikolai Kondrashov } 6459614219eSNikolai Kondrashov 6469614219eSNikolai Kondrashov /* Replace report descriptor, if it matches */ 6479614219eSNikolai Kondrashov if (hdev->dev_rsize == orig_desc_size) { 6489614219eSNikolai Kondrashov hid_dbg(hdev, 6499614219eSNikolai Kondrashov "device report descriptor matches the expected size, replacing\n"); 6509614219eSNikolai Kondrashov desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL); 6519614219eSNikolai Kondrashov if (desc_copy_ptr == NULL) { 6529614219eSNikolai Kondrashov rc = -ENOMEM; 6539614219eSNikolai Kondrashov goto cleanup; 6549614219eSNikolai Kondrashov } 6559614219eSNikolai Kondrashov desc_copy_size = desc_size; 6569614219eSNikolai Kondrashov } else { 6579614219eSNikolai Kondrashov hid_dbg(hdev, 6589614219eSNikolai Kondrashov "device report descriptor doesn't match the expected size (%u != %u), preserving\n", 6599614219eSNikolai Kondrashov hdev->dev_rsize, orig_desc_size); 6609614219eSNikolai Kondrashov desc_copy_ptr = NULL; 6619614219eSNikolai Kondrashov desc_copy_size = 0; 6629614219eSNikolai Kondrashov } 6639614219eSNikolai Kondrashov 6649614219eSNikolai Kondrashov /* Output parameters */ 6659614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 6669614219eSNikolai Kondrashov params->desc_ptr = desc_copy_ptr; 6679614219eSNikolai Kondrashov desc_copy_ptr = NULL; 6689614219eSNikolai Kondrashov params->desc_size = desc_copy_size; 6699614219eSNikolai Kondrashov 6709614219eSNikolai Kondrashov rc = 0; 6719614219eSNikolai Kondrashov cleanup: 6729614219eSNikolai Kondrashov kfree(desc_copy_ptr); 6739614219eSNikolai Kondrashov return rc; 6749614219eSNikolai Kondrashov } 6759614219eSNikolai Kondrashov 6769614219eSNikolai Kondrashov /** 6779614219eSNikolai Kondrashov * uclogic_params_init_with_pen_unused() - initialize tablet interface 6789614219eSNikolai Kondrashov * parameters preserving original reports and generic HID processing, but 6799614219eSNikolai Kondrashov * disabling pen usage. 6809614219eSNikolai Kondrashov * 6819614219eSNikolai Kondrashov * @params: Parameters to initialize (to be cleaned with 6829614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of 6839614219eSNikolai Kondrashov * error. Cannot be NULL. 6849614219eSNikolai Kondrashov */ 6859614219eSNikolai Kondrashov static void uclogic_params_init_with_pen_unused(struct uclogic_params *params) 6869614219eSNikolai Kondrashov { 6879614219eSNikolai Kondrashov memset(params, 0, sizeof(*params)); 6889614219eSNikolai Kondrashov params->pen_unused = true; 6899614219eSNikolai Kondrashov } 6909614219eSNikolai Kondrashov 6919614219eSNikolai Kondrashov /** 6929614219eSNikolai Kondrashov * uclogic_params_init() - initialize a Huion tablet interface and discover 6939614219eSNikolai Kondrashov * its parameters. 6949614219eSNikolai Kondrashov * 6959614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 6969614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 6979614219eSNikolai Kondrashov * Cannot be NULL. 6989614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 6999614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 7009614219eSNikolai Kondrashov * 7019614219eSNikolai Kondrashov * Returns: 7029614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 7039614219eSNikolai Kondrashov */ 7049614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params, 7059614219eSNikolai Kondrashov struct hid_device *hdev) 7069614219eSNikolai Kondrashov { 7079614219eSNikolai Kondrashov int rc; 7082c3a88c6SNikolai Kondrashov struct usb_device *udev = hid_to_usb_dev(hdev); 7099614219eSNikolai Kondrashov struct usb_interface *iface = to_usb_interface(hdev->dev.parent); 7109614219eSNikolai Kondrashov __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 7119614219eSNikolai Kondrashov bool found; 7129614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 7139614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 7142c3a88c6SNikolai Kondrashov static const char transition_ver[] = "HUION_T153_160607"; 7152c3a88c6SNikolai Kondrashov char *ver_ptr = NULL; 7162c3a88c6SNikolai Kondrashov const size_t ver_len = sizeof(transition_ver) + 1; 7179614219eSNikolai Kondrashov 7189614219eSNikolai Kondrashov /* Check arguments */ 7199614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 7209614219eSNikolai Kondrashov rc = -EINVAL; 7219614219eSNikolai Kondrashov goto cleanup; 7229614219eSNikolai Kondrashov } 7239614219eSNikolai Kondrashov 7249614219eSNikolai Kondrashov /* If it's not a pen interface */ 7259614219eSNikolai Kondrashov if (bInterfaceNumber != 0) { 7269614219eSNikolai Kondrashov /* TODO: Consider marking the interface invalid */ 7279614219eSNikolai Kondrashov uclogic_params_init_with_pen_unused(&p); 7289614219eSNikolai Kondrashov goto output; 7299614219eSNikolai Kondrashov } 7309614219eSNikolai Kondrashov 7312c3a88c6SNikolai Kondrashov /* Try to get firmware version */ 7322c3a88c6SNikolai Kondrashov ver_ptr = kzalloc(ver_len, GFP_KERNEL); 7332c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 7342c3a88c6SNikolai Kondrashov rc = -ENOMEM; 7352c3a88c6SNikolai Kondrashov goto cleanup; 7362c3a88c6SNikolai Kondrashov } 7372c3a88c6SNikolai Kondrashov rc = usb_string(udev, 201, ver_ptr, ver_len); 7382c3a88c6SNikolai Kondrashov if (ver_ptr == NULL) { 7392c3a88c6SNikolai Kondrashov rc = -ENOMEM; 7402c3a88c6SNikolai Kondrashov goto cleanup; 7412c3a88c6SNikolai Kondrashov } 7422c3a88c6SNikolai Kondrashov if (rc == -EPIPE) { 7432c3a88c6SNikolai Kondrashov *ver_ptr = '\0'; 7442c3a88c6SNikolai Kondrashov } else if (rc < 0) { 7452c3a88c6SNikolai Kondrashov hid_err(hdev, 7462c3a88c6SNikolai Kondrashov "failed retrieving Huion firmware version: %d\n", rc); 7472c3a88c6SNikolai Kondrashov goto cleanup; 7482c3a88c6SNikolai Kondrashov } 7492c3a88c6SNikolai Kondrashov 7502c3a88c6SNikolai Kondrashov /* If this is a transition firmware */ 7512c3a88c6SNikolai Kondrashov if (strcmp(ver_ptr, transition_ver) == 0) { 7522c3a88c6SNikolai Kondrashov hid_dbg(hdev, 7532c3a88c6SNikolai Kondrashov "transition firmware detected, not probing pen v2 parameters\n"); 7542c3a88c6SNikolai Kondrashov } else { 7552c3a88c6SNikolai Kondrashov /* Try to probe v2 pen parameters */ 7562c3a88c6SNikolai Kondrashov rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev); 7572c3a88c6SNikolai Kondrashov if (rc != 0) { 7582c3a88c6SNikolai Kondrashov hid_err(hdev, 7592c3a88c6SNikolai Kondrashov "failed probing pen v2 parameters: %d\n", rc); 7602c3a88c6SNikolai Kondrashov goto cleanup; 7612c3a88c6SNikolai Kondrashov } else if (found) { 7622c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters found\n"); 7632c3a88c6SNikolai Kondrashov /* Create v2 buttonpad parameters */ 7642c3a88c6SNikolai Kondrashov rc = uclogic_params_frame_init_with_desc( 7652c3a88c6SNikolai Kondrashov &p.frame, 7662c3a88c6SNikolai Kondrashov uclogic_rdesc_buttonpad_v2_arr, 7672c3a88c6SNikolai Kondrashov uclogic_rdesc_buttonpad_v2_size, 7682c3a88c6SNikolai Kondrashov UCLOGIC_RDESC_BUTTONPAD_V2_ID); 7692c3a88c6SNikolai Kondrashov if (rc != 0) { 7702c3a88c6SNikolai Kondrashov hid_err(hdev, 7712c3a88c6SNikolai Kondrashov "failed creating v2 buttonpad parameters: %d\n", 7722c3a88c6SNikolai Kondrashov rc); 7732c3a88c6SNikolai Kondrashov goto cleanup; 7742c3a88c6SNikolai Kondrashov } 7752c3a88c6SNikolai Kondrashov /* Set bitmask marking frame reports in pen reports */ 7762c3a88c6SNikolai Kondrashov p.pen_frame_flag = 0x20; 7772c3a88c6SNikolai Kondrashov goto output; 7782c3a88c6SNikolai Kondrashov } 7792c3a88c6SNikolai Kondrashov hid_dbg(hdev, "pen v2 parameters not found\n"); 7802c3a88c6SNikolai Kondrashov } 7812c3a88c6SNikolai Kondrashov 782eecb5b84SNikolai Kondrashov /* Try to probe v1 pen parameters */ 783eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 7849614219eSNikolai Kondrashov if (rc != 0) { 7859614219eSNikolai Kondrashov hid_err(hdev, 786eecb5b84SNikolai Kondrashov "failed probing pen v1 parameters: %d\n", rc); 7879614219eSNikolai Kondrashov goto cleanup; 7889614219eSNikolai Kondrashov } else if (found) { 789eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters found\n"); 790eecb5b84SNikolai Kondrashov /* Try to probe v1 buttonpad */ 791eecb5b84SNikolai Kondrashov rc = uclogic_params_frame_init_v1_buttonpad( 7929614219eSNikolai Kondrashov &p.frame, 7939614219eSNikolai Kondrashov &found, hdev); 7949614219eSNikolai Kondrashov if (rc != 0) { 7959614219eSNikolai Kondrashov hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc); 7969614219eSNikolai Kondrashov goto cleanup; 7979614219eSNikolai Kondrashov } 798eecb5b84SNikolai Kondrashov hid_dbg(hdev, "buttonpad v1 parameters%s found\n", 7999614219eSNikolai Kondrashov (found ? "" : " not")); 8009614219eSNikolai Kondrashov if (found) { 8019614219eSNikolai Kondrashov /* Set bitmask marking frame reports */ 8029614219eSNikolai Kondrashov p.pen_frame_flag = 0x20; 8039614219eSNikolai Kondrashov } 8049614219eSNikolai Kondrashov goto output; 8059614219eSNikolai Kondrashov } 806eecb5b84SNikolai Kondrashov hid_dbg(hdev, "pen v1 parameters not found\n"); 8079614219eSNikolai Kondrashov 8089614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 8099614219eSNikolai Kondrashov 8109614219eSNikolai Kondrashov output: 8119614219eSNikolai Kondrashov /* Output parameters */ 8129614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 8139614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 8149614219eSNikolai Kondrashov rc = 0; 8159614219eSNikolai Kondrashov cleanup: 8162c3a88c6SNikolai Kondrashov kfree(ver_ptr); 8179614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 8189614219eSNikolai Kondrashov return rc; 8199614219eSNikolai Kondrashov } 8209614219eSNikolai Kondrashov 8219614219eSNikolai Kondrashov /** 8229614219eSNikolai Kondrashov * uclogic_params_init() - initialize a tablet interface and discover its 8239614219eSNikolai Kondrashov * parameters. 8249614219eSNikolai Kondrashov * 8259614219eSNikolai Kondrashov * @params: Parameters to fill in (to be cleaned with 8269614219eSNikolai Kondrashov * uclogic_params_cleanup()). Not modified in case of error. 8279614219eSNikolai Kondrashov * Cannot be NULL. 8289614219eSNikolai Kondrashov * @hdev: The HID device of the tablet interface to initialize and get 8299614219eSNikolai Kondrashov * parameters from. Cannot be NULL. 8309614219eSNikolai Kondrashov * 8319614219eSNikolai Kondrashov * Returns: 8329614219eSNikolai Kondrashov * Zero, if successful. A negative errno code on error. 8339614219eSNikolai Kondrashov */ 8349614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params, 8359614219eSNikolai Kondrashov struct hid_device *hdev) 8369614219eSNikolai Kondrashov { 8379614219eSNikolai Kondrashov int rc; 8389614219eSNikolai Kondrashov struct usb_device *udev = hid_to_usb_dev(hdev); 8399614219eSNikolai Kondrashov __u8 bNumInterfaces = udev->config->desc.bNumInterfaces; 8409614219eSNikolai Kondrashov struct usb_interface *iface = to_usb_interface(hdev->dev.parent); 8419614219eSNikolai Kondrashov __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; 8429614219eSNikolai Kondrashov bool found; 8439614219eSNikolai Kondrashov /* The resulting parameters (noop) */ 8449614219eSNikolai Kondrashov struct uclogic_params p = {0, }; 8459614219eSNikolai Kondrashov 8469614219eSNikolai Kondrashov /* Check arguments */ 8479614219eSNikolai Kondrashov if (params == NULL || hdev == NULL) { 8489614219eSNikolai Kondrashov rc = -EINVAL; 8499614219eSNikolai Kondrashov goto cleanup; 8509614219eSNikolai Kondrashov } 8519614219eSNikolai Kondrashov 8529614219eSNikolai Kondrashov /* 8539614219eSNikolai Kondrashov * Set replacement report descriptor if the original matches the 8549614219eSNikolai Kondrashov * specified size. Otherwise keep interface unchanged. 8559614219eSNikolai Kondrashov */ 8569614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \ 8579614219eSNikolai Kondrashov uclogic_params_init_with_opt_desc( \ 8589614219eSNikolai Kondrashov &p, hdev, \ 8599614219eSNikolai Kondrashov UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \ 8609614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_arr, \ 8619614219eSNikolai Kondrashov uclogic_rdesc_##_new_desc_token##_size) 8629614219eSNikolai Kondrashov 8639614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \ 8649614219eSNikolai Kondrashov (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX)) 8659614219eSNikolai Kondrashov 8669614219eSNikolai Kondrashov /* 8679614219eSNikolai Kondrashov * Handle specific interfaces for specific tablets. 8689614219eSNikolai Kondrashov * 8699614219eSNikolai Kondrashov * Observe the following logic: 8709614219eSNikolai Kondrashov * 8719614219eSNikolai Kondrashov * If the interface is recognized as producing certain useful input: 8729614219eSNikolai Kondrashov * Mark interface as valid. 8739614219eSNikolai Kondrashov * Output interface parameters. 8749614219eSNikolai Kondrashov * Else, if the interface is recognized as *not* producing any useful 8759614219eSNikolai Kondrashov * input: 8769614219eSNikolai Kondrashov * Mark interface as invalid. 8779614219eSNikolai Kondrashov * Else: 8789614219eSNikolai Kondrashov * Mark interface as valid. 8799614219eSNikolai Kondrashov * Output noop parameters. 8809614219eSNikolai Kondrashov * 8819614219eSNikolai Kondrashov * Rule of thumb: it is better to disable a broken interface than let 8829614219eSNikolai Kondrashov * it spew garbage input. 8839614219eSNikolai Kondrashov */ 8849614219eSNikolai Kondrashov 8859614219eSNikolai Kondrashov switch (VID_PID(hdev->vendor, hdev->product)) { 8869614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 8879614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_PF1209): 8889614219eSNikolai Kondrashov rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed); 8899614219eSNikolai Kondrashov if (rc != 0) 8909614219eSNikolai Kondrashov goto cleanup; 8919614219eSNikolai Kondrashov break; 8929614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 8939614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U): 8949614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed); 8959614219eSNikolai Kondrashov if (rc != 0) 8969614219eSNikolai Kondrashov goto cleanup; 8979614219eSNikolai Kondrashov break; 8989614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 8999614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U): 9009614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed); 9019614219eSNikolai Kondrashov if (rc != 0) 9029614219eSNikolai Kondrashov goto cleanup; 9039614219eSNikolai Kondrashov break; 9049614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9059614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U): 9069614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed); 9079614219eSNikolai Kondrashov if (rc != 0) 9089614219eSNikolai Kondrashov goto cleanup; 9099614219eSNikolai Kondrashov break; 9109614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9119614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_WP1062): 9129614219eSNikolai Kondrashov rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed); 9139614219eSNikolai Kondrashov if (rc != 0) 9149614219eSNikolai Kondrashov goto cleanup; 9159614219eSNikolai Kondrashov break; 9169614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9179614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850): 9189614219eSNikolai Kondrashov switch (bInterfaceNumber) { 9199614219eSNikolai Kondrashov case 0: 9209614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0); 9219614219eSNikolai Kondrashov if (rc != 0) 9229614219eSNikolai Kondrashov goto cleanup; 9239614219eSNikolai Kondrashov break; 9249614219eSNikolai Kondrashov case 1: 9259614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1); 9269614219eSNikolai Kondrashov if (rc != 0) 9279614219eSNikolai Kondrashov goto cleanup; 9289614219eSNikolai Kondrashov break; 9299614219eSNikolai Kondrashov case 2: 9309614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2); 9319614219eSNikolai Kondrashov if (rc != 0) 9329614219eSNikolai Kondrashov goto cleanup; 9339614219eSNikolai Kondrashov break; 9349614219eSNikolai Kondrashov } 9359614219eSNikolai Kondrashov break; 9369614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9379614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60): 9389614219eSNikolai Kondrashov /* 9399614219eSNikolai Kondrashov * If it is not a three-interface version, which is known to 9409614219eSNikolai Kondrashov * respond to initialization. 9419614219eSNikolai Kondrashov */ 9429614219eSNikolai Kondrashov if (bNumInterfaces != 3) { 9439614219eSNikolai Kondrashov switch (bInterfaceNumber) { 9449614219eSNikolai Kondrashov case 0: 9459614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG0, 9469614219eSNikolai Kondrashov twha60_fixed0); 9479614219eSNikolai Kondrashov if (rc != 0) 9489614219eSNikolai Kondrashov goto cleanup; 9499614219eSNikolai Kondrashov break; 9509614219eSNikolai Kondrashov case 1: 9519614219eSNikolai Kondrashov rc = WITH_OPT_DESC(TWHA60_ORIG1, 9529614219eSNikolai Kondrashov twha60_fixed1); 9539614219eSNikolai Kondrashov if (rc != 0) 9549614219eSNikolai Kondrashov goto cleanup; 9559614219eSNikolai Kondrashov break; 9569614219eSNikolai Kondrashov } 9579614219eSNikolai Kondrashov break; 9589614219eSNikolai Kondrashov } 9599614219eSNikolai Kondrashov /* FALL THROUGH */ 9609614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_HUION, 9619614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 9629614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9639614219eSNikolai Kondrashov USB_DEVICE_ID_HUION_TABLET): 9649614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9659614219eSNikolai Kondrashov USB_DEVICE_ID_YIYNOVA_TABLET): 9669614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9679614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81): 9689614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9699614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3): 9709614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9719614219eSNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45): 9720c15efe9SNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UCLOGIC, 9730c15efe9SNikolai Kondrashov USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47): 9749614219eSNikolai Kondrashov rc = uclogic_params_huion_init(&p, hdev); 9759614219eSNikolai Kondrashov if (rc != 0) 9769614219eSNikolai Kondrashov goto cleanup; 9779614219eSNikolai Kondrashov break; 9789614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGTIZER, 9799614219eSNikolai Kondrashov USB_DEVICE_ID_UGTIZER_TABLET_GP0610): 9809614219eSNikolai Kondrashov case VID_PID(USB_VENDOR_ID_UGEE, 9819614219eSNikolai Kondrashov USB_DEVICE_ID_UGEE_TABLET_EX07S): 9829614219eSNikolai Kondrashov /* If this is the pen interface */ 9839614219eSNikolai Kondrashov if (bInterfaceNumber == 1) { 984eecb5b84SNikolai Kondrashov /* Probe v1 pen parameters */ 985eecb5b84SNikolai Kondrashov rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev); 9869614219eSNikolai Kondrashov if (rc != 0) { 9879614219eSNikolai Kondrashov hid_err(hdev, "pen probing failed: %d\n", rc); 9889614219eSNikolai Kondrashov goto cleanup; 9899614219eSNikolai Kondrashov } 9909614219eSNikolai Kondrashov if (!found) { 9919614219eSNikolai Kondrashov hid_warn(hdev, "pen parameters not found"); 9929614219eSNikolai Kondrashov uclogic_params_init_invalid(&p); 9939614219eSNikolai Kondrashov } 9949614219eSNikolai Kondrashov } else { 9959614219eSNikolai Kondrashov /* TODO: Consider marking the interface invalid */ 9969614219eSNikolai Kondrashov uclogic_params_init_with_pen_unused(&p); 9979614219eSNikolai Kondrashov } 9989614219eSNikolai Kondrashov break; 9999614219eSNikolai Kondrashov } 10009614219eSNikolai Kondrashov 10019614219eSNikolai Kondrashov #undef VID_PID 10029614219eSNikolai Kondrashov #undef WITH_OPT_DESC 10039614219eSNikolai Kondrashov 10049614219eSNikolai Kondrashov /* Output parameters */ 10059614219eSNikolai Kondrashov memcpy(params, &p, sizeof(*params)); 10069614219eSNikolai Kondrashov memset(&p, 0, sizeof(p)); 10079614219eSNikolai Kondrashov rc = 0; 10089614219eSNikolai Kondrashov cleanup: 10099614219eSNikolai Kondrashov uclogic_params_cleanup(&p); 10109614219eSNikolai Kondrashov return rc; 10119614219eSNikolai Kondrashov } 1012