brintos

brintos / linux-shallow public Read only

0
0
Text · 15.6 KiB · d800893 Raw
577 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 *  HID driver for UC-Logic devices not fully compliant with HID standard4 *5 *  Copyright (c) 2010-2014 Nikolai Kondrashov6 *  Copyright (c) 2013 Martin Rusko7 */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 <linux/device.h>17#include <linux/hid.h>18#include <linux/module.h>19#include <linux/timer.h>20#include "usbhid/usbhid.h"21#include "hid-uclogic-params.h"22 23#include "hid-ids.h"24 25/**26 * uclogic_inrange_timeout - handle pen in-range state timeout.27 * Emulate input events normally generated when pen goes out of range for28 * tablets which don't report that.29 *30 * @t:	The timer the timeout handler is attached to, stored in a struct31 *	uclogic_drvdata.32 */33static void uclogic_inrange_timeout(struct timer_list *t)34{35	struct uclogic_drvdata *drvdata = from_timer(drvdata, t,36							inrange_timer);37	struct input_dev *input = drvdata->pen_input;38 39	if (input == NULL)40		return;41	input_report_abs(input, ABS_PRESSURE, 0);42	/* If BTN_TOUCH state is changing */43	if (test_bit(BTN_TOUCH, input->key)) {44		input_event(input, EV_MSC, MSC_SCAN,45				/* Digitizer Tip Switch usage */46				0xd0042);47		input_report_key(input, BTN_TOUCH, 0);48	}49	input_report_key(input, BTN_TOOL_PEN, 0);50	input_sync(input);51}52 53static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,54					unsigned int *rsize)55{56	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);57 58	if (drvdata->desc_ptr != NULL) {59		*rsize = drvdata->desc_size;60		return drvdata->desc_ptr;61	}62	return rdesc;63}64 65static int uclogic_input_mapping(struct hid_device *hdev,66				 struct hid_input *hi,67				 struct hid_field *field,68				 struct hid_usage *usage,69				 unsigned long **bit,70				 int *max)71{72	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);73	struct uclogic_params *params = &drvdata->params;74 75	/* Discard invalid pen usages */76	if (params->pen.usage_invalid && (field->application == HID_DG_PEN))77		return -1;78 79	/* Let hid-core decide what to do */80	return 0;81}82 83static int uclogic_input_configured(struct hid_device *hdev,84		struct hid_input *hi)85{86	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);87	struct uclogic_params *params = &drvdata->params;88	const char *suffix = NULL;89	struct hid_field *field;90	size_t i;91	const struct uclogic_params_frame *frame;92 93	/* no report associated (HID_QUIRK_MULTI_INPUT not set) */94	if (!hi->report)95		return 0;96 97	/*98	 * If this is the input corresponding to the pen report99	 * in need of tweaking.100	 */101	if (hi->report->id == params->pen.id) {102		/* Remember the input device so we can simulate events */103		drvdata->pen_input = hi->input;104	}105 106	/* If it's one of the frame devices */107	for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {108		frame = &params->frame_list[i];109		if (hi->report->id == frame->id) {110			/* Assign custom suffix, if any */111			suffix = frame->suffix;112			/*113			 * Disable EV_MSC reports for touch ring interfaces to114			 * make the Wacom driver pickup touch ring extents115			 */116			if (frame->touch_byte > 0)117				__clear_bit(EV_MSC, hi->input->evbit);118		}119	}120 121	if (!suffix) {122		field = hi->report->field[0];123 124		switch (field->application) {125		case HID_GD_KEYBOARD:126			suffix = "Keyboard";127			break;128		case HID_GD_MOUSE:129			suffix = "Mouse";130			break;131		case HID_GD_KEYPAD:132			suffix = "Pad";133			break;134		case HID_DG_PEN:135		case HID_DG_DIGITIZER:136			suffix = "Pen";137			break;138		case HID_CP_CONSUMER_CONTROL:139			suffix = "Consumer Control";140			break;141		case HID_GD_SYSTEM_CONTROL:142			suffix = "System Control";143			break;144		}145	}146 147	if (suffix)148		hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL,149						 "%s %s", hdev->name, suffix);150 151	return 0;152}153 154static int uclogic_probe(struct hid_device *hdev,155		const struct hid_device_id *id)156{157	int rc;158	struct uclogic_drvdata *drvdata = NULL;159	bool params_initialized = false;160 161	if (!hid_is_usb(hdev))162		return -EINVAL;163 164	/*165	 * libinput requires the pad interface to be on a different node166	 * than the pen, so use QUIRK_MULTI_INPUT for all tablets.167	 */168	hdev->quirks |= HID_QUIRK_MULTI_INPUT;169	hdev->quirks |= HID_QUIRK_HIDINPUT_FORCE;170 171	/* Allocate and assign driver data */172	drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);173	if (drvdata == NULL) {174		rc = -ENOMEM;175		goto failure;176	}177	timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0);178	drvdata->re_state = U8_MAX;179	drvdata->quirks = id->driver_data;180	hid_set_drvdata(hdev, drvdata);181 182	/* Initialize the device and retrieve interface parameters */183	rc = uclogic_params_init(&drvdata->params, hdev);184	if (rc != 0) {185		hid_err(hdev, "failed probing parameters: %d\n", rc);186		goto failure;187	}188	params_initialized = true;189	hid_dbg(hdev, "parameters:\n");190	uclogic_params_hid_dbg(hdev, &drvdata->params);191	if (drvdata->params.invalid) {192		hid_info(hdev, "interface is invalid, ignoring\n");193		rc = -ENODEV;194		goto failure;195	}196 197	/* Generate replacement report descriptor */198	rc = uclogic_params_get_desc(&drvdata->params,199				     &drvdata->desc_ptr,200				     &drvdata->desc_size);201	if (rc) {202		hid_err(hdev,203			"failed generating replacement report descriptor: %d\n",204			rc);205		goto failure;206	}207 208	rc = hid_parse(hdev);209	if (rc) {210		hid_err(hdev, "parse failed\n");211		goto failure;212	}213 214	rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);215	if (rc) {216		hid_err(hdev, "hw start failed\n");217		goto failure;218	}219 220	return 0;221failure:222	/* Assume "remove" might not be called if "probe" failed */223	if (params_initialized)224		uclogic_params_cleanup(&drvdata->params);225	return rc;226}227 228#ifdef CONFIG_PM229static int uclogic_resume(struct hid_device *hdev)230{231	int rc;232	struct uclogic_params params;233 234	/* Re-initialize the device, but discard parameters */235	rc = uclogic_params_init(&params, hdev);236	if (rc != 0)237		hid_err(hdev, "failed to re-initialize the device\n");238	else239		uclogic_params_cleanup(&params);240 241	return rc;242}243#endif244 245/**246 * uclogic_exec_event_hook - if the received event is hooked schedules the247 * associated work.248 *249 * @p:		Tablet interface report parameters.250 * @event:	Raw event.251 * @size:	The size of event.252 *253 * Returns:254 *	Whether the event was hooked or not.255 */256static bool uclogic_exec_event_hook(struct uclogic_params *p, u8 *event, int size)257{258	struct uclogic_raw_event_hook *curr;259 260	if (!p->event_hooks)261		return false;262 263	list_for_each_entry(curr, &p->event_hooks->list, list) {264		if (curr->size == size && memcmp(curr->event, event, size) == 0) {265			schedule_work(&curr->work);266			return true;267		}268	}269 270	return false;271}272 273/**274 * uclogic_raw_event_pen - handle raw pen events (pen HID reports).275 *276 * @drvdata:	Driver data.277 * @data:	Report data buffer, can be modified.278 * @size:	Report data size, bytes.279 *280 * Returns:281 *	Negative value on error (stops event delivery), zero for success.282 */283static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata,284					u8 *data, int size)285{286	struct uclogic_params_pen *pen = &drvdata->params.pen;287 288	WARN_ON(drvdata == NULL);289	WARN_ON(data == NULL && size != 0);290 291	/* If in-range reports are inverted */292	if (pen->inrange ==293		UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {294		/* Invert the in-range bit */295		data[1] ^= 0x40;296	}297	/*298	 * If report contains fragmented high-resolution pen299	 * coordinates300	 */301	if (size >= 10 && pen->fragmented_hires) {302		u8 pressure_low_byte;303		u8 pressure_high_byte;304 305		/* Lift pressure bytes */306		pressure_low_byte = data[6];307		pressure_high_byte = data[7];308		/*309		 * Move Y coord to make space for high-order X310		 * coord byte311		 */312		data[6] = data[5];313		data[5] = data[4];314		/* Move high-order X coord byte */315		data[4] = data[8];316		/* Move high-order Y coord byte */317		data[7] = data[9];318		/* Place pressure bytes */319		data[8] = pressure_low_byte;320		data[9] = pressure_high_byte;321	}322	/* If we need to emulate in-range detection */323	if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {324		/* Set in-range bit */325		data[1] |= 0x40;326		/* (Re-)start in-range timeout */327		mod_timer(&drvdata->inrange_timer,328				jiffies + msecs_to_jiffies(100));329	}330	/* If we report tilt and Y direction is flipped */331	if (size >= 12 && pen->tilt_y_flipped)332		data[11] = -data[11];333 334	return 0;335}336 337/**338 * uclogic_raw_event_frame - handle raw frame events (frame HID reports).339 *340 * @drvdata:	Driver data.341 * @frame:	The parameters of the frame controls to handle.342 * @data:	Report data buffer, can be modified.343 * @size:	Report data size, bytes.344 *345 * Returns:346 *	Negative value on error (stops event delivery), zero for success.347 */348static int uclogic_raw_event_frame(349		struct uclogic_drvdata *drvdata,350		const struct uclogic_params_frame *frame,351		u8 *data, int size)352{353	WARN_ON(drvdata == NULL);354	WARN_ON(data == NULL && size != 0);355 356	/* If need to, and can, set pad device ID for Wacom drivers */357	if (frame->dev_id_byte > 0 && frame->dev_id_byte < size) {358		/* If we also have a touch ring and the finger left it */359		if (frame->touch_byte > 0 && frame->touch_byte < size &&360		    data[frame->touch_byte] == 0) {361			data[frame->dev_id_byte] = 0;362		} else {363			data[frame->dev_id_byte] = 0xf;364		}365	}366 367	/* If need to, and can, read rotary encoder state change */368	if (frame->re_lsb > 0 && frame->re_lsb / 8 < size) {369		unsigned int byte = frame->re_lsb / 8;370		unsigned int bit = frame->re_lsb % 8;371 372		u8 change;373		u8 prev_state = drvdata->re_state;374		/* Read Gray-coded state */375		u8 state = (data[byte] >> bit) & 0x3;376		/* Encode state change into 2-bit signed integer */377		if ((prev_state == 1 && state == 0) ||378		    (prev_state == 2 && state == 3)) {379			change = 1;380		} else if ((prev_state == 2 && state == 0) ||381			   (prev_state == 1 && state == 3)) {382			change = 3;383		} else {384			change = 0;385		}386		/* Write change */387		data[byte] = (data[byte] & ~((u8)3 << bit)) |388				(change << bit);389		/* Remember state */390		drvdata->re_state = state;391	}392 393	/* If need to, and can, transform the touch ring reports */394	if (frame->touch_byte > 0 && frame->touch_byte < size) {395		__s8 value = data[frame->touch_byte];396 397		if (value != 0) {398			if (frame->touch_flip_at != 0) {399				value = frame->touch_flip_at - value;400				if (value <= 0)401					value = frame->touch_max + value;402			}403			data[frame->touch_byte] = value - 1;404		}405	}406 407	/* If need to, and can, transform the bitmap dial reports */408	if (frame->bitmap_dial_byte > 0 && frame->bitmap_dial_byte < size) {409		if (data[frame->bitmap_dial_byte] == 2)410			data[frame->bitmap_dial_byte] = -1;411	}412 413	return 0;414}415 416static int uclogic_raw_event(struct hid_device *hdev,417				struct hid_report *report,418				u8 *data, int size)419{420	unsigned int report_id = report->id;421	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);422	struct uclogic_params *params = &drvdata->params;423	struct uclogic_params_pen_subreport *subreport;424	struct uclogic_params_pen_subreport *subreport_list_end;425	size_t i;426 427	/* Do not handle anything but input reports */428	if (report->type != HID_INPUT_REPORT)429		return 0;430 431	if (uclogic_exec_event_hook(params, data, size))432		return 0;433 434	while (true) {435		/* Tweak pen reports, if necessary */436		if ((report_id == params->pen.id) && (size >= 2)) {437			subreport_list_end =438				params->pen.subreport_list +439				ARRAY_SIZE(params->pen.subreport_list);440			/* Try to match a subreport */441			for (subreport = params->pen.subreport_list;442			     subreport < subreport_list_end; subreport++) {443				if (subreport->value != 0 &&444				    subreport->value == data[1]) {445					break;446				}447			}448			/* If a subreport matched */449			if (subreport < subreport_list_end) {450				/* Change to subreport ID, and restart */451				report_id = data[0] = subreport->id;452				continue;453			} else {454				return uclogic_raw_event_pen(drvdata, data, size);455			}456		}457 458		/* Tweak frame control reports, if necessary */459		for (i = 0; i < ARRAY_SIZE(params->frame_list); i++) {460			if (report_id == params->frame_list[i].id) {461				return uclogic_raw_event_frame(462					drvdata, &params->frame_list[i],463					data, size);464			}465		}466 467		break;468	}469 470	return 0;471}472 473static void uclogic_remove(struct hid_device *hdev)474{475	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);476 477	del_timer_sync(&drvdata->inrange_timer);478	hid_hw_stop(hdev);479	kfree(drvdata->desc_ptr);480	uclogic_params_cleanup(&drvdata->params);481}482 483static const struct hid_device_id uclogic_devices[] = {484	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,485				USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },486	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,487				USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },488	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,489				USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },490	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,491				USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },492	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,493				USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },494	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,495				USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },496	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,497				USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },498	{ HID_USB_DEVICE(USB_VENDOR_ID_HUION,499				USB_DEVICE_ID_HUION_TABLET) },500	{ HID_USB_DEVICE(USB_VENDOR_ID_HUION,501				USB_DEVICE_ID_HUION_TABLET2) },502	{ HID_USB_DEVICE(USB_VENDOR_ID_TRUST,503				USB_DEVICE_ID_TRUST_PANORA_TABLET) },504	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,505				USB_DEVICE_ID_HUION_TABLET) },506	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,507				USB_DEVICE_ID_YIYNOVA_TABLET) },508	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,509				USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) },510	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,511				USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) },512	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,513				USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47) },514	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,515				USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },516	{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER,517				USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },518	{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER,519				USB_DEVICE_ID_UGTIZER_TABLET_GT5040) },520	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,521				USB_DEVICE_ID_UGEE_PARBLO_A610_PRO) },522	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,523				USB_DEVICE_ID_UGEE_TABLET_G5) },524	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,525				USB_DEVICE_ID_UGEE_TABLET_EX07S) },526	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,527				USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720) },528	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,529				USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540) },530	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,531				USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },532	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,533				USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },534	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,535				USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2) },536	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,537				USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) },538	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,539				USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW),540		.driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK },541	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,542				USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S) },543	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,544				USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW),545		.driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK },546	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,547				USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },548	{ }549};550MODULE_DEVICE_TABLE(hid, uclogic_devices);551 552static struct hid_driver uclogic_driver = {553	.name = "uclogic",554	.id_table = uclogic_devices,555	.probe = uclogic_probe,556	.remove = uclogic_remove,557	.report_fixup = uclogic_report_fixup,558	.raw_event = uclogic_raw_event,559	.input_mapping = uclogic_input_mapping,560	.input_configured = uclogic_input_configured,561#ifdef CONFIG_PM562	.resume	          = uclogic_resume,563	.reset_resume     = uclogic_resume,564#endif565};566module_hid_driver(uclogic_driver);567 568MODULE_AUTHOR("Martin Rusko");569MODULE_AUTHOR("Nikolai Kondrashov");570MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard");571MODULE_LICENSE("GPL");572MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard");573 574#ifdef CONFIG_HID_KUNIT_TEST575#include "hid-uclogic-core-test.c"576#endif577