19614219eSNikolai Kondrashov // SPDX-License-Identifier: GPL-2.0+
29614219eSNikolai Kondrashov /*
39614219eSNikolai Kondrashov  *  HID driver for UC-Logic devices not fully compliant with HID standard
49614219eSNikolai Kondrashov  *  - tablet initialization and parameter retrieval
59614219eSNikolai Kondrashov  *
69614219eSNikolai Kondrashov  *  Copyright (c) 2018 Nikolai Kondrashov
79614219eSNikolai Kondrashov  */
89614219eSNikolai Kondrashov 
99614219eSNikolai Kondrashov /*
109614219eSNikolai Kondrashov  * This program is free software; you can redistribute it and/or modify it
119614219eSNikolai Kondrashov  * under the terms of the GNU General Public License as published by the Free
129614219eSNikolai Kondrashov  * Software Foundation; either version 2 of the License, or (at your option)
139614219eSNikolai Kondrashov  * any later version.
149614219eSNikolai Kondrashov  */
159614219eSNikolai Kondrashov 
169614219eSNikolai Kondrashov #include "hid-uclogic-params.h"
179614219eSNikolai Kondrashov #include "hid-uclogic-rdesc.h"
189614219eSNikolai Kondrashov #include "usbhid/usbhid.h"
199614219eSNikolai Kondrashov #include "hid-ids.h"
209614219eSNikolai Kondrashov #include <linux/ctype.h>
219614219eSNikolai Kondrashov #include <asm/unaligned.h>
229614219eSNikolai Kondrashov 
239614219eSNikolai Kondrashov /**
245abb5445SLee Jones  * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type
255abb5445SLee Jones  *                                       to a string.
269614219eSNikolai Kondrashov  *
279614219eSNikolai Kondrashov  * @inrange:	The in-range reporting type to convert.
289614219eSNikolai Kondrashov  *
299614219eSNikolai Kondrashov  * Returns:
309614219eSNikolai Kondrashov  *	The string representing the type, or NULL if the type is unknown.
319614219eSNikolai Kondrashov  */
329614219eSNikolai Kondrashov const char *uclogic_params_pen_inrange_to_str(
339614219eSNikolai Kondrashov 			enum uclogic_params_pen_inrange inrange)
349614219eSNikolai Kondrashov {
359614219eSNikolai Kondrashov 	switch (inrange) {
369614219eSNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
379614219eSNikolai Kondrashov 		return "normal";
389614219eSNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
399614219eSNikolai Kondrashov 		return "inverted";
4001309e29SNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_NONE:
4101309e29SNikolai Kondrashov 		return "none";
429614219eSNikolai Kondrashov 	default:
439614219eSNikolai Kondrashov 		return NULL;
449614219eSNikolai Kondrashov 	}
459614219eSNikolai Kondrashov }
469614219eSNikolai Kondrashov 
479614219eSNikolai Kondrashov /**
489614219eSNikolai Kondrashov  * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
499614219eSNikolai Kondrashov  * device interface, putting it into a kmalloc-allocated buffer as is, without
509614219eSNikolai Kondrashov  * character encoding conversion.
519614219eSNikolai Kondrashov  *
529614219eSNikolai Kondrashov  * @pbuf:	Location for the kmalloc-allocated buffer pointer containing
539614219eSNikolai Kondrashov  *		the retrieved descriptor. Not modified in case of error.
549614219eSNikolai Kondrashov  *		Can be NULL to have retrieved descriptor discarded.
559614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to retrieve the string
569614219eSNikolai Kondrashov  *		descriptor from. Cannot be NULL.
579614219eSNikolai Kondrashov  * @idx:	Index of the string descriptor to request from the device.
589614219eSNikolai Kondrashov  * @len:	Length of the buffer to allocate and the data to retrieve.
599614219eSNikolai Kondrashov  *
609614219eSNikolai Kondrashov  * Returns:
619614219eSNikolai Kondrashov  *	number of bytes retrieved (<= len),
629614219eSNikolai Kondrashov  *	-EPIPE, if the descriptor was not found, or
639614219eSNikolai Kondrashov  *	another negative errno code in case of other error.
649614219eSNikolai Kondrashov  */
659614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
669614219eSNikolai Kondrashov 					__u8 idx, size_t len)
679614219eSNikolai Kondrashov {
689614219eSNikolai Kondrashov 	int rc;
690a94131dSJosé Expósito 	struct usb_device *udev;
709614219eSNikolai Kondrashov 	__u8 *buf = NULL;
719614219eSNikolai Kondrashov 
729614219eSNikolai Kondrashov 	/* Check arguments */
739614219eSNikolai Kondrashov 	if (hdev == NULL) {
749614219eSNikolai Kondrashov 		rc = -EINVAL;
759614219eSNikolai Kondrashov 		goto cleanup;
769614219eSNikolai Kondrashov 	}
779614219eSNikolai Kondrashov 
780a94131dSJosé Expósito 	udev = hid_to_usb_dev(hdev);
790a94131dSJosé Expósito 
809614219eSNikolai Kondrashov 	buf = kmalloc(len, GFP_KERNEL);
819614219eSNikolai Kondrashov 	if (buf == NULL) {
829614219eSNikolai Kondrashov 		rc = -ENOMEM;
839614219eSNikolai Kondrashov 		goto cleanup;
849614219eSNikolai Kondrashov 	}
859614219eSNikolai Kondrashov 
869614219eSNikolai Kondrashov 	rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
879614219eSNikolai Kondrashov 				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
889614219eSNikolai Kondrashov 				(USB_DT_STRING << 8) + idx,
899614219eSNikolai Kondrashov 				0x0409, buf, len,
909614219eSNikolai Kondrashov 				USB_CTRL_GET_TIMEOUT);
919614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
929614219eSNikolai Kondrashov 		hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
939614219eSNikolai Kondrashov 		goto cleanup;
949614219eSNikolai Kondrashov 	} else if (rc < 0) {
959614219eSNikolai Kondrashov 		hid_err(hdev,
96a876e7e2STom Rix 			"failed retrieving string descriptor #%u: %d\n",
979614219eSNikolai Kondrashov 			idx, rc);
989614219eSNikolai Kondrashov 		goto cleanup;
999614219eSNikolai Kondrashov 	}
1009614219eSNikolai Kondrashov 
1019614219eSNikolai Kondrashov 	if (pbuf != NULL) {
1029614219eSNikolai Kondrashov 		*pbuf = buf;
1039614219eSNikolai Kondrashov 		buf = NULL;
1049614219eSNikolai Kondrashov 	}
1059614219eSNikolai Kondrashov 
1069614219eSNikolai Kondrashov cleanup:
1079614219eSNikolai Kondrashov 	kfree(buf);
1089614219eSNikolai Kondrashov 	return rc;
1099614219eSNikolai Kondrashov }
1109614219eSNikolai Kondrashov 
1119614219eSNikolai Kondrashov /**
1129614219eSNikolai Kondrashov  * uclogic_params_pen_cleanup - free resources used by struct
1139614219eSNikolai Kondrashov  * uclogic_params_pen (tablet interface's pen input parameters).
1149614219eSNikolai Kondrashov  * Can be called repeatedly.
1159614219eSNikolai Kondrashov  *
1169614219eSNikolai Kondrashov  * @pen:	Pen input parameters to cleanup. Cannot be NULL.
1179614219eSNikolai Kondrashov  */
1189614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
1199614219eSNikolai Kondrashov {
1209614219eSNikolai Kondrashov 	kfree(pen->desc_ptr);
1219614219eSNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
1229614219eSNikolai Kondrashov }
1239614219eSNikolai Kondrashov 
1249614219eSNikolai Kondrashov /**
125eecb5b84SNikolai Kondrashov  * uclogic_params_pen_init_v1() - initialize tablet interface pen
126eecb5b84SNikolai Kondrashov  * input and retrieve its parameters from the device, using v1 protocol.
1279614219eSNikolai Kondrashov  *
1289614219eSNikolai Kondrashov  * @pen:	Pointer to the pen parameters to initialize (to be
1299614219eSNikolai Kondrashov  *		cleaned up with uclogic_params_pen_cleanup()). Not modified in
1309614219eSNikolai Kondrashov  *		case of error, or if parameters are not found. Cannot be NULL.
1319614219eSNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
1329614219eSNikolai Kondrashov  *		were found, and to false if not (e.g. device was
1339614219eSNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
1349614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
1359614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
1369614219eSNikolai Kondrashov  *
1379614219eSNikolai Kondrashov  * Returns:
1389614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
1399614219eSNikolai Kondrashov  */
140eecb5b84SNikolai Kondrashov static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
1419614219eSNikolai Kondrashov 				      bool *pfound,
1429614219eSNikolai Kondrashov 				      struct hid_device *hdev)
1439614219eSNikolai Kondrashov {
1449614219eSNikolai Kondrashov 	int rc;
1459614219eSNikolai Kondrashov 	bool found = false;
1469614219eSNikolai Kondrashov 	/* Buffer for (part of) the string descriptor */
1479614219eSNikolai Kondrashov 	__u8 *buf = NULL;
1489614219eSNikolai Kondrashov 	/* Minimum descriptor length required, maximum seen so far is 18 */
1499614219eSNikolai Kondrashov 	const int len = 12;
1509614219eSNikolai Kondrashov 	s32 resolution;
1519614219eSNikolai Kondrashov 	/* Pen report descriptor template parameters */
1529614219eSNikolai Kondrashov 	s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
1539614219eSNikolai Kondrashov 	__u8 *desc_ptr = NULL;
1549614219eSNikolai Kondrashov 
1559614219eSNikolai Kondrashov 	/* Check arguments */
1569614219eSNikolai Kondrashov 	if (pen == NULL || pfound == NULL || hdev == NULL) {
1579614219eSNikolai Kondrashov 		rc = -EINVAL;
1589614219eSNikolai Kondrashov 		goto cleanup;
1599614219eSNikolai Kondrashov 	}
1609614219eSNikolai Kondrashov 
1619614219eSNikolai Kondrashov 	/*
1629614219eSNikolai Kondrashov 	 * Read string descriptor containing pen input parameters.
1639614219eSNikolai Kondrashov 	 * The specific string descriptor and data were discovered by sniffing
1649614219eSNikolai Kondrashov 	 * the Windows driver traffic.
1659614219eSNikolai Kondrashov 	 * NOTE: This enables fully-functional tablet mode.
1669614219eSNikolai Kondrashov 	 */
1679614219eSNikolai Kondrashov 	rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
1689614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
1699614219eSNikolai Kondrashov 		hid_dbg(hdev,
1709614219eSNikolai Kondrashov 			"string descriptor with pen parameters not found, assuming not compatible\n");
1719614219eSNikolai Kondrashov 		goto finish;
1729614219eSNikolai Kondrashov 	} else if (rc < 0) {
1739614219eSNikolai Kondrashov 		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
1749614219eSNikolai Kondrashov 		goto cleanup;
1759614219eSNikolai Kondrashov 	} else if (rc != len) {
1769614219eSNikolai Kondrashov 		hid_dbg(hdev,
1779614219eSNikolai Kondrashov 			"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
1789614219eSNikolai Kondrashov 			rc, len);
1799614219eSNikolai Kondrashov 		goto finish;
1809614219eSNikolai Kondrashov 	}
1819614219eSNikolai Kondrashov 
1829614219eSNikolai Kondrashov 	/*
1839614219eSNikolai Kondrashov 	 * Fill report descriptor parameters from the string descriptor
1849614219eSNikolai Kondrashov 	 */
1859614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
1869614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 2);
1879614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
1889614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 4);
1899614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
1909614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 8);
1919614219eSNikolai Kondrashov 	resolution = get_unaligned_le16(buf + 10);
1929614219eSNikolai Kondrashov 	if (resolution == 0) {
1939614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
1949614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
1959614219eSNikolai Kondrashov 	} else {
1969614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
1979614219eSNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
1989614219eSNikolai Kondrashov 			resolution;
1999614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
2009614219eSNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
2019614219eSNikolai Kondrashov 			resolution;
2029614219eSNikolai Kondrashov 	}
2039614219eSNikolai Kondrashov 	kfree(buf);
2049614219eSNikolai Kondrashov 	buf = NULL;
2059614219eSNikolai Kondrashov 
2069614219eSNikolai Kondrashov 	/*
2079614219eSNikolai Kondrashov 	 * Generate pen report descriptor
2089614219eSNikolai Kondrashov 	 */
2099614219eSNikolai Kondrashov 	desc_ptr = uclogic_rdesc_template_apply(
210a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_pen_template_arr,
211a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_pen_template_size,
2129614219eSNikolai Kondrashov 				desc_params, ARRAY_SIZE(desc_params));
2139614219eSNikolai Kondrashov 	if (desc_ptr == NULL) {
2149614219eSNikolai Kondrashov 		rc = -ENOMEM;
2159614219eSNikolai Kondrashov 		goto cleanup;
2169614219eSNikolai Kondrashov 	}
2179614219eSNikolai Kondrashov 
2189614219eSNikolai Kondrashov 	/*
2199614219eSNikolai Kondrashov 	 * Fill-in the parameters
2209614219eSNikolai Kondrashov 	 */
2219614219eSNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
2229614219eSNikolai Kondrashov 	pen->desc_ptr = desc_ptr;
2239614219eSNikolai Kondrashov 	desc_ptr = NULL;
224a985de58SNikolai Kondrashov 	pen->desc_size = uclogic_rdesc_v1_pen_template_size;
225a985de58SNikolai Kondrashov 	pen->id = UCLOGIC_RDESC_V1_PEN_ID;
2269614219eSNikolai Kondrashov 	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
2279614219eSNikolai Kondrashov 	found = true;
2289614219eSNikolai Kondrashov finish:
2299614219eSNikolai Kondrashov 	*pfound = found;
2309614219eSNikolai Kondrashov 	rc = 0;
2319614219eSNikolai Kondrashov cleanup:
2329614219eSNikolai Kondrashov 	kfree(desc_ptr);
2339614219eSNikolai Kondrashov 	kfree(buf);
2349614219eSNikolai Kondrashov 	return rc;
2359614219eSNikolai Kondrashov }
2369614219eSNikolai Kondrashov 
2379614219eSNikolai Kondrashov /**
2382c3a88c6SNikolai Kondrashov  * uclogic_params_get_le24() - get a 24-bit little-endian number from a
2392c3a88c6SNikolai Kondrashov  * buffer.
2402c3a88c6SNikolai Kondrashov  *
2412c3a88c6SNikolai Kondrashov  * @p:	The pointer to the number buffer.
2422c3a88c6SNikolai Kondrashov  *
2432c3a88c6SNikolai Kondrashov  * Returns:
2442c3a88c6SNikolai Kondrashov  *	The retrieved number
2452c3a88c6SNikolai Kondrashov  */
2462c3a88c6SNikolai Kondrashov static s32 uclogic_params_get_le24(const void *p)
2472c3a88c6SNikolai Kondrashov {
2482c3a88c6SNikolai Kondrashov 	const __u8 *b = p;
2492c3a88c6SNikolai Kondrashov 	return b[0] | (b[1] << 8UL) | (b[2] << 16UL);
2502c3a88c6SNikolai Kondrashov }
2512c3a88c6SNikolai Kondrashov 
2522c3a88c6SNikolai Kondrashov /**
2532c3a88c6SNikolai Kondrashov  * uclogic_params_pen_init_v2() - initialize tablet interface pen
2542c3a88c6SNikolai Kondrashov  * input and retrieve its parameters from the device, using v2 protocol.
2552c3a88c6SNikolai Kondrashov  *
2562c3a88c6SNikolai Kondrashov  * @pen:	Pointer to the pen parameters to initialize (to be
2572c3a88c6SNikolai Kondrashov  *		cleaned up with uclogic_params_pen_cleanup()). Not modified in
2582c3a88c6SNikolai Kondrashov  *		case of error, or if parameters are not found. Cannot be NULL.
2592c3a88c6SNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
2602c3a88c6SNikolai Kondrashov  *		were found, and to false if not (e.g. device was
2612c3a88c6SNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
2622c3a88c6SNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
2632c3a88c6SNikolai Kondrashov  *		parameters from. Cannot be NULL.
2642c3a88c6SNikolai Kondrashov  *
2652c3a88c6SNikolai Kondrashov  * Returns:
2662c3a88c6SNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
2672c3a88c6SNikolai Kondrashov  */
2682c3a88c6SNikolai Kondrashov static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
2692c3a88c6SNikolai Kondrashov 					bool *pfound,
2702c3a88c6SNikolai Kondrashov 					struct hid_device *hdev)
2712c3a88c6SNikolai Kondrashov {
2722c3a88c6SNikolai Kondrashov 	int rc;
2732c3a88c6SNikolai Kondrashov 	bool found = false;
2742c3a88c6SNikolai Kondrashov 	/* Buffer for (part of) the string descriptor */
2752c3a88c6SNikolai Kondrashov 	__u8 *buf = NULL;
2762c3a88c6SNikolai Kondrashov 	/* Descriptor length required */
2772c3a88c6SNikolai Kondrashov 	const int len = 18;
2782c3a88c6SNikolai Kondrashov 	s32 resolution;
2792c3a88c6SNikolai Kondrashov 	/* Pen report descriptor template parameters */
2802c3a88c6SNikolai Kondrashov 	s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
2812c3a88c6SNikolai Kondrashov 	__u8 *desc_ptr = NULL;
2822c3a88c6SNikolai Kondrashov 
2832c3a88c6SNikolai Kondrashov 	/* Check arguments */
2842c3a88c6SNikolai Kondrashov 	if (pen == NULL || pfound == NULL || hdev == NULL) {
2852c3a88c6SNikolai Kondrashov 		rc = -EINVAL;
2862c3a88c6SNikolai Kondrashov 		goto cleanup;
2872c3a88c6SNikolai Kondrashov 	}
2882c3a88c6SNikolai Kondrashov 
2892c3a88c6SNikolai Kondrashov 	/*
2902c3a88c6SNikolai Kondrashov 	 * Read string descriptor containing pen input parameters.
2912c3a88c6SNikolai Kondrashov 	 * The specific string descriptor and data were discovered by sniffing
2922c3a88c6SNikolai Kondrashov 	 * the Windows driver traffic.
2932c3a88c6SNikolai Kondrashov 	 * NOTE: This enables fully-functional tablet mode.
2942c3a88c6SNikolai Kondrashov 	 */
2952c3a88c6SNikolai Kondrashov 	rc = uclogic_params_get_str_desc(&buf, hdev, 200, len);
2962c3a88c6SNikolai Kondrashov 	if (rc == -EPIPE) {
2972c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
2982c3a88c6SNikolai Kondrashov 			"string descriptor with pen parameters not found, assuming not compatible\n");
2992c3a88c6SNikolai Kondrashov 		goto finish;
3002c3a88c6SNikolai Kondrashov 	} else if (rc < 0) {
3012c3a88c6SNikolai Kondrashov 		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
3022c3a88c6SNikolai Kondrashov 		goto cleanup;
3032c3a88c6SNikolai Kondrashov 	} else if (rc != len) {
3042c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
3052c3a88c6SNikolai Kondrashov 			"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
3062c3a88c6SNikolai Kondrashov 			rc, len);
3072c3a88c6SNikolai Kondrashov 		goto finish;
3082c3a88c6SNikolai Kondrashov 	} else {
3092c3a88c6SNikolai Kondrashov 		size_t i;
3102c3a88c6SNikolai Kondrashov 		/*
3112c3a88c6SNikolai Kondrashov 		 * Check it's not just a catch-all UTF-16LE-encoded ASCII
3122c3a88c6SNikolai Kondrashov 		 * string (such as the model name) some tablets put into all
3132c3a88c6SNikolai Kondrashov 		 * unknown string descriptors.
3142c3a88c6SNikolai Kondrashov 		 */
3152c3a88c6SNikolai Kondrashov 		for (i = 2;
3162c3a88c6SNikolai Kondrashov 		     i < len &&
3172c3a88c6SNikolai Kondrashov 			(buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
3182c3a88c6SNikolai Kondrashov 		     i += 2);
3192c3a88c6SNikolai Kondrashov 		if (i >= len) {
3202c3a88c6SNikolai Kondrashov 			hid_dbg(hdev,
3212c3a88c6SNikolai Kondrashov 				"string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
3222c3a88c6SNikolai Kondrashov 			goto finish;
3232c3a88c6SNikolai Kondrashov 		}
3242c3a88c6SNikolai Kondrashov 	}
3252c3a88c6SNikolai Kondrashov 
3262c3a88c6SNikolai Kondrashov 	/*
3272c3a88c6SNikolai Kondrashov 	 * Fill report descriptor parameters from the string descriptor
3282c3a88c6SNikolai Kondrashov 	 */
3292c3a88c6SNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
3302c3a88c6SNikolai Kondrashov 		uclogic_params_get_le24(buf + 2);
3312c3a88c6SNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
3322c3a88c6SNikolai Kondrashov 		uclogic_params_get_le24(buf + 5);
3332c3a88c6SNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
3342c3a88c6SNikolai Kondrashov 		get_unaligned_le16(buf + 8);
3352c3a88c6SNikolai Kondrashov 	resolution = get_unaligned_le16(buf + 10);
3362c3a88c6SNikolai Kondrashov 	if (resolution == 0) {
3372c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
3382c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
3392c3a88c6SNikolai Kondrashov 	} else {
3402c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
3412c3a88c6SNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
3422c3a88c6SNikolai Kondrashov 			resolution;
3432c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
3442c3a88c6SNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
3452c3a88c6SNikolai Kondrashov 			resolution;
3462c3a88c6SNikolai Kondrashov 	}
3472c3a88c6SNikolai Kondrashov 	kfree(buf);
3482c3a88c6SNikolai Kondrashov 	buf = NULL;
3492c3a88c6SNikolai Kondrashov 
3502c3a88c6SNikolai Kondrashov 	/*
3512c3a88c6SNikolai Kondrashov 	 * Generate pen report descriptor
3522c3a88c6SNikolai Kondrashov 	 */
3532c3a88c6SNikolai Kondrashov 	desc_ptr = uclogic_rdesc_template_apply(
354a985de58SNikolai Kondrashov 				uclogic_rdesc_v2_pen_template_arr,
355a985de58SNikolai Kondrashov 				uclogic_rdesc_v2_pen_template_size,
3562c3a88c6SNikolai Kondrashov 				desc_params, ARRAY_SIZE(desc_params));
3572c3a88c6SNikolai Kondrashov 	if (desc_ptr == NULL) {
3582c3a88c6SNikolai Kondrashov 		rc = -ENOMEM;
3592c3a88c6SNikolai Kondrashov 		goto cleanup;
3602c3a88c6SNikolai Kondrashov 	}
3612c3a88c6SNikolai Kondrashov 
3622c3a88c6SNikolai Kondrashov 	/*
3632c3a88c6SNikolai Kondrashov 	 * Fill-in the parameters
3642c3a88c6SNikolai Kondrashov 	 */
3652c3a88c6SNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
3662c3a88c6SNikolai Kondrashov 	pen->desc_ptr = desc_ptr;
3672c3a88c6SNikolai Kondrashov 	desc_ptr = NULL;
368a985de58SNikolai Kondrashov 	pen->desc_size = uclogic_rdesc_v2_pen_template_size;
369a985de58SNikolai Kondrashov 	pen->id = UCLOGIC_RDESC_V2_PEN_ID;
3702c3a88c6SNikolai Kondrashov 	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
3712c3a88c6SNikolai Kondrashov 	pen->fragmented_hires = true;
3721324c5acSNikolai Kondrashov 	pen->tilt_y_flipped = true;
3732c3a88c6SNikolai Kondrashov 	found = true;
3742c3a88c6SNikolai Kondrashov finish:
3752c3a88c6SNikolai Kondrashov 	*pfound = found;
3762c3a88c6SNikolai Kondrashov 	rc = 0;
3772c3a88c6SNikolai Kondrashov cleanup:
3782c3a88c6SNikolai Kondrashov 	kfree(desc_ptr);
3792c3a88c6SNikolai Kondrashov 	kfree(buf);
3802c3a88c6SNikolai Kondrashov 	return rc;
3812c3a88c6SNikolai Kondrashov }
3822c3a88c6SNikolai Kondrashov 
3832c3a88c6SNikolai Kondrashov /**
3849614219eSNikolai Kondrashov  * uclogic_params_frame_cleanup - free resources used by struct
3859614219eSNikolai Kondrashov  * uclogic_params_frame (tablet interface's frame controls input parameters).
3869614219eSNikolai Kondrashov  * Can be called repeatedly.
3879614219eSNikolai Kondrashov  *
3889614219eSNikolai Kondrashov  * @frame:	Frame controls input parameters to cleanup. Cannot be NULL.
3899614219eSNikolai Kondrashov  */
3909614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
3919614219eSNikolai Kondrashov {
3929614219eSNikolai Kondrashov 	kfree(frame->desc_ptr);
3939614219eSNikolai Kondrashov 	memset(frame, 0, sizeof(*frame));
3949614219eSNikolai Kondrashov }
3959614219eSNikolai Kondrashov 
3969614219eSNikolai Kondrashov /**
3979614219eSNikolai Kondrashov  * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
3989614219eSNikolai Kondrashov  * parameters with a static report descriptor.
3999614219eSNikolai Kondrashov  *
4009614219eSNikolai Kondrashov  * @frame:	Pointer to the frame parameters to initialize (to be cleaned
4019614219eSNikolai Kondrashov  *		up with uclogic_params_frame_cleanup()). Not modified in case
4029614219eSNikolai Kondrashov  *		of error. Cannot be NULL.
4039614219eSNikolai Kondrashov  * @desc_ptr:	Report descriptor pointer. Can be NULL, if desc_size is zero.
4049614219eSNikolai Kondrashov  * @desc_size:	Report descriptor size.
4059614219eSNikolai Kondrashov  * @id:		Report ID used for frame reports, if they should be tweaked,
4069614219eSNikolai Kondrashov  *		zero if not.
4079614219eSNikolai Kondrashov  *
4089614219eSNikolai Kondrashov  * Returns:
4099614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
4109614219eSNikolai Kondrashov  */
4119614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc(
4129614219eSNikolai Kondrashov 					struct uclogic_params_frame *frame,
4139614219eSNikolai Kondrashov 					const __u8 *desc_ptr,
4149614219eSNikolai Kondrashov 					size_t desc_size,
4159614219eSNikolai Kondrashov 					unsigned int id)
4169614219eSNikolai Kondrashov {
4179614219eSNikolai Kondrashov 	__u8 *copy_desc_ptr;
4189614219eSNikolai Kondrashov 
4199614219eSNikolai Kondrashov 	if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
4209614219eSNikolai Kondrashov 		return -EINVAL;
4219614219eSNikolai Kondrashov 
4229614219eSNikolai Kondrashov 	copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
4239614219eSNikolai Kondrashov 	if (copy_desc_ptr == NULL)
4249614219eSNikolai Kondrashov 		return -ENOMEM;
4259614219eSNikolai Kondrashov 
4269614219eSNikolai Kondrashov 	memset(frame, 0, sizeof(*frame));
4279614219eSNikolai Kondrashov 	frame->desc_ptr = copy_desc_ptr;
4289614219eSNikolai Kondrashov 	frame->desc_size = desc_size;
4299614219eSNikolai Kondrashov 	frame->id = id;
4309614219eSNikolai Kondrashov 	return 0;
4319614219eSNikolai Kondrashov }
4329614219eSNikolai Kondrashov 
4339614219eSNikolai Kondrashov /**
4342e28f3e0SNikolai Kondrashov  * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame
4352e28f3e0SNikolai Kondrashov  * controls.
4369614219eSNikolai Kondrashov  *
4379614219eSNikolai Kondrashov  * @frame:	Pointer to the frame parameters to initialize (to be cleaned
4389614219eSNikolai Kondrashov  *		up with uclogic_params_frame_cleanup()). Not modified in case
4399614219eSNikolai Kondrashov  *		of error, or if parameters are not found. Cannot be NULL.
4409614219eSNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
4419614219eSNikolai Kondrashov  *		were found, and to false if not (e.g. device was
4429614219eSNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
4439614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
4449614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
4459614219eSNikolai Kondrashov  *
4469614219eSNikolai Kondrashov  * Returns:
4479614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
4489614219eSNikolai Kondrashov  */
4492e28f3e0SNikolai Kondrashov static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,
4509614219eSNikolai Kondrashov 					bool *pfound,
4519614219eSNikolai Kondrashov 					struct hid_device *hdev)
4529614219eSNikolai Kondrashov {
4539614219eSNikolai Kondrashov 	int rc;
4549614219eSNikolai Kondrashov 	bool found = false;
455aa320fdbSJosé Expósito 	struct usb_device *usb_dev;
4569614219eSNikolai Kondrashov 	char *str_buf = NULL;
4579614219eSNikolai Kondrashov 	const size_t str_len = 16;
4589614219eSNikolai Kondrashov 
4599614219eSNikolai Kondrashov 	/* Check arguments */
4609614219eSNikolai Kondrashov 	if (frame == NULL || pfound == NULL || hdev == NULL) {
4619614219eSNikolai Kondrashov 		rc = -EINVAL;
4629614219eSNikolai Kondrashov 		goto cleanup;
4639614219eSNikolai Kondrashov 	}
4649614219eSNikolai Kondrashov 
465aa320fdbSJosé Expósito 	usb_dev = hid_to_usb_dev(hdev);
466aa320fdbSJosé Expósito 
4679614219eSNikolai Kondrashov 	/*
4689614219eSNikolai Kondrashov 	 * Enable generic button mode
4699614219eSNikolai Kondrashov 	 */
4709614219eSNikolai Kondrashov 	str_buf = kzalloc(str_len, GFP_KERNEL);
4719614219eSNikolai Kondrashov 	if (str_buf == NULL) {
4729614219eSNikolai Kondrashov 		rc = -ENOMEM;
4739614219eSNikolai Kondrashov 		goto cleanup;
4749614219eSNikolai Kondrashov 	}
4759614219eSNikolai Kondrashov 
4769614219eSNikolai Kondrashov 	rc = usb_string(usb_dev, 123, str_buf, str_len);
4779614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
4789614219eSNikolai Kondrashov 		hid_dbg(hdev,
4799614219eSNikolai Kondrashov 			"generic button -enabling string descriptor not found\n");
4809614219eSNikolai Kondrashov 	} else if (rc < 0) {
4819614219eSNikolai Kondrashov 		goto cleanup;
4829614219eSNikolai Kondrashov 	} else if (strncmp(str_buf, "HK On", rc) != 0) {
4839614219eSNikolai Kondrashov 		hid_dbg(hdev,
4849614219eSNikolai Kondrashov 			"invalid response to enabling generic buttons: \"%s\"\n",
4859614219eSNikolai Kondrashov 			str_buf);
4869614219eSNikolai Kondrashov 	} else {
4879614219eSNikolai Kondrashov 		hid_dbg(hdev, "generic buttons enabled\n");
4889614219eSNikolai Kondrashov 		rc = uclogic_params_frame_init_with_desc(
4899614219eSNikolai Kondrashov 				frame,
490a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_frame_arr,
491a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_frame_size,
492a985de58SNikolai Kondrashov 				UCLOGIC_RDESC_V1_FRAME_ID);
4939614219eSNikolai Kondrashov 		if (rc != 0)
4949614219eSNikolai Kondrashov 			goto cleanup;
4959614219eSNikolai Kondrashov 		found = true;
4969614219eSNikolai Kondrashov 	}
4979614219eSNikolai Kondrashov 
4989614219eSNikolai Kondrashov 	*pfound = found;
4999614219eSNikolai Kondrashov 	rc = 0;
5009614219eSNikolai Kondrashov cleanup:
5019614219eSNikolai Kondrashov 	kfree(str_buf);
5029614219eSNikolai Kondrashov 	return rc;
5039614219eSNikolai Kondrashov }
5049614219eSNikolai Kondrashov 
5059614219eSNikolai Kondrashov /**
5069614219eSNikolai Kondrashov  * uclogic_params_cleanup - free resources used by struct uclogic_params
5079614219eSNikolai Kondrashov  * (tablet interface's parameters).
5089614219eSNikolai Kondrashov  * Can be called repeatedly.
5099614219eSNikolai Kondrashov  *
5109614219eSNikolai Kondrashov  * @params:	Input parameters to cleanup. Cannot be NULL.
5119614219eSNikolai Kondrashov  */
5129614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params)
5139614219eSNikolai Kondrashov {
5149614219eSNikolai Kondrashov 	if (!params->invalid) {
515*337fa051SNikolai Kondrashov 		size_t i;
5169614219eSNikolai Kondrashov 		kfree(params->desc_ptr);
5179614219eSNikolai Kondrashov 		uclogic_params_pen_cleanup(&params->pen);
518*337fa051SNikolai Kondrashov 		for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)
519*337fa051SNikolai Kondrashov 			uclogic_params_frame_cleanup(&params->frame_list[i]);
520*337fa051SNikolai Kondrashov 
5219614219eSNikolai Kondrashov 		memset(params, 0, sizeof(*params));
5229614219eSNikolai Kondrashov 	}
5239614219eSNikolai Kondrashov }
5249614219eSNikolai Kondrashov 
5259614219eSNikolai Kondrashov /**
5265abb5445SLee Jones  * uclogic_params_get_desc() - Get a replacement report descriptor for a
5275abb5445SLee Jones  *                             tablet's interface.
5289614219eSNikolai Kondrashov  *
5299614219eSNikolai Kondrashov  * @params:	The parameters of a tablet interface to get report
5309614219eSNikolai Kondrashov  *		descriptor for. Cannot be NULL.
5319614219eSNikolai Kondrashov  * @pdesc:	Location for the resulting, kmalloc-allocated report
5329614219eSNikolai Kondrashov  *		descriptor pointer, or for NULL, if there's no replacement
5339614219eSNikolai Kondrashov  *		report descriptor. Not modified in case of error. Cannot be
5349614219eSNikolai Kondrashov  *		NULL.
5359614219eSNikolai Kondrashov  * @psize:	Location for the resulting report descriptor size, not set if
5369614219eSNikolai Kondrashov  *		there's no replacement report descriptor. Not modified in case
5379614219eSNikolai Kondrashov  *		of error. Cannot be NULL.
5389614219eSNikolai Kondrashov  *
5399614219eSNikolai Kondrashov  * Returns:
5409614219eSNikolai Kondrashov  *	Zero, if successful.
5419614219eSNikolai Kondrashov  *	-EINVAL, if invalid arguments are supplied.
5429614219eSNikolai Kondrashov  *	-ENOMEM, if failed to allocate memory.
5439614219eSNikolai Kondrashov  */
5449614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params,
5459614219eSNikolai Kondrashov 				__u8 **pdesc,
5469614219eSNikolai Kondrashov 				unsigned int *psize)
5479614219eSNikolai Kondrashov {
548*337fa051SNikolai Kondrashov 	int rc = -ENOMEM;
549*337fa051SNikolai Kondrashov 	bool present = false;
550*337fa051SNikolai Kondrashov 	unsigned int size = 0;
5519614219eSNikolai Kondrashov 	__u8 *desc = NULL;
552*337fa051SNikolai Kondrashov 	size_t i;
5539614219eSNikolai Kondrashov 
5549614219eSNikolai Kondrashov 	/* Check arguments */
5559614219eSNikolai Kondrashov 	if (params == NULL || pdesc == NULL || psize == NULL)
5569614219eSNikolai Kondrashov 		return -EINVAL;
5579614219eSNikolai Kondrashov 
558*337fa051SNikolai Kondrashov 	/* Concatenate descriptors */
559*337fa051SNikolai Kondrashov #define ADD_DESC(_desc_ptr, _desc_size) \
560*337fa051SNikolai Kondrashov 	do {                                                        \
561*337fa051SNikolai Kondrashov 		unsigned int new_size;                              \
562*337fa051SNikolai Kondrashov 		__u8 *new_desc;                                     \
563*337fa051SNikolai Kondrashov 		if ((_desc_ptr) == NULL) {                          \
564*337fa051SNikolai Kondrashov 			break;                                      \
565*337fa051SNikolai Kondrashov 		}                                                   \
566*337fa051SNikolai Kondrashov 		new_size = size + (_desc_size);                     \
567*337fa051SNikolai Kondrashov 		new_desc = krealloc(desc, new_size, GFP_KERNEL);    \
568*337fa051SNikolai Kondrashov 		if (new_desc == NULL) {                             \
569*337fa051SNikolai Kondrashov 			goto cleanup;                               \
570*337fa051SNikolai Kondrashov 		}                                                   \
571*337fa051SNikolai Kondrashov 		memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \
572*337fa051SNikolai Kondrashov 		desc = new_desc;                                    \
573*337fa051SNikolai Kondrashov 		size = new_size;                                    \
574*337fa051SNikolai Kondrashov 		present = true;                                     \
575*337fa051SNikolai Kondrashov 	} while (0)
5769614219eSNikolai Kondrashov 
577*337fa051SNikolai Kondrashov 	ADD_DESC(params->desc_ptr, params->desc_size);
578*337fa051SNikolai Kondrashov 	ADD_DESC(params->pen.desc_ptr, params->pen.desc_size);
579*337fa051SNikolai Kondrashov 	for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
580*337fa051SNikolai Kondrashov 		ADD_DESC(params->frame_list[i].desc_ptr,
581*337fa051SNikolai Kondrashov 				params->frame_list[i].desc_size);
5829614219eSNikolai Kondrashov 	}
5839614219eSNikolai Kondrashov 
584*337fa051SNikolai Kondrashov #undef ADD_DESC
5859614219eSNikolai Kondrashov 
586*337fa051SNikolai Kondrashov 	if (present) {
5879614219eSNikolai Kondrashov 		*pdesc = desc;
588*337fa051SNikolai Kondrashov 		*psize = size;
589*337fa051SNikolai Kondrashov 		desc = NULL;
590*337fa051SNikolai Kondrashov 	}
591*337fa051SNikolai Kondrashov 	rc = 0;
592*337fa051SNikolai Kondrashov cleanup:
593*337fa051SNikolai Kondrashov 	kfree(desc);
594*337fa051SNikolai Kondrashov 	return rc;
5959614219eSNikolai Kondrashov }
5969614219eSNikolai Kondrashov 
5979614219eSNikolai Kondrashov /**
5989614219eSNikolai Kondrashov  * uclogic_params_init_invalid() - initialize tablet interface parameters,
5999614219eSNikolai Kondrashov  * specifying the interface is invalid.
6009614219eSNikolai Kondrashov  *
6019614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
6029614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Cannot be NULL.
6039614219eSNikolai Kondrashov  */
6049614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params)
6059614219eSNikolai Kondrashov {
6069614219eSNikolai Kondrashov 	params->invalid = true;
6079614219eSNikolai Kondrashov }
6089614219eSNikolai Kondrashov 
6099614219eSNikolai Kondrashov /**
6109614219eSNikolai Kondrashov  * uclogic_params_init_with_opt_desc() - initialize tablet interface
6119614219eSNikolai Kondrashov  * parameters with an optional replacement report descriptor. Only modify
6129614219eSNikolai Kondrashov  * report descriptor, if the original report descriptor matches the expected
6139614219eSNikolai Kondrashov  * size.
6149614219eSNikolai Kondrashov  *
6159614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
6169614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Not modified in case of
6179614219eSNikolai Kondrashov  *			error. Cannot be NULL.
6189614219eSNikolai Kondrashov  * @hdev:		The HID device of the tablet interface create the
6199614219eSNikolai Kondrashov  *			parameters for. Cannot be NULL.
6209614219eSNikolai Kondrashov  * @orig_desc_size:	Expected size of the original report descriptor to
6219614219eSNikolai Kondrashov  *			be replaced.
6229614219eSNikolai Kondrashov  * @desc_ptr:		Pointer to the replacement report descriptor.
6239614219eSNikolai Kondrashov  *			Can be NULL, if desc_size is zero.
6249614219eSNikolai Kondrashov  * @desc_size:		Size of the replacement report descriptor.
6259614219eSNikolai Kondrashov  *
6269614219eSNikolai Kondrashov  * Returns:
6279614219eSNikolai Kondrashov  *	Zero, if successful. -EINVAL if an invalid argument was passed.
6289614219eSNikolai Kondrashov  *	-ENOMEM, if failed to allocate memory.
6299614219eSNikolai Kondrashov  */
6309614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
6319614219eSNikolai Kondrashov 					     struct hid_device *hdev,
6329614219eSNikolai Kondrashov 					     unsigned int orig_desc_size,
6339614219eSNikolai Kondrashov 					     __u8 *desc_ptr,
6349614219eSNikolai Kondrashov 					     unsigned int desc_size)
6359614219eSNikolai Kondrashov {
6369614219eSNikolai Kondrashov 	__u8 *desc_copy_ptr = NULL;
6379614219eSNikolai Kondrashov 	unsigned int desc_copy_size;
6389614219eSNikolai Kondrashov 	int rc;
6399614219eSNikolai Kondrashov 
6409614219eSNikolai Kondrashov 	/* Check arguments */
6419614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL ||
6429614219eSNikolai Kondrashov 	    (desc_ptr == NULL && desc_size != 0)) {
6439614219eSNikolai Kondrashov 		rc = -EINVAL;
6449614219eSNikolai Kondrashov 		goto cleanup;
6459614219eSNikolai Kondrashov 	}
6469614219eSNikolai Kondrashov 
6479614219eSNikolai Kondrashov 	/* Replace report descriptor, if it matches */
6489614219eSNikolai Kondrashov 	if (hdev->dev_rsize == orig_desc_size) {
6499614219eSNikolai Kondrashov 		hid_dbg(hdev,
6509614219eSNikolai Kondrashov 			"device report descriptor matches the expected size, replacing\n");
6519614219eSNikolai Kondrashov 		desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
6529614219eSNikolai Kondrashov 		if (desc_copy_ptr == NULL) {
6539614219eSNikolai Kondrashov 			rc = -ENOMEM;
6549614219eSNikolai Kondrashov 			goto cleanup;
6559614219eSNikolai Kondrashov 		}
6569614219eSNikolai Kondrashov 		desc_copy_size = desc_size;
6579614219eSNikolai Kondrashov 	} else {
6589614219eSNikolai Kondrashov 		hid_dbg(hdev,
6599614219eSNikolai Kondrashov 			"device report descriptor doesn't match the expected size (%u != %u), preserving\n",
6609614219eSNikolai Kondrashov 			hdev->dev_rsize, orig_desc_size);
6619614219eSNikolai Kondrashov 		desc_copy_ptr = NULL;
6629614219eSNikolai Kondrashov 		desc_copy_size = 0;
6639614219eSNikolai Kondrashov 	}
6649614219eSNikolai Kondrashov 
6659614219eSNikolai Kondrashov 	/* Output parameters */
6669614219eSNikolai Kondrashov 	memset(params, 0, sizeof(*params));
6679614219eSNikolai Kondrashov 	params->desc_ptr = desc_copy_ptr;
6689614219eSNikolai Kondrashov 	desc_copy_ptr = NULL;
6699614219eSNikolai Kondrashov 	params->desc_size = desc_copy_size;
6709614219eSNikolai Kondrashov 
6719614219eSNikolai Kondrashov 	rc = 0;
6729614219eSNikolai Kondrashov cleanup:
6739614219eSNikolai Kondrashov 	kfree(desc_copy_ptr);
6749614219eSNikolai Kondrashov 	return rc;
6759614219eSNikolai Kondrashov }
6769614219eSNikolai Kondrashov 
6779614219eSNikolai Kondrashov /**
6785abb5445SLee Jones  * uclogic_params_huion_init() - initialize a Huion tablet interface and discover
6799614219eSNikolai Kondrashov  * its parameters.
6809614219eSNikolai Kondrashov  *
6819614219eSNikolai Kondrashov  * @params:	Parameters to fill in (to be cleaned with
6829614219eSNikolai Kondrashov  *		uclogic_params_cleanup()). Not modified in case of error.
6839614219eSNikolai Kondrashov  *		Cannot be NULL.
6849614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
6859614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
6869614219eSNikolai Kondrashov  *
6879614219eSNikolai Kondrashov  * Returns:
6889614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
6899614219eSNikolai Kondrashov  */
6909614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params,
6919614219eSNikolai Kondrashov 				     struct hid_device *hdev)
6929614219eSNikolai Kondrashov {
6939614219eSNikolai Kondrashov 	int rc;
694ff6b548aSJosé Expósito 	struct usb_device *udev;
695ff6b548aSJosé Expósito 	struct usb_interface *iface;
696ff6b548aSJosé Expósito 	__u8 bInterfaceNumber;
6979614219eSNikolai Kondrashov 	bool found;
6989614219eSNikolai Kondrashov 	/* The resulting parameters (noop) */
6999614219eSNikolai Kondrashov 	struct uclogic_params p = {0, };
7002c3a88c6SNikolai Kondrashov 	static const char transition_ver[] = "HUION_T153_160607";
7012c3a88c6SNikolai Kondrashov 	char *ver_ptr = NULL;
7022c3a88c6SNikolai Kondrashov 	const size_t ver_len = sizeof(transition_ver) + 1;
7039614219eSNikolai Kondrashov 
7049614219eSNikolai Kondrashov 	/* Check arguments */
7059614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL) {
7069614219eSNikolai Kondrashov 		rc = -EINVAL;
7079614219eSNikolai Kondrashov 		goto cleanup;
7089614219eSNikolai Kondrashov 	}
7099614219eSNikolai Kondrashov 
710ff6b548aSJosé Expósito 	udev = hid_to_usb_dev(hdev);
711ff6b548aSJosé Expósito 	iface = to_usb_interface(hdev->dev.parent);
712ff6b548aSJosé Expósito 	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
713ff6b548aSJosé Expósito 
7149614219eSNikolai Kondrashov 	/* If it's not a pen interface */
7159614219eSNikolai Kondrashov 	if (bInterfaceNumber != 0) {
716606dadc1SNikolai Kondrashov 		uclogic_params_init_invalid(&p);
7179614219eSNikolai Kondrashov 		goto output;
7189614219eSNikolai Kondrashov 	}
7199614219eSNikolai Kondrashov 
7202c3a88c6SNikolai Kondrashov 	/* Try to get firmware version */
7212c3a88c6SNikolai Kondrashov 	ver_ptr = kzalloc(ver_len, GFP_KERNEL);
7222c3a88c6SNikolai Kondrashov 	if (ver_ptr == NULL) {
7232c3a88c6SNikolai Kondrashov 		rc = -ENOMEM;
7242c3a88c6SNikolai Kondrashov 		goto cleanup;
7252c3a88c6SNikolai Kondrashov 	}
7262c3a88c6SNikolai Kondrashov 	rc = usb_string(udev, 201, ver_ptr, ver_len);
7272c3a88c6SNikolai Kondrashov 	if (rc == -EPIPE) {
7282c3a88c6SNikolai Kondrashov 		*ver_ptr = '\0';
7292c3a88c6SNikolai Kondrashov 	} else if (rc < 0) {
7302c3a88c6SNikolai Kondrashov 		hid_err(hdev,
7312c3a88c6SNikolai Kondrashov 			"failed retrieving Huion firmware version: %d\n", rc);
7322c3a88c6SNikolai Kondrashov 		goto cleanup;
7332c3a88c6SNikolai Kondrashov 	}
7342c3a88c6SNikolai Kondrashov 
7352c3a88c6SNikolai Kondrashov 	/* If this is a transition firmware */
7362c3a88c6SNikolai Kondrashov 	if (strcmp(ver_ptr, transition_ver) == 0) {
7372c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
7382c3a88c6SNikolai Kondrashov 			"transition firmware detected, not probing pen v2 parameters\n");
7392c3a88c6SNikolai Kondrashov 	} else {
7402c3a88c6SNikolai Kondrashov 		/* Try to probe v2 pen parameters */
7412c3a88c6SNikolai Kondrashov 		rc = uclogic_params_pen_init_v2(&p.pen, &found, hdev);
7422c3a88c6SNikolai Kondrashov 		if (rc != 0) {
7432c3a88c6SNikolai Kondrashov 			hid_err(hdev,
7442c3a88c6SNikolai Kondrashov 				"failed probing pen v2 parameters: %d\n", rc);
7452c3a88c6SNikolai Kondrashov 			goto cleanup;
7462c3a88c6SNikolai Kondrashov 		} else if (found) {
7472c3a88c6SNikolai Kondrashov 			hid_dbg(hdev, "pen v2 parameters found\n");
7482e28f3e0SNikolai Kondrashov 			/* Create v2 frame parameters */
7492c3a88c6SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
750*337fa051SNikolai Kondrashov 					&p.frame_list[0],
751a985de58SNikolai Kondrashov 					uclogic_rdesc_v2_frame_arr,
752a985de58SNikolai Kondrashov 					uclogic_rdesc_v2_frame_size,
753a985de58SNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_ID);
7542c3a88c6SNikolai Kondrashov 			if (rc != 0) {
7552c3a88c6SNikolai Kondrashov 				hid_err(hdev,
7562e28f3e0SNikolai Kondrashov 					"failed creating v2 frame parameters: %d\n",
7572c3a88c6SNikolai Kondrashov 					rc);
7582c3a88c6SNikolai Kondrashov 				goto cleanup;
7592c3a88c6SNikolai Kondrashov 			}
7608b013098SNikolai Kondrashov 			/* Link frame button subreports from pen reports */
761e6be956fSNikolai Kondrashov 			p.pen.subreport_list[0].value = 0xe0;
7628b013098SNikolai Kondrashov 			p.pen.subreport_list[0].id =
763a985de58SNikolai Kondrashov 				UCLOGIC_RDESC_V2_FRAME_ID;
7642c3a88c6SNikolai Kondrashov 			goto output;
7652c3a88c6SNikolai Kondrashov 		}
7662c3a88c6SNikolai Kondrashov 		hid_dbg(hdev, "pen v2 parameters not found\n");
7672c3a88c6SNikolai Kondrashov 	}
7682c3a88c6SNikolai Kondrashov 
769eecb5b84SNikolai Kondrashov 	/* Try to probe v1 pen parameters */
770eecb5b84SNikolai Kondrashov 	rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
7719614219eSNikolai Kondrashov 	if (rc != 0) {
7729614219eSNikolai Kondrashov 		hid_err(hdev,
773eecb5b84SNikolai Kondrashov 			"failed probing pen v1 parameters: %d\n", rc);
7749614219eSNikolai Kondrashov 		goto cleanup;
7759614219eSNikolai Kondrashov 	} else if (found) {
776eecb5b84SNikolai Kondrashov 		hid_dbg(hdev, "pen v1 parameters found\n");
7772e28f3e0SNikolai Kondrashov 		/* Try to probe v1 frame */
778*337fa051SNikolai Kondrashov 		rc = uclogic_params_frame_init_v1(&p.frame_list[0],
7799614219eSNikolai Kondrashov 						  &found, hdev);
7809614219eSNikolai Kondrashov 		if (rc != 0) {
7812e28f3e0SNikolai Kondrashov 			hid_err(hdev, "v1 frame probing failed: %d\n", rc);
7829614219eSNikolai Kondrashov 			goto cleanup;
7839614219eSNikolai Kondrashov 		}
7842e28f3e0SNikolai Kondrashov 		hid_dbg(hdev, "frame v1 parameters%s found\n",
7859614219eSNikolai Kondrashov 			(found ? "" : " not"));
7869614219eSNikolai Kondrashov 		if (found) {
7878b013098SNikolai Kondrashov 			/* Link frame button subreports from pen reports */
788e6be956fSNikolai Kondrashov 			p.pen.subreport_list[0].value = 0xe0;
7898b013098SNikolai Kondrashov 			p.pen.subreport_list[0].id =
790a985de58SNikolai Kondrashov 				UCLOGIC_RDESC_V1_FRAME_ID;
7919614219eSNikolai Kondrashov 		}
7929614219eSNikolai Kondrashov 		goto output;
7939614219eSNikolai Kondrashov 	}
794eecb5b84SNikolai Kondrashov 	hid_dbg(hdev, "pen v1 parameters not found\n");
7959614219eSNikolai Kondrashov 
7969614219eSNikolai Kondrashov 	uclogic_params_init_invalid(&p);
7979614219eSNikolai Kondrashov 
7989614219eSNikolai Kondrashov output:
7999614219eSNikolai Kondrashov 	/* Output parameters */
8009614219eSNikolai Kondrashov 	memcpy(params, &p, sizeof(*params));
8019614219eSNikolai Kondrashov 	memset(&p, 0, sizeof(p));
8029614219eSNikolai Kondrashov 	rc = 0;
8039614219eSNikolai Kondrashov cleanup:
8042c3a88c6SNikolai Kondrashov 	kfree(ver_ptr);
8059614219eSNikolai Kondrashov 	uclogic_params_cleanup(&p);
8069614219eSNikolai Kondrashov 	return rc;
8079614219eSNikolai Kondrashov }
8089614219eSNikolai Kondrashov 
8099614219eSNikolai Kondrashov /**
8109614219eSNikolai Kondrashov  * uclogic_params_init() - initialize a tablet interface and discover its
8119614219eSNikolai Kondrashov  * parameters.
8129614219eSNikolai Kondrashov  *
8139614219eSNikolai Kondrashov  * @params:	Parameters to fill in (to be cleaned with
8149614219eSNikolai Kondrashov  *		uclogic_params_cleanup()). Not modified in case of error.
8159614219eSNikolai Kondrashov  *		Cannot be NULL.
8169614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
8178547b778SNikolai Kondrashov  *		parameters from. Cannot be NULL. Must be using the USB low-level
8188547b778SNikolai Kondrashov  *		driver, i.e. be an actual USB tablet.
8199614219eSNikolai Kondrashov  *
8209614219eSNikolai Kondrashov  * Returns:
8219614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
8229614219eSNikolai Kondrashov  */
8239614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params,
8249614219eSNikolai Kondrashov 			struct hid_device *hdev)
8259614219eSNikolai Kondrashov {
8269614219eSNikolai Kondrashov 	int rc;
827f364c571SJosé Expósito 	struct usb_device *udev;
828f364c571SJosé Expósito 	__u8  bNumInterfaces;
829f364c571SJosé Expósito 	struct usb_interface *iface;
830f364c571SJosé Expósito 	__u8 bInterfaceNumber;
8319614219eSNikolai Kondrashov 	bool found;
8329614219eSNikolai Kondrashov 	/* The resulting parameters (noop) */
8339614219eSNikolai Kondrashov 	struct uclogic_params p = {0, };
8349614219eSNikolai Kondrashov 
8359614219eSNikolai Kondrashov 	/* Check arguments */
836f83baa0cSGreg Kroah-Hartman 	if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {
8379614219eSNikolai Kondrashov 		rc = -EINVAL;
8389614219eSNikolai Kondrashov 		goto cleanup;
8399614219eSNikolai Kondrashov 	}
8409614219eSNikolai Kondrashov 
841f364c571SJosé Expósito 	udev = hid_to_usb_dev(hdev);
842f364c571SJosé Expósito 	bNumInterfaces = udev->config->desc.bNumInterfaces;
843f364c571SJosé Expósito 	iface = to_usb_interface(hdev->dev.parent);
844f364c571SJosé Expósito 	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
845f364c571SJosé Expósito 
8469614219eSNikolai Kondrashov 	/*
8479614219eSNikolai Kondrashov 	 * Set replacement report descriptor if the original matches the
8489614219eSNikolai Kondrashov 	 * specified size. Otherwise keep interface unchanged.
8499614219eSNikolai Kondrashov 	 */
8509614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
8519614219eSNikolai Kondrashov 	uclogic_params_init_with_opt_desc(                  \
8529614219eSNikolai Kondrashov 		&p, hdev,                                   \
8539614219eSNikolai Kondrashov 		UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \
8549614219eSNikolai Kondrashov 		uclogic_rdesc_##_new_desc_token##_arr,      \
8559614219eSNikolai Kondrashov 		uclogic_rdesc_##_new_desc_token##_size)
8569614219eSNikolai Kondrashov 
8579614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \
8589614219eSNikolai Kondrashov 	(((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
8599614219eSNikolai Kondrashov 
8609614219eSNikolai Kondrashov 	/*
8619614219eSNikolai Kondrashov 	 * Handle specific interfaces for specific tablets.
8629614219eSNikolai Kondrashov 	 *
8639614219eSNikolai Kondrashov 	 * Observe the following logic:
8649614219eSNikolai Kondrashov 	 *
8659614219eSNikolai Kondrashov 	 * If the interface is recognized as producing certain useful input:
8669614219eSNikolai Kondrashov 	 *	Mark interface as valid.
8679614219eSNikolai Kondrashov 	 *	Output interface parameters.
8689614219eSNikolai Kondrashov 	 * Else, if the interface is recognized as *not* producing any useful
8699614219eSNikolai Kondrashov 	 * input:
8709614219eSNikolai Kondrashov 	 *	Mark interface as invalid.
8719614219eSNikolai Kondrashov 	 * Else:
8729614219eSNikolai Kondrashov 	 *	Mark interface as valid.
8739614219eSNikolai Kondrashov 	 *	Output noop parameters.
8749614219eSNikolai Kondrashov 	 *
8759614219eSNikolai Kondrashov 	 * Rule of thumb: it is better to disable a broken interface than let
8769614219eSNikolai Kondrashov 	 *		  it spew garbage input.
8779614219eSNikolai Kondrashov 	 */
8789614219eSNikolai Kondrashov 
8799614219eSNikolai Kondrashov 	switch (VID_PID(hdev->vendor, hdev->product)) {
8809614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
8819614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
8829614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
8839614219eSNikolai Kondrashov 		if (rc != 0)
8849614219eSNikolai Kondrashov 			goto cleanup;
8859614219eSNikolai Kondrashov 		break;
8869614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
8879614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
8889614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_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_WP5540U):
8949c17f735SNikolai Kondrashov 		if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {
8959c17f735SNikolai Kondrashov 			if (bInterfaceNumber == 0) {
8969c17f735SNikolai Kondrashov 				/* Try to probe v1 pen parameters */
8979c17f735SNikolai Kondrashov 				rc = uclogic_params_pen_init_v1(&p.pen,
8989c17f735SNikolai Kondrashov 								&found, hdev);
8999c17f735SNikolai Kondrashov 				if (rc != 0) {
9009c17f735SNikolai Kondrashov 					hid_err(hdev,
9019c17f735SNikolai Kondrashov 						"pen probing failed: %d\n",
9029c17f735SNikolai Kondrashov 						rc);
9039c17f735SNikolai Kondrashov 					goto cleanup;
9049c17f735SNikolai Kondrashov 				}
9059c17f735SNikolai Kondrashov 				if (!found) {
9069c17f735SNikolai Kondrashov 					hid_warn(hdev,
9079c17f735SNikolai Kondrashov 						 "pen parameters not found");
9089c17f735SNikolai Kondrashov 				}
9099c17f735SNikolai Kondrashov 			} else {
9109c17f735SNikolai Kondrashov 				uclogic_params_init_invalid(&p);
9119c17f735SNikolai Kondrashov 			}
9129c17f735SNikolai Kondrashov 		} else {
9139614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
9149614219eSNikolai Kondrashov 			if (rc != 0)
9159614219eSNikolai Kondrashov 				goto cleanup;
9169c17f735SNikolai Kondrashov 		}
9179614219eSNikolai Kondrashov 		break;
9189614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9199614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
9209614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
9219614219eSNikolai Kondrashov 		if (rc != 0)
9229614219eSNikolai Kondrashov 			goto cleanup;
9239614219eSNikolai Kondrashov 		break;
9249614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9259614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
9269614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
9279614219eSNikolai Kondrashov 		if (rc != 0)
9289614219eSNikolai Kondrashov 			goto cleanup;
9299614219eSNikolai Kondrashov 		break;
9309614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9319614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
9329614219eSNikolai Kondrashov 		switch (bInterfaceNumber) {
9339614219eSNikolai Kondrashov 		case 0:
9349614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
9359614219eSNikolai Kondrashov 			if (rc != 0)
9369614219eSNikolai Kondrashov 				goto cleanup;
9379614219eSNikolai Kondrashov 			break;
9389614219eSNikolai Kondrashov 		case 1:
9399614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
9409614219eSNikolai Kondrashov 			if (rc != 0)
9419614219eSNikolai Kondrashov 				goto cleanup;
9429614219eSNikolai Kondrashov 			break;
9439614219eSNikolai Kondrashov 		case 2:
9449614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
9459614219eSNikolai Kondrashov 			if (rc != 0)
9469614219eSNikolai Kondrashov 				goto cleanup;
9479614219eSNikolai Kondrashov 			break;
9489614219eSNikolai Kondrashov 		}
9499614219eSNikolai Kondrashov 		break;
9509614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9519614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
9529614219eSNikolai Kondrashov 		/*
9539614219eSNikolai Kondrashov 		 * If it is not a three-interface version, which is known to
9549614219eSNikolai Kondrashov 		 * respond to initialization.
9559614219eSNikolai Kondrashov 		 */
9569614219eSNikolai Kondrashov 		if (bNumInterfaces != 3) {
9579614219eSNikolai Kondrashov 			switch (bInterfaceNumber) {
9589614219eSNikolai Kondrashov 			case 0:
9599614219eSNikolai Kondrashov 				rc = WITH_OPT_DESC(TWHA60_ORIG0,
9609614219eSNikolai Kondrashov 							twha60_fixed0);
9619614219eSNikolai Kondrashov 				if (rc != 0)
9629614219eSNikolai Kondrashov 					goto cleanup;
9639614219eSNikolai Kondrashov 				break;
9649614219eSNikolai Kondrashov 			case 1:
9659614219eSNikolai Kondrashov 				rc = WITH_OPT_DESC(TWHA60_ORIG1,
9669614219eSNikolai Kondrashov 							twha60_fixed1);
9679614219eSNikolai Kondrashov 				if (rc != 0)
9689614219eSNikolai Kondrashov 					goto cleanup;
9699614219eSNikolai Kondrashov 				break;
9709614219eSNikolai Kondrashov 			}
9719614219eSNikolai Kondrashov 			break;
9729614219eSNikolai Kondrashov 		}
973df561f66SGustavo A. R. Silva 		fallthrough;
9749614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_HUION,
9759614219eSNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET):
976315ffcc9SKyle Godbey 	case VID_PID(USB_VENDOR_ID_HUION,
97785e86071SNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET2):
9789614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9799614219eSNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET):
9809614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9819614219eSNikolai Kondrashov 		     USB_DEVICE_ID_YIYNOVA_TABLET):
9829614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9839614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
9849614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9859614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
9869614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9879614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
9880c15efe9SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
9890c15efe9SNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):
9909614219eSNikolai Kondrashov 		rc = uclogic_params_huion_init(&p, hdev);
9919614219eSNikolai Kondrashov 		if (rc != 0)
9929614219eSNikolai Kondrashov 			goto cleanup;
9939614219eSNikolai Kondrashov 		break;
9949614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGTIZER,
9959614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
996022fc531SMartijn van de Streek 	case VID_PID(USB_VENDOR_ID_UGTIZER,
997022fc531SMartijn van de Streek 		     USB_DEVICE_ID_UGTIZER_TABLET_GT5040):
998c3e5a67cSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
999c3e5a67cSNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
1000492a9e9aSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
1001492a9e9aSNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
100288bb346dSWang Xuerui 	case VID_PID(USB_VENDOR_ID_UGEE,
100388bb346dSWang Xuerui 		     USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
10049614219eSNikolai Kondrashov 		/* If this is the pen interface */
10059614219eSNikolai Kondrashov 		if (bInterfaceNumber == 1) {
1006eecb5b84SNikolai Kondrashov 			/* Probe v1 pen parameters */
1007eecb5b84SNikolai Kondrashov 			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
10089614219eSNikolai Kondrashov 			if (rc != 0) {
10099614219eSNikolai Kondrashov 				hid_err(hdev, "pen probing failed: %d\n", rc);
10109614219eSNikolai Kondrashov 				goto cleanup;
10119614219eSNikolai Kondrashov 			}
10129614219eSNikolai Kondrashov 			if (!found) {
10139614219eSNikolai Kondrashov 				hid_warn(hdev, "pen parameters not found");
10149614219eSNikolai Kondrashov 				uclogic_params_init_invalid(&p);
10159614219eSNikolai Kondrashov 			}
10169614219eSNikolai Kondrashov 		} else {
1017606dadc1SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
10189614219eSNikolai Kondrashov 		}
10199614219eSNikolai Kondrashov 		break;
10201ee7c685SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
102108367be1SNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
102208367be1SNikolai Kondrashov 		/* If this is the pen and frame interface */
102308367be1SNikolai Kondrashov 		if (bInterfaceNumber == 1) {
102408367be1SNikolai Kondrashov 			/* Probe v1 pen parameters */
102508367be1SNikolai Kondrashov 			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
102608367be1SNikolai Kondrashov 			if (rc != 0) {
102708367be1SNikolai Kondrashov 				hid_err(hdev, "pen probing failed: %d\n", rc);
102808367be1SNikolai Kondrashov 				goto cleanup;
102908367be1SNikolai Kondrashov 			}
103008367be1SNikolai Kondrashov 			/* Initialize frame parameters */
103108367be1SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
1032*337fa051SNikolai Kondrashov 				&p.frame_list[0],
103308367be1SNikolai Kondrashov 				uclogic_rdesc_xppen_deco01_frame_arr,
103408367be1SNikolai Kondrashov 				uclogic_rdesc_xppen_deco01_frame_size,
103508367be1SNikolai Kondrashov 				0);
103608367be1SNikolai Kondrashov 			if (rc != 0)
103708367be1SNikolai Kondrashov 				goto cleanup;
103808367be1SNikolai Kondrashov 		} else {
1039606dadc1SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
104008367be1SNikolai Kondrashov 		}
104108367be1SNikolai Kondrashov 		break;
1042f7271b2aSCristian Klein 	case VID_PID(USB_VENDOR_ID_TRUST,
1043f7271b2aSCristian Klein 		     USB_DEVICE_ID_TRUST_PANORA_TABLET):
104408367be1SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
1045e902ed93SNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_TABLET_G5):
1046e902ed93SNikolai Kondrashov 		/* Ignore non-pen interfaces */
1047e902ed93SNikolai Kondrashov 		if (bInterfaceNumber != 1) {
1048e902ed93SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
1049e902ed93SNikolai Kondrashov 			break;
1050e902ed93SNikolai Kondrashov 		}
1051e902ed93SNikolai Kondrashov 
1052e902ed93SNikolai Kondrashov 		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1053e902ed93SNikolai Kondrashov 		if (rc != 0) {
1054e902ed93SNikolai Kondrashov 			hid_err(hdev, "pen probing failed: %d\n", rc);
1055e902ed93SNikolai Kondrashov 			goto cleanup;
1056e902ed93SNikolai Kondrashov 		} else if (found) {
1057e902ed93SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
1058*337fa051SNikolai Kondrashov 				&p.frame_list[0],
1059e902ed93SNikolai Kondrashov 				uclogic_rdesc_ugee_g5_frame_arr,
1060e902ed93SNikolai Kondrashov 				uclogic_rdesc_ugee_g5_frame_size,
1061e902ed93SNikolai Kondrashov 				UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
1062e902ed93SNikolai Kondrashov 			if (rc != 0) {
1063e902ed93SNikolai Kondrashov 				hid_err(hdev,
10642e28f3e0SNikolai Kondrashov 					"failed creating frame parameters: %d\n",
1065e902ed93SNikolai Kondrashov 					rc);
1066e902ed93SNikolai Kondrashov 				goto cleanup;
1067e902ed93SNikolai Kondrashov 			}
1068*337fa051SNikolai Kondrashov 			p.frame_list[0].re_lsb =
1069e902ed93SNikolai Kondrashov 				UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
1070*337fa051SNikolai Kondrashov 			p.frame_list[0].dev_id_byte =
1071e902ed93SNikolai Kondrashov 				UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
1072e902ed93SNikolai Kondrashov 		} else {
1073e902ed93SNikolai Kondrashov 			hid_warn(hdev, "pen parameters not found");
1074e902ed93SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
1075e902ed93SNikolai Kondrashov 		}
1076e902ed93SNikolai Kondrashov 
1077e902ed93SNikolai Kondrashov 		break;
1078e902ed93SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
10791ee7c685SNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_TABLET_EX07S):
10801ee7c685SNikolai Kondrashov 		/* Ignore non-pen interfaces */
10811ee7c685SNikolai Kondrashov 		if (bInterfaceNumber != 1) {
10821ee7c685SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
10831ee7c685SNikolai Kondrashov 			break;
10841ee7c685SNikolai Kondrashov 		}
10851ee7c685SNikolai Kondrashov 
10861ee7c685SNikolai Kondrashov 		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
10871ee7c685SNikolai Kondrashov 		if (rc != 0) {
10881ee7c685SNikolai Kondrashov 			hid_err(hdev, "pen probing failed: %d\n", rc);
10891ee7c685SNikolai Kondrashov 			goto cleanup;
10901ee7c685SNikolai Kondrashov 		} else if (found) {
10911ee7c685SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
1092*337fa051SNikolai Kondrashov 				&p.frame_list[0],
10932e28f3e0SNikolai Kondrashov 				uclogic_rdesc_ugee_ex07_frame_arr,
10942e28f3e0SNikolai Kondrashov 				uclogic_rdesc_ugee_ex07_frame_size,
10951ee7c685SNikolai Kondrashov 				0);
10961ee7c685SNikolai Kondrashov 			if (rc != 0) {
10971ee7c685SNikolai Kondrashov 				hid_err(hdev,
10982e28f3e0SNikolai Kondrashov 					"failed creating frame parameters: %d\n",
10991ee7c685SNikolai Kondrashov 					rc);
11001ee7c685SNikolai Kondrashov 				goto cleanup;
11011ee7c685SNikolai Kondrashov 			}
11021ee7c685SNikolai Kondrashov 		} else {
11031ee7c685SNikolai Kondrashov 			hid_warn(hdev, "pen parameters not found");
11041ee7c685SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
11051ee7c685SNikolai Kondrashov 		}
11061ee7c685SNikolai Kondrashov 
11071ee7c685SNikolai Kondrashov 		break;
11089614219eSNikolai Kondrashov 	}
11099614219eSNikolai Kondrashov 
11109614219eSNikolai Kondrashov #undef VID_PID
11119614219eSNikolai Kondrashov #undef WITH_OPT_DESC
11129614219eSNikolai Kondrashov 
11139614219eSNikolai Kondrashov 	/* Output parameters */
11149614219eSNikolai Kondrashov 	memcpy(params, &p, sizeof(*params));
11159614219eSNikolai Kondrashov 	memset(&p, 0, sizeof(p));
11169614219eSNikolai Kondrashov 	rc = 0;
11179614219eSNikolai Kondrashov cleanup:
11189614219eSNikolai Kondrashov 	uclogic_params_cleanup(&p);
11199614219eSNikolai Kondrashov 	return rc;
11209614219eSNikolai Kondrashov }
1121