19614219eSNikolai Kondrashov // SPDX-License-Identifier: GPL-2.0+
29614219eSNikolai Kondrashov /*
39614219eSNikolai Kondrashov  *  HID driver for UC-Logic devices not fully compliant with HID standard
49614219eSNikolai Kondrashov  *  - tablet initialization and parameter retrieval
59614219eSNikolai Kondrashov  *
69614219eSNikolai Kondrashov  *  Copyright (c) 2018 Nikolai Kondrashov
79614219eSNikolai Kondrashov  */
89614219eSNikolai Kondrashov 
99614219eSNikolai Kondrashov /*
109614219eSNikolai Kondrashov  * This program is free software; you can redistribute it and/or modify it
119614219eSNikolai Kondrashov  * under the terms of the GNU General Public License as published by the Free
129614219eSNikolai Kondrashov  * Software Foundation; either version 2 of the License, or (at your option)
139614219eSNikolai Kondrashov  * any later version.
149614219eSNikolai Kondrashov  */
159614219eSNikolai Kondrashov 
169614219eSNikolai Kondrashov #include "hid-uclogic-params.h"
179614219eSNikolai Kondrashov #include "hid-uclogic-rdesc.h"
189614219eSNikolai Kondrashov #include "usbhid/usbhid.h"
199614219eSNikolai Kondrashov #include "hid-ids.h"
209614219eSNikolai Kondrashov #include <linux/ctype.h>
219614219eSNikolai Kondrashov #include <asm/unaligned.h>
229614219eSNikolai Kondrashov 
239614219eSNikolai Kondrashov /**
249614219eSNikolai Kondrashov  * Convert a pen in-range reporting type to a string.
259614219eSNikolai Kondrashov  *
269614219eSNikolai Kondrashov  * @inrange:	The in-range reporting type to convert.
279614219eSNikolai Kondrashov  *
289614219eSNikolai Kondrashov  * Returns:
299614219eSNikolai Kondrashov  *	The string representing the type, or NULL if the type is unknown.
309614219eSNikolai Kondrashov  */
319614219eSNikolai Kondrashov const char *uclogic_params_pen_inrange_to_str(
329614219eSNikolai Kondrashov 			enum uclogic_params_pen_inrange inrange)
339614219eSNikolai Kondrashov {
349614219eSNikolai Kondrashov 	switch (inrange) {
359614219eSNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
369614219eSNikolai Kondrashov 		return "normal";
379614219eSNikolai Kondrashov 	case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
389614219eSNikolai Kondrashov 		return "inverted";
399614219eSNikolai Kondrashov 	default:
409614219eSNikolai Kondrashov 		return NULL;
419614219eSNikolai Kondrashov 	}
429614219eSNikolai Kondrashov }
439614219eSNikolai Kondrashov 
449614219eSNikolai Kondrashov /**
459614219eSNikolai Kondrashov  * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
469614219eSNikolai Kondrashov  * device interface, putting it into a kmalloc-allocated buffer as is, without
479614219eSNikolai Kondrashov  * character encoding conversion.
489614219eSNikolai Kondrashov  *
499614219eSNikolai Kondrashov  * @pbuf:	Location for the kmalloc-allocated buffer pointer containing
509614219eSNikolai Kondrashov  *		the retrieved descriptor. Not modified in case of error.
519614219eSNikolai Kondrashov  *		Can be NULL to have retrieved descriptor discarded.
529614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to retrieve the string
539614219eSNikolai Kondrashov  *		descriptor from. Cannot be NULL.
549614219eSNikolai Kondrashov  * @idx:	Index of the string descriptor to request from the device.
559614219eSNikolai Kondrashov  * @len:	Length of the buffer to allocate and the data to retrieve.
569614219eSNikolai Kondrashov  *
579614219eSNikolai Kondrashov  * Returns:
589614219eSNikolai Kondrashov  *	number of bytes retrieved (<= len),
599614219eSNikolai Kondrashov  *	-EPIPE, if the descriptor was not found, or
609614219eSNikolai Kondrashov  *	another negative errno code in case of other error.
619614219eSNikolai Kondrashov  */
629614219eSNikolai Kondrashov static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
639614219eSNikolai Kondrashov 					__u8 idx, size_t len)
649614219eSNikolai Kondrashov {
659614219eSNikolai Kondrashov 	int rc;
669614219eSNikolai Kondrashov 	struct usb_device *udev = hid_to_usb_dev(hdev);
679614219eSNikolai Kondrashov 	__u8 *buf = NULL;
689614219eSNikolai Kondrashov 
699614219eSNikolai Kondrashov 	/* Check arguments */
709614219eSNikolai Kondrashov 	if (hdev == NULL) {
719614219eSNikolai Kondrashov 		rc = -EINVAL;
729614219eSNikolai Kondrashov 		goto cleanup;
739614219eSNikolai Kondrashov 	}
749614219eSNikolai Kondrashov 
759614219eSNikolai Kondrashov 	buf = kmalloc(len, GFP_KERNEL);
769614219eSNikolai Kondrashov 	if (buf == NULL) {
779614219eSNikolai Kondrashov 		rc = -ENOMEM;
789614219eSNikolai Kondrashov 		goto cleanup;
799614219eSNikolai Kondrashov 	}
809614219eSNikolai Kondrashov 
819614219eSNikolai Kondrashov 	rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
829614219eSNikolai Kondrashov 				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
839614219eSNikolai Kondrashov 				(USB_DT_STRING << 8) + idx,
849614219eSNikolai Kondrashov 				0x0409, buf, len,
859614219eSNikolai Kondrashov 				USB_CTRL_GET_TIMEOUT);
869614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
879614219eSNikolai Kondrashov 		hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
889614219eSNikolai Kondrashov 		goto cleanup;
899614219eSNikolai Kondrashov 	} else if (rc < 0) {
909614219eSNikolai Kondrashov 		hid_err(hdev,
919614219eSNikolai Kondrashov 			"failed retrieving string descriptor #%hhu: %d\n",
929614219eSNikolai Kondrashov 			idx, rc);
939614219eSNikolai Kondrashov 		goto cleanup;
949614219eSNikolai Kondrashov 	}
959614219eSNikolai Kondrashov 
969614219eSNikolai Kondrashov 	if (pbuf != NULL) {
979614219eSNikolai Kondrashov 		*pbuf = buf;
989614219eSNikolai Kondrashov 		buf = NULL;
999614219eSNikolai Kondrashov 	}
1009614219eSNikolai Kondrashov 
1019614219eSNikolai Kondrashov cleanup:
1029614219eSNikolai Kondrashov 	kfree(buf);
1039614219eSNikolai Kondrashov 	return rc;
1049614219eSNikolai Kondrashov }
1059614219eSNikolai Kondrashov 
1069614219eSNikolai Kondrashov /**
1079614219eSNikolai Kondrashov  * uclogic_params_pen_cleanup - free resources used by struct
1089614219eSNikolai Kondrashov  * uclogic_params_pen (tablet interface's pen input parameters).
1099614219eSNikolai Kondrashov  * Can be called repeatedly.
1109614219eSNikolai Kondrashov  *
1119614219eSNikolai Kondrashov  * @pen:	Pen input parameters to cleanup. Cannot be NULL.
1129614219eSNikolai Kondrashov  */
1139614219eSNikolai Kondrashov static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
1149614219eSNikolai Kondrashov {
1159614219eSNikolai Kondrashov 	kfree(pen->desc_ptr);
1169614219eSNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
1179614219eSNikolai Kondrashov }
1189614219eSNikolai Kondrashov 
1199614219eSNikolai Kondrashov /**
1209614219eSNikolai Kondrashov  * uclogic_params_pen_init() - initialize tablet interface pen
1219614219eSNikolai Kondrashov  * input and retrieve its parameters from the device.
1229614219eSNikolai Kondrashov  *
1239614219eSNikolai Kondrashov  * @pen:	Pointer to the pen parameters to initialize (to be
1249614219eSNikolai Kondrashov  *		cleaned up with uclogic_params_pen_cleanup()). Not modified in
1259614219eSNikolai Kondrashov  *		case of error, or if parameters are not found. Cannot be NULL.
1269614219eSNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
1279614219eSNikolai Kondrashov  *		were found, and to false if not (e.g. device was
1289614219eSNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
1299614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
1309614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
1319614219eSNikolai Kondrashov  *
1329614219eSNikolai Kondrashov  * Returns:
1339614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
1349614219eSNikolai Kondrashov  */
1359614219eSNikolai Kondrashov static int uclogic_params_pen_init(struct uclogic_params_pen *pen,
1369614219eSNikolai Kondrashov 				   bool *pfound,
1379614219eSNikolai Kondrashov 				   struct hid_device *hdev)
1389614219eSNikolai Kondrashov {
1399614219eSNikolai Kondrashov 	int rc;
1409614219eSNikolai Kondrashov 	bool found = false;
1419614219eSNikolai Kondrashov 	/* Buffer for (part of) the string descriptor */
1429614219eSNikolai Kondrashov 	__u8 *buf = NULL;
1439614219eSNikolai Kondrashov 	/* Minimum descriptor length required, maximum seen so far is 18 */
1449614219eSNikolai Kondrashov 	const int len = 12;
1459614219eSNikolai Kondrashov 	s32 resolution;
1469614219eSNikolai Kondrashov 	/* Pen report descriptor template parameters */
1479614219eSNikolai Kondrashov 	s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
1489614219eSNikolai Kondrashov 	__u8 *desc_ptr = NULL;
1499614219eSNikolai Kondrashov 
1509614219eSNikolai Kondrashov 	/* Check arguments */
1519614219eSNikolai Kondrashov 	if (pen == NULL || pfound == NULL || hdev == NULL) {
1529614219eSNikolai Kondrashov 		rc = -EINVAL;
1539614219eSNikolai Kondrashov 		goto cleanup;
1549614219eSNikolai Kondrashov 	}
1559614219eSNikolai Kondrashov 
1569614219eSNikolai Kondrashov 	/*
1579614219eSNikolai Kondrashov 	 * Read string descriptor containing pen input parameters.
1589614219eSNikolai Kondrashov 	 * The specific string descriptor and data were discovered by sniffing
1599614219eSNikolai Kondrashov 	 * the Windows driver traffic.
1609614219eSNikolai Kondrashov 	 * NOTE: This enables fully-functional tablet mode.
1619614219eSNikolai Kondrashov 	 */
1629614219eSNikolai Kondrashov 	rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
1639614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
1649614219eSNikolai Kondrashov 		hid_dbg(hdev,
1659614219eSNikolai Kondrashov 			"string descriptor with pen parameters not found, assuming not compatible\n");
1669614219eSNikolai Kondrashov 		goto finish;
1679614219eSNikolai Kondrashov 	} else if (rc < 0) {
1689614219eSNikolai Kondrashov 		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
1699614219eSNikolai Kondrashov 		goto cleanup;
1709614219eSNikolai Kondrashov 	} else if (rc != len) {
1719614219eSNikolai Kondrashov 		hid_dbg(hdev,
1729614219eSNikolai Kondrashov 			"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
1739614219eSNikolai Kondrashov 			rc, len);
1749614219eSNikolai Kondrashov 		goto finish;
1759614219eSNikolai Kondrashov 	}
1769614219eSNikolai Kondrashov 
1779614219eSNikolai Kondrashov 	/*
1789614219eSNikolai Kondrashov 	 * Fill report descriptor parameters from the string descriptor
1799614219eSNikolai Kondrashov 	 */
1809614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
1819614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 2);
1829614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
1839614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 4);
1849614219eSNikolai Kondrashov 	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
1859614219eSNikolai Kondrashov 		get_unaligned_le16(buf + 8);
1869614219eSNikolai Kondrashov 	resolution = get_unaligned_le16(buf + 10);
1879614219eSNikolai Kondrashov 	if (resolution == 0) {
1889614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
1899614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
1909614219eSNikolai Kondrashov 	} else {
1919614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
1929614219eSNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
1939614219eSNikolai Kondrashov 			resolution;
1949614219eSNikolai Kondrashov 		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
1959614219eSNikolai Kondrashov 			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
1969614219eSNikolai Kondrashov 			resolution;
1979614219eSNikolai Kondrashov 	}
1989614219eSNikolai Kondrashov 	kfree(buf);
1999614219eSNikolai Kondrashov 	buf = NULL;
2009614219eSNikolai Kondrashov 
2019614219eSNikolai Kondrashov 	/*
2029614219eSNikolai Kondrashov 	 * Generate pen report descriptor
2039614219eSNikolai Kondrashov 	 */
2049614219eSNikolai Kondrashov 	desc_ptr = uclogic_rdesc_template_apply(
2059614219eSNikolai Kondrashov 				uclogic_rdesc_pen_template_arr,
2069614219eSNikolai Kondrashov 				uclogic_rdesc_pen_template_size,
2079614219eSNikolai Kondrashov 				desc_params, ARRAY_SIZE(desc_params));
2089614219eSNikolai Kondrashov 	if (desc_ptr == NULL) {
2099614219eSNikolai Kondrashov 		rc = -ENOMEM;
2109614219eSNikolai Kondrashov 		goto cleanup;
2119614219eSNikolai Kondrashov 	}
2129614219eSNikolai Kondrashov 
2139614219eSNikolai Kondrashov 	/*
2149614219eSNikolai Kondrashov 	 * Fill-in the parameters
2159614219eSNikolai Kondrashov 	 */
2169614219eSNikolai Kondrashov 	memset(pen, 0, sizeof(*pen));
2179614219eSNikolai Kondrashov 	pen->desc_ptr = desc_ptr;
2189614219eSNikolai Kondrashov 	desc_ptr = NULL;
2199614219eSNikolai Kondrashov 	pen->desc_size = uclogic_rdesc_pen_template_size;
2209614219eSNikolai Kondrashov 	pen->id = UCLOGIC_RDESC_PEN_ID;
2219614219eSNikolai Kondrashov 	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
2229614219eSNikolai Kondrashov 	found = true;
2239614219eSNikolai Kondrashov finish:
2249614219eSNikolai Kondrashov 	*pfound = found;
2259614219eSNikolai Kondrashov 	rc = 0;
2269614219eSNikolai Kondrashov cleanup:
2279614219eSNikolai Kondrashov 	kfree(desc_ptr);
2289614219eSNikolai Kondrashov 	kfree(buf);
2299614219eSNikolai Kondrashov 	return rc;
2309614219eSNikolai Kondrashov }
2319614219eSNikolai Kondrashov 
2329614219eSNikolai Kondrashov /**
2339614219eSNikolai Kondrashov  * uclogic_params_frame_cleanup - free resources used by struct
2349614219eSNikolai Kondrashov  * uclogic_params_frame (tablet interface's frame controls input parameters).
2359614219eSNikolai Kondrashov  * Can be called repeatedly.
2369614219eSNikolai Kondrashov  *
2379614219eSNikolai Kondrashov  * @frame:	Frame controls input parameters to cleanup. Cannot be NULL.
2389614219eSNikolai Kondrashov  */
2399614219eSNikolai Kondrashov static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
2409614219eSNikolai Kondrashov {
2419614219eSNikolai Kondrashov 	kfree(frame->desc_ptr);
2429614219eSNikolai Kondrashov 	memset(frame, 0, sizeof(*frame));
2439614219eSNikolai Kondrashov }
2449614219eSNikolai Kondrashov 
2459614219eSNikolai Kondrashov /**
2469614219eSNikolai Kondrashov  * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
2479614219eSNikolai Kondrashov  * parameters with a static report descriptor.
2489614219eSNikolai Kondrashov  *
2499614219eSNikolai Kondrashov  * @frame:	Pointer to the frame parameters to initialize (to be cleaned
2509614219eSNikolai Kondrashov  *		up with uclogic_params_frame_cleanup()). Not modified in case
2519614219eSNikolai Kondrashov  *		of error. Cannot be NULL.
2529614219eSNikolai Kondrashov  * @desc_ptr:	Report descriptor pointer. Can be NULL, if desc_size is zero.
2539614219eSNikolai Kondrashov  * @desc_size:	Report descriptor size.
2549614219eSNikolai Kondrashov  * @id:		Report ID used for frame reports, if they should be tweaked,
2559614219eSNikolai Kondrashov  *		zero if not.
2569614219eSNikolai Kondrashov  *
2579614219eSNikolai Kondrashov  * Returns:
2589614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
2599614219eSNikolai Kondrashov  */
2609614219eSNikolai Kondrashov static int uclogic_params_frame_init_with_desc(
2619614219eSNikolai Kondrashov 					struct uclogic_params_frame *frame,
2629614219eSNikolai Kondrashov 					const __u8 *desc_ptr,
2639614219eSNikolai Kondrashov 					size_t desc_size,
2649614219eSNikolai Kondrashov 					unsigned int id)
2659614219eSNikolai Kondrashov {
2669614219eSNikolai Kondrashov 	__u8 *copy_desc_ptr;
2679614219eSNikolai Kondrashov 
2689614219eSNikolai Kondrashov 	if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
2699614219eSNikolai Kondrashov 		return -EINVAL;
2709614219eSNikolai Kondrashov 
2719614219eSNikolai Kondrashov 	copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
2729614219eSNikolai Kondrashov 	if (copy_desc_ptr == NULL)
2739614219eSNikolai Kondrashov 		return -ENOMEM;
2749614219eSNikolai Kondrashov 
2759614219eSNikolai Kondrashov 	memset(frame, 0, sizeof(*frame));
2769614219eSNikolai Kondrashov 	frame->desc_ptr = copy_desc_ptr;
2779614219eSNikolai Kondrashov 	frame->desc_size = desc_size;
2789614219eSNikolai Kondrashov 	frame->id = id;
2799614219eSNikolai Kondrashov 	return 0;
2809614219eSNikolai Kondrashov }
2819614219eSNikolai Kondrashov 
2829614219eSNikolai Kondrashov /**
2839614219eSNikolai Kondrashov  * uclogic_params_frame_init_buttonpad() - initialize abstract buttonpad
2849614219eSNikolai Kondrashov  * on a tablet interface.
2859614219eSNikolai Kondrashov  *
2869614219eSNikolai Kondrashov  * @frame:	Pointer to the frame parameters to initialize (to be cleaned
2879614219eSNikolai Kondrashov  *		up with uclogic_params_frame_cleanup()). Not modified in case
2889614219eSNikolai Kondrashov  *		of error, or if parameters are not found. Cannot be NULL.
2899614219eSNikolai Kondrashov  * @pfound:	Location for a flag which is set to true if the parameters
2909614219eSNikolai Kondrashov  *		were found, and to false if not (e.g. device was
2919614219eSNikolai Kondrashov  *		incompatible). Not modified in case of error. Cannot be NULL.
2929614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
2939614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
2949614219eSNikolai Kondrashov  *
2959614219eSNikolai Kondrashov  * Returns:
2969614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
2979614219eSNikolai Kondrashov  */
2989614219eSNikolai Kondrashov static int uclogic_params_frame_init_buttonpad(
2999614219eSNikolai Kondrashov 					struct uclogic_params_frame *frame,
3009614219eSNikolai Kondrashov 					bool *pfound,
3019614219eSNikolai Kondrashov 					struct hid_device *hdev)
3029614219eSNikolai Kondrashov {
3039614219eSNikolai Kondrashov 	int rc;
3049614219eSNikolai Kondrashov 	bool found = false;
3059614219eSNikolai Kondrashov 	struct usb_device *usb_dev = hid_to_usb_dev(hdev);
3069614219eSNikolai Kondrashov 	char *str_buf = NULL;
3079614219eSNikolai Kondrashov 	const size_t str_len = 16;
3089614219eSNikolai Kondrashov 
3099614219eSNikolai Kondrashov 	/* Check arguments */
3109614219eSNikolai Kondrashov 	if (frame == NULL || pfound == NULL || hdev == NULL) {
3119614219eSNikolai Kondrashov 		rc = -EINVAL;
3129614219eSNikolai Kondrashov 		goto cleanup;
3139614219eSNikolai Kondrashov 	}
3149614219eSNikolai Kondrashov 
3159614219eSNikolai Kondrashov 	/*
3169614219eSNikolai Kondrashov 	 * Enable generic button mode
3179614219eSNikolai Kondrashov 	 */
3189614219eSNikolai Kondrashov 	str_buf = kzalloc(str_len, GFP_KERNEL);
3199614219eSNikolai Kondrashov 	if (str_buf == NULL) {
3209614219eSNikolai Kondrashov 		rc = -ENOMEM;
3219614219eSNikolai Kondrashov 		goto cleanup;
3229614219eSNikolai Kondrashov 	}
3239614219eSNikolai Kondrashov 
3249614219eSNikolai Kondrashov 	rc = usb_string(usb_dev, 123, str_buf, str_len);
3259614219eSNikolai Kondrashov 	if (rc == -EPIPE) {
3269614219eSNikolai Kondrashov 		hid_dbg(hdev,
3279614219eSNikolai Kondrashov 			"generic button -enabling string descriptor not found\n");
3289614219eSNikolai Kondrashov 	} else if (rc < 0) {
3299614219eSNikolai Kondrashov 		goto cleanup;
3309614219eSNikolai Kondrashov 	} else if (strncmp(str_buf, "HK On", rc) != 0) {
3319614219eSNikolai Kondrashov 		hid_dbg(hdev,
3329614219eSNikolai Kondrashov 			"invalid response to enabling generic buttons: \"%s\"\n",
3339614219eSNikolai Kondrashov 			str_buf);
3349614219eSNikolai Kondrashov 	} else {
3359614219eSNikolai Kondrashov 		hid_dbg(hdev, "generic buttons enabled\n");
3369614219eSNikolai Kondrashov 		rc = uclogic_params_frame_init_with_desc(
3379614219eSNikolai Kondrashov 				frame,
3389614219eSNikolai Kondrashov 				uclogic_rdesc_buttonpad_arr,
3399614219eSNikolai Kondrashov 				uclogic_rdesc_buttonpad_size,
3409614219eSNikolai Kondrashov 				UCLOGIC_RDESC_BUTTONPAD_ID);
3419614219eSNikolai Kondrashov 		if (rc != 0)
3429614219eSNikolai Kondrashov 			goto cleanup;
3439614219eSNikolai Kondrashov 		found = true;
3449614219eSNikolai Kondrashov 	}
3459614219eSNikolai Kondrashov 
3469614219eSNikolai Kondrashov 	*pfound = found;
3479614219eSNikolai Kondrashov 	rc = 0;
3489614219eSNikolai Kondrashov cleanup:
3499614219eSNikolai Kondrashov 	kfree(str_buf);
3509614219eSNikolai Kondrashov 	return rc;
3519614219eSNikolai Kondrashov }
3529614219eSNikolai Kondrashov 
3539614219eSNikolai Kondrashov /**
3549614219eSNikolai Kondrashov  * uclogic_params_cleanup - free resources used by struct uclogic_params
3559614219eSNikolai Kondrashov  * (tablet interface's parameters).
3569614219eSNikolai Kondrashov  * Can be called repeatedly.
3579614219eSNikolai Kondrashov  *
3589614219eSNikolai Kondrashov  * @params:	Input parameters to cleanup. Cannot be NULL.
3599614219eSNikolai Kondrashov  */
3609614219eSNikolai Kondrashov void uclogic_params_cleanup(struct uclogic_params *params)
3619614219eSNikolai Kondrashov {
3629614219eSNikolai Kondrashov 	if (!params->invalid) {
3639614219eSNikolai Kondrashov 		kfree(params->desc_ptr);
3649614219eSNikolai Kondrashov 		if (!params->pen_unused)
3659614219eSNikolai Kondrashov 			uclogic_params_pen_cleanup(&params->pen);
3669614219eSNikolai Kondrashov 		uclogic_params_frame_cleanup(&params->frame);
3679614219eSNikolai Kondrashov 		memset(params, 0, sizeof(*params));
3689614219eSNikolai Kondrashov 	}
3699614219eSNikolai Kondrashov }
3709614219eSNikolai Kondrashov 
3719614219eSNikolai Kondrashov /**
3729614219eSNikolai Kondrashov  * Get a replacement report descriptor for a tablet's interface.
3739614219eSNikolai Kondrashov  *
3749614219eSNikolai Kondrashov  * @params:	The parameters of a tablet interface to get report
3759614219eSNikolai Kondrashov  *		descriptor for. Cannot be NULL.
3769614219eSNikolai Kondrashov  * @pdesc:	Location for the resulting, kmalloc-allocated report
3779614219eSNikolai Kondrashov  *		descriptor pointer, or for NULL, if there's no replacement
3789614219eSNikolai Kondrashov  *		report descriptor. Not modified in case of error. Cannot be
3799614219eSNikolai Kondrashov  *		NULL.
3809614219eSNikolai Kondrashov  * @psize:	Location for the resulting report descriptor size, not set if
3819614219eSNikolai Kondrashov  *		there's no replacement report descriptor. Not modified in case
3829614219eSNikolai Kondrashov  *		of error. Cannot be NULL.
3839614219eSNikolai Kondrashov  *
3849614219eSNikolai Kondrashov  * Returns:
3859614219eSNikolai Kondrashov  *	Zero, if successful.
3869614219eSNikolai Kondrashov  *	-EINVAL, if invalid arguments are supplied.
3879614219eSNikolai Kondrashov  *	-ENOMEM, if failed to allocate memory.
3889614219eSNikolai Kondrashov  */
3899614219eSNikolai Kondrashov int uclogic_params_get_desc(const struct uclogic_params *params,
3909614219eSNikolai Kondrashov 				__u8 **pdesc,
3919614219eSNikolai Kondrashov 				unsigned int *psize)
3929614219eSNikolai Kondrashov {
3939614219eSNikolai Kondrashov 	bool common_present;
3949614219eSNikolai Kondrashov 	bool pen_present;
3959614219eSNikolai Kondrashov 	bool frame_present;
3969614219eSNikolai Kondrashov 	unsigned int size;
3979614219eSNikolai Kondrashov 	__u8 *desc = NULL;
3989614219eSNikolai Kondrashov 
3999614219eSNikolai Kondrashov 	/* Check arguments */
4009614219eSNikolai Kondrashov 	if (params == NULL || pdesc == NULL || psize == NULL)
4019614219eSNikolai Kondrashov 		return -EINVAL;
4029614219eSNikolai Kondrashov 
4039614219eSNikolai Kondrashov 	size = 0;
4049614219eSNikolai Kondrashov 
4059614219eSNikolai Kondrashov 	common_present = (params->desc_ptr != NULL);
4069614219eSNikolai Kondrashov 	pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL);
4079614219eSNikolai Kondrashov 	frame_present = (params->frame.desc_ptr != NULL);
4089614219eSNikolai Kondrashov 
4099614219eSNikolai Kondrashov 	if (common_present)
4109614219eSNikolai Kondrashov 		size += params->desc_size;
4119614219eSNikolai Kondrashov 	if (pen_present)
4129614219eSNikolai Kondrashov 		size += params->pen.desc_size;
4139614219eSNikolai Kondrashov 	if (frame_present)
4149614219eSNikolai Kondrashov 		size += params->frame.desc_size;
4159614219eSNikolai Kondrashov 
4169614219eSNikolai Kondrashov 	if (common_present || pen_present || frame_present) {
4179614219eSNikolai Kondrashov 		__u8 *p;
4189614219eSNikolai Kondrashov 
4199614219eSNikolai Kondrashov 		desc = kmalloc(size, GFP_KERNEL);
4209614219eSNikolai Kondrashov 		if (desc == NULL)
4219614219eSNikolai Kondrashov 			return -ENOMEM;
4229614219eSNikolai Kondrashov 		p = desc;
4239614219eSNikolai Kondrashov 
4249614219eSNikolai Kondrashov 		if (common_present) {
4259614219eSNikolai Kondrashov 			memcpy(p, params->desc_ptr,
4269614219eSNikolai Kondrashov 				params->desc_size);
4279614219eSNikolai Kondrashov 			p += params->desc_size;
4289614219eSNikolai Kondrashov 		}
4299614219eSNikolai Kondrashov 		if (pen_present) {
4309614219eSNikolai Kondrashov 			memcpy(p, params->pen.desc_ptr,
4319614219eSNikolai Kondrashov 				params->pen.desc_size);
4329614219eSNikolai Kondrashov 			p += params->pen.desc_size;
4339614219eSNikolai Kondrashov 		}
4349614219eSNikolai Kondrashov 		if (frame_present) {
4359614219eSNikolai Kondrashov 			memcpy(p, params->frame.desc_ptr,
4369614219eSNikolai Kondrashov 				params->frame.desc_size);
4379614219eSNikolai Kondrashov 			p += params->frame.desc_size;
4389614219eSNikolai Kondrashov 		}
4399614219eSNikolai Kondrashov 
4409614219eSNikolai Kondrashov 		WARN_ON(p != desc + size);
4419614219eSNikolai Kondrashov 
4429614219eSNikolai Kondrashov 		*psize = size;
4439614219eSNikolai Kondrashov 	}
4449614219eSNikolai Kondrashov 
4459614219eSNikolai Kondrashov 	*pdesc = desc;
4469614219eSNikolai Kondrashov 	return 0;
4479614219eSNikolai Kondrashov }
4489614219eSNikolai Kondrashov 
4499614219eSNikolai Kondrashov /**
4509614219eSNikolai Kondrashov  * uclogic_params_init_invalid() - initialize tablet interface parameters,
4519614219eSNikolai Kondrashov  * specifying the interface is invalid.
4529614219eSNikolai Kondrashov  *
4539614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
4549614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Cannot be NULL.
4559614219eSNikolai Kondrashov  */
4569614219eSNikolai Kondrashov static void uclogic_params_init_invalid(struct uclogic_params *params)
4579614219eSNikolai Kondrashov {
4589614219eSNikolai Kondrashov 	params->invalid = true;
4599614219eSNikolai Kondrashov }
4609614219eSNikolai Kondrashov 
4619614219eSNikolai Kondrashov /**
4629614219eSNikolai Kondrashov  * uclogic_params_init_with_opt_desc() - initialize tablet interface
4639614219eSNikolai Kondrashov  * parameters with an optional replacement report descriptor. Only modify
4649614219eSNikolai Kondrashov  * report descriptor, if the original report descriptor matches the expected
4659614219eSNikolai Kondrashov  * size.
4669614219eSNikolai Kondrashov  *
4679614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
4689614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Not modified in case of
4699614219eSNikolai Kondrashov  *			error. Cannot be NULL.
4709614219eSNikolai Kondrashov  * @hdev:		The HID device of the tablet interface create the
4719614219eSNikolai Kondrashov  *			parameters for. Cannot be NULL.
4729614219eSNikolai Kondrashov  * @orig_desc_size:	Expected size of the original report descriptor to
4739614219eSNikolai Kondrashov  *			be replaced.
4749614219eSNikolai Kondrashov  * @desc_ptr:		Pointer to the replacement report descriptor.
4759614219eSNikolai Kondrashov  *			Can be NULL, if desc_size is zero.
4769614219eSNikolai Kondrashov  * @desc_size:		Size of the replacement report descriptor.
4779614219eSNikolai Kondrashov  *
4789614219eSNikolai Kondrashov  * Returns:
4799614219eSNikolai Kondrashov  *	Zero, if successful. -EINVAL if an invalid argument was passed.
4809614219eSNikolai Kondrashov  *	-ENOMEM, if failed to allocate memory.
4819614219eSNikolai Kondrashov  */
4829614219eSNikolai Kondrashov static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
4839614219eSNikolai Kondrashov 					     struct hid_device *hdev,
4849614219eSNikolai Kondrashov 					     unsigned int orig_desc_size,
4859614219eSNikolai Kondrashov 					     __u8 *desc_ptr,
4869614219eSNikolai Kondrashov 					     unsigned int desc_size)
4879614219eSNikolai Kondrashov {
4889614219eSNikolai Kondrashov 	__u8 *desc_copy_ptr = NULL;
4899614219eSNikolai Kondrashov 	unsigned int desc_copy_size;
4909614219eSNikolai Kondrashov 	int rc;
4919614219eSNikolai Kondrashov 
4929614219eSNikolai Kondrashov 	/* Check arguments */
4939614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL ||
4949614219eSNikolai Kondrashov 	    (desc_ptr == NULL && desc_size != 0)) {
4959614219eSNikolai Kondrashov 		rc = -EINVAL;
4969614219eSNikolai Kondrashov 		goto cleanup;
4979614219eSNikolai Kondrashov 	}
4989614219eSNikolai Kondrashov 
4999614219eSNikolai Kondrashov 	/* Replace report descriptor, if it matches */
5009614219eSNikolai Kondrashov 	if (hdev->dev_rsize == orig_desc_size) {
5019614219eSNikolai Kondrashov 		hid_dbg(hdev,
5029614219eSNikolai Kondrashov 			"device report descriptor matches the expected size, replacing\n");
5039614219eSNikolai Kondrashov 		desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
5049614219eSNikolai Kondrashov 		if (desc_copy_ptr == NULL) {
5059614219eSNikolai Kondrashov 			rc = -ENOMEM;
5069614219eSNikolai Kondrashov 			goto cleanup;
5079614219eSNikolai Kondrashov 		}
5089614219eSNikolai Kondrashov 		desc_copy_size = desc_size;
5099614219eSNikolai Kondrashov 	} else {
5109614219eSNikolai Kondrashov 		hid_dbg(hdev,
5119614219eSNikolai Kondrashov 			"device report descriptor doesn't match the expected size (%u != %u), preserving\n",
5129614219eSNikolai Kondrashov 			hdev->dev_rsize, orig_desc_size);
5139614219eSNikolai Kondrashov 		desc_copy_ptr = NULL;
5149614219eSNikolai Kondrashov 		desc_copy_size = 0;
5159614219eSNikolai Kondrashov 	}
5169614219eSNikolai Kondrashov 
5179614219eSNikolai Kondrashov 	/* Output parameters */
5189614219eSNikolai Kondrashov 	memset(params, 0, sizeof(*params));
5199614219eSNikolai Kondrashov 	params->desc_ptr = desc_copy_ptr;
5209614219eSNikolai Kondrashov 	desc_copy_ptr = NULL;
5219614219eSNikolai Kondrashov 	params->desc_size = desc_copy_size;
5229614219eSNikolai Kondrashov 
5239614219eSNikolai Kondrashov 	rc = 0;
5249614219eSNikolai Kondrashov cleanup:
5259614219eSNikolai Kondrashov 	kfree(desc_copy_ptr);
5269614219eSNikolai Kondrashov 	return rc;
5279614219eSNikolai Kondrashov }
5289614219eSNikolai Kondrashov 
5299614219eSNikolai Kondrashov /**
5309614219eSNikolai Kondrashov  * uclogic_params_init_with_pen_unused() - initialize tablet interface
5319614219eSNikolai Kondrashov  * parameters preserving original reports and generic HID processing, but
5329614219eSNikolai Kondrashov  * disabling pen usage.
5339614219eSNikolai Kondrashov  *
5349614219eSNikolai Kondrashov  * @params:		Parameters to initialize (to be cleaned with
5359614219eSNikolai Kondrashov  *			uclogic_params_cleanup()). Not modified in case of
5369614219eSNikolai Kondrashov  *			error. Cannot be NULL.
5379614219eSNikolai Kondrashov  */
5389614219eSNikolai Kondrashov static void uclogic_params_init_with_pen_unused(struct uclogic_params *params)
5399614219eSNikolai Kondrashov {
5409614219eSNikolai Kondrashov 	memset(params, 0, sizeof(*params));
5419614219eSNikolai Kondrashov 	params->pen_unused = true;
5429614219eSNikolai Kondrashov }
5439614219eSNikolai Kondrashov 
5449614219eSNikolai Kondrashov /**
5459614219eSNikolai Kondrashov  * uclogic_params_init() - initialize a Huion tablet interface and discover
5469614219eSNikolai Kondrashov  * its parameters.
5479614219eSNikolai Kondrashov  *
5489614219eSNikolai Kondrashov  * @params:	Parameters to fill in (to be cleaned with
5499614219eSNikolai Kondrashov  *		uclogic_params_cleanup()). Not modified in case of error.
5509614219eSNikolai Kondrashov  *		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  */
5579614219eSNikolai Kondrashov static int uclogic_params_huion_init(struct uclogic_params *params,
5589614219eSNikolai Kondrashov 				     struct hid_device *hdev)
5599614219eSNikolai Kondrashov {
5609614219eSNikolai Kondrashov 	int rc;
5619614219eSNikolai Kondrashov 	struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
5629614219eSNikolai Kondrashov 	__u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
5639614219eSNikolai Kondrashov 	bool found;
5649614219eSNikolai Kondrashov 	/* The resulting parameters (noop) */
5659614219eSNikolai Kondrashov 	struct uclogic_params p = {0, };
5669614219eSNikolai Kondrashov 
5679614219eSNikolai Kondrashov 	/* Check arguments */
5689614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL) {
5699614219eSNikolai Kondrashov 		rc = -EINVAL;
5709614219eSNikolai Kondrashov 		goto cleanup;
5719614219eSNikolai Kondrashov 	}
5729614219eSNikolai Kondrashov 
5739614219eSNikolai Kondrashov 	/* If it's not a pen interface */
5749614219eSNikolai Kondrashov 	if (bInterfaceNumber != 0) {
5759614219eSNikolai Kondrashov 		/* TODO: Consider marking the interface invalid */
5769614219eSNikolai Kondrashov 		uclogic_params_init_with_pen_unused(&p);
5779614219eSNikolai Kondrashov 		goto output;
5789614219eSNikolai Kondrashov 	}
5799614219eSNikolai Kondrashov 
5809614219eSNikolai Kondrashov 	/* Try to probe pen parameters */
5819614219eSNikolai Kondrashov 	rc = uclogic_params_pen_init(&p.pen, &found, hdev);
5829614219eSNikolai Kondrashov 	if (rc != 0) {
5839614219eSNikolai Kondrashov 		hid_err(hdev,
5849614219eSNikolai Kondrashov 			"failed probing pen parameters: %d\n", rc);
5859614219eSNikolai Kondrashov 		goto cleanup;
5869614219eSNikolai Kondrashov 	} else if (found) {
5879614219eSNikolai Kondrashov 		hid_dbg(hdev, "pen parameters found\n");
5889614219eSNikolai Kondrashov 		/* Try to probe buttonpad */
5899614219eSNikolai Kondrashov 		rc = uclogic_params_frame_init_buttonpad(
5909614219eSNikolai Kondrashov 						&p.frame,
5919614219eSNikolai Kondrashov 						&found, hdev);
5929614219eSNikolai Kondrashov 		if (rc != 0) {
5939614219eSNikolai Kondrashov 			hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc);
5949614219eSNikolai Kondrashov 			goto cleanup;
5959614219eSNikolai Kondrashov 		}
5969614219eSNikolai Kondrashov 		hid_dbg(hdev, "buttonpad parameters%s found\n",
5979614219eSNikolai Kondrashov 			(found ? "" : " not"));
5989614219eSNikolai Kondrashov 		if (found) {
5999614219eSNikolai Kondrashov 			/* Set bitmask marking frame reports */
6009614219eSNikolai Kondrashov 			p.pen_frame_flag = 0x20;
6019614219eSNikolai Kondrashov 		}
6029614219eSNikolai Kondrashov 		goto output;
6039614219eSNikolai Kondrashov 	}
6049614219eSNikolai Kondrashov 	hid_dbg(hdev, "pen parameters not found\n");
6059614219eSNikolai Kondrashov 
6069614219eSNikolai Kondrashov 	uclogic_params_init_invalid(&p);
6079614219eSNikolai Kondrashov 
6089614219eSNikolai Kondrashov output:
6099614219eSNikolai Kondrashov 	/* Output parameters */
6109614219eSNikolai Kondrashov 	memcpy(params, &p, sizeof(*params));
6119614219eSNikolai Kondrashov 	memset(&p, 0, sizeof(p));
6129614219eSNikolai Kondrashov 	rc = 0;
6139614219eSNikolai Kondrashov cleanup:
6149614219eSNikolai Kondrashov 	uclogic_params_cleanup(&p);
6159614219eSNikolai Kondrashov 	return rc;
6169614219eSNikolai Kondrashov }
6179614219eSNikolai Kondrashov 
6189614219eSNikolai Kondrashov /**
6199614219eSNikolai Kondrashov  * uclogic_params_init() - initialize a tablet interface and discover its
6209614219eSNikolai Kondrashov  * parameters.
6219614219eSNikolai Kondrashov  *
6229614219eSNikolai Kondrashov  * @params:	Parameters to fill in (to be cleaned with
6239614219eSNikolai Kondrashov  *		uclogic_params_cleanup()). Not modified in case of error.
6249614219eSNikolai Kondrashov  *		Cannot be NULL.
6259614219eSNikolai Kondrashov  * @hdev:	The HID device of the tablet interface to initialize and get
6269614219eSNikolai Kondrashov  *		parameters from. Cannot be NULL.
6279614219eSNikolai Kondrashov  *
6289614219eSNikolai Kondrashov  * Returns:
6299614219eSNikolai Kondrashov  *	Zero, if successful. A negative errno code on error.
6309614219eSNikolai Kondrashov  */
6319614219eSNikolai Kondrashov int uclogic_params_init(struct uclogic_params *params,
6329614219eSNikolai Kondrashov 			struct hid_device *hdev)
6339614219eSNikolai Kondrashov {
6349614219eSNikolai Kondrashov 	int rc;
6359614219eSNikolai Kondrashov 	struct usb_device *udev = hid_to_usb_dev(hdev);
6369614219eSNikolai Kondrashov 	__u8  bNumInterfaces = udev->config->desc.bNumInterfaces;
6379614219eSNikolai Kondrashov 	struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
6389614219eSNikolai Kondrashov 	__u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
6399614219eSNikolai Kondrashov 	bool found;
6409614219eSNikolai Kondrashov 	/* The resulting parameters (noop) */
6419614219eSNikolai Kondrashov 	struct uclogic_params p = {0, };
6429614219eSNikolai Kondrashov 
6439614219eSNikolai Kondrashov 	/* Check arguments */
6449614219eSNikolai Kondrashov 	if (params == NULL || hdev == NULL) {
6459614219eSNikolai Kondrashov 		rc = -EINVAL;
6469614219eSNikolai Kondrashov 		goto cleanup;
6479614219eSNikolai Kondrashov 	}
6489614219eSNikolai Kondrashov 
6499614219eSNikolai Kondrashov 	/*
6509614219eSNikolai Kondrashov 	 * Set replacement report descriptor if the original matches the
6519614219eSNikolai Kondrashov 	 * specified size. Otherwise keep interface unchanged.
6529614219eSNikolai Kondrashov 	 */
6539614219eSNikolai Kondrashov #define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
6549614219eSNikolai Kondrashov 	uclogic_params_init_with_opt_desc(                  \
6559614219eSNikolai Kondrashov 		&p, hdev,                                   \
6569614219eSNikolai Kondrashov 		UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \
6579614219eSNikolai Kondrashov 		uclogic_rdesc_##_new_desc_token##_arr,      \
6589614219eSNikolai Kondrashov 		uclogic_rdesc_##_new_desc_token##_size)
6599614219eSNikolai Kondrashov 
6609614219eSNikolai Kondrashov #define VID_PID(_vid, _pid) \
6619614219eSNikolai Kondrashov 	(((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
6629614219eSNikolai Kondrashov 
6639614219eSNikolai Kondrashov 	/*
6649614219eSNikolai Kondrashov 	 * Handle specific interfaces for specific tablets.
6659614219eSNikolai Kondrashov 	 *
6669614219eSNikolai Kondrashov 	 * Observe the following logic:
6679614219eSNikolai Kondrashov 	 *
6689614219eSNikolai Kondrashov 	 * If the interface is recognized as producing certain useful input:
6699614219eSNikolai Kondrashov 	 *	Mark interface as valid.
6709614219eSNikolai Kondrashov 	 *	Output interface parameters.
6719614219eSNikolai Kondrashov 	 * Else, if the interface is recognized as *not* producing any useful
6729614219eSNikolai Kondrashov 	 * input:
6739614219eSNikolai Kondrashov 	 *	Mark interface as invalid.
6749614219eSNikolai Kondrashov 	 * Else:
6759614219eSNikolai Kondrashov 	 *	Mark interface as valid.
6769614219eSNikolai Kondrashov 	 *	Output noop parameters.
6779614219eSNikolai Kondrashov 	 *
6789614219eSNikolai Kondrashov 	 * Rule of thumb: it is better to disable a broken interface than let
6799614219eSNikolai Kondrashov 	 *		  it spew garbage input.
6809614219eSNikolai Kondrashov 	 */
6819614219eSNikolai Kondrashov 
6829614219eSNikolai Kondrashov 	switch (VID_PID(hdev->vendor, hdev->product)) {
6839614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
6849614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
6859614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
6869614219eSNikolai Kondrashov 		if (rc != 0)
6879614219eSNikolai Kondrashov 			goto cleanup;
6889614219eSNikolai Kondrashov 		break;
6899614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
6909614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
6919614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
6929614219eSNikolai Kondrashov 		if (rc != 0)
6939614219eSNikolai Kondrashov 			goto cleanup;
6949614219eSNikolai Kondrashov 		break;
6959614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
6969614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
6979614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
6989614219eSNikolai Kondrashov 		if (rc != 0)
6999614219eSNikolai Kondrashov 			goto cleanup;
7009614219eSNikolai Kondrashov 		break;
7019614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7029614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
7039614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
7049614219eSNikolai Kondrashov 		if (rc != 0)
7059614219eSNikolai Kondrashov 			goto cleanup;
7069614219eSNikolai Kondrashov 		break;
7079614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7089614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
7099614219eSNikolai Kondrashov 		rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
7109614219eSNikolai Kondrashov 		if (rc != 0)
7119614219eSNikolai Kondrashov 			goto cleanup;
7129614219eSNikolai Kondrashov 		break;
7139614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7149614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
7159614219eSNikolai Kondrashov 		switch (bInterfaceNumber) {
7169614219eSNikolai Kondrashov 		case 0:
7179614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
7189614219eSNikolai Kondrashov 			if (rc != 0)
7199614219eSNikolai Kondrashov 				goto cleanup;
7209614219eSNikolai Kondrashov 			break;
7219614219eSNikolai Kondrashov 		case 1:
7229614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
7239614219eSNikolai Kondrashov 			if (rc != 0)
7249614219eSNikolai Kondrashov 				goto cleanup;
7259614219eSNikolai Kondrashov 			break;
7269614219eSNikolai Kondrashov 		case 2:
7279614219eSNikolai Kondrashov 			rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
7289614219eSNikolai Kondrashov 			if (rc != 0)
7299614219eSNikolai Kondrashov 				goto cleanup;
7309614219eSNikolai Kondrashov 			break;
7319614219eSNikolai Kondrashov 		}
7329614219eSNikolai Kondrashov 		break;
7339614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7349614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
7359614219eSNikolai Kondrashov 		/*
7369614219eSNikolai Kondrashov 		 * If it is not a three-interface version, which is known to
7379614219eSNikolai Kondrashov 		 * respond to initialization.
7389614219eSNikolai Kondrashov 		 */
7399614219eSNikolai Kondrashov 		if (bNumInterfaces != 3) {
7409614219eSNikolai Kondrashov 			switch (bInterfaceNumber) {
7419614219eSNikolai Kondrashov 			case 0:
7429614219eSNikolai Kondrashov 				rc = WITH_OPT_DESC(TWHA60_ORIG0,
7439614219eSNikolai Kondrashov 							twha60_fixed0);
7449614219eSNikolai Kondrashov 				if (rc != 0)
7459614219eSNikolai Kondrashov 					goto cleanup;
7469614219eSNikolai Kondrashov 				break;
7479614219eSNikolai Kondrashov 			case 1:
7489614219eSNikolai Kondrashov 				rc = WITH_OPT_DESC(TWHA60_ORIG1,
7499614219eSNikolai Kondrashov 							twha60_fixed1);
7509614219eSNikolai Kondrashov 				if (rc != 0)
7519614219eSNikolai Kondrashov 					goto cleanup;
7529614219eSNikolai Kondrashov 				break;
7539614219eSNikolai Kondrashov 			}
7549614219eSNikolai Kondrashov 			break;
7559614219eSNikolai Kondrashov 		}
7569614219eSNikolai Kondrashov 		/* FALL THROUGH */
7579614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_HUION,
7589614219eSNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET):
7599614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7609614219eSNikolai Kondrashov 		     USB_DEVICE_ID_HUION_TABLET):
7619614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7629614219eSNikolai Kondrashov 		     USB_DEVICE_ID_YIYNOVA_TABLET):
7639614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7649614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
7659614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7669614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
7679614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UCLOGIC,
7689614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
7699614219eSNikolai Kondrashov 		rc = uclogic_params_huion_init(&p, hdev);
7709614219eSNikolai Kondrashov 		if (rc != 0)
7719614219eSNikolai Kondrashov 			goto cleanup;
7729614219eSNikolai Kondrashov 		break;
7739614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGTIZER,
7749614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
7759614219eSNikolai Kondrashov 	case VID_PID(USB_VENDOR_ID_UGEE,
7769614219eSNikolai Kondrashov 		     USB_DEVICE_ID_UGEE_TABLET_EX07S):
7779614219eSNikolai Kondrashov 		/* If this is the pen interface */
7789614219eSNikolai Kondrashov 		if (bInterfaceNumber == 1) {
7799614219eSNikolai Kondrashov 			/* Probe pen parameters */
7809614219eSNikolai Kondrashov 			rc = uclogic_params_pen_init(&p.pen, &found, hdev);
7819614219eSNikolai Kondrashov 			if (rc != 0) {
7829614219eSNikolai Kondrashov 				hid_err(hdev, "pen probing failed: %d\n", rc);
7839614219eSNikolai Kondrashov 				goto cleanup;
7849614219eSNikolai Kondrashov 			}
7859614219eSNikolai Kondrashov 			if (!found) {
7869614219eSNikolai Kondrashov 				hid_warn(hdev, "pen parameters not found");
7879614219eSNikolai Kondrashov 				uclogic_params_init_invalid(&p);
7889614219eSNikolai Kondrashov 			}
7899614219eSNikolai Kondrashov 		} else {
7909614219eSNikolai Kondrashov 			/* TODO: Consider marking the interface invalid */
7919614219eSNikolai Kondrashov 			uclogic_params_init_with_pen_unused(&p);
7929614219eSNikolai Kondrashov 		}
7939614219eSNikolai Kondrashov 		break;
7949614219eSNikolai Kondrashov 	}
7959614219eSNikolai Kondrashov 
7969614219eSNikolai Kondrashov #undef VID_PID
7979614219eSNikolai Kondrashov #undef WITH_OPT_DESC
7989614219eSNikolai Kondrashov 
7999614219eSNikolai Kondrashov 	/* Output parameters */
8009614219eSNikolai Kondrashov 	memcpy(params, &p, sizeof(*params));
8019614219eSNikolai Kondrashov 	memset(&p, 0, sizeof(p));
8029614219eSNikolai Kondrashov 	rc = 0;
8039614219eSNikolai Kondrashov cleanup:
8049614219eSNikolai Kondrashov 	uclogic_params_cleanup(&p);
8059614219eSNikolai Kondrashov 	return rc;
8069614219eSNikolai Kondrashov }
807