diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-05 17:53:40 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-05 17:53:40 -0800 | 
| commit | cf26057a9441173ad552e90cea3344607075c9ad (patch) | |
| tree | 52410b934b7270e49df1917c2534cc40e30664bc /drivers/hid | |
| parent | 1686cc1a31f45a3fd090e5d0c6fce777422e13fa (diff) | |
| parent | bd8879faafe6d057237461c4d58d8b0d37b9e3ee (diff) | |
| download | linux-cf26057a9441173ad552e90cea3344607075c9ad.tar.bz2 | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina:
 - high-resolution scrolling support that gracefully handles differences
   between MS and Logitech implementations in HW, from Peter Hutterer
   and Harry Cutts
 - MSI IRQ support for intel-ish driver, from Song Hongyan
 - support for new hardware (Cougar 700K, Odys Winbook 13, ASUS FX503VD,
   ASUS T101HA) from Daniel M. Lambea, Hans de Goede and Aleix Roca
   Nonell
 - other small assorted fixups
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (22 commits)
  HID: i2c-hid: Add Odys Winbook 13 to descriptor override
  HID: lenovo: Add checks to fix of_led_classdev_register
  HID: intel-ish-hid: add MSI interrupt support
  HID: debug: Change to use DEFINE_SHOW_ATTRIBUTE macro
  HID: doc: fix wrong data structure reference for UHID_OUTPUT
  HID: intel-ish-hid: fixes incorrect error handling
  HID: asus: Add support for the ASUS T101HA keyboard dock
  HID: logitech: Use LDJ_DEVICE macro for existing Logitech mice
  HID: logitech: Enable high-resolution scrolling on Logitech mice
  HID: logitech: Add function to enable HID++ 1.0 "scrolling acceleration"
  HID: logitech-hidpp: fix typo, hiddpp to hidpp
  HID: input: use the Resolution Multiplier for high-resolution scrolling
  HID: core: process the Resolution Multiplier
  HID: core: store the collections as a basic tree
  Input: add `REL_WHEEL_HI_RES` and `REL_HWHEEL_HI_RES`
  HID: input: support Microsoft wireless radio control hotkey
  HID: use macros in IS_INPUT_APPLICATION
  HID: asus: Add support for the ASUS FX503VD laptop
  HID: asus: Add event handler to catch unmapped Asus Vendor UsagePage codes
  HID: cougar: Add support for Cougar 700K Gaming Keyboard
  ...
Diffstat (limited to 'drivers/hid')
| -rw-r--r-- | drivers/hid/hid-asus.c | 28 | ||||
| -rw-r--r-- | drivers/hid/hid-core.c | 174 | ||||
| -rw-r--r-- | drivers/hid/hid-cougar.c | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-debug.c | 12 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 3 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 108 | ||||
| -rw-r--r-- | drivers/hid/hid-lenovo.c | 10 | ||||
| -rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 375 | ||||
| -rw-r--r-- | drivers/hid/hidraw.c | 8 | ||||
| -rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c | 8 | ||||
| -rw-r--r-- | drivers/hid/intel-ish-hid/ipc/pci-ish.c | 7 | ||||
| -rw-r--r-- | drivers/hid/intel-ish-hid/ishtp-hid.c | 2 | 
12 files changed, 680 insertions, 57 deletions
| diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index a1fa2fc8c9b5..951bb17ae8b2 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -70,6 +70,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");  #define QUIRK_T100_KEYBOARD		BIT(6)  #define QUIRK_T100CHI			BIT(7)  #define QUIRK_G752_KEYBOARD		BIT(8) +#define QUIRK_T101HA_DOCK		BIT(9)  #define I2C_KEYBOARD_QUIRKS			(QUIRK_FIX_NOTEBOOK_REPORT | \  						 QUIRK_NO_INIT_REPORTS | \ @@ -241,6 +242,18 @@ static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size)  	return 1;  } +static int asus_event(struct hid_device *hdev, struct hid_field *field, +		      struct hid_usage *usage, __s32 value) +{ +	if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 && +	    (usage->hid & HID_USAGE) != 0x00 && !usage->type) { +		hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n", +			 usage->hid & HID_USAGE); +	} + +	return 0; +} +  static int asus_raw_event(struct hid_device *hdev,  		struct hid_report *report, u8 *data, int size)  { @@ -510,6 +523,7 @@ static int asus_input_mapping(struct hid_device *hdev,  		case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP);		break;  		case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF);		break;  		case 0x6c: asus_map_key_clear(KEY_SLEEP);		break; +		case 0x7c: asus_map_key_clear(KEY_MICMUTE);		break;  		case 0x82: asus_map_key_clear(KEY_CAMERA);		break;  		case 0x88: asus_map_key_clear(KEY_RFKILL);			break;  		case 0xb5: asus_map_key_clear(KEY_CALC);			break; @@ -528,6 +542,9 @@ static int asus_input_mapping(struct hid_device *hdev,  		/* Fn+Space Power4Gear Hybrid */  		case 0x5c: asus_map_key_clear(KEY_PROG3);		break; +		/* Fn+F5 "fan" symbol on FX503VD */ +		case 0x99: asus_map_key_clear(KEY_PROG4);		break; +  		default:  			/* ASUS lazily declares 256 usages, ignore the rest,  			 * as some make the keyboard appear as a pointer device. */ @@ -683,6 +700,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)  		return ret;  	} +	/* use hid-multitouch for T101HA touchpad */ +	if (id->driver_data & QUIRK_T101HA_DOCK && +	    hdev->collection->usage == HID_GD_MOUSE) +		return -ENODEV; +  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);  	if (ret) {  		hid_err(hdev, "Asus hw start failed: %d\n", ret); @@ -806,11 +828,16 @@ static const struct hid_device_id asus_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,  		USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, +		USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD), +	  QUIRK_USE_KBD_BACKLIGHT }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,  		USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),  	  QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,  		USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD),  	  QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, +		USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD), QUIRK_T101HA_DOCK },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, @@ -832,6 +859,7 @@ static struct hid_driver asus_driver = {  #ifdef CONFIG_PM  	.reset_resume           = asus_reset_resume,  #endif +	.event			= asus_event,  	.raw_event		= asus_raw_event  };  module_hid_driver(asus_driver); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5bec9244c45b..f41d5fe51abe 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -172,6 +172,8 @@ static int open_collection(struct hid_parser *parser, unsigned type)  	collection->type = type;  	collection->usage = usage;  	collection->level = parser->collection_stack_ptr - 1; +	collection->parent = parser->active_collection; +	parser->active_collection = collection;  	if (type == HID_COLLECTION_APPLICATION)  		parser->device->maxapplication++; @@ -190,6 +192,8 @@ static int close_collection(struct hid_parser *parser)  		return -EINVAL;  	}  	parser->collection_stack_ptr--; +	if (parser->active_collection) +		parser->active_collection = parser->active_collection->parent;  	return 0;  } @@ -290,6 +294,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign  		field->usage[i].collection_index =  			parser->local.collection_index[j];  		field->usage[i].usage_index = i; +		field->usage[i].resolution_multiplier = 1;  	}  	field->maxusage = usages; @@ -943,6 +948,167 @@ struct hid_report *hid_validate_values(struct hid_device *hid,  }  EXPORT_SYMBOL_GPL(hid_validate_values); +static int hid_calculate_multiplier(struct hid_device *hid, +				     struct hid_field *multiplier) +{ +	int m; +	__s32 v = *multiplier->value; +	__s32 lmin = multiplier->logical_minimum; +	__s32 lmax = multiplier->logical_maximum; +	__s32 pmin = multiplier->physical_minimum; +	__s32 pmax = multiplier->physical_maximum; + +	/* +	 * "Because OS implementations will generally divide the control's +	 * reported count by the Effective Resolution Multiplier, designers +	 * should take care not to establish a potential Effective +	 * Resolution Multiplier of zero." +	 * HID Usage Table, v1.12, Section 4.3.1, p31 +	 */ +	if (lmax - lmin == 0) +		return 1; +	/* +	 * Handling the unit exponent is left as an exercise to whoever +	 * finds a device where that exponent is not 0. +	 */ +	m = ((v - lmin)/(lmax - lmin) * (pmax - pmin) + pmin); +	if (unlikely(multiplier->unit_exponent != 0)) { +		hid_warn(hid, +			 "unsupported Resolution Multiplier unit exponent %d\n", +			 multiplier->unit_exponent); +	} + +	/* There are no devices with an effective multiplier > 255 */ +	if (unlikely(m == 0 || m > 255 || m < -255)) { +		hid_warn(hid, "unsupported Resolution Multiplier %d\n", m); +		m = 1; +	} + +	return m; +} + +static void hid_apply_multiplier_to_field(struct hid_device *hid, +					  struct hid_field *field, +					  struct hid_collection *multiplier_collection, +					  int effective_multiplier) +{ +	struct hid_collection *collection; +	struct hid_usage *usage; +	int i; + +	/* +	 * If multiplier_collection is NULL, the multiplier applies +	 * to all fields in the report. +	 * Otherwise, it is the Logical Collection the multiplier applies to +	 * but our field may be in a subcollection of that collection. +	 */ +	for (i = 0; i < field->maxusage; i++) { +		usage = &field->usage[i]; + +		collection = &hid->collection[usage->collection_index]; +		while (collection && collection != multiplier_collection) +			collection = collection->parent; + +		if (collection || multiplier_collection == NULL) +			usage->resolution_multiplier = effective_multiplier; + +	} +} + +static void hid_apply_multiplier(struct hid_device *hid, +				 struct hid_field *multiplier) +{ +	struct hid_report_enum *rep_enum; +	struct hid_report *rep; +	struct hid_field *field; +	struct hid_collection *multiplier_collection; +	int effective_multiplier; +	int i; + +	/* +	 * "The Resolution Multiplier control must be contained in the same +	 * Logical Collection as the control(s) to which it is to be applied. +	 * If no Resolution Multiplier is defined, then the Resolution +	 * Multiplier defaults to 1.  If more than one control exists in a +	 * Logical Collection, the Resolution Multiplier is associated with +	 * all controls in the collection. If no Logical Collection is +	 * defined, the Resolution Multiplier is associated with all +	 * controls in the report." +	 * HID Usage Table, v1.12, Section 4.3.1, p30 +	 * +	 * Thus, search from the current collection upwards until we find a +	 * logical collection. Then search all fields for that same parent +	 * collection. Those are the fields the multiplier applies to. +	 * +	 * If we have more than one multiplier, it will overwrite the +	 * applicable fields later. +	 */ +	multiplier_collection = &hid->collection[multiplier->usage->collection_index]; +	while (multiplier_collection && +	       multiplier_collection->type != HID_COLLECTION_LOGICAL) +		multiplier_collection = multiplier_collection->parent; + +	effective_multiplier = hid_calculate_multiplier(hid, multiplier); + +	rep_enum = &hid->report_enum[HID_INPUT_REPORT]; +	list_for_each_entry(rep, &rep_enum->report_list, list) { +		for (i = 0; i < rep->maxfield; i++) { +			field = rep->field[i]; +			hid_apply_multiplier_to_field(hid, field, +						      multiplier_collection, +						      effective_multiplier); +		} +	} +} + +/* + * hid_setup_resolution_multiplier - set up all resolution multipliers + * + * @device: hid device + * + * Search for all Resolution Multiplier Feature Reports and apply their + * value to all matching Input items. This only updates the internal struct + * fields. + * + * The Resolution Multiplier is applied by the hardware. If the multiplier + * is anything other than 1, the hardware will send pre-multiplied events + * so that the same physical interaction generates an accumulated + *	accumulated_value = value * * multiplier + * This may be achieved by sending + * - "value * multiplier" for each event, or + * - "value" but "multiplier" times as frequently, or + * - a combination of the above + * The only guarantee is that the same physical interaction always generates + * an accumulated 'value * multiplier'. + * + * This function must be called before any event processing and after + * any SetRequest to the Resolution Multiplier. + */ +void hid_setup_resolution_multiplier(struct hid_device *hid) +{ +	struct hid_report_enum *rep_enum; +	struct hid_report *rep; +	struct hid_usage *usage; +	int i, j; + +	rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; +	list_for_each_entry(rep, &rep_enum->report_list, list) { +		for (i = 0; i < rep->maxfield; i++) { +			/* Ignore if report count is out of bounds. */ +			if (rep->field[i]->report_count < 1) +				continue; + +			for (j = 0; j < rep->field[i]->maxusage; j++) { +				usage = &rep->field[i]->usage[j]; +				if (usage->hid == HID_GD_RESOLUTION_MULTIPLIER) +					hid_apply_multiplier(hid, +							     rep->field[i]); +			} +		} +	} +} +EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier); +  /**   * hid_open_report - open a driver-specific device report   * @@ -1039,9 +1205,17 @@ int hid_open_report(struct hid_device *device)  				hid_err(device, "unbalanced delimiter at end of report description\n");  				goto err;  			} + +			/* +			 * fetch initial values in case the device's +			 * default multiplier isn't the recommended 1 +			 */ +			hid_setup_resolution_multiplier(device); +  			kfree(parser->collection_stack);  			vfree(parser);  			device->status |= HID_STAT_PARSED; +  			return 0;  		}  	} diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c index 3f0916b64c60..e0bb7b34f3a4 100644 --- a/drivers/hid/hid-cougar.c +++ b/drivers/hid/hid-cougar.c @@ -326,6 +326,8 @@ module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644);  static struct hid_device_id cougar_id_table[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,  			 USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, +			 USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD) },  	{}  };  MODULE_DEVICE_TABLE(hid, cougar_id_table); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index b48100236df8..c530476edba6 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -1072,11 +1072,6 @@ static int hid_debug_rdesc_show(struct seq_file *f, void *p)  	return 0;  } -static int hid_debug_rdesc_open(struct inode *inode, struct file *file) -{ -	return single_open(file, hid_debug_rdesc_show, inode->i_private); -} -  static int hid_debug_events_open(struct inode *inode, struct file *file)  {  	int err = 0; @@ -1211,12 +1206,7 @@ static int hid_debug_events_release(struct inode *inode, struct file *file)  	return 0;  } -static const struct file_operations hid_debug_rdesc_fops = { -	.open           = hid_debug_rdesc_open, -	.read           = seq_read, -	.llseek         = seq_lseek, -	.release        = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(hid_debug_rdesc);  static const struct file_operations hid_debug_events_fops = {  	.owner =        THIS_MODULE, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 27519eb8ee63..518fa76414f5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -187,12 +187,14 @@  #define USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD	0x17e0  #define USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD	0x1807  #define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD	0x8502 +#define USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD	0x183d  #define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD	0x184a  #define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD	0x8585  #define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD	0x0101  #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854  #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837  #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822 +#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD	0x1869  #define USB_VENDOR_ID_ATEN		0x0557  #define USB_DEVICE_ID_ATEN_UC100KM	0x2004 @@ -1025,6 +1027,7 @@  #define USB_VENDOR_ID_SOLID_YEAR			0x060b  #define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD	0x500a +#define USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD	0x700a  #define USB_VENDOR_ID_SOUNDGRAPH	0x15c2  #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST	0x0034 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d6fab5798487..59a5608b8dc0 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -712,7 +712,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  				map_abs_clear(usage->hid & 0xf);  			break; -		case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: +		case HID_GD_WHEEL: +			if (field->flags & HID_MAIN_ITEM_RELATIVE) { +				set_bit(REL_WHEEL, input->relbit); +				map_rel(REL_WHEEL_HI_RES); +			} else { +				map_abs(usage->hid & 0xf); +			} +			break; +		case HID_GD_SLIDER: case HID_GD_DIAL:  			if (field->flags & HID_MAIN_ITEM_RELATIVE)  				map_rel(usage->hid & 0xf);  			else @@ -1012,7 +1020,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x22f: map_key_clear(KEY_ZOOMRESET);	break;  		case 0x233: map_key_clear(KEY_SCROLLUP);	break;  		case 0x234: map_key_clear(KEY_SCROLLDOWN);	break; -		case 0x238: map_rel(REL_HWHEEL);		break; +		case 0x238: /* AC Pan */ +			set_bit(REL_HWHEEL, input->relbit); +			map_rel(REL_HWHEEL_HI_RES); +			break;  		case 0x23d: map_key_clear(KEY_EDIT);		break;  		case 0x25f: map_key_clear(KEY_CANCEL);		break;  		case 0x269: map_key_clear(KEY_INSERT);		break; @@ -1200,6 +1211,38 @@ ignore:  } +static void hidinput_handle_scroll(struct hid_usage *usage, +				   struct input_dev *input, +				   __s32 value) +{ +	int code; +	int hi_res, lo_res; + +	if (value == 0) +		return; + +	if (usage->code == REL_WHEEL_HI_RES) +		code = REL_WHEEL; +	else +		code = REL_HWHEEL; + +	/* +	 * Windows reports one wheel click as value 120. Where a high-res +	 * scroll wheel is present, a fraction of 120 is reported instead. +	 * Our REL_WHEEL_HI_RES axis does the same because all HW must +	 * adhere to the 120 expectation. +	 */ +	hi_res = value * 120/usage->resolution_multiplier; + +	usage->wheel_accumulated += hi_res; +	lo_res = usage->wheel_accumulated/120; +	if (lo_res) +		usage->wheel_accumulated -= lo_res * 120; + +	input_event(input, EV_REL, code, lo_res); +	input_event(input, EV_REL, usage->code, hi_res); +} +  void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)  {  	struct input_dev *input; @@ -1262,6 +1305,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct  	if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */  		return; +	if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES || +					usage->code == REL_HWHEEL_HI_RES)) { +		hidinput_handle_scroll(usage, input, value); +		return; +	} +  	if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&  			(usage->code == ABS_VOLUME)) {  		int count = abs(value); @@ -1489,6 +1538,58 @@ static void hidinput_close(struct input_dev *dev)  	hid_hw_close(hid);  } +static void hidinput_change_resolution_multipliers(struct hid_device *hid) +{ +	struct hid_report_enum *rep_enum; +	struct hid_report *rep; +	struct hid_usage *usage; +	int i, j; + +	rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; +	list_for_each_entry(rep, &rep_enum->report_list, list) { +		bool update_needed = false; + +		if (rep->maxfield == 0) +			continue; + +		/* +		 * If we have more than one feature within this report we +		 * need to fill in the bits from the others before we can +		 * overwrite the ones for the Resolution Multiplier. +		 */ +		if (rep->maxfield > 1) { +			hid_hw_request(hid, rep, HID_REQ_GET_REPORT); +			hid_hw_wait(hid); +		} + +		for (i = 0; i < rep->maxfield; i++) { +			__s32 logical_max = rep->field[i]->logical_maximum; + +			/* There is no good reason for a Resolution +			 * Multiplier to have a count other than 1. +			 * Ignore that case. +			 */ +			if (rep->field[i]->report_count != 1) +				continue; + +			for (j = 0; j < rep->field[i]->maxusage; j++) { +				usage = &rep->field[i]->usage[j]; + +				if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) +					continue; + +				*rep->field[i]->value = logical_max; +				update_needed = true; +			} +		} +		if (update_needed) +			hid_hw_request(hid, rep, HID_REQ_SET_REPORT); +	} + +	/* refresh our structs */ +	hid_setup_resolution_multiplier(hid); +} +  static void report_features(struct hid_device *hid)  {  	struct hid_driver *drv = hid->driver; @@ -1782,6 +1883,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  		}  	} +	hidinput_change_resolution_multipliers(hid); +  	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {  		if (drv->input_configured &&  		    drv->input_configured(hid, hidinput)) @@ -1840,4 +1943,3 @@ void hidinput_disconnect(struct hid_device *hid)  	cancel_work_sync(&hid->led_work);  }  EXPORT_SYMBOL_GPL(hidinput_disconnect); - diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 643b6eb54442..eacc76d2ab96 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -743,7 +743,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)  	data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;  	data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;  	data_pointer->led_mute.dev = dev; -	led_classdev_register(dev, &data_pointer->led_mute); +	ret = led_classdev_register(dev, &data_pointer->led_mute); +	if (ret < 0) +		goto err;  	data_pointer->led_micmute.name = name_micmute;  	data_pointer->led_micmute.brightness_get = @@ -751,7 +753,11 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)  	data_pointer->led_micmute.brightness_set =  		lenovo_led_brightness_set_tpkbd;  	data_pointer->led_micmute.dev = dev; -	led_classdev_register(dev, &data_pointer->led_micmute); +	ret = led_classdev_register(dev, &data_pointer->led_micmute); +	if (ret < 0) { +		led_classdev_unregister(&data_pointer->led_mute); +		goto err; +	}  	lenovo_features_set_tpkbd(hdev); diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 19cc980eebce..15ed6177a7a3 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -21,6 +21,7 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/sched.h> +#include <linux/sched/clock.h>  #include <linux/kfifo.h>  #include <linux/input/mt.h>  #include <linux/workqueue.h> @@ -64,6 +65,14 @@ MODULE_PARM_DESC(disable_tap_to_click,  #define HIDPP_QUIRK_NO_HIDINPUT			BIT(23)  #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS	BIT(24)  #define HIDPP_QUIRK_UNIFYING			BIT(25) +#define HIDPP_QUIRK_HI_RES_SCROLL_1P0		BIT(26) +#define HIDPP_QUIRK_HI_RES_SCROLL_X2120		BIT(27) +#define HIDPP_QUIRK_HI_RES_SCROLL_X2121		BIT(28) + +/* Convenience constant to check for any high-res support. */ +#define HIDPP_QUIRK_HI_RES_SCROLL	(HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \ +					 HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \ +					 HIDPP_QUIRK_HI_RES_SCROLL_X2121)  #define HIDPP_QUIRK_DELAYED_INIT		HIDPP_QUIRK_NO_HIDINPUT @@ -128,6 +137,25 @@ struct hidpp_battery {  	bool online;  }; +/** + * struct hidpp_scroll_counter - Utility class for processing high-resolution + *                             scroll events. + * @dev: the input device for which events should be reported. + * @wheel_multiplier: the scalar multiplier to be applied to each wheel event + * @remainder: counts the number of high-resolution units moved since the last + *             low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should + *             only be used by class methods. + * @direction: direction of last movement (1 or -1) + * @last_time: last event time, used to reset remainder after inactivity + */ +struct hidpp_scroll_counter { +	struct input_dev *dev; +	int wheel_multiplier; +	int remainder; +	int direction; +	unsigned long long last_time; +}; +  struct hidpp_device {  	struct hid_device *hid_dev;  	struct mutex send_mutex; @@ -149,6 +177,7 @@ struct hidpp_device {  	unsigned long capabilities;  	struct hidpp_battery battery; +	struct hidpp_scroll_counter vertical_wheel_counter;  };  /* HID++ 1.0 error codes */ @@ -391,6 +420,67 @@ static void hidpp_prefix_name(char **name, int name_length)  	*name = new_name;  } +/** + * hidpp_scroll_counter_handle_scroll() - Send high- and low-resolution scroll + *                                        events given a high-resolution wheel + *                                        movement. + * @counter: a hid_scroll_counter struct describing the wheel. + * @hi_res_value: the movement of the wheel, in the mouse's high-resolution + *                units. + * + * Given a high-resolution movement, this function converts the movement into + * fractions of 120 and emits high-resolution scroll events for the input + * device. It also uses the multiplier from &struct hid_scroll_counter to + * emit low-resolution scroll events when appropriate for + * backwards-compatibility with userspace input libraries. + */ +static void hidpp_scroll_counter_handle_scroll(struct hidpp_scroll_counter *counter, +					       int hi_res_value) +{ +	int low_res_value, remainder, direction; +	unsigned long long now, previous; + +	hi_res_value = hi_res_value * 120/counter->wheel_multiplier; +	input_report_rel(counter->dev, REL_WHEEL_HI_RES, hi_res_value); + +	remainder = counter->remainder; +	direction = hi_res_value > 0 ? 1 : -1; + +	now = sched_clock(); +	previous = counter->last_time; +	counter->last_time = now; +	/* +	 * Reset the remainder after a period of inactivity or when the +	 * direction changes. This prevents the REL_WHEEL emulation point +	 * from sliding for devices that don't always provide the same +	 * number of movements per detent. +	 */ +	if (now - previous > 1000000000 || direction != counter->direction) +		remainder = 0; + +	counter->direction = direction; +	remainder += hi_res_value; + +	/* Some wheels will rest 7/8ths of a detent from the previous detent +	 * after slow movement, so we want the threshold for low-res events to +	 * be in the middle between two detents (e.g. after 4/8ths) as +	 * opposed to on the detents themselves (8/8ths). +	 */ +	if (abs(remainder) >= 60) { +		/* Add (or subtract) 1 because we want to trigger when the wheel +		 * is half-way to the next detent (i.e. scroll 1 detent after a +		 * 1/2 detent movement, 2 detents after a 1 1/2 detent movement, +		 * etc.). +		 */ +		low_res_value = remainder / 120; +		if (low_res_value == 0) +			low_res_value = (hi_res_value > 0 ? 1 : -1); +		input_report_rel(counter->dev, REL_WHEEL, low_res_value); +		remainder -= low_res_value * 120; +	} +	counter->remainder = remainder; +} +  /* -------------------------------------------------------------------------- */  /* HIDP++ 1.0 commands                                                        */  /* -------------------------------------------------------------------------- */ @@ -400,32 +490,53 @@ static void hidpp_prefix_name(char **name, int name_length)  #define HIDPP_SET_LONG_REGISTER				0x82  #define HIDPP_GET_LONG_REGISTER				0x83 -#define HIDPP_REG_GENERAL				0x00 - -static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev) +/** + * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register. + * @hidpp_dev: the device to set the register on. + * @register_address: the address of the register to modify. + * @byte: the byte of the register to modify. Should be less than 3. + * Return: 0 if successful, otherwise a negative error code. + */ +static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev, +	u8 register_address, u8 byte, u8 bit)  {  	struct hidpp_report response;  	int ret;  	u8 params[3] = { 0 };  	ret = hidpp_send_rap_command_sync(hidpp_dev, -					REPORT_ID_HIDPP_SHORT, -					HIDPP_GET_REGISTER, -					HIDPP_REG_GENERAL, -					NULL, 0, &response); +					  REPORT_ID_HIDPP_SHORT, +					  HIDPP_GET_REGISTER, +					  register_address, +					  NULL, 0, &response);  	if (ret)  		return ret;  	memcpy(params, response.rap.params, 3); -	/* Set the battery bit */ -	params[0] |= BIT(4); +	params[byte] |= BIT(bit);  	return hidpp_send_rap_command_sync(hidpp_dev, -					REPORT_ID_HIDPP_SHORT, -					HIDPP_SET_REGISTER, -					HIDPP_REG_GENERAL, -					params, 3, &response); +					   REPORT_ID_HIDPP_SHORT, +					   HIDPP_SET_REGISTER, +					   register_address, +					   params, 3, &response); +} + + +#define HIDPP_REG_GENERAL				0x00 + +static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev) +{ +	return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4); +} + +#define HIDPP_REG_FEATURES				0x01 + +/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */ +static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev) +{ +	return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);  }  #define HIDPP_REG_BATTERY_STATUS			0x07 @@ -1137,6 +1248,99 @@ static int hidpp_battery_get_property(struct power_supply *psy,  }  /* -------------------------------------------------------------------------- */ +/* 0x2120: Hi-resolution scrolling                                            */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING			0x2120 + +#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE	0x10 + +static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp, +	bool enabled, u8 *multiplier) +{ +	u8 feature_index; +	u8 feature_type; +	int ret; +	u8 params[1]; +	struct hidpp_report response; + +	ret = hidpp_root_get_feature(hidpp, +				     HIDPP_PAGE_HI_RESOLUTION_SCROLLING, +				     &feature_index, +				     &feature_type); +	if (ret) +		return ret; + +	params[0] = enabled ? BIT(0) : 0; +	ret = hidpp_send_fap_command_sync(hidpp, feature_index, +					  CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE, +					  params, sizeof(params), &response); +	if (ret) +		return ret; +	*multiplier = response.fap.params[1]; +	return 0; +} + +/* -------------------------------------------------------------------------- */ +/* 0x2121: HiRes Wheel                                                        */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_HIRES_WHEEL		0x2121 + +#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY	0x00 +#define CMD_HIRES_WHEEL_SET_WHEEL_MODE		0x20 + +static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp, +	u8 *multiplier) +{ +	u8 feature_index; +	u8 feature_type; +	int ret; +	struct hidpp_report response; + +	ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, +				     &feature_index, &feature_type); +	if (ret) +		goto return_default; + +	ret = hidpp_send_fap_command_sync(hidpp, feature_index, +					  CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY, +					  NULL, 0, &response); +	if (ret) +		goto return_default; + +	*multiplier = response.fap.params[0]; +	return 0; +return_default: +	hid_warn(hidpp->hid_dev, +		 "Couldn't get wheel multiplier (error %d)\n", ret); +	return ret; +} + +static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert, +	bool high_resolution, bool use_hidpp) +{ +	u8 feature_index; +	u8 feature_type; +	int ret; +	u8 params[1]; +	struct hidpp_report response; + +	ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, +				     &feature_index, &feature_type); +	if (ret) +		return ret; + +	params[0] = (invert          ? BIT(2) : 0) | +		    (high_resolution ? BIT(1) : 0) | +		    (use_hidpp       ? BIT(0) : 0); + +	return hidpp_send_fap_command_sync(hidpp, feature_index, +					   CMD_HIRES_WHEEL_SET_WHEEL_MODE, +					   params, sizeof(params), &response); +} + +/* -------------------------------------------------------------------------- */  /* 0x4301: Solar Keyboard                                                     */  /* -------------------------------------------------------------------------- */ @@ -1465,7 +1669,7 @@ struct hidpp_ff_work_data {  	u8 size;  }; -static const signed short hiddpp_ff_effects[] = { +static const signed short hidpp_ff_effects[] = {  	FF_CONSTANT,  	FF_PERIODIC,  	FF_SINE, @@ -1480,7 +1684,7 @@ static const signed short hiddpp_ff_effects[] = {  	-1  }; -static const signed short hiddpp_ff_effects_v2[] = { +static const signed short hidpp_ff_effects_v2[] = {  	FF_RAMP,  	FF_FRICTION,  	FF_INERTIA, @@ -1873,11 +2077,11 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)  	version = bcdDevice & 255;  	/* Set supported force feedback capabilities */ -	for (j = 0; hiddpp_ff_effects[j] >= 0; j++) -		set_bit(hiddpp_ff_effects[j], dev->ffbit); +	for (j = 0; hidpp_ff_effects[j] >= 0; j++) +		set_bit(hidpp_ff_effects[j], dev->ffbit);  	if (version > 1) -		for (j = 0; hiddpp_ff_effects_v2[j] >= 0; j++) -			set_bit(hiddpp_ff_effects_v2[j], dev->ffbit); +		for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++) +			set_bit(hidpp_ff_effects_v2[j], dev->ffbit);  	/* Read number of slots available in device */  	error = hidpp_send_fap_command_sync(hidpp, feature_index, @@ -2387,10 +2591,15 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)  		input_report_key(mydata->input, BTN_RIGHT,  			!!(data[1] & M560_MOUSE_BTN_RIGHT)); -		if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) +		if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) {  			input_report_rel(mydata->input, REL_HWHEEL, -1); -		else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) +			input_report_rel(mydata->input, REL_HWHEEL_HI_RES, +					 -120); +		} else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) {  			input_report_rel(mydata->input, REL_HWHEEL, 1); +			input_report_rel(mydata->input, REL_HWHEEL_HI_RES, +					 120); +		}  		v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);  		input_report_rel(mydata->input, REL_X, v); @@ -2399,7 +2608,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)  		input_report_rel(mydata->input, REL_Y, v);  		v = hid_snto32(data[6], 8); -		input_report_rel(mydata->input, REL_WHEEL, v); +		hidpp_scroll_counter_handle_scroll( +				&hidpp->vertical_wheel_counter, v);  		input_sync(mydata->input);  	} @@ -2426,6 +2636,8 @@ static void m560_populate_input(struct hidpp_device *hidpp,  	__set_bit(REL_Y, mydata->input->relbit);  	__set_bit(REL_WHEEL, mydata->input->relbit);  	__set_bit(REL_HWHEEL, mydata->input->relbit); +	__set_bit(REL_WHEEL_HI_RES, mydata->input->relbit); +	__set_bit(REL_HWHEEL_HI_RES, mydata->input->relbit);  }  static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -2528,6 +2740,37 @@ static int g920_get_config(struct hidpp_device *hidpp)  }  /* -------------------------------------------------------------------------- */ +/* High-resolution scroll wheels                                              */ +/* -------------------------------------------------------------------------- */ + +static int hi_res_scroll_enable(struct hidpp_device *hidpp) +{ +	int ret; +	u8 multiplier = 1; + +	if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) { +		ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false); +		if (ret == 0) +			ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier); +	} else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) { +		ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true, +							   &multiplier); +	} else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */ { +		ret = hidpp10_enable_scrolling_acceleration(hidpp); +		multiplier = 8; +	} +	if (ret) +		return ret; + +	if (multiplier == 0) +		multiplier = 1; + +	hidpp->vertical_wheel_counter.wheel_multiplier = multiplier; +	hid_info(hidpp->hid_dev, "multiplier = %d\n", multiplier); +	return 0; +} + +/* -------------------------------------------------------------------------- */  /* Generic HID++ devices                                                      */  /* -------------------------------------------------------------------------- */ @@ -2572,6 +2815,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,  		wtp_populate_input(hidpp, input, origin_is_hid_core);  	else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)  		m560_populate_input(hidpp, input, origin_is_hid_core); + +	if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) +		hidpp->vertical_wheel_counter.dev = input;  }  static int hidpp_input_configured(struct hid_device *hdev, @@ -2690,6 +2936,27 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,  	return 0;  } +static int hidpp_event(struct hid_device *hdev, struct hid_field *field, +	struct hid_usage *usage, __s32 value) +{ +	/* This function will only be called for scroll events, due to the +	 * restriction imposed in hidpp_usages. +	 */ +	struct hidpp_device *hidpp = hid_get_drvdata(hdev); +	struct hidpp_scroll_counter *counter = &hidpp->vertical_wheel_counter; +	/* A scroll event may occur before the multiplier has been retrieved or +	 * the input device set, or high-res scroll enabling may fail. In such +	 * cases we must return early (falling back to default behaviour) to +	 * avoid a crash in hidpp_scroll_counter_handle_scroll. +	 */ +	if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0 +	    || counter->dev == NULL || counter->wheel_multiplier == 0) +		return 0; + +	hidpp_scroll_counter_handle_scroll(counter, value); +	return 1; +} +  static int hidpp_initialize_battery(struct hidpp_device *hidpp)  {  	static atomic_t battery_no = ATOMIC_INIT(0); @@ -2901,6 +3168,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)  	if (hidpp->battery.ps)  		power_supply_changed(hidpp->battery.ps); +	if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) +		hi_res_scroll_enable(hidpp); +  	if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)  		/* if the input nodes are already created, we can stop now */  		return; @@ -3086,35 +3356,63 @@ static void hidpp_remove(struct hid_device *hdev)  	mutex_destroy(&hidpp->send_mutex);  } +#define LDJ_DEVICE(product) \ +	HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \ +		   USB_VENDOR_ID_LOGITECH, (product)) +  static const struct hid_device_id hidpp_devices[] = {  	{ /* wireless touchpad */ -	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, -		USB_VENDOR_ID_LOGITECH, 0x4011), +	  LDJ_DEVICE(0x4011),  	  .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |  			 HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },  	{ /* wireless touchpad T650 */ -	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, -		USB_VENDOR_ID_LOGITECH, 0x4101), +	  LDJ_DEVICE(0x4101),  	  .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },  	{ /* wireless touchpad T651 */  	  HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,  		USB_DEVICE_ID_LOGITECH_T651),  	  .driver_data = HIDPP_QUIRK_CLASS_WTP }, +	{ /* Mouse Logitech Anywhere MX */ +	  LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, +	{ /* Mouse Logitech Cube */ +	  LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, +	{ /* Mouse Logitech M335 */ +	  LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech M515 */ +	  LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },  	{ /* Mouse logitech M560 */ -	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, -		USB_VENDOR_ID_LOGITECH, 0x402d), -	  .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, +	  LDJ_DEVICE(0x402d), +	  .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 +		| HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, +	{ /* Mouse Logitech M705 (firmware RQM17) */ +	  LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, +	{ /* Mouse Logitech M705 (firmware RQM67) */ +	  LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech M720 */ +	  LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech MX Anywhere 2 */ +	  LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech MX Anywhere 2S */ +	  LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech MX Master */ +	  LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech MX Master 2S */ +	  LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, +	{ /* Mouse Logitech Performance MX */ +	  LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },  	{ /* Keyboard logitech K400 */ -	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, -		USB_VENDOR_ID_LOGITECH, 0x4024), +	  LDJ_DEVICE(0x4024),  	  .driver_data = HIDPP_QUIRK_CLASS_K400 },  	{ /* Solar Keyboard Logitech K750 */ -	  HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, -		USB_VENDOR_ID_LOGITECH, 0x4002), +	  LDJ_DEVICE(0x4002),  	  .driver_data = HIDPP_QUIRK_CLASS_K750 }, -	{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, -		USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, +	{ LDJ_DEVICE(HID_ANY_ID) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),  		.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, @@ -3123,12 +3421,19 @@ static const struct hid_device_id hidpp_devices[] = {  MODULE_DEVICE_TABLE(hid, hidpp_devices); +static const struct hid_usage_id hidpp_usages[] = { +	{ HID_GD_WHEEL, EV_REL, REL_WHEEL_HI_RES }, +	{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; +  static struct hid_driver hidpp_driver = {  	.name = "logitech-hidpp-device",  	.id_table = hidpp_devices,  	.probe = hidpp_probe,  	.remove = hidpp_remove,  	.raw_event = hidpp_raw_event, +	.usage_table = hidpp_usages, +	.event = hidpp_event,  	.input_configured = hidpp_input_configured,  	.input_mapping = hidpp_input_mapping,  	.input_mapped = hidpp_input_mapped, diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 4a44e48e08b2..9fc51eff1079 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -107,8 +107,6 @@ out:  /*   * The first byte of the report buffer is expected to be a report number. - * - * This function is to be called with the minors_lock mutex held.   */  static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)  { @@ -117,6 +115,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,  	__u8 *buf;  	int ret = 0; +	lockdep_assert_held(&minors_lock); +  	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {  		ret = -ENODEV;  		goto out; @@ -181,8 +181,6 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t   * of buffer is the report number to request, or 0x0 if the defice does not   * use numbered reports. The report_type parameter can be HID_FEATURE_REPORT   * or HID_INPUT_REPORT. - * - * This function is to be called with the minors_lock mutex held.   */  static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)  { @@ -192,6 +190,8 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t  	int ret = 0, len;  	unsigned char report_number; +	lockdep_assert_held(&minors_lock); +  	if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {  		ret = -ENODEV;  		goto out; diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c index 89f2976f9c53..fd1b6eea6d2f 100644 --- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c +++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c @@ -346,6 +346,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {  		},  		.driver_data = (void *)&sipodev_desc  	}, +	{ +		.ident = "Odys Winbook 13", +		.matches = { +			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AXDIA International GmbH"), +			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "WINBOOK 13"), +		}, +		.driver_data = (void *)&sipodev_desc +	},  	{ }	/* Terminate list */  }; diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 8793cc49f855..a6e1ee744f4d 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -117,6 +117,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  {  	int ret;  	struct ish_hw *hw; +	unsigned long irq_flag = 0;  	struct ishtp_device *ishtp;  	struct device *dev = &pdev->dev; @@ -156,8 +157,12 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;  	/* request and enable interrupt */ +	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); +	if (!pdev->msi_enabled && !pdev->msix_enabled) +		irq_flag = IRQF_SHARED; +  	ret = devm_request_irq(dev, pdev->irq, ish_irq_handler, -			       IRQF_SHARED, KBUILD_MODNAME, ishtp); +			       irq_flag, KBUILD_MODNAME, ishtp);  	if (ret) {  		dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);  		return ret; diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.c b/drivers/hid/intel-ish-hid/ishtp-hid.c index cd23903ddcf1..e918d78e541c 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid.c @@ -222,7 +222,7 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,  err_hid_device:  	kfree(hid_data);  err_hid_data: -	kfree(hid); +	hid_destroy_device(hid);  	return rv;  } |