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