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