brintos

brintos / linux-shallow public Read only

0
0
Text · 52.4 KiB · ef26c7d Raw
1866 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *  HID driver for UC-Logic devices not fully compliant with HID standard4 *  - tablet initialization and parameter retrieval5 *6 *  Copyright (c) 2018 Nikolai Kondrashov7 */8 9/*10 * This program is free software; you can redistribute it and/or modify it11 * under the terms of the GNU General Public License as published by the Free12 * Software Foundation; either version 2 of the License, or (at your option)13 * any later version.14 */15 16#include "hid-uclogic-params.h"17#include "hid-uclogic-rdesc.h"18#include "usbhid/usbhid.h"19#include "hid-ids.h"20#include <linux/ctype.h>21#include <linux/string.h>22#include <linux/unaligned.h>23 24/**25 * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type26 *                                       to a string.27 * @inrange:	The in-range reporting type to convert.28 *29 * Return:30 * * The string representing the type, or31 * * %NULL if the type is unknown.32 */33static const char *uclogic_params_pen_inrange_to_str(34				enum uclogic_params_pen_inrange inrange)35{36	switch (inrange) {37	case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:38		return "normal";39	case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:40		return "inverted";41	case UCLOGIC_PARAMS_PEN_INRANGE_NONE:42		return "none";43	default:44		return NULL;45	}46}47 48/**49 * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters50 * @hdev:	The HID device the pen parameters describe.51 * @pen:	The pen parameters to dump.52 *53 * Dump tablet interface pen parameters with hid_dbg(). The dump is indented54 * with a tab.55 */56static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev,57					const struct uclogic_params_pen *pen)58{59	size_t i;60 61	hid_dbg(hdev, "\t.usage_invalid = %s\n",62		(pen->usage_invalid ? "true" : "false"));63	hid_dbg(hdev, "\t.desc_ptr = %p\n", pen->desc_ptr);64	hid_dbg(hdev, "\t.desc_size = %u\n", pen->desc_size);65	hid_dbg(hdev, "\t.id = %u\n", pen->id);66	hid_dbg(hdev, "\t.subreport_list = {\n");67	for (i = 0; i < ARRAY_SIZE(pen->subreport_list); i++) {68		hid_dbg(hdev, "\t\t{0x%02hhx, %hhu}%s\n",69			pen->subreport_list[i].value,70			pen->subreport_list[i].id,71			i < (ARRAY_SIZE(pen->subreport_list) - 1) ? "," : "");72	}73	hid_dbg(hdev, "\t}\n");74	hid_dbg(hdev, "\t.inrange = %s\n",75		uclogic_params_pen_inrange_to_str(pen->inrange));76	hid_dbg(hdev, "\t.fragmented_hires = %s\n",77		(pen->fragmented_hires ? "true" : "false"));78	hid_dbg(hdev, "\t.tilt_y_flipped = %s\n",79		(pen->tilt_y_flipped ? "true" : "false"));80}81 82/**83 * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters84 * @hdev:	The HID device the pen parameters describe.85 * @frame:	The frame parameters to dump.86 *87 * Dump tablet interface frame parameters with hid_dbg(). The dump is88 * indented with two tabs.89 */90static void uclogic_params_frame_hid_dbg(91				const struct hid_device *hdev,92				const struct uclogic_params_frame *frame)93{94	hid_dbg(hdev, "\t\t.desc_ptr = %p\n", frame->desc_ptr);95	hid_dbg(hdev, "\t\t.desc_size = %u\n", frame->desc_size);96	hid_dbg(hdev, "\t\t.id = %u\n", frame->id);97	hid_dbg(hdev, "\t\t.suffix = %s\n", frame->suffix);98	hid_dbg(hdev, "\t\t.re_lsb = %u\n", frame->re_lsb);99	hid_dbg(hdev, "\t\t.dev_id_byte = %u\n", frame->dev_id_byte);100	hid_dbg(hdev, "\t\t.touch_byte = %u\n", frame->touch_byte);101	hid_dbg(hdev, "\t\t.touch_max = %hhd\n", frame->touch_max);102	hid_dbg(hdev, "\t\t.touch_flip_at = %hhd\n",103		frame->touch_flip_at);104	hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",105		frame->bitmap_dial_byte);106}107 108/**109 * uclogic_params_hid_dbg() - Dump tablet interface parameters110 * @hdev:	The HID device the parameters describe.111 * @params:	The parameters to dump.112 *113 * Dump tablet interface parameters with hid_dbg().114 */115void uclogic_params_hid_dbg(const struct hid_device *hdev,116				const struct uclogic_params *params)117{118	size_t i;119 120	hid_dbg(hdev, ".invalid = %s\n",121		params->invalid ? "true" : "false");122	hid_dbg(hdev, ".desc_ptr = %p\n", params->desc_ptr);123	hid_dbg(hdev, ".desc_size = %u\n", params->desc_size);124	hid_dbg(hdev, ".pen = {\n");125	uclogic_params_pen_hid_dbg(hdev, &params->pen);126	hid_dbg(hdev, "\t}\n");127	hid_dbg(hdev, ".frame_list = {\n");128	for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {129		hid_dbg(hdev, "\t{\n");130		uclogic_params_frame_hid_dbg(hdev, &params->frame_list[i]);131		hid_dbg(hdev, "\t}%s\n",132			i < (ARRAY_SIZE(params->frame_list) - 1) ? "," : "");133	}134	hid_dbg(hdev, "}\n");135}136 137/**138 * uclogic_params_get_str_desc - retrieve a string descriptor from a HID139 * device interface, putting it into a kmalloc-allocated buffer as is, without140 * character encoding conversion.141 *142 * @pbuf:	Location for the kmalloc-allocated buffer pointer containing143 *		the retrieved descriptor. Not modified in case of error.144 *		Can be NULL to have retrieved descriptor discarded.145 * @hdev:	The HID device of the tablet interface to retrieve the string146 *		descriptor from. Cannot be NULL.147 * @idx:	Index of the string descriptor to request from the device.148 * @len:	Length of the buffer to allocate and the data to retrieve.149 *150 * Returns:151 *	number of bytes retrieved (<= len),152 *	-EPIPE, if the descriptor was not found, or153 *	another negative errno code in case of other error.154 */155static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,156					__u8 idx, size_t len)157{158	int rc;159	struct usb_device *udev;160	__u8 *buf = NULL;161 162	/* Check arguments */163	if (hdev == NULL) {164		rc = -EINVAL;165		goto cleanup;166	}167 168	udev = hid_to_usb_dev(hdev);169 170	buf = kmalloc(len, GFP_KERNEL);171	if (buf == NULL) {172		rc = -ENOMEM;173		goto cleanup;174	}175 176	rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),177				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,178				(USB_DT_STRING << 8) + idx,179				0x0409, buf, len,180				USB_CTRL_GET_TIMEOUT);181	if (rc == -EPIPE) {182		hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);183		goto cleanup;184	} else if (rc < 0) {185		hid_err(hdev,186			"failed retrieving string descriptor #%u: %d\n",187			idx, rc);188		goto cleanup;189	}190 191	if (pbuf != NULL) {192		*pbuf = buf;193		buf = NULL;194	}195 196cleanup:197	kfree(buf);198	return rc;199}200 201/**202 * uclogic_params_pen_cleanup - free resources used by struct203 * uclogic_params_pen (tablet interface's pen input parameters).204 * Can be called repeatedly.205 *206 * @pen:	Pen input parameters to cleanup. Cannot be NULL.207 */208static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)209{210	kfree(pen->desc_ptr);211	memset(pen, 0, sizeof(*pen));212}213 214/**215 * uclogic_params_pen_init_v1() - initialize tablet interface pen216 * input and retrieve its parameters from the device, using v1 protocol.217 *218 * @pen:	Pointer to the pen parameters to initialize (to be219 *		cleaned up with uclogic_params_pen_cleanup()). Not modified in220 *		case of error, or if parameters are not found. Cannot be NULL.221 * @pfound:	Location for a flag which is set to true if the parameters222 *		were found, and to false if not (e.g. device was223 *		incompatible). Not modified in case of error. Cannot be NULL.224 * @hdev:	The HID device of the tablet interface to initialize and get225 *		parameters from. Cannot be NULL.226 *227 * Returns:228 *	Zero, if successful. A negative errno code on error.229 */230static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen,231				      bool *pfound,232				      struct hid_device *hdev)233{234	int rc;235	bool found = false;236	/* Buffer for (part of) the string descriptor */237	__u8 *buf = NULL;238	/* Minimum descriptor length required, maximum seen so far is 18 */239	const int len = 12;240	s32 resolution;241	/* Pen report descriptor template parameters */242	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];243	__u8 *desc_ptr = NULL;244 245	/* Check arguments */246	if (pen == NULL || pfound == NULL || hdev == NULL) {247		rc = -EINVAL;248		goto cleanup;249	}250 251	/*252	 * Read string descriptor containing pen input parameters.253	 * The specific string descriptor and data were discovered by sniffing254	 * the Windows driver traffic.255	 * NOTE: This enables fully-functional tablet mode.256	 */257	rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);258	if (rc == -EPIPE) {259		hid_dbg(hdev,260			"string descriptor with pen parameters not found, assuming not compatible\n");261		goto finish;262	} else if (rc < 0) {263		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);264		goto cleanup;265	} else if (rc != len) {266		hid_dbg(hdev,267			"string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",268			rc, len);269		goto finish;270	}271 272	/*273	 * Fill report descriptor parameters from the string descriptor274	 */275	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =276		get_unaligned_le16(buf + 2);277	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =278		get_unaligned_le16(buf + 4);279	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =280		get_unaligned_le16(buf + 8);281	resolution = get_unaligned_le16(buf + 10);282	if (resolution == 0) {283		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;284		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;285	} else {286		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =287			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /288			resolution;289		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =290			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /291			resolution;292	}293	kfree(buf);294	buf = NULL;295 296	/*297	 * Generate pen report descriptor298	 */299	desc_ptr = uclogic_rdesc_template_apply(300				uclogic_rdesc_v1_pen_template_arr,301				uclogic_rdesc_v1_pen_template_size,302				desc_params, ARRAY_SIZE(desc_params));303	if (desc_ptr == NULL) {304		rc = -ENOMEM;305		goto cleanup;306	}307 308	/*309	 * Fill-in the parameters310	 */311	memset(pen, 0, sizeof(*pen));312	pen->desc_ptr = desc_ptr;313	desc_ptr = NULL;314	pen->desc_size = uclogic_rdesc_v1_pen_template_size;315	pen->id = UCLOGIC_RDESC_V1_PEN_ID;316	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;317	found = true;318finish:319	*pfound = found;320	rc = 0;321cleanup:322	kfree(desc_ptr);323	kfree(buf);324	return rc;325}326 327/**328 * uclogic_params_get_le24() - get a 24-bit little-endian number from a329 * buffer.330 *331 * @p:	The pointer to the number buffer.332 *333 * Returns:334 *	The retrieved number335 */336static s32 uclogic_params_get_le24(const void *p)337{338	const __u8 *b = p;339	return b[0] | (b[1] << 8UL) | (b[2] << 16UL);340}341 342/**343 * uclogic_params_pen_init_v2() - initialize tablet interface pen344 * input and retrieve its parameters from the device, using v2 protocol.345 *346 * @pen:		Pointer to the pen parameters to initialize (to be347 *			cleaned up with uclogic_params_pen_cleanup()). Not348 *			modified in case of error, or if parameters are not349 *			found. Cannot be NULL.350 * @pfound:		Location for a flag which is set to true if the351 *			parameters were found, and to false if not (e.g.352 *			device was incompatible). Not modified in case of353 *			error. Cannot be NULL.354 * @pparams_ptr:	Location for a kmalloc'ed pointer to the retrieved raw355 *			parameters, which could be used to identify the tablet356 *			to some extent. Should be freed with kfree after use.357 *			NULL, if not needed. Not modified in case of error.358 *			Only set if *pfound is set to true.359 * @pparams_len:	Location for the length of the retrieved raw360 *			parameters. NULL, if not needed. Not modified in case361 *			of error. Only set if *pfound is set to true.362 * @hdev:		The HID device of the tablet interface to initialize363 *			and get parameters from. Cannot be NULL.364 *365 * Returns:366 *	Zero, if successful. A negative errno code on error.367 */368static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen,369					bool *pfound,370					__u8 **pparams_ptr,371					size_t *pparams_len,372					struct hid_device *hdev)373{374	int rc;375	bool found = false;376	/* Buffer for (part of) the parameter string descriptor */377	__u8 *buf = NULL;378	/* Parameter string descriptor required length */379	const int params_len_min = 18;380	/* Parameter string descriptor accepted length */381	const int params_len_max = 32;382	/* Parameter string descriptor received length */383	int params_len;384	size_t i;385	s32 resolution;386	/* Pen report descriptor template parameters */387	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];388	__u8 *desc_ptr = NULL;389 390	/* Check arguments */391	if (pen == NULL || pfound == NULL || hdev == NULL) {392		rc = -EINVAL;393		goto cleanup;394	}395 396	/*397	 * Read string descriptor containing pen input parameters.398	 * The specific string descriptor and data were discovered by sniffing399	 * the Windows driver traffic.400	 * NOTE: This enables fully-functional tablet mode.401	 */402	rc = uclogic_params_get_str_desc(&buf, hdev, 200, params_len_max);403	if (rc == -EPIPE) {404		hid_dbg(hdev,405			"string descriptor with pen parameters not found, assuming not compatible\n");406		goto finish;407	} else if (rc < 0) {408		hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);409		goto cleanup;410	} else if (rc < params_len_min) {411		hid_dbg(hdev,412			"string descriptor with pen parameters is too short (got %d, expected at least %d), assuming not compatible\n",413			rc, params_len_min);414		goto finish;415	}416 417	params_len = rc;418 419	/*420	 * Check it's not just a catch-all UTF-16LE-encoded ASCII421	 * string (such as the model name) some tablets put into all422	 * unknown string descriptors.423	 */424	for (i = 2;425	     i < params_len &&426		(buf[i] >= 0x20 && buf[i] < 0x7f && buf[i + 1] == 0);427	     i += 2);428	if (i >= params_len) {429		hid_dbg(hdev,430			"string descriptor with pen parameters seems to contain only text, assuming not compatible\n");431		goto finish;432	}433 434	/*435	 * Fill report descriptor parameters from the string descriptor436	 */437	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =438		uclogic_params_get_le24(buf + 2);439	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =440		uclogic_params_get_le24(buf + 5);441	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =442		get_unaligned_le16(buf + 8);443	resolution = get_unaligned_le16(buf + 10);444	if (resolution == 0) {445		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;446		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;447	} else {448		desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =449			desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /450			resolution;451		desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =452			desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /453			resolution;454	}455 456	/*457	 * Generate pen report descriptor458	 */459	desc_ptr = uclogic_rdesc_template_apply(460				uclogic_rdesc_v2_pen_template_arr,461				uclogic_rdesc_v2_pen_template_size,462				desc_params, ARRAY_SIZE(desc_params));463	if (desc_ptr == NULL) {464		rc = -ENOMEM;465		goto cleanup;466	}467 468	/*469	 * Fill-in the parameters470	 */471	memset(pen, 0, sizeof(*pen));472	pen->desc_ptr = desc_ptr;473	desc_ptr = NULL;474	pen->desc_size = uclogic_rdesc_v2_pen_template_size;475	pen->id = UCLOGIC_RDESC_V2_PEN_ID;476	pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_NONE;477	pen->fragmented_hires = true;478	pen->tilt_y_flipped = true;479	found = true;480	if (pparams_ptr != NULL) {481		*pparams_ptr = buf;482		buf = NULL;483	}484	if (pparams_len != NULL)485		*pparams_len = params_len;486 487finish:488	*pfound = found;489	rc = 0;490cleanup:491	kfree(desc_ptr);492	kfree(buf);493	return rc;494}495 496/**497 * uclogic_params_frame_cleanup - free resources used by struct498 * uclogic_params_frame (tablet interface's frame controls input parameters).499 * Can be called repeatedly.500 *501 * @frame:	Frame controls input parameters to cleanup. Cannot be NULL.502 */503static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)504{505	kfree(frame->desc_ptr);506	memset(frame, 0, sizeof(*frame));507}508 509/**510 * uclogic_params_frame_init_with_desc() - initialize tablet's frame control511 * parameters with a static report descriptor.512 *513 * @frame:	Pointer to the frame parameters to initialize (to be cleaned514 *		up with uclogic_params_frame_cleanup()). Not modified in case515 *		of error. Cannot be NULL.516 * @desc_ptr:	Report descriptor pointer. Can be NULL, if desc_size is zero.517 * @desc_size:	Report descriptor size.518 * @id:		Report ID used for frame reports, if they should be tweaked,519 *		zero if not.520 *521 * Returns:522 *	Zero, if successful. A negative errno code on error.523 */524static int uclogic_params_frame_init_with_desc(525					struct uclogic_params_frame *frame,526					const __u8 *desc_ptr,527					size_t desc_size,528					unsigned int id)529{530	__u8 *copy_desc_ptr;531 532	if (frame == NULL || (desc_ptr == NULL && desc_size != 0))533		return -EINVAL;534 535	copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);536	if (copy_desc_ptr == NULL)537		return -ENOMEM;538 539	memset(frame, 0, sizeof(*frame));540	frame->desc_ptr = copy_desc_ptr;541	frame->desc_size = desc_size;542	frame->id = id;543	return 0;544}545 546/**547 * uclogic_params_frame_init_v1() - initialize v1 tablet interface frame548 * controls.549 *550 * @frame:	Pointer to the frame parameters to initialize (to be cleaned551 *		up with uclogic_params_frame_cleanup()). Not modified in case552 *		of error, or if parameters are not found. Cannot be NULL.553 * @pfound:	Location for a flag which is set to true if the parameters554 *		were found, and to false if not (e.g. device was555 *		incompatible). Not modified in case of error. Cannot be NULL.556 * @hdev:	The HID device of the tablet interface to initialize and get557 *		parameters from. Cannot be NULL.558 *559 * Returns:560 *	Zero, if successful. A negative errno code on error.561 */562static int uclogic_params_frame_init_v1(struct uclogic_params_frame *frame,563					bool *pfound,564					struct hid_device *hdev)565{566	int rc;567	bool found = false;568	struct usb_device *usb_dev;569	char *str_buf = NULL;570	const size_t str_len = 16;571 572	/* Check arguments */573	if (frame == NULL || pfound == NULL || hdev == NULL) {574		rc = -EINVAL;575		goto cleanup;576	}577 578	usb_dev = hid_to_usb_dev(hdev);579 580	/*581	 * Enable generic button mode582	 */583	str_buf = kzalloc(str_len, GFP_KERNEL);584	if (str_buf == NULL) {585		rc = -ENOMEM;586		goto cleanup;587	}588 589	rc = usb_string(usb_dev, 123, str_buf, str_len);590	if (rc == -EPIPE) {591		hid_dbg(hdev,592			"generic button -enabling string descriptor not found\n");593	} else if (rc < 0) {594		goto cleanup;595	} else if (strncmp(str_buf, "HK On", rc) != 0) {596		hid_dbg(hdev,597			"invalid response to enabling generic buttons: \"%s\"\n",598			str_buf);599	} else {600		hid_dbg(hdev, "generic buttons enabled\n");601		rc = uclogic_params_frame_init_with_desc(602				frame,603				uclogic_rdesc_v1_frame_arr,604				uclogic_rdesc_v1_frame_size,605				UCLOGIC_RDESC_V1_FRAME_ID);606		if (rc != 0)607			goto cleanup;608		found = true;609	}610 611	*pfound = found;612	rc = 0;613cleanup:614	kfree(str_buf);615	return rc;616}617 618/**619 * uclogic_params_cleanup_event_hooks - free resources used by the list of raw620 * event hooks.621 * Can be called repeatedly.622 *623 * @params: Input parameters to cleanup. Cannot be NULL.624 */625static void uclogic_params_cleanup_event_hooks(struct uclogic_params *params)626{627	struct uclogic_raw_event_hook *curr, *n;628 629	if (!params || !params->event_hooks)630		return;631 632	list_for_each_entry_safe(curr, n, &params->event_hooks->list, list) {633		cancel_work_sync(&curr->work);634		list_del(&curr->list);635		kfree(curr->event);636		kfree(curr);637	}638 639	kfree(params->event_hooks);640	params->event_hooks = NULL;641}642 643/**644 * uclogic_params_cleanup - free resources used by struct uclogic_params645 * (tablet interface's parameters).646 * Can be called repeatedly.647 *648 * @params:	Input parameters to cleanup. Cannot be NULL.649 */650void uclogic_params_cleanup(struct uclogic_params *params)651{652	if (!params->invalid) {653		size_t i;654		kfree(params->desc_ptr);655		uclogic_params_pen_cleanup(&params->pen);656		for (i = 0; i < ARRAY_SIZE(params->frame_list); i++)657			uclogic_params_frame_cleanup(&params->frame_list[i]);658 659		uclogic_params_cleanup_event_hooks(params);660		memset(params, 0, sizeof(*params));661	}662}663 664/**665 * uclogic_params_get_desc() - Get a replacement report descriptor for a666 *                             tablet's interface.667 *668 * @params:	The parameters of a tablet interface to get report669 *		descriptor for. Cannot be NULL.670 * @pdesc:	Location for the resulting, kmalloc-allocated report671 *		descriptor pointer, or for NULL, if there's no replacement672 *		report descriptor. Not modified in case of error. Cannot be673 *		NULL.674 * @psize:	Location for the resulting report descriptor size, not set if675 *		there's no replacement report descriptor. Not modified in case676 *		of error. Cannot be NULL.677 *678 * Returns:679 *	Zero, if successful.680 *	-EINVAL, if invalid arguments are supplied.681 *	-ENOMEM, if failed to allocate memory.682 */683int uclogic_params_get_desc(const struct uclogic_params *params,684				const __u8 **pdesc,685				unsigned int *psize)686{687	int rc = -ENOMEM;688	bool present = false;689	unsigned int size = 0;690	__u8 *desc = NULL;691	size_t i;692 693	/* Check arguments */694	if (params == NULL || pdesc == NULL || psize == NULL)695		return -EINVAL;696 697	/* Concatenate descriptors */698#define ADD_DESC(_desc_ptr, _desc_size) \699	do {                                                        \700		unsigned int new_size;                              \701		__u8 *new_desc;                                     \702		if ((_desc_ptr) == NULL) {                          \703			break;                                      \704		}                                                   \705		new_size = size + (_desc_size);                     \706		new_desc = krealloc(desc, new_size, GFP_KERNEL);    \707		if (new_desc == NULL) {                             \708			goto cleanup;                               \709		}                                                   \710		memcpy(new_desc + size, (_desc_ptr), (_desc_size)); \711		desc = new_desc;                                    \712		size = new_size;                                    \713		present = true;                                     \714	} while (0)715 716	ADD_DESC(params->desc_ptr, params->desc_size);717	ADD_DESC(params->pen.desc_ptr, params->pen.desc_size);718	for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {719		ADD_DESC(params->frame_list[i].desc_ptr,720				params->frame_list[i].desc_size);721	}722 723#undef ADD_DESC724 725	if (present) {726		*pdesc = desc;727		*psize = size;728		desc = NULL;729	}730	rc = 0;731cleanup:732	kfree(desc);733	return rc;734}735 736/**737 * uclogic_params_init_invalid() - initialize tablet interface parameters,738 * specifying the interface is invalid.739 *740 * @params:		Parameters to initialize (to be cleaned with741 *			uclogic_params_cleanup()). Cannot be NULL.742 */743static void uclogic_params_init_invalid(struct uclogic_params *params)744{745	params->invalid = true;746}747 748/**749 * uclogic_params_init_with_opt_desc() - initialize tablet interface750 * parameters with an optional replacement report descriptor. Only modify751 * report descriptor, if the original report descriptor matches the expected752 * size.753 *754 * @params:		Parameters to initialize (to be cleaned with755 *			uclogic_params_cleanup()). Not modified in case of756 *			error. Cannot be NULL.757 * @hdev:		The HID device of the tablet interface create the758 *			parameters for. Cannot be NULL.759 * @orig_desc_size:	Expected size of the original report descriptor to760 *			be replaced.761 * @desc_ptr:		Pointer to the replacement report descriptor.762 *			Can be NULL, if desc_size is zero.763 * @desc_size:		Size of the replacement report descriptor.764 *765 * Returns:766 *	Zero, if successful. -EINVAL if an invalid argument was passed.767 *	-ENOMEM, if failed to allocate memory.768 */769static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,770					     struct hid_device *hdev,771					     unsigned int orig_desc_size,772					     const __u8 *desc_ptr,773					     unsigned int desc_size)774{775	__u8 *desc_copy_ptr = NULL;776	unsigned int desc_copy_size;777	int rc;778 779	/* Check arguments */780	if (params == NULL || hdev == NULL ||781	    (desc_ptr == NULL && desc_size != 0)) {782		rc = -EINVAL;783		goto cleanup;784	}785 786	/* Replace report descriptor, if it matches */787	if (hdev->dev_rsize == orig_desc_size) {788		hid_dbg(hdev,789			"device report descriptor matches the expected size, replacing\n");790		desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);791		if (desc_copy_ptr == NULL) {792			rc = -ENOMEM;793			goto cleanup;794		}795		desc_copy_size = desc_size;796	} else {797		hid_dbg(hdev,798			"device report descriptor doesn't match the expected size (%u != %u), preserving\n",799			hdev->dev_rsize, orig_desc_size);800		desc_copy_ptr = NULL;801		desc_copy_size = 0;802	}803 804	/* Output parameters */805	memset(params, 0, sizeof(*params));806	params->desc_ptr = desc_copy_ptr;807	desc_copy_ptr = NULL;808	params->desc_size = desc_copy_size;809 810	rc = 0;811cleanup:812	kfree(desc_copy_ptr);813	return rc;814}815 816/**817 * uclogic_params_huion_init() - initialize a Huion tablet interface and discover818 * its parameters.819 *820 * @params:	Parameters to fill in (to be cleaned with821 *		uclogic_params_cleanup()). Not modified in case of error.822 *		Cannot be NULL.823 * @hdev:	The HID device of the tablet interface to initialize and get824 *		parameters from. Cannot be NULL.825 *826 * Returns:827 *	Zero, if successful. A negative errno code on error.828 */829static int uclogic_params_huion_init(struct uclogic_params *params,830				     struct hid_device *hdev)831{832	int rc;833	struct usb_device *udev;834	struct usb_interface *iface;835	__u8 bInterfaceNumber;836	bool found;837	/* The resulting parameters (noop) */838	struct uclogic_params p = {0, };839	static const char transition_ver[] = "HUION_T153_160607";840	char *ver_ptr = NULL;841	const size_t ver_len = sizeof(transition_ver) + 1;842	__u8 *params_ptr = NULL;843	size_t params_len = 0;844	/* Parameters string descriptor of a model with touch ring (HS610) */845	const __u8 touch_ring_model_params_buf[] = {846		0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00,847		0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01,848		0x04, 0x3C, 0x3E849	};850 851	/* Check arguments */852	if (params == NULL || hdev == NULL) {853		rc = -EINVAL;854		goto cleanup;855	}856 857	udev = hid_to_usb_dev(hdev);858	iface = to_usb_interface(hdev->dev.parent);859	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;860 861	/* If it's a custom keyboard interface */862	if (bInterfaceNumber == 1) {863		/* Keep everything intact, but mark pen usage invalid */864		p.pen.usage_invalid = true;865		goto output;866	/* Else, if it's not a pen interface */867	} else if (bInterfaceNumber != 0) {868		uclogic_params_init_invalid(&p);869		goto output;870	}871 872	/* Try to get firmware version */873	ver_ptr = kzalloc(ver_len, GFP_KERNEL);874	if (ver_ptr == NULL) {875		rc = -ENOMEM;876		goto cleanup;877	}878	rc = usb_string(udev, 201, ver_ptr, ver_len);879	if (rc == -EPIPE) {880		*ver_ptr = '\0';881	} else if (rc < 0) {882		hid_err(hdev,883			"failed retrieving Huion firmware version: %d\n", rc);884		goto cleanup;885	}886 887	/* The firmware is used in userspace as unique identifier */888	strscpy(hdev->uniq, ver_ptr, sizeof(hdev->uniq));889 890	/* If this is a transition firmware */891	if (strcmp(ver_ptr, transition_ver) == 0) {892		hid_dbg(hdev,893			"transition firmware detected, not probing pen v2 parameters\n");894	} else {895		/* Try to probe v2 pen parameters */896		rc = uclogic_params_pen_init_v2(&p.pen, &found,897						&params_ptr, &params_len,898						hdev);899		if (rc != 0) {900			hid_err(hdev,901				"failed probing pen v2 parameters: %d\n", rc);902			goto cleanup;903		} else if (found) {904			hid_dbg(hdev, "pen v2 parameters found\n");905			/* Create v2 frame button parameters */906			rc = uclogic_params_frame_init_with_desc(907					&p.frame_list[0],908					uclogic_rdesc_v2_frame_buttons_arr,909					uclogic_rdesc_v2_frame_buttons_size,910					UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID);911			if (rc != 0) {912				hid_err(hdev,913					"failed creating v2 frame button parameters: %d\n",914					rc);915				goto cleanup;916			}917 918			/* Link from pen sub-report */919			p.pen.subreport_list[0].value = 0xe0;920			p.pen.subreport_list[0].id =921				UCLOGIC_RDESC_V2_FRAME_BUTTONS_ID;922 923			/* If this is the model with touch ring */924			if (params_ptr != NULL &&925			    params_len == sizeof(touch_ring_model_params_buf) &&926			    memcmp(params_ptr, touch_ring_model_params_buf,927				   params_len) == 0) {928				/* Create touch ring parameters */929				rc = uclogic_params_frame_init_with_desc(930					&p.frame_list[1],931					uclogic_rdesc_v2_frame_touch_ring_arr,932					uclogic_rdesc_v2_frame_touch_ring_size,933					UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);934				if (rc != 0) {935					hid_err(hdev,936						"failed creating v2 frame touch ring parameters: %d\n",937						rc);938					goto cleanup;939				}940				p.frame_list[1].suffix = "Touch Ring";941				p.frame_list[1].dev_id_byte =942					UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;943				p.frame_list[1].touch_byte = 5;944				p.frame_list[1].touch_max = 12;945				p.frame_list[1].touch_flip_at = 7;946			} else {947				/* Create touch strip parameters */948				rc = uclogic_params_frame_init_with_desc(949					&p.frame_list[1],950					uclogic_rdesc_v2_frame_touch_strip_arr,951					uclogic_rdesc_v2_frame_touch_strip_size,952					UCLOGIC_RDESC_V2_FRAME_TOUCH_ID);953				if (rc != 0) {954					hid_err(hdev,955						"failed creating v2 frame touch strip parameters: %d\n",956						rc);957					goto cleanup;958				}959				p.frame_list[1].suffix = "Touch Strip";960				p.frame_list[1].dev_id_byte =961					UCLOGIC_RDESC_V2_FRAME_TOUCH_DEV_ID_BYTE;962				p.frame_list[1].touch_byte = 5;963				p.frame_list[1].touch_max = 8;964			}965 966			/* Link from pen sub-report */967			p.pen.subreport_list[1].value = 0xf0;968			p.pen.subreport_list[1].id =969				UCLOGIC_RDESC_V2_FRAME_TOUCH_ID;970 971			/* Create v2 frame dial parameters */972			rc = uclogic_params_frame_init_with_desc(973					&p.frame_list[2],974					uclogic_rdesc_v2_frame_dial_arr,975					uclogic_rdesc_v2_frame_dial_size,976					UCLOGIC_RDESC_V2_FRAME_DIAL_ID);977			if (rc != 0) {978				hid_err(hdev,979					"failed creating v2 frame dial parameters: %d\n",980					rc);981				goto cleanup;982			}983			p.frame_list[2].suffix = "Dial";984			p.frame_list[2].dev_id_byte =985				UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE;986			p.frame_list[2].bitmap_dial_byte = 5;987 988			/* Link from pen sub-report */989			p.pen.subreport_list[2].value = 0xf1;990			p.pen.subreport_list[2].id =991				UCLOGIC_RDESC_V2_FRAME_DIAL_ID;992 993			goto output;994		}995		hid_dbg(hdev, "pen v2 parameters not found\n");996	}997 998	/* Try to probe v1 pen parameters */999	rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1000	if (rc != 0) {1001		hid_err(hdev,1002			"failed probing pen v1 parameters: %d\n", rc);1003		goto cleanup;1004	} else if (found) {1005		hid_dbg(hdev, "pen v1 parameters found\n");1006		/* Try to probe v1 frame */1007		rc = uclogic_params_frame_init_v1(&p.frame_list[0],1008						  &found, hdev);1009		if (rc != 0) {1010			hid_err(hdev, "v1 frame probing failed: %d\n", rc);1011			goto cleanup;1012		}1013		hid_dbg(hdev, "frame v1 parameters%s found\n",1014			(found ? "" : " not"));1015		if (found) {1016			/* Link frame button subreports from pen reports */1017			p.pen.subreport_list[0].value = 0xe0;1018			p.pen.subreport_list[0].id =1019				UCLOGIC_RDESC_V1_FRAME_ID;1020		}1021		goto output;1022	}1023	hid_dbg(hdev, "pen v1 parameters not found\n");1024 1025	uclogic_params_init_invalid(&p);1026 1027output:1028	/* Output parameters */1029	memcpy(params, &p, sizeof(*params));1030	memset(&p, 0, sizeof(p));1031	rc = 0;1032cleanup:1033	kfree(params_ptr);1034	kfree(ver_ptr);1035	uclogic_params_cleanup(&p);1036	return rc;1037}1038 1039/**1040 * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or1041 * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data.1042 *1043 * @hdev:	The HID device of the tablet interface to initialize and get1044 *		parameters from. Cannot be NULL.1045 * @magic_arr:	The magic data that should be sent to probe the interface.1046 *		Cannot be NULL.1047 * @magic_size:	Size of the magic data.1048 * @endpoint:	Endpoint where the magic data should be sent.1049 *1050 * Returns:1051 *	Zero, if successful. A negative errno code on error.1052 */1053static int uclogic_probe_interface(struct hid_device *hdev, const u8 *magic_arr,1054				   size_t magic_size, int endpoint)1055{1056	struct usb_device *udev;1057	unsigned int pipe = 0;1058	int sent;1059	u8 *buf = NULL;1060	int rc = 0;1061 1062	if (!hdev || !magic_arr) {1063		rc = -EINVAL;1064		goto cleanup;1065	}1066 1067	buf = kmemdup(magic_arr, magic_size, GFP_KERNEL);1068	if (!buf) {1069		rc = -ENOMEM;1070		goto cleanup;1071	}1072 1073	udev = hid_to_usb_dev(hdev);1074	pipe = usb_sndintpipe(udev, endpoint);1075 1076	rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000);1077	if (rc || sent != magic_size) {1078		hid_err(hdev, "Interface probing failed: %d\n", rc);1079		rc = -1;1080		goto cleanup;1081	}1082 1083	rc = 0;1084cleanup:1085	kfree(buf);1086	return rc;1087}1088 1089/**1090 * uclogic_params_parse_ugee_v2_desc - parse the string descriptor containing1091 * pen and frame parameters returned by UGEE v2 devices.1092 *1093 * @str_desc:		String descriptor, cannot be NULL.1094 * @str_desc_size:	Size of the string descriptor.1095 * @desc_params:	Output description params list.1096 * @desc_params_size:	Size of the output description params list.1097 * @frame_type:		Output frame type.1098 *1099 * Returns:1100 *	Zero, if successful. A negative errno code on error.1101 */1102static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc,1103					     size_t str_desc_size,1104					     s32 *desc_params,1105					     size_t desc_params_size,1106					     enum uclogic_params_frame_type *frame_type)1107{1108	s32 pen_x_lm, pen_y_lm;1109	s32 pen_x_pm, pen_y_pm;1110	s32 pen_pressure_lm;1111	s32 frame_num_buttons;1112	s32 resolution;1113 1114	/* Minimum descriptor length required, maximum seen so far is 14 */1115	const int min_str_desc_size = 12;1116 1117	if (!str_desc || str_desc_size < min_str_desc_size)1118		return -EINVAL;1119 1120	if (desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)1121		return -EINVAL;1122 1123	pen_x_lm = get_unaligned_le16(str_desc + 2);1124	pen_y_lm = get_unaligned_le16(str_desc + 4);1125	frame_num_buttons = str_desc[6];1126	*frame_type = str_desc[7];1127	pen_pressure_lm = get_unaligned_le16(str_desc + 8);1128 1129	resolution = get_unaligned_le16(str_desc + 10);1130	if (resolution == 0) {1131		pen_x_pm = 0;1132		pen_y_pm = 0;1133	} else {1134		pen_x_pm = pen_x_lm * 1000 / resolution;1135		pen_y_pm = pen_y_lm * 1000 / resolution;1136	}1137 1138	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = pen_x_lm;1139	desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = pen_x_pm;1140	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = pen_y_lm;1141	desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = pen_y_pm;1142	desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = pen_pressure_lm;1143	desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = frame_num_buttons;1144 1145	return 0;1146}1147 1148/**1149 * uclogic_params_ugee_v2_init_frame_buttons() - initialize a UGEE v2 frame with1150 * buttons.1151 * @p:			Parameters to fill in, cannot be NULL.1152 * @desc_params:	Device description params list.1153 * @desc_params_size:	Size of the description params list.1154 *1155 * Returns:1156 *	Zero, if successful. A negative errno code on error.1157 */1158static int uclogic_params_ugee_v2_init_frame_buttons(struct uclogic_params *p,1159						     const s32 *desc_params,1160						     size_t desc_params_size)1161{1162	__u8 *rdesc_frame = NULL;1163	int rc = 0;1164 1165	if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)1166		return -EINVAL;1167 1168	rdesc_frame = uclogic_rdesc_template_apply(1169				uclogic_rdesc_ugee_v2_frame_btn_template_arr,1170				uclogic_rdesc_ugee_v2_frame_btn_template_size,1171				desc_params, UCLOGIC_RDESC_PH_ID_NUM);1172	if (!rdesc_frame)1173		return -ENOMEM;1174 1175	rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],1176						 rdesc_frame,1177						 uclogic_rdesc_ugee_v2_frame_btn_template_size,1178						 UCLOGIC_RDESC_V1_FRAME_ID);1179	kfree(rdesc_frame);1180	return rc;1181}1182 1183/**1184 * uclogic_params_ugee_v2_init_frame_dial() - initialize a UGEE v2 frame with a1185 * bitmap dial.1186 * @p:			Parameters to fill in, cannot be NULL.1187 * @desc_params:	Device description params list.1188 * @desc_params_size:	Size of the description params list.1189 *1190 * Returns:1191 *	Zero, if successful. A negative errno code on error.1192 */1193static int uclogic_params_ugee_v2_init_frame_dial(struct uclogic_params *p,1194						  const s32 *desc_params,1195						  size_t desc_params_size)1196{1197	__u8 *rdesc_frame = NULL;1198	int rc = 0;1199 1200	if (!p || desc_params_size != UCLOGIC_RDESC_PH_ID_NUM)1201		return -EINVAL;1202 1203	rdesc_frame = uclogic_rdesc_template_apply(1204				uclogic_rdesc_ugee_v2_frame_dial_template_arr,1205				uclogic_rdesc_ugee_v2_frame_dial_template_size,1206				desc_params, UCLOGIC_RDESC_PH_ID_NUM);1207	if (!rdesc_frame)1208		return -ENOMEM;1209 1210	rc = uclogic_params_frame_init_with_desc(&p->frame_list[0],1211						 rdesc_frame,1212						 uclogic_rdesc_ugee_v2_frame_dial_template_size,1213						 UCLOGIC_RDESC_V1_FRAME_ID);1214	kfree(rdesc_frame);1215	if (rc)1216		return rc;1217 1218	p->frame_list[0].bitmap_dial_byte = 7;1219	return 0;1220}1221 1222/**1223 * uclogic_params_ugee_v2_init_frame_mouse() - initialize a UGEE v2 frame with a1224 * mouse.1225 * @p:			Parameters to fill in, cannot be NULL.1226 *1227 * Returns:1228 *	Zero, if successful. A negative errno code on error.1229 */1230static int uclogic_params_ugee_v2_init_frame_mouse(struct uclogic_params *p)1231{1232	int rc = 0;1233 1234	if (!p)1235		return -EINVAL;1236 1237	rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],1238						 uclogic_rdesc_ugee_v2_frame_mouse_template_arr,1239						 uclogic_rdesc_ugee_v2_frame_mouse_template_size,1240						 UCLOGIC_RDESC_V1_FRAME_ID);1241	return rc;1242}1243 1244/**1245 * uclogic_params_ugee_v2_has_battery() - check whether a UGEE v2 device has1246 * battery or not.1247 * @hdev:	The HID device of the tablet interface.1248 *1249 * Returns:1250 *	True if the device has battery, false otherwise.1251 */1252static bool uclogic_params_ugee_v2_has_battery(struct hid_device *hdev)1253{1254	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);1255 1256	if (drvdata->quirks & UCLOGIC_BATTERY_QUIRK)1257		return true;1258 1259	/* The XP-PEN Deco LW vendor, product and version are identical to the1260	 * Deco L. The only difference reported by their firmware is the product1261	 * name. Add a quirk to support battery reporting on the wireless1262	 * version.1263	 */1264	if (hdev->vendor == USB_VENDOR_ID_UGEE &&1265	    hdev->product == USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) {1266		struct usb_device *udev = hid_to_usb_dev(hdev);1267 1268		if (strstarts(udev->product, "Deco LW"))1269			return true;1270	}1271 1272	return false;1273}1274 1275/**1276 * uclogic_params_ugee_v2_init_battery() - initialize UGEE v2 battery reporting.1277 * @hdev:	The HID device of the tablet interface, cannot be NULL.1278 * @p:		Parameters to fill in, cannot be NULL.1279 *1280 * Returns:1281 *	Zero, if successful. A negative errno code on error.1282 */1283static int uclogic_params_ugee_v2_init_battery(struct hid_device *hdev,1284					       struct uclogic_params *p)1285{1286	int rc = 0;1287 1288	if (!hdev || !p)1289		return -EINVAL;1290 1291	/* Some tablets contain invalid characters in hdev->uniq, throwing a1292	 * "hwmon: '<name>' is not a valid name attribute, please fix" error.1293	 * Use the device vendor and product IDs instead.1294	 */1295	snprintf(hdev->uniq, sizeof(hdev->uniq), "%x-%x", hdev->vendor,1296		 hdev->product);1297 1298	rc = uclogic_params_frame_init_with_desc(&p->frame_list[1],1299						 uclogic_rdesc_ugee_v2_battery_template_arr,1300						 uclogic_rdesc_ugee_v2_battery_template_size,1301						 UCLOGIC_RDESC_UGEE_V2_BATTERY_ID);1302	if (rc)1303		return rc;1304 1305	p->frame_list[1].suffix = "Battery";1306	p->pen.subreport_list[1].value = 0xf2;1307	p->pen.subreport_list[1].id = UCLOGIC_RDESC_UGEE_V2_BATTERY_ID;1308 1309	return rc;1310}1311 1312/**1313 * uclogic_params_ugee_v2_reconnect_work() - When a wireless tablet looses1314 * connection to the USB dongle and reconnects, either because of its physical1315 * distance or because it was switches off and on using the frame's switch,1316 * uclogic_probe_interface() needs to be called again to enable the tablet.1317 *1318 * @work: The work that triggered this function.1319 */1320static void uclogic_params_ugee_v2_reconnect_work(struct work_struct *work)1321{1322	struct uclogic_raw_event_hook *event_hook;1323 1324	event_hook = container_of(work, struct uclogic_raw_event_hook, work);1325	uclogic_probe_interface(event_hook->hdev, uclogic_ugee_v2_probe_arr,1326				uclogic_ugee_v2_probe_size,1327				uclogic_ugee_v2_probe_endpoint);1328}1329 1330/**1331 * uclogic_params_ugee_v2_init_event_hooks() - initialize the list of events1332 * to be hooked for UGEE v2 devices.1333 * @hdev:	The HID device of the tablet interface to initialize and get1334 *		parameters from.1335 * @p:		Parameters to fill in, cannot be NULL.1336 *1337 * Returns:1338 *	Zero, if successful. A negative errno code on error.1339 */1340static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev,1341						   struct uclogic_params *p)1342{1343	struct uclogic_raw_event_hook *event_hook;1344	__u8 reconnect_event[] = {1345		/* Event received on wireless tablet reconnection */1346		0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x001347	};1348 1349	if (!p)1350		return -EINVAL;1351 1352	/* The reconnection event is only received if the tablet has battery */1353	if (!uclogic_params_ugee_v2_has_battery(hdev))1354		return 0;1355 1356	p->event_hooks = kzalloc(sizeof(*p->event_hooks), GFP_KERNEL);1357	if (!p->event_hooks)1358		return -ENOMEM;1359 1360	INIT_LIST_HEAD(&p->event_hooks->list);1361 1362	event_hook = kzalloc(sizeof(*event_hook), GFP_KERNEL);1363	if (!event_hook)1364		return -ENOMEM;1365 1366	INIT_WORK(&event_hook->work, uclogic_params_ugee_v2_reconnect_work);1367	event_hook->hdev = hdev;1368	event_hook->size = ARRAY_SIZE(reconnect_event);1369	event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL);1370	if (!event_hook->event)1371		return -ENOMEM;1372 1373	list_add_tail(&event_hook->list, &p->event_hooks->list);1374 1375	return 0;1376}1377 1378/**1379 * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by1380 * discovering their parameters.1381 *1382 * These tables, internally designed as v2 to differentiate them from older1383 * models, expect a payload of magic data in orther to be switched to the fully1384 * functional mode and expose their parameters in a similar way to the1385 * information present in uclogic_params_pen_init_v1() but with some1386 * differences.1387 *1388 * @params:	Parameters to fill in (to be cleaned with1389 *		uclogic_params_cleanup()). Not modified in case of error.1390 *		Cannot be NULL.1391 * @hdev:	The HID device of the tablet interface to initialize and get1392 *		parameters from. Cannot be NULL.1393 *1394 * Returns:1395 *	Zero, if successful. A negative errno code on error.1396 */1397static int uclogic_params_ugee_v2_init(struct uclogic_params *params,1398				       struct hid_device *hdev)1399{1400	int rc = 0;1401	struct uclogic_drvdata *drvdata;1402	struct usb_interface *iface;1403	__u8 bInterfaceNumber;1404	const int str_desc_len = 12;1405	__u8 *str_desc = NULL;1406	__u8 *rdesc_pen = NULL;1407	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];1408	enum uclogic_params_frame_type frame_type;1409	/* The resulting parameters (noop) */1410	struct uclogic_params p = {0, };1411 1412	if (!params || !hdev) {1413		rc = -EINVAL;1414		goto cleanup;1415	}1416 1417	drvdata = hid_get_drvdata(hdev);1418	iface = to_usb_interface(hdev->dev.parent);1419	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;1420 1421	if (bInterfaceNumber == 0) {1422		rc = uclogic_params_ugee_v2_init_frame_mouse(&p);1423		if (rc)1424			goto cleanup;1425 1426		goto output;1427	}1428 1429	if (bInterfaceNumber != 2) {1430		uclogic_params_init_invalid(&p);1431		goto output;1432	}1433 1434	/*1435	 * Initialize the interface by sending magic data.1436	 * The specific data was discovered by sniffing the Windows driver1437	 * traffic.1438	 */1439	rc = uclogic_probe_interface(hdev, uclogic_ugee_v2_probe_arr,1440				     uclogic_ugee_v2_probe_size,1441				     uclogic_ugee_v2_probe_endpoint);1442	if (rc) {1443		uclogic_params_init_invalid(&p);1444		goto output;1445	}1446 1447	/*1448	 * Read the string descriptor containing pen and frame parameters.1449	 * The specific string descriptor and data were discovered by sniffing1450	 * the Windows driver traffic.1451	 */1452	rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);1453	if (rc != str_desc_len) {1454		hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);1455		uclogic_params_init_invalid(&p);1456		goto output;1457	}1458 1459	rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len,1460					       desc_params,1461					       ARRAY_SIZE(desc_params),1462					       &frame_type);1463	if (rc)1464		goto cleanup;1465 1466	kfree(str_desc);1467	str_desc = NULL;1468 1469	/* Initialize the pen interface */1470	rdesc_pen = uclogic_rdesc_template_apply(1471				uclogic_rdesc_ugee_v2_pen_template_arr,1472				uclogic_rdesc_ugee_v2_pen_template_size,1473				desc_params, ARRAY_SIZE(desc_params));1474	if (!rdesc_pen) {1475		rc = -ENOMEM;1476		goto cleanup;1477	}1478 1479	p.pen.desc_ptr = rdesc_pen;1480	p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;1481	p.pen.id = 0x02;1482	p.pen.subreport_list[0].value = 0xf0;1483	p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;1484 1485	/* Initialize the frame interface */1486	if (drvdata->quirks & UCLOGIC_MOUSE_FRAME_QUIRK)1487		frame_type = UCLOGIC_PARAMS_FRAME_MOUSE;1488 1489	switch (frame_type) {1490	case UCLOGIC_PARAMS_FRAME_DIAL:1491	case UCLOGIC_PARAMS_FRAME_MOUSE:1492		rc = uclogic_params_ugee_v2_init_frame_dial(&p, desc_params,1493							    ARRAY_SIZE(desc_params));1494		break;1495	case UCLOGIC_PARAMS_FRAME_BUTTONS:1496	default:1497		rc = uclogic_params_ugee_v2_init_frame_buttons(&p, desc_params,1498							       ARRAY_SIZE(desc_params));1499		break;1500	}1501 1502	if (rc)1503		goto cleanup;1504 1505	/* Initialize the battery interface*/1506	if (uclogic_params_ugee_v2_has_battery(hdev)) {1507		rc = uclogic_params_ugee_v2_init_battery(hdev, &p);1508		if (rc) {1509			hid_err(hdev, "error initializing battery: %d\n", rc);1510			goto cleanup;1511		}1512	}1513 1514	/* Create a list of raw events to be ignored */1515	rc = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);1516	if (rc) {1517		hid_err(hdev, "error initializing event hook list: %d\n", rc);1518		goto cleanup;1519	}1520 1521output:1522	/* Output parameters */1523	memcpy(params, &p, sizeof(*params));1524	memset(&p, 0, sizeof(p));1525	rc = 0;1526cleanup:1527	kfree(str_desc);1528	uclogic_params_cleanup(&p);1529	return rc;1530}1531 1532/**1533 * uclogic_params_init() - initialize a tablet interface and discover its1534 * parameters.1535 *1536 * @params:	Parameters to fill in (to be cleaned with1537 *		uclogic_params_cleanup()). Not modified in case of error.1538 *		Cannot be NULL.1539 * @hdev:	The HID device of the tablet interface to initialize and get1540 *		parameters from. Cannot be NULL. Must be using the USB low-level1541 *		driver, i.e. be an actual USB tablet.1542 *1543 * Returns:1544 *	Zero, if successful. A negative errno code on error.1545 */1546int uclogic_params_init(struct uclogic_params *params,1547			struct hid_device *hdev)1548{1549	int rc;1550	struct usb_device *udev;1551	__u8  bNumInterfaces;1552	struct usb_interface *iface;1553	__u8 bInterfaceNumber;1554	bool found;1555	/* The resulting parameters (noop) */1556	struct uclogic_params p = {0, };1557 1558	/* Check arguments */1559	if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {1560		rc = -EINVAL;1561		goto cleanup;1562	}1563 1564	udev = hid_to_usb_dev(hdev);1565	bNumInterfaces = udev->config->desc.bNumInterfaces;1566	iface = to_usb_interface(hdev->dev.parent);1567	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;1568 1569	/*1570	 * Set replacement report descriptor if the original matches the1571	 * specified size. Otherwise keep interface unchanged.1572	 */1573#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \1574	uclogic_params_init_with_opt_desc(                  \1575		&p, hdev,                                   \1576		UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \1577		uclogic_rdesc_##_new_desc_token##_arr,      \1578		uclogic_rdesc_##_new_desc_token##_size)1579 1580#define VID_PID(_vid, _pid) \1581	(((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))1582 1583	/*1584	 * Handle specific interfaces for specific tablets.1585	 *1586	 * Observe the following logic:1587	 *1588	 * If the interface is recognized as producing certain useful input:1589	 *	Mark interface as valid.1590	 *	Output interface parameters.1591	 * Else, if the interface is recognized as *not* producing any useful1592	 * input:1593	 *	Mark interface as invalid.1594	 * Else:1595	 *	Mark interface as valid.1596	 *	Output noop parameters.1597	 *1598	 * Rule of thumb: it is better to disable a broken interface than let1599	 *		  it spew garbage input.1600	 */1601 1602	switch (VID_PID(hdev->vendor, hdev->product)) {1603	case VID_PID(USB_VENDOR_ID_UCLOGIC,1604		     USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):1605		rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);1606		if (rc != 0)1607			goto cleanup;1608		break;1609	case VID_PID(USB_VENDOR_ID_UCLOGIC,1610		     USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):1611		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);1612		if (rc != 0)1613			goto cleanup;1614		break;1615	case VID_PID(USB_VENDOR_ID_UCLOGIC,1616		     USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):1617		if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {1618			if (bInterfaceNumber == 0) {1619				/* Try to probe v1 pen parameters */1620				rc = uclogic_params_pen_init_v1(&p.pen,1621								&found, hdev);1622				if (rc != 0) {1623					hid_err(hdev,1624						"pen probing failed: %d\n",1625						rc);1626					goto cleanup;1627				}1628				if (!found) {1629					hid_warn(hdev,1630						 "pen parameters not found");1631				}1632			} else {1633				uclogic_params_init_invalid(&p);1634			}1635		} else {1636			rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);1637			if (rc != 0)1638				goto cleanup;1639		}1640		break;1641	case VID_PID(USB_VENDOR_ID_UCLOGIC,1642		     USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):1643		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);1644		if (rc != 0)1645			goto cleanup;1646		break;1647	case VID_PID(USB_VENDOR_ID_UCLOGIC,1648		     USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):1649		rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);1650		if (rc != 0)1651			goto cleanup;1652		break;1653	case VID_PID(USB_VENDOR_ID_UCLOGIC,1654		     USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):1655		switch (bInterfaceNumber) {1656		case 0:1657			rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);1658			if (rc != 0)1659				goto cleanup;1660			break;1661		case 1:1662			rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);1663			if (rc != 0)1664				goto cleanup;1665			break;1666		case 2:1667			rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);1668			if (rc != 0)1669				goto cleanup;1670			break;1671		}1672		break;1673	case VID_PID(USB_VENDOR_ID_UCLOGIC,1674		     USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):1675		/*1676		 * If it is not a three-interface version, which is known to1677		 * respond to initialization.1678		 */1679		if (bNumInterfaces != 3) {1680			switch (bInterfaceNumber) {1681			case 0:1682				rc = WITH_OPT_DESC(TWHA60_ORIG0,1683							twha60_fixed0);1684				if (rc != 0)1685					goto cleanup;1686				break;1687			case 1:1688				rc = WITH_OPT_DESC(TWHA60_ORIG1,1689							twha60_fixed1);1690				if (rc != 0)1691					goto cleanup;1692				break;1693			}1694			break;1695		}1696		fallthrough;1697	case VID_PID(USB_VENDOR_ID_HUION,1698		     USB_DEVICE_ID_HUION_TABLET):1699	case VID_PID(USB_VENDOR_ID_HUION,1700		     USB_DEVICE_ID_HUION_TABLET2):1701	case VID_PID(USB_VENDOR_ID_UCLOGIC,1702		     USB_DEVICE_ID_HUION_TABLET):1703	case VID_PID(USB_VENDOR_ID_UCLOGIC,1704		     USB_DEVICE_ID_YIYNOVA_TABLET):1705	case VID_PID(USB_VENDOR_ID_UCLOGIC,1706		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):1707	case VID_PID(USB_VENDOR_ID_UCLOGIC,1708		     USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):1709	case VID_PID(USB_VENDOR_ID_UCLOGIC,1710		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):1711	case VID_PID(USB_VENDOR_ID_UCLOGIC,1712		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):1713		rc = uclogic_params_huion_init(&p, hdev);1714		if (rc != 0)1715			goto cleanup;1716		break;1717	case VID_PID(USB_VENDOR_ID_UGTIZER,1718		     USB_DEVICE_ID_UGTIZER_TABLET_GP0610):1719	case VID_PID(USB_VENDOR_ID_UGTIZER,1720		     USB_DEVICE_ID_UGTIZER_TABLET_GT5040):1721	case VID_PID(USB_VENDOR_ID_UGEE,1722		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):1723	case VID_PID(USB_VENDOR_ID_UGEE,1724		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):1725	case VID_PID(USB_VENDOR_ID_UGEE,1726		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06):1727	case VID_PID(USB_VENDOR_ID_UGEE,1728		     USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):1729		/* If this is the pen interface */1730		if (bInterfaceNumber == 1) {1731			/* Probe v1 pen parameters */1732			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1733			if (rc != 0) {1734				hid_err(hdev, "pen probing failed: %d\n", rc);1735				goto cleanup;1736			}1737			if (!found) {1738				hid_warn(hdev, "pen parameters not found");1739				uclogic_params_init_invalid(&p);1740			}1741		} else {1742			uclogic_params_init_invalid(&p);1743		}1744		break;1745	case VID_PID(USB_VENDOR_ID_UGEE,1746		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):1747		/* If this is the pen and frame interface */1748		if (bInterfaceNumber == 1) {1749			/* Probe v1 pen parameters */1750			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1751			if (rc != 0) {1752				hid_err(hdev, "pen probing failed: %d\n", rc);1753				goto cleanup;1754			}1755			/* Initialize frame parameters */1756			rc = uclogic_params_frame_init_with_desc(1757				&p.frame_list[0],1758				uclogic_rdesc_xppen_deco01_frame_arr,1759				uclogic_rdesc_xppen_deco01_frame_size,1760				0);1761			if (rc != 0)1762				goto cleanup;1763		} else {1764			uclogic_params_init_invalid(&p);1765		}1766		break;1767	case VID_PID(USB_VENDOR_ID_UGEE,1768		     USB_DEVICE_ID_UGEE_PARBLO_A610_PRO):1769	case VID_PID(USB_VENDOR_ID_UGEE,1770		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2):1771	case VID_PID(USB_VENDOR_ID_UGEE,1772		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):1773	case VID_PID(USB_VENDOR_ID_UGEE,1774		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW):1775	case VID_PID(USB_VENDOR_ID_UGEE,1776		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S):1777	case VID_PID(USB_VENDOR_ID_UGEE,1778		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW):1779		rc = uclogic_params_ugee_v2_init(&p, hdev);1780		if (rc != 0)1781			goto cleanup;1782		break;1783	case VID_PID(USB_VENDOR_ID_TRUST,1784		     USB_DEVICE_ID_TRUST_PANORA_TABLET):1785	case VID_PID(USB_VENDOR_ID_UGEE,1786		     USB_DEVICE_ID_UGEE_TABLET_G5):1787		/* Ignore non-pen interfaces */1788		if (bInterfaceNumber != 1) {1789			uclogic_params_init_invalid(&p);1790			break;1791		}1792 1793		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1794		if (rc != 0) {1795			hid_err(hdev, "pen probing failed: %d\n", rc);1796			goto cleanup;1797		} else if (found) {1798			rc = uclogic_params_frame_init_with_desc(1799				&p.frame_list[0],1800				uclogic_rdesc_ugee_g5_frame_arr,1801				uclogic_rdesc_ugee_g5_frame_size,1802				UCLOGIC_RDESC_UGEE_G5_FRAME_ID);1803			if (rc != 0) {1804				hid_err(hdev,1805					"failed creating frame parameters: %d\n",1806					rc);1807				goto cleanup;1808			}1809			p.frame_list[0].re_lsb =1810				UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;1811			p.frame_list[0].dev_id_byte =1812				UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;1813		} else {1814			hid_warn(hdev, "pen parameters not found");1815			uclogic_params_init_invalid(&p);1816		}1817 1818		break;1819	case VID_PID(USB_VENDOR_ID_UGEE,1820		     USB_DEVICE_ID_UGEE_TABLET_EX07S):1821		/* Ignore non-pen interfaces */1822		if (bInterfaceNumber != 1) {1823			uclogic_params_init_invalid(&p);1824			break;1825		}1826 1827		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);1828		if (rc != 0) {1829			hid_err(hdev, "pen probing failed: %d\n", rc);1830			goto cleanup;1831		} else if (found) {1832			rc = uclogic_params_frame_init_with_desc(1833				&p.frame_list[0],1834				uclogic_rdesc_ugee_ex07_frame_arr,1835				uclogic_rdesc_ugee_ex07_frame_size,1836				0);1837			if (rc != 0) {1838				hid_err(hdev,1839					"failed creating frame parameters: %d\n",1840					rc);1841				goto cleanup;1842			}1843		} else {1844			hid_warn(hdev, "pen parameters not found");1845			uclogic_params_init_invalid(&p);1846		}1847 1848		break;1849	}1850 1851#undef VID_PID1852#undef WITH_OPT_DESC1853 1854	/* Output parameters */1855	memcpy(params, &p, sizeof(*params));1856	memset(&p, 0, sizeof(p));1857	rc = 0;1858cleanup:1859	uclogic_params_cleanup(&p);1860	return rc;1861}1862 1863#ifdef CONFIG_HID_KUNIT_TEST1864#include "hid-uclogic-params-test.c"1865#endif1866