2964 lines · 75.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *  USB Wacom tablet support - system specific code */ #include "wacom_wac.h"#include "wacom.h"#include <linux/input/mt.h> #define WAC_MSG_RETRIES		5#define WAC_CMD_RETRIES		10 #define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP)#define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP)#define DEV_ATTR_RO_PERM (S_IRUSR | S_IRGRP) static int wacom_get_report(struct hid_device *hdev, u8 type, u8 *buf,			    size_t size, unsigned int retries){	int retval; 	do {		retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,				HID_REQ_GET_REPORT);	} while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries); 	if (retval < 0)		hid_err(hdev, "wacom_get_report: ran out of retries "			"(last error = %d)\n", retval); 	return retval;} static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,			    size_t size, unsigned int retries){	int retval; 	do {		retval = hid_hw_raw_request(hdev, buf[0], buf, size, type,				HID_REQ_SET_REPORT);	} while ((retval == -ETIMEDOUT || retval == -EAGAIN) && --retries); 	if (retval < 0)		hid_err(hdev, "wacom_set_report: ran out of retries "			"(last error = %d)\n", retval); 	return retval;} static void wacom_wac_queue_insert(struct hid_device *hdev,				   struct kfifo_rec_ptr_2 *fifo,				   u8 *raw_data, int size){	bool warned = false; 	while (kfifo_avail(fifo) < size && !kfifo_is_empty(fifo)) {		if (!warned)			hid_warn(hdev, "%s: kfifo has filled, starting to drop events\n", __func__);		warned = true; 		kfifo_skip(fifo);	} 	if (!kfifo_in(fifo, raw_data, size))		hid_warn_ratelimited(hdev, "%s: report is too large (%d)\n",				     __func__, size);} static void wacom_wac_queue_flush(struct hid_device *hdev,				  struct kfifo_rec_ptr_2 *fifo){	while (!kfifo_is_empty(fifo)) {		int size = kfifo_peek_len(fifo);		u8 *buf __free(kfree) = kzalloc(size, GFP_ATOMIC);		unsigned int count;		int err; 		if (!buf) {			kfifo_skip(fifo);			continue;		} 		count = kfifo_out(fifo, buf, size);		if (count != size) {			// Hard to say what is the "right" action in this			// circumstance. Skipping the entry and continuing			// to flush seems reasonable enough, however.			hid_warn(hdev, "%s: removed fifo entry with unexpected size\n",				 __func__);			continue;		}		err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, size, false);		if (err) {			hid_warn(hdev, "%s: unable to flush event due to error %d\n",				 __func__, err);		}	}} static int wacom_wac_pen_serial_enforce(struct hid_device *hdev,		struct hid_report *report, u8 *raw_data, int report_size){	struct wacom *wacom = hid_get_drvdata(hdev);	struct wacom_wac *wacom_wac = &wacom->wacom_wac;	struct wacom_features *features = &wacom_wac->features;	bool flush = false;	bool insert = false;	int i, j; 	if (wacom_wac->serial[0] || !(features->quirks & WACOM_QUIRK_TOOLSERIAL))		return 0; 	/* Queue events which have invalid tool type or serial number */	for (i = 0; i < report->maxfield; i++) {		for (j = 0; j < report->field[i]->maxusage; j++) {			struct hid_field *field = report->field[i];			struct hid_usage *usage = &field->usage[j];			unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);			unsigned int offset;			unsigned int size;			unsigned int value; 			if (equivalent_usage != HID_DG_INRANGE &&			    equivalent_usage != HID_DG_TOOLSERIALNUMBER &&			    equivalent_usage != WACOM_HID_WD_SERIALHI &&			    equivalent_usage != WACOM_HID_WD_TOOLTYPE)				continue; 			offset = field->report_offset;			size = field->report_size;			value = hid_field_extract(hdev, raw_data+1, offset + j * size, size); 			/* If we go out of range, we need to flush the queue ASAP */			if (equivalent_usage == HID_DG_INRANGE)				value = !value; 			if (value) {				flush = true;				switch (equivalent_usage) {				case HID_DG_TOOLSERIALNUMBER:					wacom_wac->serial[0] = value;					break; 				case WACOM_HID_WD_SERIALHI:					wacom_wac->serial[0] |= ((__u64)value) << 32;					break; 				case WACOM_HID_WD_TOOLTYPE:					wacom_wac->id[0] = value;					break;				}			}			else {				insert = true;			}		}	} 	if (flush)		wacom_wac_queue_flush(hdev, wacom_wac->pen_fifo);	else if (insert)		wacom_wac_queue_insert(hdev, wacom_wac->pen_fifo,				       raw_data, report_size); 	return insert && !flush;} static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,		u8 *raw_data, int size){	struct wacom *wacom = hid_get_drvdata(hdev); 	if (wacom->wacom_wac.features.type == BOOTLOADER)		return 0; 	if (wacom_wac_pen_serial_enforce(hdev, report, raw_data, size))		return -1; 	wacom->wacom_wac.data = raw_data; 	wacom_wac_irq(&wacom->wacom_wac, size); 	return 0;} static int wacom_open(struct input_dev *dev){	struct wacom *wacom = input_get_drvdata(dev); 	return hid_hw_open(wacom->hdev);} static void wacom_close(struct input_dev *dev){	struct wacom *wacom = input_get_drvdata(dev); 	/*	 * wacom->hdev should never be null, but surprisingly, I had the case	 * once while unplugging the Wacom Wireless Receiver.	 */	if (wacom->hdev)		hid_hw_close(wacom->hdev);} /* * Calculate the resolution of the X or Y axis using hidinput_calc_abs_res. */static int wacom_calc_hid_res(int logical_extents, int physical_extents,			       unsigned unit, int exponent){	struct hid_field field = {		.logical_maximum = logical_extents,		.physical_maximum = physical_extents,		.unit = unit,		.unit_exponent = exponent,	}; 	return hidinput_calc_abs_res(&field, ABS_X);} static void wacom_hid_usage_quirk(struct hid_device *hdev,		struct hid_field *field, struct hid_usage *usage){	struct wacom *wacom = hid_get_drvdata(hdev);	struct wacom_features *features = &wacom->wacom_wac.features;	unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); 	/*	 * The Dell Canvas 27 needs to be switched to its vendor-defined	 * report to provide the best resolution.	 */	if (hdev->vendor == USB_VENDOR_ID_WACOM &&	    hdev->product == 0x4200 &&	    field->application == HID_UP_MSVENDOR) {		wacom->wacom_wac.mode_report = field->report->id;		wacom->wacom_wac.mode_value = 2;	} 	/*	 * ISDv4 devices which predate HID's adoption of the	 * HID_DG_BARELSWITCH2 usage use 0x000D0000 in its	 * position instead. We can accurately detect if a	 * usage with that value should be HID_DG_BARRELSWITCH2	 * based on the surrounding usages, which have remained	 * constant across generations.	 */	if (features->type == HID_GENERIC &&	    usage->hid == 0x000D0000 &&	    field->application == HID_DG_PEN &&	    field->physical == HID_DG_STYLUS) {		int i = usage->usage_index; 		if (i-4 >= 0 && i+1 < field->maxusage &&		    field->usage[i-4].hid == HID_DG_TIPSWITCH &&		    field->usage[i-3].hid == HID_DG_BARRELSWITCH &&		    field->usage[i-2].hid == HID_DG_ERASER &&		    field->usage[i-1].hid == HID_DG_INVERT &&		    field->usage[i+1].hid == HID_DG_INRANGE) {			usage->hid = HID_DG_BARRELSWITCH2;		}	} 	/*	 * Wacom's AES devices use different vendor-defined usages to	 * report serial number information compared to their branded	 * hardware. The usages are also sometimes ill-defined and do	 * not have the correct logical min/max values set. Lets patch	 * the descriptor to use the branded usage convention and fix	 * the errors.	 */	if (usage->hid == WACOM_HID_WT_SERIALNUMBER &&	    field->report_size == 16 &&	    field->index + 2 < field->report->maxfield) {		struct hid_field *a = field->report->field[field->index + 1];		struct hid_field *b = field->report->field[field->index + 2]; 		if (a->maxusage > 0 &&		    a->usage[0].hid == HID_DG_TOOLSERIALNUMBER &&		    a->report_size == 32 &&		    b->maxusage > 0 &&		    b->usage[0].hid == 0xFF000000 &&		    b->report_size == 8) {			features->quirks |= WACOM_QUIRK_AESPEN;			usage->hid = WACOM_HID_WD_TOOLTYPE;			field->logical_minimum = S16_MIN;			field->logical_maximum = S16_MAX;			a->logical_minimum = S32_MIN;			a->logical_maximum = S32_MAX;			b->usage[0].hid = WACOM_HID_WD_SERIALHI;			b->logical_minimum = 0;			b->logical_maximum = U8_MAX;		}	} 	/* 2nd-generation Intuos Pro Large has incorrect Y maximum */	if (hdev->vendor == USB_VENDOR_ID_WACOM &&	    hdev->product == 0x0358 &&	    WACOM_PEN_FIELD(field) &&	    equivalent_usage == HID_GD_Y) {		field->logical_maximum = 43200;	}} static void wacom_feature_mapping(struct hid_device *hdev,		struct hid_field *field, struct hid_usage *usage){	struct wacom *wacom = hid_get_drvdata(hdev);	struct wacom_features *features = &wacom->wacom_wac.features;	struct hid_data *hid_data = &wacom->wacom_wac.hid_data;	unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);	u8 *data;	int ret;	u32 n; 	wacom_hid_usage_quirk(hdev, field, usage); 	switch (equivalent_usage) {	case WACOM_HID_WD_TOUCH_RING_SETTING:		wacom->generic_has_leds = true;		break;	case HID_DG_CONTACTMAX:		/* leave touch_max as is if predefined */		if (!features->touch_max) {			/* read manually */			n = hid_report_len(field->report);			data = hid_alloc_report_buf(field->report, GFP_KERNEL);			if (!data)				break;			data[0] = field->report->id;			ret = wacom_get_report(hdev, HID_FEATURE_REPORT,					       data, n, WAC_CMD_RETRIES);			if (ret == n && features->type == HID_GENERIC) {				ret = hid_report_raw_event(hdev,					HID_FEATURE_REPORT, data, n, n, 0);			} else if (ret == 2 && features->type != HID_GENERIC) {				features->touch_max = data[1];			} else {				features->touch_max = 16;				hid_warn(hdev, "wacom_feature_mapping: "					 "could not get HID_DG_CONTACTMAX, "					 "defaulting to %d\n",					  features->touch_max);			}			kfree(data);		}		break;	case HID_DG_INPUTMODE:		/* Ignore if value index is out of bounds. */		if (usage->usage_index >= field->report_count) {			dev_err(&hdev->dev, "HID_DG_INPUTMODE out of range\n");			break;		} 		hid_data->inputmode = field->report->id;		hid_data->inputmode_index = usage->usage_index;		hid_data->inputmode_field_index = field->index;		break; 	case HID_UP_DIGITIZER:		if (field->report->id == 0x0B &&		    (field->application == WACOM_HID_G9_PEN ||		     field->application == WACOM_HID_G11_PEN)) {			wacom->wacom_wac.mode_report = field->report->id;			wacom->wacom_wac.mode_value = 0;		}		break; 	case WACOM_HID_WD_DATAMODE:		wacom->wacom_wac.mode_report = field->report->id;		wacom->wacom_wac.mode_value = 2;		break; 	case WACOM_HID_UP_G9:	case WACOM_HID_UP_G11:		if (field->report->id == 0x03 &&		    (field->application == WACOM_HID_G9_TOUCHSCREEN ||		     field->application == WACOM_HID_G11_TOUCHSCREEN)) {			wacom->wacom_wac.mode_report = field->report->id;			wacom->wacom_wac.mode_value = 0;		}		break;	case WACOM_HID_WD_OFFSETLEFT:	case WACOM_HID_WD_OFFSETTOP:	case WACOM_HID_WD_OFFSETRIGHT:	case WACOM_HID_WD_OFFSETBOTTOM:		/* read manually */		n = hid_report_len(field->report);		data = hid_alloc_report_buf(field->report, GFP_KERNEL);		if (!data)			break;		data[0] = field->report->id;		ret = wacom_get_report(hdev, HID_FEATURE_REPORT,					data, n, WAC_CMD_RETRIES);		if (ret == n) {			ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT,						   data, n, n, 0);		} else {			hid_warn(hdev, "%s: could not retrieve sensor offsets\n",				 __func__);		}		kfree(data);		break;	}} /* * Interface Descriptor of wacom devices can be incomplete and * inconsistent so wacom_features table is used to store stylus * device's packet lengths, various maximum values, and tablet * resolution based on product ID's. * * For devices that contain 2 interfaces, wacom_features table is * inaccurate for the touch interface.  Since the Interface Descriptor * for touch interfaces has pretty complete data, this function exists * to query tablet for this missing information instead of hard coding in * an additional table. * * A typical Interface Descriptor for a stylus will contain a * boot mouse application collection that is not of interest and this * function will ignore it. * * It also contains a digitizer application collection that also is not * of interest since any information it contains would be duplicate * of what is in wacom_features. Usually it defines a report of an array * of bytes that could be used as max length of the stylus packet returned. * If it happens to define a Digitizer-Stylus Physical Collection then * the X and Y logical values contain valid data but it is ignored. * * A typical Interface Descriptor for a touch interface will contain a * Digitizer-Finger Physical Collection which will define both logical * X/Y maximum as well as the physical size of tablet. Since touch * interfaces haven't supported pressure or distance, this is enough * information to override invalid values in the wacom_features table. * * Intuos5 touch interface and 3rd gen Bamboo Touch do not contain useful * data. We deal with them after returning from this function. */static void wacom_usage_mapping(struct hid_device *hdev,		struct hid_field *field, struct hid_usage *usage){	struct wacom *wacom = hid_get_drvdata(hdev);	struct wacom_features *features = &wacom->wacom_wac.features;	bool finger = WACOM_FINGER_FIELD(field);	bool pen = WACOM_PEN_FIELD(field);	unsigned equivalent_usage = wacom_equivalent_usage(usage->hid); 	/*	* Requiring Stylus Usage will ignore boot mouse	* X/Y values and some cases of invalid Digitizer X/Y	* values commonly reported.	*/	if (pen)		features->device_type |= WACOM_DEVICETYPE_PEN;	else if (finger)		features->device_type |= WACOM_DEVICETYPE_TOUCH;	else		return; 	wacom_hid_usage_quirk(hdev, field, usage); 	switch (equivalent_usage) {	case HID_GD_X:		features->x_max = field->logical_maximum;		if (finger) {			features->x_phy = field->physical_maximum;			if ((features->type != BAMBOO_PT) &&			    (features->type != BAMBOO_TOUCH)) {				features->unit = field->unit;				features->unitExpo = field->unit_exponent;			}		}		break;	case HID_GD_Y:		features->y_max = field->logical_maximum;		if (finger) {			features->y_phy = field->physical_maximum;			if ((features->type != BAMBOO_PT) &&			    (features->type != BAMBOO_TOUCH)) {				features->unit = field->unit;				features->unitExpo = field->unit_exponent;			}		}		break;	case HID_DG_TIPPRESSURE:		if (pen)			features->pressure_max = field->logical_maximum;		break;	} 	if (features->type == HID_GENERIC)		wacom_wac_usage_mapping(hdev, field, usage);} static void wacom_post_parse_hid(struct hid_device *hdev,				 struct wacom_features *features){	struct wacom *wacom = hid_get_drvdata(hdev);	struct wacom_wac *wacom_wac = &wacom->wacom_wac; 

Lines 1–500 of 2964.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t