19614219eSNikolai Kondrashov // SPDX-License-Identifier: GPL-2.0+
29614219eSNikolai Kondrashov /*
39614219eSNikolai Kondrashov  *  HID driver for UC-Logic devices not fully compliant with HID standard
49614219eSNikolai Kondrashov  *  - tablet initialization and parameter retrieval
59614219eSNikolai Kondrashov  *
69614219eSNikolai Kondrashov  *  Copyright (c) 2018 Nikolai Kondrashov
79614219eSNikolai Kondrashov  */
89614219eSNikolai Kondrashov 
99614219eSNikolai Kondrashov /*
109614219eSNikolai Kondrashov  * This program is free software; you can redistribute it and/or modify it
119614219eSNikolai Kondrashov  * under the terms of the GNU General Public License as published by the Free
129614219eSNikolai Kondrashov  * Software Foundation; either version 2 of the License, or (at your option)
139614219eSNikolai Kondrashov  * any later version.
149614219eSNikolai Kondrashov  */
159614219eSNikolai Kondrashov 
169614219eSNikolai Kondrashov #include "hid-uclogic-params.h"
179614219eSNikolai Kondrashov #include "hid-uclogic-rdesc.h"
189614219eSNikolai Kondrashov #include "usbhid/usbhid.h"
199614219eSNikolai Kondrashov #include "hid-ids.h"
209614219eSNikolai Kondrashov #include <linux/ctype.h>
219614219eSNikolai Kondrashov #include <asm/unaligned.h>
229614219eSNikolai Kondrashov 
239614219eSNikolai Kondrashov /**
245abb5445SLee Jones  * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type
255abb5445SLee Jones  *                                       to a string.
269614219eSNikolai Kondrashov  *
279614219eSNikolai Kondrashov  * @inrange:	The in-range reporting type to convert.
289614219eSNikolai Kondrashov  *
299614219eSNikolai Kondrashov  * Returns:
309614219eSNikolai Kondrashov  *	The string representing the type, or NULL if the type is unknown.
319614219eSNikolai Kondrashov  */
32a228809fSNikolai Kondrashov static const char *uclogic_params_pen_inrange_to_str(
339614219eSNikolai Kondrashov 				enum uclogic_params_pen_inrange inrange)
349614219eSNikolai Kondrashov {
359614219eSNikolai Kondrashov 	switch (inrange) {
369614219eSNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
379614219eSNikolai Kondrashov 		return "normal";
389614219eSNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
399614219eSNikolai Kondrashov 		return "inverted";
4001309e29SNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_NONE:
4101309e29SNikolai Kondrashov 		return "none";
429614219eSNikolai Kondrashov 	default:
439614219eSNikolai Kondrashov 		return NULL;
449614219eSNikolai Kondrashov 	}
459614219eSNikolai Kondrashov }
469614219eSNikolai Kondrashov 
479614219eSNikolai Kondrashov /**
48a228809fSNikolai Kondrashov  * Dump tablet interface pen parameters with hid_dbg(), indented with one tab.
49a228809fSNikolai Kondrashov  *
50a228809fSNikolai Kondrashov  * @hdev:	The HID device the pen parameters describe.
51a228809fSNikolai Kondrashov  * @pen:	The pen parameters to dump.
52a228809fSNikolai Kondrashov  */
53a228809fSNikolai Kondrashov static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev,
54a228809fSNikolai Kondrashov 					const struct uclogic_params_pen *pen)
55a228809fSNikolai Kondrashov {
56a228809fSNikolai Kondrashov 	size_t i;
57a228809fSNikolai Kondrashov 
58a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.usage_invalid = %s\n",
59a228809fSNikolai Kondrashov 		(pen->usage_invalid ? "true" : "false"));
60a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr);
61a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size);
62a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.id = %u\n", pen->id);
63a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.subreport_list = {\n");
64a228809fSNikolai Kondrashov 	for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) {
65a228809fSNikolai Kondrashov 		hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n",
66a228809fSNikolai Kondrashov 			pen->subreport_list[i].value,
67a228809fSNikolai Kondrashov 			pen->subreport_list[i].id,
68a228809fSNikolai Kondrashov 			i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : "");
69a228809fSNikolai Kondrashov 	}
70a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t}\n");
71a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.inrange = %s\n",
72a228809fSNikolai Kondrashov 		uclogic_params_pen_inrange_to_str(pen->inrange));
73a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.fragmented_hires = %s\n",
74a228809fSNikolai Kondrashov 		(pen->fragmented_hires ? "true" : "false"));
75a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t.tilt_y_flipped = %s\n",
76a228809fSNikolai Kondrashov 		(pen->tilt_y_flipped ? "true" : "false"));
77a228809fSNikolai Kondrashov }
78a228809fSNikolai Kondrashov 
79a228809fSNikolai Kondrashov /**
80a228809fSNikolai Kondrashov  * Dump tablet interface frame parameters with hid_dbg(), indented with two
81a228809fSNikolai Kondrashov  * tabs.
82a228809fSNikolai Kondrashov  *
83a228809fSNikolai Kondrashov  * @hdev:	The HID device the pen parameters describe.
84a228809fSNikolai Kondrashov  * @frame:	The frame parameters to dump.
85a228809fSNikolai Kondrashov  */
86a228809fSNikolai Kondrashov static void uclogic_params_frame_hid_dbg(
87a228809fSNikolai Kondrashov 				const struct hid_device *hdev,
88a228809fSNikolai Kondrashov 				const struct uclogic_params_frame *frame)
89a228809fSNikolai Kondrashov {
90a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr);
91a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size);
92a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.id = %u\n", frame->id);
93a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix);
94a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb);
95a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte);
96caf7e934SNikolai Kondrashov 	hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte);
97caf7e934SNikolai Kondrashov 	hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max);
98caf7e934SNikolai Kondrashov 	hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n",
99caf7e934SNikolai Kondrashov 		frame->touch_flip_at);
100a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",
101a228809fSNikolai Kondrashov 		frame->bitmap_dial_byte);
102a228809fSNikolai Kondrashov }
103a228809fSNikolai Kondrashov 
104a228809fSNikolai Kondrashov /**
105a228809fSNikolai Kondrashov  * Dump tablet interface parameters with hid_dbg().
106a228809fSNikolai Kondrashov  *
107a228809fSNikolai Kondrashov  * @hdev:	The HID device the parameters describe.
108a228809fSNikolai Kondrashov  * @params:	The parameters to dump.
109a228809fSNikolai Kondrashov  */
110a228809fSNikolai Kondrashov void uclogic_params_hid_dbg(const struct hid_device *hdev,
111a228809fSNikolai Kondrashov 				const struct uclogic_params *params)
112a228809fSNikolai Kondrashov {
113a228809fSNikolai Kondrashov 	size_t i;
114a228809fSNikolai Kondrashov 
115a228809fSNikolai Kondrashov 	hid_dbg(hdev, ".invalid = %s\n",
116a228809fSNikolai Kondrashov 		params->invalid ? "true" : "false");
117a228809fSNikolai Kondrashov 	hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr);
118a228809fSNikolai Kondrashov 	hid_dbg(hdev, ".desc_size = %u\n", params->desc_size);
119a228809fSNikolai Kondrashov 	hid_dbg(hdev, ".pen = {\n");
120a228809fSNikolai Kondrashov 	uclogic_params_pen_hid_dbg(hdev, &params->pen);
121a228809fSNikolai Kondrashov 	hid_dbg(hdev, "\t}\n");
122a228809fSNikolai Kondrashov 	hid_dbg(hdev, ".frame_list = {\n");
123a228809fSNikolai Kondrashov 	for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
124a228809fSNikolai Kondrashov 		hid_dbg(hdev, "\t{\n");
125a228809fSNikolai Kondrashov 		uclogic_params_frame_hid_dbg(hdev, &params->frame_list[i]);
126a228809fSNikolai Kondrashov 		hid_dbg(hdev, "\t}%s\n",
127a228809fSNikolai Kondrashov 			i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : "");
128a228809fSNikolai Kondrashov 	}
129a228809fSNikolai Kondrashov 	hid_dbg(hdev, "}\n");
130a228809fSNikolai Kondrashov }
131a228809fSNikolai Kondrashov 
132a228809fSNikolai Kondrashov /**
1339614219eSNikolai Kondrashov  * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
1349614219eSNikolai Kondrashov  * device interface, putting it into a kmalloc-allocated buffer as is, without
1359614219eSNikolai Kondrashov  * character encoding conversion.
1369614219eSNikolai Kondrashov  *
1379614219eSNikolai Kondrashov  * @pbuf:	Location for the kmalloc-allocated buffer pointer containing
1389614219eSNikolai Kondrashov  *		the retrieved descriptor. Not modified in case of error.
1399614219eSNikolai Kondrashov  *		Can be NULL to have retrieved descriptor discarded.
1409614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to retrieve the string
1419614219eSNikolai Kondrashov  *		descriptor from. Cannot be NULL.
1429614219eSNikolai Kondrashov  * @idx:	Index of the string descriptor to request from the device.
1439614219eSNikolai Kondrashov  * @len:	Length of the buffer to allocate and the data to retrieve.
1449614219eSNikolai Kondrashov  *
1459614219eSNikolai Kondrashov  * Returns:
1469614219eSNikolai Kondrashov  *	number of bytes retrieved (<= len),
1479614219eSNikolai Kondrashov  *	-EPIPE, if the descriptor was not found, or
1489614219eSNikolai Kondrashov  *	another negative errno code in case of other error.
1499614219eSNikolai Kondrashov  */
1509614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
1519614219eSNikolai Kondrashov 					__u8 idx, size_t len)
1529614219eSNikolai Kondrashov {
1539614219eSNikolai Kondrashov 	int rc;
1540a94131dSJosé Expósito 	struct usb_device *udev;
1559614219eSNikolai Kondrashov 	__u8 *buf = NULL;
1569614219eSNikolai Kondrashov 
1579614219eSNikolai Kondrashov 	/* Check arguments */
1589614219eSNikolai Kondrashov 	if (hdev == NULL) {
1599614219eSNikolai Kondrashov 		rc = -EINVAL;
1609614219eSNikolai Kondrashov 		goto cleanup;
1619614219eSNikolai Kondrashov 	}
1629614219eSNikolai Kondrashov 
1630a94131dSJosé Expósito 	udev = hid_to_usb_dev(hdev);
1640a94131dSJosé Expósito 
1659614219eSNikolai Kondrashov 	buf = kmalloc(len, GFP_KERNEL);
1669614219eSNikolai Kondrashov 	if (buf == NULL) {
1679614219eSNikolai Kondrashov 		rc = -ENOMEM;
1689614219eSNikolai Kondrashov 		goto cleanup;
1699614219eSNikolai Kondrashov 	}
1709614219eSNikolai Kondrashov 
1719614219eSNikolai Kondrashov 	rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
1729614219eSNikolai Kondrashov 				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
1739614219eSNikolai Kondrashov 				(USB_DT_STRING << 8) + idx,
1749614219eSNikolai Kondrashov 				0x0409, buf, len,
1759614219eSNikolai Kondrashov 				USB_CTRL_GET_TIMEOUT);
1769614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
1779614219eSNikolai Kondrashov 		hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
1789614219eSNikolai Kondrashov 		goto cleanup;
1799614219eSNikolai Kondrashov 	} else if (rc < 0) {
1809614219eSNikolai Kondrashov 		hid_err(hdev,
181a876e7e2STom Rix 			"failed retrieving string descriptor #%u: %d\n",
1829614219eSNikolai Kondrashov 			idx, rc);
1839614219eSNikolai Kondrashov 		goto cleanup;
1849614219eSNikolai Kondrashov 	}
1859614219eSNikolai Kondrashov 
1869614219eSNikolai Kondrashov 	if (pbuf != NULL) {
1879614219eSNikolai Kondrashov 		*pbuf = buf;
1889614219eSNikolai Kondrashov 		buf = NULL;
1899614219eSNikolai Kondrashov 	}
1909614219eSNikolai Kondrashov 
1919614219eSNikolai Kondrashov cleanup:
1929614219eSNikolai Kondrashov 	kfree(buf);
1939614219eSNikolai Kondrashov 	return rc;
1949614219eSNikolai Kondrashov }
1959614219eSNikolai Kondrashov 
1969614219eSNikolai Kondrashov /**
1979614219eSNikolai Kondrashov  * uclogic_params_pen_cleanup - free resources used by struct
1989614219eSNikolai Kondrashov  * uclogic_params_pen (tablet interface's pen input parameters).
1999614219eSNikolai Kondrashov  * Can be called repeatedly.
2009614219eSNikolai Kondrashov  *
2019614219eSNikolai Kondrashov  * @pen:	Pen input parameters to cleanup. Cannot be NULL.
2029614219eSNikolai Kondrashov  */
2039614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
2049614219eSNikolai Kondrashov {
2059614219eSNikolai Kondrashov 	kfree(pen->desc_ptr);
2069614219eSNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
2079614219eSNikolai Kondrashov }
2089614219eSNikolai Kondrashov 
2099614219eSNikolai Kondrashov /**
210eecb5b84SNikolai Kondrashov  * uclogic_params_pen_init_v1() - initialize tablet interface pen
211eecb5b84SNikolai Kondrashov  * input and retrieve its parameters from the device, using v1 protocol.
2129614219eSNikolai Kondrashov  *
2139614219eSNikolai Kondrashov  * @pen:	Pointer to the pen parameters to initialize (to be
2149614219eSNikolai Kondrashov  *		cleaned up with uclogic_params_pen_cleanup()). Not modified in
2159614219eSNikolai Kondrashov  *		case of error, or if parameters are not found. Cannot be NULL.
2169614219eSNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
2179614219eSNikolai Kondrashov  *		were found, and to false if not (e.g. device was
2189614219eSNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
2199614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
2209614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
2219614219eSNikolai Kondrashov  *
2229614219eSNikolai Kondrashov  * Returns:
2239614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
2249614219eSNikolai Kondrashov  */
225eecb5b84SNikolai Kondrashov static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,
2269614219eSNikolai Kondrashov 				      bool *pfound,
2279614219eSNikolai Kondrashov 				      struct hid_device *hdev)
2289614219eSNikolai Kondrashov {
2299614219eSNikolai Kondrashov 	int rc;
2309614219eSNikolai Kondrashov 	bool found = false;
2319614219eSNikolai Kondrashov 	/* Buffer for (part of) the string descriptor */
2329614219eSNikolai Kondrashov 	__u8 *buf = NULL;
2339614219eSNikolai Kondrashov 	/* Minimum descriptor length required, maximum seen so far is 18 */
2349614219eSNikolai Kondrashov 	const int len = 12;
2359614219eSNikolai Kondrashov 	s32 resolution;
2369614219eSNikolai Kondrashov 	/* Pen report descriptor template parameters */
23776e645beSJosé Expósito 	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
2389614219eSNikolai Kondrashov 	__u8 *desc_ptr = NULL;
2399614219eSNikolai Kondrashov 
2409614219eSNikolai Kondrashov 	/* Check arguments */
2419614219eSNikolai Kondrashov 	if (pen == NULL || pfound == NULL || hdev == NULL) {
2429614219eSNikolai Kondrashov 		rc = -EINVAL;
2439614219eSNikolai Kondrashov 		goto cleanup;
2449614219eSNikolai Kondrashov 	}
2459614219eSNikolai Kondrashov 
2469614219eSNikolai Kondrashov 	/*
2479614219eSNikolai Kondrashov 	 * Read string descriptor containing pen input parameters.
2489614219eSNikolai Kondrashov 	 * The specific string descriptor and data were discovered by sniffing
2499614219eSNikolai Kondrashov 	 * the Windows driver traffic.
2509614219eSNikolai Kondrashov 	 * NOTE: This enables fully-functional tablet mode.
2519614219eSNikolai Kondrashov 	 */
2529614219eSNikolai Kondrashov 	rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
2539614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
2549614219eSNikolai Kondrashov 		hid_dbg(hdev,
2559614219eSNikolai Kondrashov 			"string descriptor with pen parameters not found, assuming not compatible\n");
2569614219eSNikolai Kondrashov 		goto finish;
2579614219eSNikolai Kondrashov 	} else if (rc < 0) {
2589614219eSNikolai Kondrashov 		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
2599614219eSNikolai Kondrashov 		goto cleanup;
2609614219eSNikolai Kondrashov 	} else if (rc != len) {
2619614219eSNikolai Kondrashov 		hid_dbg(hdev,
2629614219eSNikolai Kondrashov 			"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
2639614219eSNikolai Kondrashov 			rc, len);
2649614219eSNikolai Kondrashov 		goto finish;
2659614219eSNikolai Kondrashov 	}
2669614219eSNikolai Kondrashov 
2679614219eSNikolai Kondrashov 	/*
2689614219eSNikolai Kondrashov 	 * Fill report descriptor parameters from the string descriptor
2699614219eSNikolai Kondrashov 	 */
2709614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
2719614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 2);
2729614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
2739614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 4);
2749614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
2759614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 8);
2769614219eSNikolai Kondrashov 	resolution = get_unaligned_le16(buf + 10);
2779614219eSNikolai Kondrashov 	if (resolution == 0) {
2789614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
2799614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
2809614219eSNikolai Kondrashov 	} else {
2819614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
2829614219eSNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
2839614219eSNikolai Kondrashov 			resolution;
2849614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
2859614219eSNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
2869614219eSNikolai Kondrashov 			resolution;
2879614219eSNikolai Kondrashov 	}
2889614219eSNikolai Kondrashov 	kfree(buf);
2899614219eSNikolai Kondrashov 	buf = NULL;
2909614219eSNikolai Kondrashov 
2919614219eSNikolai Kondrashov 	/*
2929614219eSNikolai Kondrashov 	 * Generate pen report descriptor
2939614219eSNikolai Kondrashov 	 */
2949614219eSNikolai Kondrashov 	desc_ptr = uclogic_rdesc_template_apply(
295a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_pen_template_arr,
296a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_pen_template_size,
2979614219eSNikolai Kondrashov 				desc_params, ARRAY_SIZE(desc_params));
2989614219eSNikolai Kondrashov 	if (desc_ptr == NULL) {
2999614219eSNikolai Kondrashov 		rc = -ENOMEM;
3009614219eSNikolai Kondrashov 		goto cleanup;
3019614219eSNikolai Kondrashov 	}
3029614219eSNikolai Kondrashov 
3039614219eSNikolai Kondrashov 	/*
3049614219eSNikolai Kondrashov 	 * Fill-in the parameters
3059614219eSNikolai Kondrashov 	 */
3069614219eSNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
3079614219eSNikolai Kondrashov 	pen->desc_ptr = desc_ptr;
3089614219eSNikolai Kondrashov 	desc_ptr = NULL;
309a985de58SNikolai Kondrashov 	pen->desc_size = uclogic_rdesc_v1_pen_template_size;
310a985de58SNikolai Kondrashov 	pen->id = UCLOGIC_RDESC_V1_PEN_ID;
3119614219eSNikolai Kondrashov 	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
3129614219eSNikolai Kondrashov 	found = true;
3139614219eSNikolai Kondrashov finish:
3149614219eSNikolai Kondrashov 	*pfound = found;
3159614219eSNikolai Kondrashov 	rc = 0;
3169614219eSNikolai Kondrashov cleanup:
3179614219eSNikolai Kondrashov 	kfree(desc_ptr);
3189614219eSNikolai Kondrashov 	kfree(buf);
3199614219eSNikolai Kondrashov 	return rc;
3209614219eSNikolai Kondrashov }
3219614219eSNikolai Kondrashov 
3229614219eSNikolai Kondrashov /**
3232c3a88c6SNikolai Kondrashov  * uclogic_params_get_le24() - get a 24-bit little-endian number from a
3242c3a88c6SNikolai Kondrashov  * buffer.
3252c3a88c6SNikolai Kondrashov  *
3262c3a88c6SNikolai Kondrashov  * @p:	The pointer to the number buffer.
3272c3a88c6SNikolai Kondrashov  *
3282c3a88c6SNikolai Kondrashov  * Returns:
3292c3a88c6SNikolai Kondrashov  *	The retrieved number
3302c3a88c6SNikolai Kondrashov  */
3312c3a88c6SNikolai Kondrashov static s32 uclogic_params_get_le24(const void *p)
3322c3a88c6SNikolai Kondrashov {
3332c3a88c6SNikolai Kondrashov 	const __u8 *b = p;
3342c3a88c6SNikolai Kondrashov 	return b[0] | (b[1] << 8UL) | (b[2] << 16UL);
3352c3a88c6SNikolai Kondrashov }
3362c3a88c6SNikolai Kondrashov 
3372c3a88c6SNikolai Kondrashov /**
3382c3a88c6SNikolai Kondrashov  * uclogic_params_pen_init_v2() - initialize tablet interface pen
3392c3a88c6SNikolai Kondrashov  * input and retrieve its parameters from the device, using v2 protocol.
3402c3a88c6SNikolai Kondrashov  *
3412c3a88c6SNikolai Kondrashov  * @pen:		Pointer to the pen parameters to initialize (to be
342945d5dd5SNikolai Kondrashov  *			cleaned up with uclogic_params_pen_cleanup()). Not
343945d5dd5SNikolai Kondrashov  *			modified in case of error, or if parameters are not
344945d5dd5SNikolai Kondrashov  *			found. Cannot be NULL.
345945d5dd5SNikolai Kondrashov  * @pfound:		Location for a flag which is set to true if the
346945d5dd5SNikolai Kondrashov  *			parameters were found, and to false if not (e.g.
347945d5dd5SNikolai Kondrashov  *			device was incompatible). Not modified in case of
348945d5dd5SNikolai Kondrashov  *			error. Cannot be NULL.
349945d5dd5SNikolai Kondrashov  * @pparams_ptr:	Location for a kmalloc'ed pointer to the retrieved raw
350945d5dd5SNikolai Kondrashov  *			parameters, which could be used to identify the tablet
351945d5dd5SNikolai Kondrashov  *			to some extent. Should be freed with kfree after use.
352945d5dd5SNikolai Kondrashov  *			NULL, if not needed. Not modified in case of error.
353945d5dd5SNikolai Kondrashov  *			Only set if *pfound is set to true.
354945d5dd5SNikolai Kondrashov  * @pparams_len:	Location for the length of the retrieved raw
355945d5dd5SNikolai Kondrashov  *			parameters. NULL, if not needed. Not modified in case
356945d5dd5SNikolai Kondrashov  *			of error. Only set if *pfound is set to true.
357945d5dd5SNikolai Kondrashov  * @hdev:		The HID device of the tablet interface to initialize
358945d5dd5SNikolai Kondrashov  *			and get parameters from. Cannot be NULL.
3592c3a88c6SNikolai Kondrashov  *
3602c3a88c6SNikolai Kondrashov  * Returns:
3612c3a88c6SNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
3622c3a88c6SNikolai Kondrashov  */
3632c3a88c6SNikolai Kondrashov static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,
3642c3a88c6SNikolai Kondrashov 					bool *pfound,
365945d5dd5SNikolai Kondrashov 					__u8 **pparams_ptr,
366945d5dd5SNikolai Kondrashov 					size_t *pparams_len,
3672c3a88c6SNikolai Kondrashov 					struct hid_device *hdev)
3682c3a88c6SNikolai Kondrashov {
3692c3a88c6SNikolai Kondrashov 	int rc;
3702c3a88c6SNikolai Kondrashov 	bool found = false;
371945d5dd5SNikolai Kondrashov 	/* Buffer for (part of) the parameter string descriptor */
3722c3a88c6SNikolai Kondrashov 	__u8 *buf = NULL;
373945d5dd5SNikolai Kondrashov 	/* Parameter string descriptor required length */
374945d5dd5SNikolai Kondrashov 	const int params_len_min = 18;
375945d5dd5SNikolai Kondrashov 	/* Parameter string descriptor accepted length */
376945d5dd5SNikolai Kondrashov 	const int params_len_max = 32;
377945d5dd5SNikolai Kondrashov 	/* Parameter string descriptor received length */
378945d5dd5SNikolai Kondrashov 	int params_len;
379945d5dd5SNikolai Kondrashov 	size_t i;
3802c3a88c6SNikolai Kondrashov 	s32 resolution;
3812c3a88c6SNikolai Kondrashov 	/* Pen report descriptor template parameters */
38276e645beSJosé Expósito 	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
3832c3a88c6SNikolai Kondrashov 	__u8 *desc_ptr = NULL;
3842c3a88c6SNikolai Kondrashov 
3852c3a88c6SNikolai Kondrashov 	/* Check arguments */
3862c3a88c6SNikolai Kondrashov 	if (pen == NULL || pfound == NULL || hdev == NULL) {
3872c3a88c6SNikolai Kondrashov 		rc = -EINVAL;
3882c3a88c6SNikolai Kondrashov 		goto cleanup;
3892c3a88c6SNikolai Kondrashov 	}
3902c3a88c6SNikolai Kondrashov 
3912c3a88c6SNikolai Kondrashov 	/*
3922c3a88c6SNikolai Kondrashov 	 * Read string descriptor containing pen input parameters.
3932c3a88c6SNikolai Kondrashov 	 * The specific string descriptor and data were discovered by sniffing
3942c3a88c6SNikolai Kondrashov 	 * the Windows driver traffic.
3952c3a88c6SNikolai Kondrashov 	 * NOTE: This enables fully-functional tablet mode.
3962c3a88c6SNikolai Kondrashov 	 */
397945d5dd5SNikolai Kondrashov 	rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max);
3982c3a88c6SNikolai Kondrashov 	if (rc == -EPIPE) {
3992c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
4002c3a88c6SNikolai Kondrashov 			"string descriptor with pen parameters not found, assuming not compatible\n");
4012c3a88c6SNikolai Kondrashov 		goto finish;
4022c3a88c6SNikolai Kondrashov 	} else if (rc < 0) {
4032c3a88c6SNikolai Kondrashov 		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
4042c3a88c6SNikolai Kondrashov 		goto cleanup;
405945d5dd5SNikolai Kondrashov 	} else if (rc < params_len_min) {
4062c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
407945d5dd5SNikolai Kondrashov 			"string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n",
408945d5dd5SNikolai Kondrashov 			rc, params_len_min);
4092c3a88c6SNikolai Kondrashov 		goto finish;
410945d5dd5SNikolai Kondrashov 	}
411945d5dd5SNikolai Kondrashov 
412945d5dd5SNikolai Kondrashov 	params_len = rc;
413945d5dd5SNikolai Kondrashov 
4142c3a88c6SNikolai Kondrashov 	/*
4152c3a88c6SNikolai Kondrashov 	 * Check it's not just a catch-all UTF-16LE-encoded ASCII
4162c3a88c6SNikolai Kondrashov 	 * string (such as the model name) some tablets put into all
4172c3a88c6SNikolai Kondrashov 	 * unknown string descriptors.
4182c3a88c6SNikolai Kondrashov 	 */
4192c3a88c6SNikolai Kondrashov 	for (i = 2;
420945d5dd5SNikolai Kondrashov 	     i < params_len &&
4212c3a88c6SNikolai Kondrashov 		(buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);
4222c3a88c6SNikolai Kondrashov 	     i += 2);
423945d5dd5SNikolai Kondrashov 	if (i >= params_len) {
4242c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
4252c3a88c6SNikolai Kondrashov 			"string descriptor with pen parameters seems to contain only text, assuming not compatible\n");
4262c3a88c6SNikolai Kondrashov 		goto finish;
4272c3a88c6SNikolai Kondrashov 	}
4282c3a88c6SNikolai Kondrashov 
4292c3a88c6SNikolai Kondrashov 	/*
4302c3a88c6SNikolai Kondrashov 	 * Fill report descriptor parameters from the string descriptor
4312c3a88c6SNikolai Kondrashov 	 */
4322c3a88c6SNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
4332c3a88c6SNikolai Kondrashov 		uclogic_params_get_le24(buf + 2);
4342c3a88c6SNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
4352c3a88c6SNikolai Kondrashov 		uclogic_params_get_le24(buf + 5);
4362c3a88c6SNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
4372c3a88c6SNikolai Kondrashov 		get_unaligned_le16(buf + 8);
4382c3a88c6SNikolai Kondrashov 	resolution = get_unaligned_le16(buf + 10);
4392c3a88c6SNikolai Kondrashov 	if (resolution == 0) {
4402c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
4412c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
4422c3a88c6SNikolai Kondrashov 	} else {
4432c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
4442c3a88c6SNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
4452c3a88c6SNikolai Kondrashov 			resolution;
4462c3a88c6SNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
4472c3a88c6SNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
4482c3a88c6SNikolai Kondrashov 			resolution;
4492c3a88c6SNikolai Kondrashov 	}
4502c3a88c6SNikolai Kondrashov 
4512c3a88c6SNikolai Kondrashov 	/*
4522c3a88c6SNikolai Kondrashov 	 * Generate pen report descriptor
4532c3a88c6SNikolai Kondrashov 	 */
4542c3a88c6SNikolai Kondrashov 	desc_ptr = uclogic_rdesc_template_apply(
455a985de58SNikolai Kondrashov 				uclogic_rdesc_v2_pen_template_arr,
456a985de58SNikolai Kondrashov 				uclogic_rdesc_v2_pen_template_size,
4572c3a88c6SNikolai Kondrashov 				desc_params, ARRAY_SIZE(desc_params));
4582c3a88c6SNikolai Kondrashov 	if (desc_ptr == NULL) {
4592c3a88c6SNikolai Kondrashov 		rc = -ENOMEM;
4602c3a88c6SNikolai Kondrashov 		goto cleanup;
4612c3a88c6SNikolai Kondrashov 	}
4622c3a88c6SNikolai Kondrashov 
4632c3a88c6SNikolai Kondrashov 	/*
4642c3a88c6SNikolai Kondrashov 	 * Fill-in the parameters
4652c3a88c6SNikolai Kondrashov 	 */
4662c3a88c6SNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
4672c3a88c6SNikolai Kondrashov 	pen->desc_ptr = desc_ptr;
4682c3a88c6SNikolai Kondrashov 	desc_ptr = NULL;
469a985de58SNikolai Kondrashov 	pen->desc_size = uclogic_rdesc_v2_pen_template_size;
470a985de58SNikolai Kondrashov 	pen->id = UCLOGIC_RDESC_V2_PEN_ID;
4712c3a88c6SNikolai Kondrashov 	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;
4722c3a88c6SNikolai Kondrashov 	pen->fragmented_hires = true;
4731324c5acSNikolai Kondrashov 	pen->tilt_y_flipped = true;
4742c3a88c6SNikolai Kondrashov 	found = true;
475945d5dd5SNikolai Kondrashov 	if (pparams_ptr != NULL) {
476945d5dd5SNikolai Kondrashov 		*pparams_ptr = buf;
477945d5dd5SNikolai Kondrashov 		buf = NULL;
478945d5dd5SNikolai Kondrashov 	}
479945d5dd5SNikolai Kondrashov 	if (pparams_len != NULL)
480945d5dd5SNikolai Kondrashov 		*pparams_len = params_len;
481945d5dd5SNikolai Kondrashov 
4822c3a88c6SNikolai Kondrashov finish:
4832c3a88c6SNikolai Kondrashov 	*pfound = found;
4842c3a88c6SNikolai Kondrashov 	rc = 0;
4852c3a88c6SNikolai Kondrashov cleanup:
4862c3a88c6SNikolai Kondrashov 	kfree(desc_ptr);
4872c3a88c6SNikolai Kondrashov 	kfree(buf);
4882c3a88c6SNikolai Kondrashov 	return rc;
4892c3a88c6SNikolai Kondrashov }
4902c3a88c6SNikolai Kondrashov 
4912c3a88c6SNikolai Kondrashov /**
4929614219eSNikolai Kondrashov  * uclogic_params_frame_cleanup - free resources used by struct
4939614219eSNikolai Kondrashov  * uclogic_params_frame (tablet interface's frame controls input parameters).
4949614219eSNikolai Kondrashov  * Can be called repeatedly.
4959614219eSNikolai Kondrashov  *
4969614219eSNikolai Kondrashov  * @frame:	Frame controls input parameters to cleanup. Cannot be NULL.
4979614219eSNikolai Kondrashov  */
4989614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
4999614219eSNikolai Kondrashov {
5009614219eSNikolai Kondrashov 	kfree(frame->desc_ptr);
5019614219eSNikolai Kondrashov 	memset(frame, 0, sizeof(*frame));
5029614219eSNikolai Kondrashov }
5039614219eSNikolai Kondrashov 
5049614219eSNikolai Kondrashov /**
5059614219eSNikolai Kondrashov  * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
5069614219eSNikolai Kondrashov  * parameters with a static report descriptor.
5079614219eSNikolai Kondrashov  *
5089614219eSNikolai Kondrashov  * @frame:	Pointer to the frame parameters to initialize (to be cleaned
5099614219eSNikolai Kondrashov  *		up with uclogic_params_frame_cleanup()). Not modified in case
5109614219eSNikolai Kondrashov  *		of error. Cannot be NULL.
5119614219eSNikolai Kondrashov  * @desc_ptr:	Report descriptor pointer. Can be NULL, if desc_size is zero.
5129614219eSNikolai Kondrashov  * @desc_size:	Report descriptor size.
5139614219eSNikolai Kondrashov  * @id:		Report ID used for frame reports, if they should be tweaked,
5149614219eSNikolai Kondrashov  *		zero if not.
5159614219eSNikolai Kondrashov  *
5169614219eSNikolai Kondrashov  * Returns:
5179614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
5189614219eSNikolai Kondrashov  */
5199614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc(
5209614219eSNikolai Kondrashov 					struct uclogic_params_frame *frame,
5219614219eSNikolai Kondrashov 					const __u8 *desc_ptr,
5229614219eSNikolai Kondrashov 					size_t desc_size,
5239614219eSNikolai Kondrashov 					unsigned int id)
5249614219eSNikolai Kondrashov {
5259614219eSNikolai Kondrashov 	__u8 *copy_desc_ptr;
5269614219eSNikolai Kondrashov 
5279614219eSNikolai Kondrashov 	if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
5289614219eSNikolai Kondrashov 		return -EINVAL;
5299614219eSNikolai Kondrashov 
5309614219eSNikolai Kondrashov 	copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
5319614219eSNikolai Kondrashov 	if (copy_desc_ptr == NULL)
5329614219eSNikolai Kondrashov 		return -ENOMEM;
5339614219eSNikolai Kondrashov 
5349614219eSNikolai Kondrashov 	memset(frame, 0, sizeof(*frame));
5359614219eSNikolai Kondrashov 	frame->desc_ptr = copy_desc_ptr;
5369614219eSNikolai Kondrashov 	frame->desc_size = desc_size;
5379614219eSNikolai Kondrashov 	frame->id = id;
5389614219eSNikolai Kondrashov 	return 0;
5399614219eSNikolai Kondrashov }
5409614219eSNikolai Kondrashov 
5419614219eSNikolai Kondrashov /**
5422e28f3e0SNikolai Kondrashov  * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame
5432e28f3e0SNikolai Kondrashov  * controls.
5449614219eSNikolai Kondrashov  *
5459614219eSNikolai Kondrashov  * @frame:	Pointer to the frame parameters to initialize (to be cleaned
5469614219eSNikolai Kondrashov  *		up with uclogic_params_frame_cleanup()). Not modified in case
5479614219eSNikolai Kondrashov  *		of error, or if parameters are not found. Cannot be NULL.
5489614219eSNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
5499614219eSNikolai Kondrashov  *		were found, and to false if not (e.g. device was
5509614219eSNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
5519614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
5529614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
5539614219eSNikolai Kondrashov  *
5549614219eSNikolai Kondrashov  * Returns:
5559614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
5569614219eSNikolai Kondrashov  */
5572e28f3e0SNikolai Kondrashov static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,
5589614219eSNikolai Kondrashov 					bool *pfound,
5599614219eSNikolai Kondrashov 					struct hid_device *hdev)
5609614219eSNikolai Kondrashov {
5619614219eSNikolai Kondrashov 	int rc;
5629614219eSNikolai Kondrashov 	bool found = false;
563aa320fdbSJosé Expósito 	struct usb_device *usb_dev;
5649614219eSNikolai Kondrashov 	char *str_buf = NULL;
5659614219eSNikolai Kondrashov 	const size_t str_len = 16;
5669614219eSNikolai Kondrashov 
5679614219eSNikolai Kondrashov 	/* Check arguments */
5689614219eSNikolai Kondrashov 	if (frame == NULL || pfound == NULL || hdev == NULL) {
5699614219eSNikolai Kondrashov 		rc = -EINVAL;
5709614219eSNikolai Kondrashov 		goto cleanup;
5719614219eSNikolai Kondrashov 	}
5729614219eSNikolai Kondrashov 
573aa320fdbSJosé Expósito 	usb_dev = hid_to_usb_dev(hdev);
574aa320fdbSJosé Expósito 
5759614219eSNikolai Kondrashov 	/*
5769614219eSNikolai Kondrashov 	 * Enable generic button mode
5779614219eSNikolai Kondrashov 	 */
5789614219eSNikolai Kondrashov 	str_buf = kzalloc(str_len, GFP_KERNEL);
5799614219eSNikolai Kondrashov 	if (str_buf == NULL) {
5809614219eSNikolai Kondrashov 		rc = -ENOMEM;
5819614219eSNikolai Kondrashov 		goto cleanup;
5829614219eSNikolai Kondrashov 	}
5839614219eSNikolai Kondrashov 
5849614219eSNikolai Kondrashov 	rc = usb_string(usb_dev, 123, str_buf, str_len);
5859614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
5869614219eSNikolai Kondrashov 		hid_dbg(hdev,
5879614219eSNikolai Kondrashov 			"generic button -enabling string descriptor not found\n");
5889614219eSNikolai Kondrashov 	} else if (rc < 0) {
5899614219eSNikolai Kondrashov 		goto cleanup;
5909614219eSNikolai Kondrashov 	} else if (strncmp(str_buf, "HK On", rc) != 0) {
5919614219eSNikolai Kondrashov 		hid_dbg(hdev,
5929614219eSNikolai Kondrashov 			"invalid response to enabling generic buttons: \"%s\"\n",
5939614219eSNikolai Kondrashov 			str_buf);
5949614219eSNikolai Kondrashov 	} else {
5959614219eSNikolai Kondrashov 		hid_dbg(hdev, "generic buttons enabled\n");
5969614219eSNikolai Kondrashov 		rc = uclogic_params_frame_init_with_desc(
5979614219eSNikolai Kondrashov 				frame,
598a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_frame_arr,
599a985de58SNikolai Kondrashov 				uclogic_rdesc_v1_frame_size,
600a985de58SNikolai Kondrashov 				UCLOGIC_RDESC_V1_FRAME_ID);
6019614219eSNikolai Kondrashov 		if (rc != 0)
6029614219eSNikolai Kondrashov 			goto cleanup;
6039614219eSNikolai Kondrashov 		found = true;
6049614219eSNikolai Kondrashov 	}
6059614219eSNikolai Kondrashov 
6069614219eSNikolai Kondrashov 	*pfound = found;
6079614219eSNikolai Kondrashov 	rc = 0;
6089614219eSNikolai Kondrashov cleanup:
6099614219eSNikolai Kondrashov 	kfree(str_buf);
6109614219eSNikolai Kondrashov 	return rc;
6119614219eSNikolai Kondrashov }
6129614219eSNikolai Kondrashov 
6139614219eSNikolai Kondrashov /**
6149614219eSNikolai Kondrashov  * uclogic_params_cleanup - free resources used by struct uclogic_params
6159614219eSNikolai Kondrashov  * (tablet interface's parameters).
6169614219eSNikolai Kondrashov  * Can be called repeatedly.
6179614219eSNikolai Kondrashov  *
6189614219eSNikolai Kondrashov  * @params:	Input parameters to cleanup. Cannot be NULL.
6199614219eSNikolai Kondrashov  */
6209614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params)
6219614219eSNikolai Kondrashov {
6229614219eSNikolai Kondrashov 	if (!params->invalid) {
623337fa051SNikolai Kondrashov 		size_t i;
6249614219eSNikolai Kondrashov 		kfree(params->desc_ptr);
6259614219eSNikolai Kondrashov 		uclogic_params_pen_cleanup(&params->pen);
626337fa051SNikolai Kondrashov 		for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)
627337fa051SNikolai Kondrashov 			uclogic_params_frame_cleanup(&params->frame_list[i]);
628337fa051SNikolai Kondrashov 
6299614219eSNikolai Kondrashov 		memset(params, 0, sizeof(*params));
6309614219eSNikolai Kondrashov 	}
6319614219eSNikolai Kondrashov }
6329614219eSNikolai Kondrashov 
6339614219eSNikolai Kondrashov /**
6345abb5445SLee Jones  * uclogic_params_get_desc() - Get a replacement report descriptor for a
6355abb5445SLee Jones  *                             tablet's interface.
6369614219eSNikolai Kondrashov  *
6379614219eSNikolai Kondrashov  * @params:	The parameters of a tablet interface to get report
6389614219eSNikolai Kondrashov  *		descriptor for. Cannot be NULL.
6399614219eSNikolai Kondrashov  * @pdesc:	Location for the resulting, kmalloc-allocated report
6409614219eSNikolai Kondrashov  *		descriptor pointer, or for NULL, if there's no replacement
6419614219eSNikolai Kondrashov  *		report descriptor. Not modified in case of error. Cannot be
6429614219eSNikolai Kondrashov  *		NULL.
6439614219eSNikolai Kondrashov  * @psize:	Location for the resulting report descriptor size, not set if
6449614219eSNikolai Kondrashov  *		there's no replacement report descriptor. Not modified in case
6459614219eSNikolai Kondrashov  *		of error. Cannot be NULL.
6469614219eSNikolai Kondrashov  *
6479614219eSNikolai Kondrashov  * Returns:
6489614219eSNikolai Kondrashov  *	Zero, if successful.
6499614219eSNikolai Kondrashov  *	-EINVAL, if invalid arguments are supplied.
6509614219eSNikolai Kondrashov  *	-ENOMEM, if failed to allocate memory.
6519614219eSNikolai Kondrashov  */
6529614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params,
6539614219eSNikolai Kondrashov 				__u8 **pdesc,
6549614219eSNikolai Kondrashov 				unsigned int *psize)
6559614219eSNikolai Kondrashov {
656337fa051SNikolai Kondrashov 	int rc = -ENOMEM;
657337fa051SNikolai Kondrashov 	bool present = false;
658337fa051SNikolai Kondrashov 	unsigned int size = 0;
6599614219eSNikolai Kondrashov 	__u8 *desc = NULL;
660337fa051SNikolai Kondrashov 	size_t i;
6619614219eSNikolai Kondrashov 
6629614219eSNikolai Kondrashov 	/* Check arguments */
6639614219eSNikolai Kondrashov 	if (params == NULL || pdesc == NULL || psize == NULL)
6649614219eSNikolai Kondrashov 		return -EINVAL;
6659614219eSNikolai Kondrashov 
666337fa051SNikolai Kondrashov 	/* Concatenate descriptors */
667337fa051SNikolai Kondrashov #define ADD_DESC(_desc_ptr, _desc_size) \
668337fa051SNikolai Kondrashov 	do {                                                        \
669337fa051SNikolai Kondrashov 		unsigned int new_size;                              \
670337fa051SNikolai Kondrashov 		__u8 *new_desc;                                     \
671337fa051SNikolai Kondrashov 		if ((_desc_ptr) == NULL) {                          \
672337fa051SNikolai Kondrashov 			break;                                      \
673337fa051SNikolai Kondrashov 		}                                                   \
674337fa051SNikolai Kondrashov 		new_size = size + (_desc_size);                     \
675337fa051SNikolai Kondrashov 		new_desc = krealloc(desc, new_size, GFP_KERNEL);    \
676337fa051SNikolai Kondrashov 		if (new_desc == NULL) {                             \
677337fa051SNikolai Kondrashov 			goto cleanup;                               \
678337fa051SNikolai Kondrashov 		}                                                   \
679337fa051SNikolai Kondrashov 		memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \
680337fa051SNikolai Kondrashov 		desc = new_desc;                                    \
681337fa051SNikolai Kondrashov 		size = new_size;                                    \
682337fa051SNikolai Kondrashov 		present = true;                                     \
683337fa051SNikolai Kondrashov 	} while (0)
6849614219eSNikolai Kondrashov 
685337fa051SNikolai Kondrashov 	ADD_DESC(params->desc_ptr, params->desc_size);
686337fa051SNikolai Kondrashov 	ADD_DESC(params->pen.desc_ptr, params->pen.desc_size);
687337fa051SNikolai Kondrashov 	for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {
688337fa051SNikolai Kondrashov 		ADD_DESC(params->frame_list[i].desc_ptr,
689337fa051SNikolai Kondrashov 				params->frame_list[i].desc_size);
6909614219eSNikolai Kondrashov 	}
6919614219eSNikolai Kondrashov 
692337fa051SNikolai Kondrashov #undef ADD_DESC
6939614219eSNikolai Kondrashov 
694337fa051SNikolai Kondrashov 	if (present) {
6959614219eSNikolai Kondrashov 		*pdesc = desc;
696337fa051SNikolai Kondrashov 		*psize = size;
697337fa051SNikolai Kondrashov 		desc = NULL;
698337fa051SNikolai Kondrashov 	}
699337fa051SNikolai Kondrashov 	rc = 0;
700337fa051SNikolai Kondrashov cleanup:
701337fa051SNikolai Kondrashov 	kfree(desc);
702337fa051SNikolai Kondrashov 	return rc;
7039614219eSNikolai Kondrashov }
7049614219eSNikolai Kondrashov 
7059614219eSNikolai Kondrashov /**
7069614219eSNikolai Kondrashov  * uclogic_params_init_invalid() - initialize tablet interface parameters,
7079614219eSNikolai Kondrashov  * specifying the interface is invalid.
7089614219eSNikolai Kondrashov  *
7099614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
7109614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Cannot be NULL.
7119614219eSNikolai Kondrashov  */
7129614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params)
7139614219eSNikolai Kondrashov {
7149614219eSNikolai Kondrashov 	params->invalid = true;
7159614219eSNikolai Kondrashov }
7169614219eSNikolai Kondrashov 
7179614219eSNikolai Kondrashov /**
7189614219eSNikolai Kondrashov  * uclogic_params_init_with_opt_desc() - initialize tablet interface
7199614219eSNikolai Kondrashov  * parameters with an optional replacement report descriptor. Only modify
7209614219eSNikolai Kondrashov  * report descriptor, if the original report descriptor matches the expected
7219614219eSNikolai Kondrashov  * size.
7229614219eSNikolai Kondrashov  *
7239614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
7249614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Not modified in case of
7259614219eSNikolai Kondrashov  *			error. Cannot be NULL.
7269614219eSNikolai Kondrashov  * @hdev:		The HID device of the tablet interface create the
7279614219eSNikolai Kondrashov  *			parameters for. Cannot be NULL.
7289614219eSNikolai Kondrashov  * @orig_desc_size:	Expected size of the original report descriptor to
7299614219eSNikolai Kondrashov  *			be replaced.
7309614219eSNikolai Kondrashov  * @desc_ptr:		Pointer to the replacement report descriptor.
7319614219eSNikolai Kondrashov  *			Can be NULL, if desc_size is zero.
7329614219eSNikolai Kondrashov  * @desc_size:		Size of the replacement report descriptor.
7339614219eSNikolai Kondrashov  *
7349614219eSNikolai Kondrashov  * Returns:
7359614219eSNikolai Kondrashov  *	Zero, if successful. -EINVAL if an invalid argument was passed.
7369614219eSNikolai Kondrashov  *	-ENOMEM, if failed to allocate memory.
7379614219eSNikolai Kondrashov  */
7389614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
7399614219eSNikolai Kondrashov 					     struct hid_device *hdev,
7409614219eSNikolai Kondrashov 					     unsigned int orig_desc_size,
7419614219eSNikolai Kondrashov 					     __u8 *desc_ptr,
7429614219eSNikolai Kondrashov 					     unsigned int desc_size)
7439614219eSNikolai Kondrashov {
7449614219eSNikolai Kondrashov 	__u8 *desc_copy_ptr = NULL;
7459614219eSNikolai Kondrashov 	unsigned int desc_copy_size;
7469614219eSNikolai Kondrashov 	int rc;
7479614219eSNikolai Kondrashov 
7489614219eSNikolai Kondrashov 	/* Check arguments */
7499614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL ||
7509614219eSNikolai Kondrashov 	    (desc_ptr == NULL && desc_size != 0)) {
7519614219eSNikolai Kondrashov 		rc = -EINVAL;
7529614219eSNikolai Kondrashov 		goto cleanup;
7539614219eSNikolai Kondrashov 	}
7549614219eSNikolai Kondrashov 
7559614219eSNikolai Kondrashov 	/* Replace report descriptor, if it matches */
7569614219eSNikolai Kondrashov 	if (hdev->dev_rsize == orig_desc_size) {
7579614219eSNikolai Kondrashov 		hid_dbg(hdev,
7589614219eSNikolai Kondrashov 			"device report descriptor matches the expected size, replacing\n");
7599614219eSNikolai Kondrashov 		desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
7609614219eSNikolai Kondrashov 		if (desc_copy_ptr == NULL) {
7619614219eSNikolai Kondrashov 			rc = -ENOMEM;
7629614219eSNikolai Kondrashov 			goto cleanup;
7639614219eSNikolai Kondrashov 		}
7649614219eSNikolai Kondrashov 		desc_copy_size = desc_size;
7659614219eSNikolai Kondrashov 	} else {
7669614219eSNikolai Kondrashov 		hid_dbg(hdev,
7679614219eSNikolai Kondrashov 			"device report descriptor doesn't match the expected size (%u != %u), preserving\n",
7689614219eSNikolai Kondrashov 			hdev->dev_rsize, orig_desc_size);
7699614219eSNikolai Kondrashov 		desc_copy_ptr = NULL;
7709614219eSNikolai Kondrashov 		desc_copy_size = 0;
7719614219eSNikolai Kondrashov 	}
7729614219eSNikolai Kondrashov 
7739614219eSNikolai Kondrashov 	/* Output parameters */
7749614219eSNikolai Kondrashov 	memset(params, 0, sizeof(*params));
7759614219eSNikolai Kondrashov 	params->desc_ptr = desc_copy_ptr;
7769614219eSNikolai Kondrashov 	desc_copy_ptr = NULL;
7779614219eSNikolai Kondrashov 	params->desc_size = desc_copy_size;
7789614219eSNikolai Kondrashov 
7799614219eSNikolai Kondrashov 	rc = 0;
7809614219eSNikolai Kondrashov cleanup:
7819614219eSNikolai Kondrashov 	kfree(desc_copy_ptr);
7829614219eSNikolai Kondrashov 	return rc;
7839614219eSNikolai Kondrashov }
7849614219eSNikolai Kondrashov 
7859614219eSNikolai Kondrashov /**
7865abb5445SLee Jones  * uclogic_params_huion_init() - initialize a Huion tablet interface and discover
7879614219eSNikolai Kondrashov  * its parameters.
7889614219eSNikolai Kondrashov  *
7899614219eSNikolai Kondrashov  * @params:	Parameters to fill in (to be cleaned with
7909614219eSNikolai Kondrashov  *		uclogic_params_cleanup()). Not modified in case of error.
7919614219eSNikolai Kondrashov  *		Cannot be NULL.
7929614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
7939614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
7949614219eSNikolai Kondrashov  *
7959614219eSNikolai Kondrashov  * Returns:
7969614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
7979614219eSNikolai Kondrashov  */
7989614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params,
7999614219eSNikolai Kondrashov 				     struct hid_device *hdev)
8009614219eSNikolai Kondrashov {
8019614219eSNikolai Kondrashov 	int rc;
802ff6b548aSJosé Expósito 	struct usb_device *udev;
803ff6b548aSJosé Expósito 	struct usb_interface *iface;
804ff6b548aSJosé Expósito 	__u8 bInterfaceNumber;
8059614219eSNikolai Kondrashov 	bool found;
8069614219eSNikolai Kondrashov 	/* The resulting parameters (noop) */
8079614219eSNikolai Kondrashov 	struct uclogic_params p = {0, };
8082c3a88c6SNikolai Kondrashov 	static const char transition_ver[] = "HUION_T153_160607";
8092c3a88c6SNikolai Kondrashov 	char *ver_ptr = NULL;
8102c3a88c6SNikolai Kondrashov 	const size_t ver_len = sizeof(transition_ver) + 1;
811118dfdeaSNikolai Kondrashov 	__u8 *params_ptr = NULL;
812118dfdeaSNikolai Kondrashov 	size_t params_len = 0;
813118dfdeaSNikolai Kondrashov 	/* Parameters string descriptor of a model with touch ring (HS610) */
814118dfdeaSNikolai Kondrashov 	const __u8 touch_ring_model_params_buf[] = {
815118dfdeaSNikolai Kondrashov 		0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00,
816118dfdeaSNikolai Kondrashov 		0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01,
817118dfdeaSNikolai Kondrashov 		0x04, 0x3C, 0x3E
818118dfdeaSNikolai Kondrashov 	};
8199614219eSNikolai Kondrashov 
8209614219eSNikolai Kondrashov 	/* Check arguments */
8219614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL) {
8229614219eSNikolai Kondrashov 		rc = -EINVAL;
8239614219eSNikolai Kondrashov 		goto cleanup;
8249614219eSNikolai Kondrashov 	}
8259614219eSNikolai Kondrashov 
826ff6b548aSJosé Expósito 	udev = hid_to_usb_dev(hdev);
827ff6b548aSJosé Expósito 	iface = to_usb_interface(hdev->dev.parent);
828ff6b548aSJosé Expósito 	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
829ff6b548aSJosé Expósito 
830d64a6e44SNikolai Kondrashov 	/* If it's a custom keyboard interface */
831d64a6e44SNikolai Kondrashov 	if (bInterfaceNumber == 1) {
8324c60bc7dSNikolai Kondrashov 		/* Keep everything intact, but mark pen usage invalid */
8334c60bc7dSNikolai Kondrashov 		p.pen.usage_invalid = true;
834d64a6e44SNikolai Kondrashov 		goto output;
835d64a6e44SNikolai Kondrashov 	/* Else, if it's not a pen interface */
836d64a6e44SNikolai Kondrashov 	} else if (bInterfaceNumber != 0) {
837606dadc1SNikolai Kondrashov 		uclogic_params_init_invalid(&p);
8389614219eSNikolai Kondrashov 		goto output;
8399614219eSNikolai Kondrashov 	}
8409614219eSNikolai Kondrashov 
8412c3a88c6SNikolai Kondrashov 	/* Try to get firmware version */
8422c3a88c6SNikolai Kondrashov 	ver_ptr = kzalloc(ver_len, GFP_KERNEL);
8432c3a88c6SNikolai Kondrashov 	if (ver_ptr == NULL) {
8442c3a88c6SNikolai Kondrashov 		rc = -ENOMEM;
8452c3a88c6SNikolai Kondrashov 		goto cleanup;
8462c3a88c6SNikolai Kondrashov 	}
8472c3a88c6SNikolai Kondrashov 	rc = usb_string(udev, 201, ver_ptr, ver_len);
8482c3a88c6SNikolai Kondrashov 	if (rc == -EPIPE) {
8492c3a88c6SNikolai Kondrashov 		*ver_ptr = '\0';
8502c3a88c6SNikolai Kondrashov 	} else if (rc < 0) {
8512c3a88c6SNikolai Kondrashov 		hid_err(hdev,
8522c3a88c6SNikolai Kondrashov 			"failed retrieving Huion firmware version: %d\n", rc);
8532c3a88c6SNikolai Kondrashov 		goto cleanup;
8542c3a88c6SNikolai Kondrashov 	}
8552c3a88c6SNikolai Kondrashov 
8562c3a88c6SNikolai Kondrashov 	/* If this is a transition firmware */
8572c3a88c6SNikolai Kondrashov 	if (strcmp(ver_ptr, transition_ver) == 0) {
8582c3a88c6SNikolai Kondrashov 		hid_dbg(hdev,
8592c3a88c6SNikolai Kondrashov 			"transition firmware detected, not probing pen v2 parameters\n");
8602c3a88c6SNikolai Kondrashov 	} else {
8612c3a88c6SNikolai Kondrashov 		/* Try to probe v2 pen parameters */
862945d5dd5SNikolai Kondrashov 		rc = uclogic_params_pen_init_v2(&p.pen, &found,
863118dfdeaSNikolai Kondrashov 						&params_ptr, &params_len,
864118dfdeaSNikolai Kondrashov 						hdev);
8652c3a88c6SNikolai Kondrashov 		if (rc != 0) {
8662c3a88c6SNikolai Kondrashov 			hid_err(hdev,
8672c3a88c6SNikolai Kondrashov 				"failed probing pen v2 parameters: %d\n", rc);
8682c3a88c6SNikolai Kondrashov 			goto cleanup;
8692c3a88c6SNikolai Kondrashov 		} else if (found) {
8702c3a88c6SNikolai Kondrashov 			hid_dbg(hdev, "pen v2 parameters found\n");
871c3e6e59aSNikolai Kondrashov 			/* Create v2 frame button parameters */
8722c3a88c6SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
873337fa051SNikolai Kondrashov 					&p.frame_list[0],
874c3e6e59aSNikolai Kondrashov 					uclogic_rdesc_v2_frame_buttons_arr,
875c3e6e59aSNikolai Kondrashov 					uclogic_rdesc_v2_frame_buttons_size,
876c3e6e59aSNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID);
8772c3a88c6SNikolai Kondrashov 			if (rc != 0) {
8782c3a88c6SNikolai Kondrashov 				hid_err(hdev,
879c3e6e59aSNikolai Kondrashov 					"failed creating v2 frame button parameters: %d\n",
8802c3a88c6SNikolai Kondrashov 					rc);
8812c3a88c6SNikolai Kondrashov 				goto cleanup;
8822c3a88c6SNikolai Kondrashov 			}
883c3e6e59aSNikolai Kondrashov 
884118dfdeaSNikolai Kondrashov 			/* Link from pen sub-report */
885118dfdeaSNikolai Kondrashov 			p.pen.subreport_list[0].value = 0xe0;
886118dfdeaSNikolai Kondrashov 			p.pen.subreport_list[0].id =
887118dfdeaSNikolai Kondrashov 				UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID;
888118dfdeaSNikolai Kondrashov 
889118dfdeaSNikolai Kondrashov 			/* If this is the model with touch ring */
890118dfdeaSNikolai Kondrashov 			if (params_ptr != NULL &&
891118dfdeaSNikolai Kondrashov 			    params_len == sizeof(touch_ring_model_params_buf) &&
892118dfdeaSNikolai Kondrashov 			    memcmp(params_ptr, touch_ring_model_params_buf,
893118dfdeaSNikolai Kondrashov 				   params_len) == 0) {
894118dfdeaSNikolai Kondrashov 				/* Create touch ring parameters */
895c3e6e59aSNikolai Kondrashov 				rc = uclogic_params_frame_init_with_desc(
896c3e6e59aSNikolai Kondrashov 					&p.frame_list[1],
897c3e6e59aSNikolai Kondrashov 					uclogic_rdesc_v2_frame_touch_ring_arr,
898c3e6e59aSNikolai Kondrashov 					uclogic_rdesc_v2_frame_touch_ring_size,
899caf7e934SNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);
900c3e6e59aSNikolai Kondrashov 				if (rc != 0) {
901c3e6e59aSNikolai Kondrashov 					hid_err(hdev,
902c3e6e59aSNikolai Kondrashov 						"failed creating v2 frame touch ring parameters: %d\n",
903c3e6e59aSNikolai Kondrashov 						rc);
904c3e6e59aSNikolai Kondrashov 					goto cleanup;
905c3e6e59aSNikolai Kondrashov 				}
906c3e6e59aSNikolai Kondrashov 				p.frame_list[1].suffix = "Touch Ring";
907c3e6e59aSNikolai Kondrashov 				p.frame_list[1].dev_id_byte =
908caf7e934SNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;
909caf7e934SNikolai Kondrashov 				p.frame_list[1].touch_byte = 5;
910caf7e934SNikolai Kondrashov 				p.frame_list[1].touch_max = 12;
911fbc08b4eSNikolai Kondrashov 				p.frame_list[1].touch_flip_at = 7;
912118dfdeaSNikolai Kondrashov 			} else {
913118dfdeaSNikolai Kondrashov 				/* Create touch strip parameters */
914118dfdeaSNikolai Kondrashov 				rc = uclogic_params_frame_init_with_desc(
915118dfdeaSNikolai Kondrashov 					&p.frame_list[1],
916118dfdeaSNikolai Kondrashov 					uclogic_rdesc_v2_frame_touch_strip_arr,
917118dfdeaSNikolai Kondrashov 					uclogic_rdesc_v2_frame_touch_strip_size,
918118dfdeaSNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);
919118dfdeaSNikolai Kondrashov 				if (rc != 0) {
920118dfdeaSNikolai Kondrashov 					hid_err(hdev,
921118dfdeaSNikolai Kondrashov 						"failed creating v2 frame touch strip parameters: %d\n",
922118dfdeaSNikolai Kondrashov 						rc);
923118dfdeaSNikolai Kondrashov 					goto cleanup;
924118dfdeaSNikolai Kondrashov 				}
925118dfdeaSNikolai Kondrashov 				p.frame_list[1].suffix = "Touch Strip";
926118dfdeaSNikolai Kondrashov 				p.frame_list[1].dev_id_byte =
927118dfdeaSNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;
928118dfdeaSNikolai Kondrashov 				p.frame_list[1].touch_byte = 5;
929118dfdeaSNikolai Kondrashov 				p.frame_list[1].touch_max = 8;
930118dfdeaSNikolai Kondrashov 			}
931118dfdeaSNikolai Kondrashov 
932118dfdeaSNikolai Kondrashov 			/* Link from pen sub-report */
933118dfdeaSNikolai Kondrashov 			p.pen.subreport_list[1].value = 0xf0;
934118dfdeaSNikolai Kondrashov 			p.pen.subreport_list[1].id =
935118dfdeaSNikolai Kondrashov 				UCLOGIC_RDESC_V2_FRAME_TOUCH_ID;
936c3e6e59aSNikolai Kondrashov 
9376facd076SNikolai Kondrashov 			/* Create v2 frame dial parameters */
9386facd076SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
9396facd076SNikolai Kondrashov 					&p.frame_list[2],
9406facd076SNikolai Kondrashov 					uclogic_rdesc_v2_frame_dial_arr,
9416facd076SNikolai Kondrashov 					uclogic_rdesc_v2_frame_dial_size,
9426facd076SNikolai Kondrashov 					UCLOGIC_RDESC_V2_FRAME_DIAL_ID);
9436facd076SNikolai Kondrashov 			if (rc != 0) {
9446facd076SNikolai Kondrashov 				hid_err(hdev,
9456facd076SNikolai Kondrashov 					"failed creating v2 frame dial parameters: %d\n",
9466facd076SNikolai Kondrashov 					rc);
9476facd076SNikolai Kondrashov 				goto cleanup;
9486facd076SNikolai Kondrashov 			}
9496facd076SNikolai Kondrashov 			p.frame_list[2].suffix = "Dial";
9506facd076SNikolai Kondrashov 			p.frame_list[2].dev_id_byte =
9516facd076SNikolai Kondrashov 				UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE;
9526facd076SNikolai Kondrashov 			p.frame_list[2].bitmap_dial_byte = 5;
9536facd076SNikolai Kondrashov 
954118dfdeaSNikolai Kondrashov 			/* Link from pen sub-report */
9556facd076SNikolai Kondrashov 			p.pen.subreport_list[2].value = 0xf1;
9566facd076SNikolai Kondrashov 			p.pen.subreport_list[2].id =
9576facd076SNikolai Kondrashov 				UCLOGIC_RDESC_V2_FRAME_DIAL_ID;
958118dfdeaSNikolai Kondrashov 
9592c3a88c6SNikolai Kondrashov 			goto output;
9602c3a88c6SNikolai Kondrashov 		}
9612c3a88c6SNikolai Kondrashov 		hid_dbg(hdev, "pen v2 parameters not found\n");
9622c3a88c6SNikolai Kondrashov 	}
9632c3a88c6SNikolai Kondrashov 
964eecb5b84SNikolai Kondrashov 	/* Try to probe v1 pen parameters */
965eecb5b84SNikolai Kondrashov 	rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
9669614219eSNikolai Kondrashov 	if (rc != 0) {
9679614219eSNikolai Kondrashov 		hid_err(hdev,
968eecb5b84SNikolai Kondrashov 			"failed probing pen v1 parameters: %d\n", rc);
9699614219eSNikolai Kondrashov 		goto cleanup;
9709614219eSNikolai Kondrashov 	} else if (found) {
971eecb5b84SNikolai Kondrashov 		hid_dbg(hdev, "pen v1 parameters found\n");
9722e28f3e0SNikolai Kondrashov 		/* Try to probe v1 frame */
973337fa051SNikolai Kondrashov 		rc = uclogic_params_frame_init_v1(&p.frame_list[0],
9749614219eSNikolai Kondrashov 						  &found, hdev);
9759614219eSNikolai Kondrashov 		if (rc != 0) {
9762e28f3e0SNikolai Kondrashov 			hid_err(hdev, "v1 frame probing failed: %d\n", rc);
9779614219eSNikolai Kondrashov 			goto cleanup;
9789614219eSNikolai Kondrashov 		}
9792e28f3e0SNikolai Kondrashov 		hid_dbg(hdev, "frame v1 parameters%s found\n",
9809614219eSNikolai Kondrashov 			(found ? "" : " not"));
9819614219eSNikolai Kondrashov 		if (found) {
9828b013098SNikolai Kondrashov 			/* Link frame button subreports from pen reports */
983e6be956fSNikolai Kondrashov 			p.pen.subreport_list[0].value = 0xe0;
9848b013098SNikolai Kondrashov 			p.pen.subreport_list[0].id =
985a985de58SNikolai Kondrashov 				UCLOGIC_RDESC_V1_FRAME_ID;
9869614219eSNikolai Kondrashov 		}
9879614219eSNikolai Kondrashov 		goto output;
9889614219eSNikolai Kondrashov 	}
989eecb5b84SNikolai Kondrashov 	hid_dbg(hdev, "pen v1 parameters not found\n");
9909614219eSNikolai Kondrashov 
9919614219eSNikolai Kondrashov 	uclogic_params_init_invalid(&p);
9929614219eSNikolai Kondrashov 
9939614219eSNikolai Kondrashov output:
9949614219eSNikolai Kondrashov 	/* Output parameters */
9959614219eSNikolai Kondrashov 	memcpy(params, &p, sizeof(*params));
9969614219eSNikolai Kondrashov 	memset(&p, 0, sizeof(p));
9979614219eSNikolai Kondrashov 	rc = 0;
9989614219eSNikolai Kondrashov cleanup:
999118dfdeaSNikolai Kondrashov 	kfree(params_ptr);
10002c3a88c6SNikolai Kondrashov 	kfree(ver_ptr);
10019614219eSNikolai Kondrashov 	uclogic_params_cleanup(&p);
10029614219eSNikolai Kondrashov 	return rc;
10039614219eSNikolai Kondrashov }
10049614219eSNikolai Kondrashov 
10059614219eSNikolai Kondrashov /**
1006*0cb1fc09SJosé Expósito  * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or
1007*0cb1fc09SJosé Expósito  * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.
1008*0cb1fc09SJosé Expósito  *
1009*0cb1fc09SJosé Expósito  * @hdev:	The HID device of the tablet interface to initialize and get
1010*0cb1fc09SJosé Expósito  *		parameters from. Cannot be NULL.
1011*0cb1fc09SJosé Expósito  * @magic_arr:	The magic data that should be sent to probe the interface.
1012*0cb1fc09SJosé Expósito  *		Cannot be NULL.
1013*0cb1fc09SJosé Expósito  * @magic_size:	Size of the magic data.
1014*0cb1fc09SJosé Expósito  * @endpoint:	Endpoint where the magic data should be sent.
1015*0cb1fc09SJosé Expósito  *
1016*0cb1fc09SJosé Expósito  * Returns:
1017*0cb1fc09SJosé Expósito  *	Zero, if successful. A negative errno code on error.
1018*0cb1fc09SJosé Expósito  */
1019*0cb1fc09SJosé Expósito static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr,
1020*0cb1fc09SJosé Expósito 				   int magic_size, int endpoint)
1021*0cb1fc09SJosé Expósito {
1022*0cb1fc09SJosé Expósito 	struct usb_device *udev;
1023*0cb1fc09SJosé Expósito 	unsigned int pipe = 0;
1024*0cb1fc09SJosé Expósito 	int sent;
1025*0cb1fc09SJosé Expósito 	u8 *buf = NULL;
1026*0cb1fc09SJosé Expósito 	int rc = 0;
1027*0cb1fc09SJosé Expósito 
1028*0cb1fc09SJosé Expósito 	if (!hdev || !magic_arr) {
1029*0cb1fc09SJosé Expósito 		rc = -EINVAL;
1030*0cb1fc09SJosé Expósito 		goto cleanup;
1031*0cb1fc09SJosé Expósito 	}
1032*0cb1fc09SJosé Expósito 
1033*0cb1fc09SJosé Expósito 	buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);
1034*0cb1fc09SJosé Expósito 	if (!buf) {
1035*0cb1fc09SJosé Expósito 		rc = -ENOMEM;
1036*0cb1fc09SJosé Expósito 		goto cleanup;
1037*0cb1fc09SJosé Expósito 	}
1038*0cb1fc09SJosé Expósito 
1039*0cb1fc09SJosé Expósito 	udev = hid_to_usb_dev(hdev);
1040*0cb1fc09SJosé Expósito 	pipe = usb_sndintpipe(udev, endpoint);
1041*0cb1fc09SJosé Expósito 
1042*0cb1fc09SJosé Expósito 	rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);
1043*0cb1fc09SJosé Expósito 	if (rc || sent != magic_size) {
1044*0cb1fc09SJosé Expósito 		hid_err(hdev, "Interface probing failed: %d\n", rc);
1045*0cb1fc09SJosé Expósito 		rc = -1;
1046*0cb1fc09SJosé Expósito 		goto cleanup;
1047*0cb1fc09SJosé Expósito 	}
1048*0cb1fc09SJosé Expósito 
1049*0cb1fc09SJosé Expósito 	rc = 0;
1050*0cb1fc09SJosé Expósito cleanup:
1051*0cb1fc09SJosé Expósito 	kfree(buf);
1052*0cb1fc09SJosé Expósito 	return rc;
1053*0cb1fc09SJosé Expósito }
1054*0cb1fc09SJosé Expósito 
1055*0cb1fc09SJosé Expósito /**
1056*0cb1fc09SJosé Expósito  * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by
1057*0cb1fc09SJosé Expósito  * discovering their parameters.
1058*0cb1fc09SJosé Expósito  *
1059*0cb1fc09SJosé Expósito  * These tables, internally designed as v2 to differentiate them from older
1060*0cb1fc09SJosé Expósito  * models, expect a payload of magic data in orther to be switched to the fully
1061*0cb1fc09SJosé Expósito  * functional mode and expose their parameters in a similar way to the
1062*0cb1fc09SJosé Expósito  * information present in uclogic_params_pen_init_v1() but with some
1063*0cb1fc09SJosé Expósito  * differences.
1064*0cb1fc09SJosé Expósito  *
1065*0cb1fc09SJosé Expósito  * @params:	Parameters to fill in (to be cleaned with
1066*0cb1fc09SJosé Expósito  *		uclogic_params_cleanup()). Not modified in case of error.
1067*0cb1fc09SJosé Expósito  *		Cannot be NULL.
1068*0cb1fc09SJosé Expósito  * @hdev:	The HID device of the tablet interface to initialize and get
1069*0cb1fc09SJosé Expósito  *		parameters from. Cannot be NULL.
1070*0cb1fc09SJosé Expósito  *
1071*0cb1fc09SJosé Expósito  * Returns:
1072*0cb1fc09SJosé Expósito  *	Zero, if successful. A negative errno code on error.
1073*0cb1fc09SJosé Expósito  */
1074*0cb1fc09SJosé Expósito static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
1075*0cb1fc09SJosé Expósito 				       struct hid_device *hdev)
1076*0cb1fc09SJosé Expósito {
1077*0cb1fc09SJosé Expósito 	int rc = 0;
1078*0cb1fc09SJosé Expósito 	struct usb_interface *iface;
1079*0cb1fc09SJosé Expósito 	__u8 bInterfaceNumber;
1080*0cb1fc09SJosé Expósito 	const int str_desc_len = 12;
1081*0cb1fc09SJosé Expósito 	__u8 *str_desc = NULL;
1082*0cb1fc09SJosé Expósito 	__u8 *rdesc_pen = NULL;
1083*0cb1fc09SJosé Expósito 	__u8 *rdesc_frame = NULL;
1084*0cb1fc09SJosé Expósito 	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
1085*0cb1fc09SJosé Expósito 	s32 resolution;
1086*0cb1fc09SJosé Expósito 	__u8 magic_arr[] = {
1087*0cb1fc09SJosé Expósito 		0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1088*0cb1fc09SJosé Expósito 	};
1089*0cb1fc09SJosé Expósito 	/* The resulting parameters (noop) */
1090*0cb1fc09SJosé Expósito 	struct uclogic_params p = {0, };
1091*0cb1fc09SJosé Expósito 
1092*0cb1fc09SJosé Expósito 	if (!params || !hdev) {
1093*0cb1fc09SJosé Expósito 		rc = -EINVAL;
1094*0cb1fc09SJosé Expósito 		goto cleanup;
1095*0cb1fc09SJosé Expósito 	}
1096*0cb1fc09SJosé Expósito 
1097*0cb1fc09SJosé Expósito 	iface = to_usb_interface(hdev->dev.parent);
1098*0cb1fc09SJosé Expósito 	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
1099*0cb1fc09SJosé Expósito 	if (bInterfaceNumber != 2) {
1100*0cb1fc09SJosé Expósito 		uclogic_params_init_invalid(&p);
1101*0cb1fc09SJosé Expósito 		goto output;
1102*0cb1fc09SJosé Expósito 	}
1103*0cb1fc09SJosé Expósito 
1104*0cb1fc09SJosé Expósito 	/*
1105*0cb1fc09SJosé Expósito 	 * Initialize the interface by sending magic data.
1106*0cb1fc09SJosé Expósito 	 * The specific data was discovered by sniffing the Windows driver
1107*0cb1fc09SJosé Expósito 	 * traffic.
1108*0cb1fc09SJosé Expósito 	 */
1109*0cb1fc09SJosé Expósito 	rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03);
1110*0cb1fc09SJosé Expósito 	if (rc) {
1111*0cb1fc09SJosé Expósito 		uclogic_params_init_invalid(&p);
1112*0cb1fc09SJosé Expósito 		goto output;
1113*0cb1fc09SJosé Expósito 	}
1114*0cb1fc09SJosé Expósito 
1115*0cb1fc09SJosé Expósito 	/*
1116*0cb1fc09SJosé Expósito 	 * Read the string descriptor containing pen and frame parameters.
1117*0cb1fc09SJosé Expósito 	 * The specific string descriptor and data were discovered by sniffing
1118*0cb1fc09SJosé Expósito 	 * the Windows driver traffic.
1119*0cb1fc09SJosé Expósito 	 */
1120*0cb1fc09SJosé Expósito 	rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
1121*0cb1fc09SJosé Expósito 	if (rc != str_desc_len) {
1122*0cb1fc09SJosé Expósito 		hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
1123*0cb1fc09SJosé Expósito 		uclogic_params_init_invalid(&p);
1124*0cb1fc09SJosé Expósito 		goto output;
1125*0cb1fc09SJosé Expósito 	}
1126*0cb1fc09SJosé Expósito 
1127*0cb1fc09SJosé Expósito 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
1128*0cb1fc09SJosé Expósito 		get_unaligned_le16(str_desc + 2);
1129*0cb1fc09SJosé Expósito 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
1130*0cb1fc09SJosé Expósito 		get_unaligned_le16(str_desc + 4);
1131*0cb1fc09SJosé Expósito 	desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = str_desc[6];
1132*0cb1fc09SJosé Expósito 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
1133*0cb1fc09SJosé Expósito 		get_unaligned_le16(str_desc + 8);
1134*0cb1fc09SJosé Expósito 	resolution = get_unaligned_le16(str_desc + 10);
1135*0cb1fc09SJosé Expósito 	if (resolution == 0) {
1136*0cb1fc09SJosé Expósito 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
1137*0cb1fc09SJosé Expósito 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
1138*0cb1fc09SJosé Expósito 	} else {
1139*0cb1fc09SJosé Expósito 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
1140*0cb1fc09SJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
1141*0cb1fc09SJosé Expósito 			resolution;
1142*0cb1fc09SJosé Expósito 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
1143*0cb1fc09SJosé Expósito 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
1144*0cb1fc09SJosé Expósito 			resolution;
1145*0cb1fc09SJosé Expósito 	}
1146*0cb1fc09SJosé Expósito 	kfree(str_desc);
1147*0cb1fc09SJosé Expósito 	str_desc = NULL;
1148*0cb1fc09SJosé Expósito 
1149*0cb1fc09SJosé Expósito 	/* Initialize the pen interface */
1150*0cb1fc09SJosé Expósito 	rdesc_pen = uclogic_rdesc_template_apply(
1151*0cb1fc09SJosé Expósito 				uclogic_rdesc_ugee_v2_pen_template_arr,
1152*0cb1fc09SJosé Expósito 				uclogic_rdesc_ugee_v2_pen_template_size,
1153*0cb1fc09SJosé Expósito 				desc_params, ARRAY_SIZE(desc_params));
1154*0cb1fc09SJosé Expósito 	if (!rdesc_pen) {
1155*0cb1fc09SJosé Expósito 		rc = -ENOMEM;
1156*0cb1fc09SJosé Expósito 		goto cleanup;
1157*0cb1fc09SJosé Expósito 	}
1158*0cb1fc09SJosé Expósito 
1159*0cb1fc09SJosé Expósito 	p.pen.desc_ptr = rdesc_pen;
1160*0cb1fc09SJosé Expósito 	p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
1161*0cb1fc09SJosé Expósito 	p.pen.id = 0x02;
1162*0cb1fc09SJosé Expósito 	p.pen.subreport_list[0].value = 0xf0;
1163*0cb1fc09SJosé Expósito 	p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;
1164*0cb1fc09SJosé Expósito 
1165*0cb1fc09SJosé Expósito 	/* Initialize the frame interface */
1166*0cb1fc09SJosé Expósito 	rdesc_frame = uclogic_rdesc_template_apply(
1167*0cb1fc09SJosé Expósito 				uclogic_rdesc_ugee_v2_frame_btn_template_arr,
1168*0cb1fc09SJosé Expósito 				uclogic_rdesc_ugee_v2_frame_btn_template_size,
1169*0cb1fc09SJosé Expósito 				desc_params, ARRAY_SIZE(desc_params));
1170*0cb1fc09SJosé Expósito 	if (!rdesc_frame) {
1171*0cb1fc09SJosé Expósito 		rc = -ENOMEM;
1172*0cb1fc09SJosé Expósito 		goto cleanup;
1173*0cb1fc09SJosé Expósito 	}
1174*0cb1fc09SJosé Expósito 
1175*0cb1fc09SJosé Expósito 	rc = uclogic_params_frame_init_with_desc(&p.frame_list[0],
1176*0cb1fc09SJosé Expósito 						 rdesc_frame,
1177*0cb1fc09SJosé Expósito 						 uclogic_rdesc_ugee_v2_frame_btn_template_size,
1178*0cb1fc09SJosé Expósito 						 UCLOGIC_RDESC_V1_FRAME_ID);
1179*0cb1fc09SJosé Expósito 	kfree(rdesc_frame);
1180*0cb1fc09SJosé Expósito 	if (rc) {
1181*0cb1fc09SJosé Expósito 		uclogic_params_init_invalid(&p);
1182*0cb1fc09SJosé Expósito 		goto output;
1183*0cb1fc09SJosé Expósito 	}
1184*0cb1fc09SJosé Expósito 
1185*0cb1fc09SJosé Expósito output:
1186*0cb1fc09SJosé Expósito 	/* Output parameters */
1187*0cb1fc09SJosé Expósito 	memcpy(params, &p, sizeof(*params));
1188*0cb1fc09SJosé Expósito 	memset(&p, 0, sizeof(p));
1189*0cb1fc09SJosé Expósito 	rc = 0;
1190*0cb1fc09SJosé Expósito cleanup:
1191*0cb1fc09SJosé Expósito 	kfree(str_desc);
1192*0cb1fc09SJosé Expósito 	uclogic_params_cleanup(&p);
1193*0cb1fc09SJosé Expósito 	return rc;
1194*0cb1fc09SJosé Expósito }
1195*0cb1fc09SJosé Expósito 
1196*0cb1fc09SJosé Expósito /**
11979614219eSNikolai Kondrashov  * uclogic_params_init() - initialize a tablet interface and discover its
11989614219eSNikolai Kondrashov  * parameters.
11999614219eSNikolai Kondrashov  *
12009614219eSNikolai Kondrashov  * @params:	Parameters to fill in (to be cleaned with
12019614219eSNikolai Kondrashov  *		uclogic_params_cleanup()). Not modified in case of error.
12029614219eSNikolai Kondrashov  *		Cannot be NULL.
12039614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
12048547b778SNikolai Kondrashov  *		parameters from. Cannot be NULL. Must be using the USB low-level
12058547b778SNikolai Kondrashov  *		driver, i.e. be an actual USB tablet.
12069614219eSNikolai Kondrashov  *
12079614219eSNikolai Kondrashov  * Returns:
12089614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
12099614219eSNikolai Kondrashov  */
12109614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params,
12119614219eSNikolai Kondrashov 			struct hid_device *hdev)
12129614219eSNikolai Kondrashov {
12139614219eSNikolai Kondrashov 	int rc;
1214f364c571SJosé Expósito 	struct usb_device *udev;
1215f364c571SJosé Expósito 	__u8  bNumInterfaces;
1216f364c571SJosé Expósito 	struct usb_interface *iface;
1217f364c571SJosé Expósito 	__u8 bInterfaceNumber;
12189614219eSNikolai Kondrashov 	bool found;
12199614219eSNikolai Kondrashov 	/* The resulting parameters (noop) */
12209614219eSNikolai Kondrashov 	struct uclogic_params p = {0, };
12219614219eSNikolai Kondrashov 
12229614219eSNikolai Kondrashov 	/* Check arguments */
1223f83baa0cSGreg Kroah-Hartman 	if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {
12249614219eSNikolai Kondrashov 		rc = -EINVAL;
12259614219eSNikolai Kondrashov 		goto cleanup;
12269614219eSNikolai Kondrashov 	}
12279614219eSNikolai Kondrashov 
1228f364c571SJosé Expósito 	udev = hid_to_usb_dev(hdev);
1229f364c571SJosé Expósito 	bNumInterfaces = udev->config->desc.bNumInterfaces;
1230f364c571SJosé Expósito 	iface = to_usb_interface(hdev->dev.parent);
1231f364c571SJosé Expósito 	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
1232f364c571SJosé Expósito 
12339614219eSNikolai Kondrashov 	/*
12349614219eSNikolai Kondrashov 	 * Set replacement report descriptor if the original matches the
12359614219eSNikolai Kondrashov 	 * specified size. Otherwise keep interface unchanged.
12369614219eSNikolai Kondrashov 	 */
12379614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
12389614219eSNikolai Kondrashov 	uclogic_params_init_with_opt_desc(                  \
12399614219eSNikolai Kondrashov 		&p, hdev,                                   \
12409614219eSNikolai Kondrashov 		UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \
12419614219eSNikolai Kondrashov 		uclogic_rdesc_##_new_desc_token##_arr,      \
12429614219eSNikolai Kondrashov 		uclogic_rdesc_##_new_desc_token##_size)
12439614219eSNikolai Kondrashov 
12449614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \
12459614219eSNikolai Kondrashov 	(((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
12469614219eSNikolai Kondrashov 
12479614219eSNikolai Kondrashov 	/*
12489614219eSNikolai Kondrashov 	 * Handle specific interfaces for specific tablets.
12499614219eSNikolai Kondrashov 	 *
12509614219eSNikolai Kondrashov 	 * Observe the following logic:
12519614219eSNikolai Kondrashov 	 *
12529614219eSNikolai Kondrashov 	 * If the interface is recognized as producing certain useful input:
12539614219eSNikolai Kondrashov 	 *	Mark interface as valid.
12549614219eSNikolai Kondrashov 	 *	Output interface parameters.
12559614219eSNikolai Kondrashov 	 * Else, if the interface is recognized as *not* producing any useful
12569614219eSNikolai Kondrashov 	 * input:
12579614219eSNikolai Kondrashov 	 *	Mark interface as invalid.
12589614219eSNikolai Kondrashov 	 * Else:
12599614219eSNikolai Kondrashov 	 *	Mark interface as valid.
12609614219eSNikolai Kondrashov 	 *	Output noop parameters.
12619614219eSNikolai Kondrashov 	 *
12629614219eSNikolai Kondrashov 	 * Rule of thumb: it is better to disable a broken interface than let
12639614219eSNikolai Kondrashov 	 *		  it spew garbage input.
12649614219eSNikolai Kondrashov 	 */
12659614219eSNikolai Kondrashov 
12669614219eSNikolai Kondrashov 	switch (VID_PID(hdev->vendor, hdev->product)) {
12679614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
12689614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
12699614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
12709614219eSNikolai Kondrashov 		if (rc != 0)
12719614219eSNikolai Kondrashov 			goto cleanup;
12729614219eSNikolai Kondrashov 		break;
12739614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
12749614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
12759614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
12769614219eSNikolai Kondrashov 		if (rc != 0)
12779614219eSNikolai Kondrashov 			goto cleanup;
12789614219eSNikolai Kondrashov 		break;
12799614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
12809614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
12819c17f735SNikolai Kondrashov 		if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {
12829c17f735SNikolai Kondrashov 			if (bInterfaceNumber == 0) {
12839c17f735SNikolai Kondrashov 				/* Try to probe v1 pen parameters */
12849c17f735SNikolai Kondrashov 				rc = uclogic_params_pen_init_v1(&p.pen,
12859c17f735SNikolai Kondrashov 								&found, hdev);
12869c17f735SNikolai Kondrashov 				if (rc != 0) {
12879c17f735SNikolai Kondrashov 					hid_err(hdev,
12889c17f735SNikolai Kondrashov 						"pen probing failed: %d\n",
12899c17f735SNikolai Kondrashov 						rc);
12909c17f735SNikolai Kondrashov 					goto cleanup;
12919c17f735SNikolai Kondrashov 				}
12929c17f735SNikolai Kondrashov 				if (!found) {
12939c17f735SNikolai Kondrashov 					hid_warn(hdev,
12949c17f735SNikolai Kondrashov 						 "pen parameters not found");
12959c17f735SNikolai Kondrashov 				}
12969c17f735SNikolai Kondrashov 			} else {
12979c17f735SNikolai Kondrashov 				uclogic_params_init_invalid(&p);
12989c17f735SNikolai Kondrashov 			}
12999c17f735SNikolai Kondrashov 		} else {
13009614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
13019614219eSNikolai Kondrashov 			if (rc != 0)
13029614219eSNikolai Kondrashov 				goto cleanup;
13039c17f735SNikolai Kondrashov 		}
13049614219eSNikolai Kondrashov 		break;
13059614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13069614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
13079614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
13089614219eSNikolai Kondrashov 		if (rc != 0)
13099614219eSNikolai Kondrashov 			goto cleanup;
13109614219eSNikolai Kondrashov 		break;
13119614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13129614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
13139614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
13149614219eSNikolai Kondrashov 		if (rc != 0)
13159614219eSNikolai Kondrashov 			goto cleanup;
13169614219eSNikolai Kondrashov 		break;
13179614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13189614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
13199614219eSNikolai Kondrashov 		switch (bInterfaceNumber) {
13209614219eSNikolai Kondrashov 		case 0:
13219614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
13229614219eSNikolai Kondrashov 			if (rc != 0)
13239614219eSNikolai Kondrashov 				goto cleanup;
13249614219eSNikolai Kondrashov 			break;
13259614219eSNikolai Kondrashov 		case 1:
13269614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
13279614219eSNikolai Kondrashov 			if (rc != 0)
13289614219eSNikolai Kondrashov 				goto cleanup;
13299614219eSNikolai Kondrashov 			break;
13309614219eSNikolai Kondrashov 		case 2:
13319614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
13329614219eSNikolai Kondrashov 			if (rc != 0)
13339614219eSNikolai Kondrashov 				goto cleanup;
13349614219eSNikolai Kondrashov 			break;
13359614219eSNikolai Kondrashov 		}
13369614219eSNikolai Kondrashov 		break;
13379614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13389614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
13399614219eSNikolai Kondrashov 		/*
13409614219eSNikolai Kondrashov 		 * If it is not a three-interface version, which is known to
13419614219eSNikolai Kondrashov 		 * respond to initialization.
13429614219eSNikolai Kondrashov 		 */
13439614219eSNikolai Kondrashov 		if (bNumInterfaces != 3) {
13449614219eSNikolai Kondrashov 			switch (bInterfaceNumber) {
13459614219eSNikolai Kondrashov 			case 0:
13469614219eSNikolai Kondrashov 				rc = WITH_OPT_DESC(TWHA60_ORIG0,
13479614219eSNikolai Kondrashov 							twha60_fixed0);
13489614219eSNikolai Kondrashov 				if (rc != 0)
13499614219eSNikolai Kondrashov 					goto cleanup;
13509614219eSNikolai Kondrashov 				break;
13519614219eSNikolai Kondrashov 			case 1:
13529614219eSNikolai Kondrashov 				rc = WITH_OPT_DESC(TWHA60_ORIG1,
13539614219eSNikolai Kondrashov 							twha60_fixed1);
13549614219eSNikolai Kondrashov 				if (rc != 0)
13559614219eSNikolai Kondrashov 					goto cleanup;
13569614219eSNikolai Kondrashov 				break;
13579614219eSNikolai Kondrashov 			}
13589614219eSNikolai Kondrashov 			break;
13599614219eSNikolai Kondrashov 		}
1360df561f66SGustavo A. R. Silva 		fallthrough;
13619614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_HUION,
13629614219eSNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET):
1363315ffcc9SKyle Godbey 	case VID_PID(USB_VENDOR_ID_HUION,
136485e86071SNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET2):
13659614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13669614219eSNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET):
13679614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13689614219eSNikolai Kondrashov 		     USB_DEVICE_ID_YIYNOVA_TABLET):
13699614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13709614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
13719614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13729614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
13739614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13749614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
13750c15efe9SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
13760c15efe9SNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):
13779614219eSNikolai Kondrashov 		rc = uclogic_params_huion_init(&p, hdev);
13789614219eSNikolai Kondrashov 		if (rc != 0)
13799614219eSNikolai Kondrashov 			goto cleanup;
13809614219eSNikolai Kondrashov 		break;
13819614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGTIZER,
13829614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
1383022fc531SMartijn van de Streek 	case VID_PID(USB_VENDOR_ID_UGTIZER,
1384022fc531SMartijn van de Streek 		     USB_DEVICE_ID_UGTIZER_TABLET_GT5040):
1385c3e5a67cSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
1386c3e5a67cSNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
1387492a9e9aSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
1388492a9e9aSNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
138988bb346dSWang Xuerui 	case VID_PID(USB_VENDOR_ID_UGEE,
139061b1db5aSRoman Romanenko 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06):
139161b1db5aSRoman Romanenko 	case VID_PID(USB_VENDOR_ID_UGEE,
139288bb346dSWang Xuerui 		     USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
13939614219eSNikolai Kondrashov 		/* If this is the pen interface */
13949614219eSNikolai Kondrashov 		if (bInterfaceNumber == 1) {
1395eecb5b84SNikolai Kondrashov 			/* Probe v1 pen parameters */
1396eecb5b84SNikolai Kondrashov 			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
13979614219eSNikolai Kondrashov 			if (rc != 0) {
13989614219eSNikolai Kondrashov 				hid_err(hdev, "pen probing failed: %d\n", rc);
13999614219eSNikolai Kondrashov 				goto cleanup;
14009614219eSNikolai Kondrashov 			}
14019614219eSNikolai Kondrashov 			if (!found) {
14029614219eSNikolai Kondrashov 				hid_warn(hdev, "pen parameters not found");
14039614219eSNikolai Kondrashov 				uclogic_params_init_invalid(&p);
14049614219eSNikolai Kondrashov 			}
14059614219eSNikolai Kondrashov 		} else {
1406606dadc1SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
14079614219eSNikolai Kondrashov 		}
14089614219eSNikolai Kondrashov 		break;
14091ee7c685SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
141008367be1SNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
141108367be1SNikolai Kondrashov 		/* If this is the pen and frame interface */
141208367be1SNikolai Kondrashov 		if (bInterfaceNumber == 1) {
141308367be1SNikolai Kondrashov 			/* Probe v1 pen parameters */
141408367be1SNikolai Kondrashov 			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
141508367be1SNikolai Kondrashov 			if (rc != 0) {
141608367be1SNikolai Kondrashov 				hid_err(hdev, "pen probing failed: %d\n", rc);
141708367be1SNikolai Kondrashov 				goto cleanup;
141808367be1SNikolai Kondrashov 			}
141908367be1SNikolai Kondrashov 			/* Initialize frame parameters */
142008367be1SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
1421337fa051SNikolai Kondrashov 				&p.frame_list[0],
142208367be1SNikolai Kondrashov 				uclogic_rdesc_xppen_deco01_frame_arr,
142308367be1SNikolai Kondrashov 				uclogic_rdesc_xppen_deco01_frame_size,
142408367be1SNikolai Kondrashov 				0);
142508367be1SNikolai Kondrashov 			if (rc != 0)
142608367be1SNikolai Kondrashov 				goto cleanup;
142708367be1SNikolai Kondrashov 		} else {
1428606dadc1SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
142908367be1SNikolai Kondrashov 		}
143008367be1SNikolai Kondrashov 		break;
1431*0cb1fc09SJosé Expósito 	case VID_PID(USB_VENDOR_ID_UGEE,
1432*0cb1fc09SJosé Expósito 		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):
1433*0cb1fc09SJosé Expósito 		rc = uclogic_params_ugee_v2_init(&p, hdev);
1434*0cb1fc09SJosé Expósito 		if (rc != 0)
1435*0cb1fc09SJosé Expósito 			goto cleanup;
1436*0cb1fc09SJosé Expósito 		break;
1437f7271b2aSCristian Klein 	case VID_PID(USB_VENDOR_ID_TRUST,
1438f7271b2aSCristian Klein 		     USB_DEVICE_ID_TRUST_PANORA_TABLET):
143908367be1SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
1440e902ed93SNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_TABLET_G5):
1441e902ed93SNikolai Kondrashov 		/* Ignore non-pen interfaces */
1442e902ed93SNikolai Kondrashov 		if (bInterfaceNumber != 1) {
1443e902ed93SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
1444e902ed93SNikolai Kondrashov 			break;
1445e902ed93SNikolai Kondrashov 		}
1446e902ed93SNikolai Kondrashov 
1447e902ed93SNikolai Kondrashov 		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
1448e902ed93SNikolai Kondrashov 		if (rc != 0) {
1449e902ed93SNikolai Kondrashov 			hid_err(hdev, "pen probing failed: %d\n", rc);
1450e902ed93SNikolai Kondrashov 			goto cleanup;
1451e902ed93SNikolai Kondrashov 		} else if (found) {
1452e902ed93SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
1453337fa051SNikolai Kondrashov 				&p.frame_list[0],
1454e902ed93SNikolai Kondrashov 				uclogic_rdesc_ugee_g5_frame_arr,
1455e902ed93SNikolai Kondrashov 				uclogic_rdesc_ugee_g5_frame_size,
1456e902ed93SNikolai Kondrashov 				UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
1457e902ed93SNikolai Kondrashov 			if (rc != 0) {
1458e902ed93SNikolai Kondrashov 				hid_err(hdev,
14592e28f3e0SNikolai Kondrashov 					"failed creating frame parameters: %d\n",
1460e902ed93SNikolai Kondrashov 					rc);
1461e902ed93SNikolai Kondrashov 				goto cleanup;
1462e902ed93SNikolai Kondrashov 			}
1463337fa051SNikolai Kondrashov 			p.frame_list[0].re_lsb =
1464e902ed93SNikolai Kondrashov 				UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
1465337fa051SNikolai Kondrashov 			p.frame_list[0].dev_id_byte =
1466e902ed93SNikolai Kondrashov 				UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
1467e902ed93SNikolai Kondrashov 		} else {
1468e902ed93SNikolai Kondrashov 			hid_warn(hdev, "pen parameters not found");
1469e902ed93SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
1470e902ed93SNikolai Kondrashov 		}
1471e902ed93SNikolai Kondrashov 
1472e902ed93SNikolai Kondrashov 		break;
1473e902ed93SNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
14741ee7c685SNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_TABLET_EX07S):
14751ee7c685SNikolai Kondrashov 		/* Ignore non-pen interfaces */
14761ee7c685SNikolai Kondrashov 		if (bInterfaceNumber != 1) {
14771ee7c685SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
14781ee7c685SNikolai Kondrashov 			break;
14791ee7c685SNikolai Kondrashov 		}
14801ee7c685SNikolai Kondrashov 
14811ee7c685SNikolai Kondrashov 		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
14821ee7c685SNikolai Kondrashov 		if (rc != 0) {
14831ee7c685SNikolai Kondrashov 			hid_err(hdev, "pen probing failed: %d\n", rc);
14841ee7c685SNikolai Kondrashov 			goto cleanup;
14851ee7c685SNikolai Kondrashov 		} else if (found) {
14861ee7c685SNikolai Kondrashov 			rc = uclogic_params_frame_init_with_desc(
1487337fa051SNikolai Kondrashov 				&p.frame_list[0],
14882e28f3e0SNikolai Kondrashov 				uclogic_rdesc_ugee_ex07_frame_arr,
14892e28f3e0SNikolai Kondrashov 				uclogic_rdesc_ugee_ex07_frame_size,
14901ee7c685SNikolai Kondrashov 				0);
14911ee7c685SNikolai Kondrashov 			if (rc != 0) {
14921ee7c685SNikolai Kondrashov 				hid_err(hdev,
14932e28f3e0SNikolai Kondrashov 					"failed creating frame parameters: %d\n",
14941ee7c685SNikolai Kondrashov 					rc);
14951ee7c685SNikolai Kondrashov 				goto cleanup;
14961ee7c685SNikolai Kondrashov 			}
14971ee7c685SNikolai Kondrashov 		} else {
14981ee7c685SNikolai Kondrashov 			hid_warn(hdev, "pen parameters not found");
14991ee7c685SNikolai Kondrashov 			uclogic_params_init_invalid(&p);
15001ee7c685SNikolai Kondrashov 		}
15011ee7c685SNikolai Kondrashov 
15021ee7c685SNikolai Kondrashov 		break;
15039614219eSNikolai Kondrashov 	}
15049614219eSNikolai Kondrashov 
15059614219eSNikolai Kondrashov #undef VID_PID
15069614219eSNikolai Kondrashov #undef WITH_OPT_DESC
15079614219eSNikolai Kondrashov 
15089614219eSNikolai Kondrashov 	/* Output parameters */
15099614219eSNikolai Kondrashov 	memcpy(params, &p, sizeof(*params));
15109614219eSNikolai Kondrashov 	memset(&p, 0, sizeof(p));
15119614219eSNikolai Kondrashov 	rc = 0;
15129614219eSNikolai Kondrashov cleanup:
15139614219eSNikolai Kondrashov 	uclogic_params_cleanup(&p);
15149614219eSNikolai Kondrashov 	return rc;
15159614219eSNikolai Kondrashov }
1516