diff options
Diffstat (limited to 'drivers/hid')
51 files changed, 5953 insertions, 1361 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 71b70e3a7a7..5e79c6ad914 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -175,6 +175,15 @@ config HID_PRODIKEYS  	  multimedia keyboard, but will lack support for the musical keyboard  	  and some additional multimedia keys. +config HID_CP2112 +	tristate "Silicon Labs CP2112 HID USB-to-SMBus Bridge support" +	depends on USB_HID && I2C && GPIOLIB +	---help--- +	Support for Silicon Labs CP2112 HID USB to SMBus Master Bridge. +	This is a HID device driver which registers as an i2c adapter +	and gpiochip to expose these functions of the CP2112. The +	customizable USB descriptor fields are exposed as sysfs attributes. +  config HID_CYPRESS  	tristate "Cypress mouse and barcode readers" if EXPERT  	depends on HID @@ -241,6 +250,8 @@ config HID_HOLTEK  	  - Sharkoon Drakonia / Perixx MX-2000 gaming mice  	  - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 /  	    Zalman ZM-GM1 +	  - SHARKOON DarkGlider Gaming mouse +	  - LEETGION Hellion Gaming Mouse  config HOLTEK_FF  	bool "Holtek On Line Grip force feedback support" @@ -322,7 +333,7 @@ config HID_LCPOWER  config HID_LENOVO_TPKBD  	tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" -	depends on USB_HID +	depends on HID  	select NEW_LEDS  	select LEDS_CLASS  	---help--- @@ -342,6 +353,7 @@ config HID_LOGITECH  config HID_LOGITECH_DJ  	tristate "Logitech Unifying receivers full support" +	depends on HIDRAW  	depends on HID_LOGITECH  	---help---  	Say Y if you want support for Logitech Unifying receivers and devices. @@ -361,19 +373,20 @@ config LOGITECH_FF  	  - Logitech WingMan Force 3D  	  - Logitech Formula Force EX  	  - Logitech WingMan Formula Force GP -	  - Logitech MOMO Force wheel  	  and if you want to enable force feedback for them.  	  Note: if you say N here, this device will still be supported, but without  	  force feedback.  config LOGIRUMBLEPAD2_FF -	bool "Logitech RumblePad/Rumblepad 2 force feedback support" +	bool "Logitech force feedback support (variant 2)"  	depends on HID_LOGITECH  	select INPUT_FF_MEMLESS  	help -	  Say Y here if you want to enable force feedback support for Logitech -	  RumblePad and Rumblepad 2 devices. +	  Say Y here if you want to enable force feedback support for: +	  - Logitech RumblePad +	  - Logitech Rumblepad 2 +	  - Logitech Formula Vibration Feedback Wheel  config LOGIG940_FF  	bool "Logitech Flight System G940 force feedback support" @@ -436,6 +449,7 @@ config HID_MULTITOUCH  	  - Chunghwa panels  	  - CVTouch panels  	  - Cypress TrueTouch panels +	  - Elan Microelectronics touch panels  	  - Elo TouchSystems IntelliTouch Plus panels  	  - GeneralTouch 'Sensing Win7-TwoFinger' panels  	  - GoodTouch panels @@ -452,9 +466,11 @@ config HID_MULTITOUCH  	  - Pixcir dual touch panels  	  - Quanta panels  	  - eGalax dual-touch panels, including the Joojoo and Wetab tablets +	  - SiS multitouch panels  	  - Stantum multitouch panels  	  - Touch International Panels  	  - Unitec Panels +	  - Wistron optical touch panels  	  - XAT optical touch panels  	  - Xiroku optical touch panels  	  - Zytronic touch panels @@ -592,7 +608,10 @@ config HID_SAITEK  	Support for Saitek devices that are not fully compliant with the  	HID standard. -	Currently only supports the PS1000 controller. +	Supported devices: +	- PS1000 Dual Analog Pad +	- R.A.T.7 Gaming Mouse +	- M.M.O.7 Gaming Mouse  config HID_SAMSUNG  	tristate "Samsung InfraRed remote control or keyboards" @@ -601,18 +620,28 @@ config HID_SAMSUNG  	Support for Samsung InfraRed remote control or keyboards.  config HID_SONY -	tristate "Sony PS2/3 accessories" +	tristate "Sony PS2/3/4 accessories"  	depends on USB_HID  	depends on NEW_LEDS  	depends on LEDS_CLASS +	select POWER_SUPPLY  	---help---  	Support for  	  * Sony PS3 6-axis controllers +	  * Sony PS4 DualShock 4 controllers  	  * Buzz controllers  	  * Sony PS3 Blue-ray Disk Remote Control (Bluetooth)  	  * Logitech Harmony adapter for Sony Playstation 3 (Bluetooth) +config SONY_FF +	bool "Sony PS2/3/4 accessories force feedback support"  +	depends on HID_SONY +	select INPUT_FF_MEMLESS +	---help--- +	Say Y here if you have a Sony PS2/3/4 accessory and want to enable +	force feedback support for it. +  config HID_SPEEDLINK  	tristate "Speedlink VAD Cezanne mouse support"  	depends on HID @@ -631,6 +660,14 @@ config HID_SUNPLUS  	---help---  	Support for Sunplus wireless desktop. +config HID_RMI +	tristate "Synaptics RMI4 device support" +	depends on HID +	---help--- +	Support for Synaptics RMI4 touchpads. +	Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid +	and want support for its special functionalities. +  config HID_GREENASIA  	tristate "GreenAsia (Product ID 0x12) game controller support"  	depends on HID @@ -773,7 +810,7 @@ config HID_ZYDACRON  config HID_SENSOR_HUB  	tristate "HID Sensors framework support" -	depends on HID +	depends on HID && HAS_IOMEM  	select MFD_CORE  	default n  	---help--- diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a959f4aecaf..a6fa6baf368 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_HID_AUREAL)        += hid-aureal.o  obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o  obj-$(CONFIG_HID_CHERRY)	+= hid-cherry.o  obj-$(CONFIG_HID_CHICONY)	+= hid-chicony.o +obj-$(CONFIG_HID_CP2112)	+= hid-cp2112.o  obj-$(CONFIG_HID_CYPRESS)	+= hid-cypress.o  obj-$(CONFIG_HID_DRAGONRISE)	+= hid-dr.o  obj-$(CONFIG_HID_EMS_FF)	+= hid-emsff.o @@ -95,7 +96,8 @@ obj-$(CONFIG_HID_PRIMAX)	+= hid-primax.o  obj-$(CONFIG_HID_ROCCAT)	+= hid-roccat.o hid-roccat-common.o \  	hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \  	hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \ -	hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-savu.o +	hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o +obj-$(CONFIG_HID_RMI)		+= hid-rmi.o  obj-$(CONFIG_HID_SAITEK)	+= hid-saitek.o  obj-$(CONFIG_HID_SAMSUNG)	+= hid-samsung.o  obj-$(CONFIG_HID_SMARTJOYPLUS)	+= hid-sjoy.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 881cf7b4f9a..f822fd2a1ad 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -46,6 +46,12 @@ module_param(iso_layout, uint, 0644);  MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. "  		"(0 = disabled, [1] = enabled)"); +static unsigned int swap_opt_cmd; +module_param(swap_opt_cmd, uint, 0644); +MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") keys. " +		"(For people who want to keep Windows PC keyboard muscle memory. " +		"[0] = as-is, Mac layout. 1 = swapped, Windows layout.)"); +  struct apple_sc {  	unsigned long quirks;  	unsigned int fn_on; @@ -150,6 +156,14 @@ static const struct apple_key_translation apple_iso_keyboard[] = {  	{ }  }; +static const struct apple_key_translation swapped_option_cmd_keys[] = { +	{ KEY_LEFTALT,	KEY_LEFTMETA }, +	{ KEY_LEFTMETA,	KEY_LEFTALT }, +	{ KEY_RIGHTALT,	KEY_RIGHTMETA }, +	{ KEY_RIGHTMETA,KEY_RIGHTALT }, +	{ } +}; +  static const struct apple_key_translation *apple_find_translation(  		const struct apple_key_translation *table, u16 from)  { @@ -242,6 +256,14 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,  		}  	} +	if (swap_opt_cmd) { +		trans = apple_find_translation(swapped_option_cmd_keys, usage->code); +		if (trans) { +			input_event(input, usage->type, trans->to, value); +			return 1; +		} +	} +  	return 0;  } @@ -447,6 +469,9 @@ static const struct hid_device_id apple_devices[] = {  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,  				USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI),  		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, +				USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS), +		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),  		.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },  	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index a42e6a394c5..0e6a42d37eb 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -297,6 +297,9 @@ static int appleir_probe(struct hid_device *hid, const struct hid_device_id *id)  	appleir->hid = hid; +	/* force input as some remotes bypass the input registration */ +	hid->quirks |= HID_QUIRK_HIDINPUT_FORCE; +  	spin_lock_init(&appleir->lock);  	setup_timer(&appleir->key_up_timer,  		    key_up_tick, (unsigned long) appleir); diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c index 64ab94a55aa..a594e478a1e 100644 --- a/drivers/hid/hid-axff.c +++ b/drivers/hid/hid-axff.c @@ -95,7 +95,7 @@ static int axff_init(struct hid_device *hid)  		}  	} -	if (field_count < 4) { +	if (field_count < 4 && hid->product != 0xf705) {  		hid_err(hid, "not enough fields in the report: %d\n",  			field_count);  		return -ENODEV; @@ -180,6 +180,7 @@ static void ax_remove(struct hid_device *hdev)  static const struct hid_device_id ax_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705), },  	{ }  };  MODULE_DEVICE_TABLE(hid, ax_devices); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b8470b1a10f..8ed66fd1ea8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -319,7 +319,7 @@ static s32 item_sdata(struct hid_item *item)  static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)  { -	__u32 raw_value; +	__s32 raw_value;  	switch (item->tag) {  	case HID_GLOBAL_ITEM_TAG_PUSH: @@ -370,10 +370,11 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)  		return 0;  	case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: -		/* Units exponent negative numbers are given through a -		 * two's complement. -		 * See "6.2.2.7 Global Items" for more information. */ -		raw_value = item_udata(item); +		/* Many devices provide unit exponent as a two's complement +		 * nibble due to the common misunderstanding of HID +		 * specification 1.11, 6.2.2.7 Global Items. Attempt to handle +		 * both this and the standard encoding. */ +		raw_value = item_sdata(item);  		if (!(raw_value & 0xfffffff0))  			parser->global.unit_exponent = hid_snto32(raw_value, 4);  		else @@ -717,6 +718,9 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)  	case HID_MAIN_ITEM_TAG_END_COLLECTION:  		break;  	case HID_MAIN_ITEM_TAG_INPUT: +		/* ignore constant inputs, they will be ignored by hid-input */ +		if (data & HID_MAIN_ITEM_CONSTANT) +			break;  		for (i = 0; i < parser->local.usage_index; i++)  			hid_scan_input_usage(parser, parser->local.usage[i]);  		break; @@ -775,6 +779,14 @@ static int hid_scan_report(struct hid_device *hid)  	    (hid->group == HID_GROUP_MULTITOUCH))  		hid->group = HID_GROUP_MULTITOUCH_WIN_8; +	/* +	* Vendor specific handlings +	*/ +	if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && +	    (hid->group == HID_GROUP_GENERIC)) +		/* hid-rmi should take care of them, not hid-generic */ +		hid->group = HID_GROUP_RMI; +  	vfree(parser);  	return 0;  } @@ -838,7 +850,17 @@ struct hid_report *hid_validate_values(struct hid_device *hid,  	 * ->numbered being checked, which may not always be the case when  	 * drivers go to access report values.  	 */ -	report = hid->report_enum[type].report_id_hash[id]; +	if (id == 0) { +		/* +		 * Validating on id 0 means we should examine the first +		 * report in the list. +		 */ +		report = list_entry( +				hid->report_enum[type].report_list.next, +				struct hid_report, list); +	} else { +		report = hid->report_enum[type].report_id_hash[id]; +	}  	if (!report) {  		hid_err(hid, "missing %s %u\n", hid_report_names[type], id);  		return NULL; @@ -1247,6 +1269,12 @@ void hid_output_report(struct hid_report *report, __u8 *data)  }  EXPORT_SYMBOL_GPL(hid_output_report); +static int hid_report_len(struct hid_report *report) +{ +	/* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ +	return ((report->size - 1) >> 3) + 1 + (report->id > 0); +} +  /*   * Allocator for buffer that is going to be passed to hid_output_report()   */ @@ -1257,7 +1285,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)  	 * of implement() working on 8 byte chunks  	 */ -	int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; +	int len = hid_report_len(report) + 7;  	return kmalloc(len, flags);  } @@ -1313,6 +1341,41 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,  	return report;  } +/* + * Implement a generic .request() callback, using .raw_request() + * DO NOT USE in hid drivers directly, but through hid_hw_request instead. + */ +void __hid_request(struct hid_device *hid, struct hid_report *report, +		int reqtype) +{ +	char *buf; +	int ret; +	int len; + +	buf = hid_alloc_report_buf(report, GFP_KERNEL); +	if (!buf) +		return; + +	len = hid_report_len(report); + +	if (reqtype == HID_REQ_SET_REPORT) +		hid_output_report(report, buf); + +	ret = hid->ll_driver->raw_request(hid, report->id, buf, len, +					  report->type, reqtype); +	if (ret < 0) { +		dbg_hid("unable to complete request: %d\n", ret); +		goto out; +	} + +	if (reqtype == HID_REQ_GET_REPORT) +		hid_input_report(hid, report->type, buf, ret, 0); + +out: +	kfree(buf); +} +EXPORT_SYMBOL_GPL(__hid_request); +  int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,  		int interrupt)  { @@ -1417,10 +1480,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i  	if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {  		ret = hdrv->raw_event(hid, report, data, size); -		if (ret < 0) { -			ret = ret < 0 ? ret : 0; +		if (ret < 0)  			goto unlock; -		}  	}  	ret = hid_report_raw_event(hid, type, data, size, interrupt); @@ -1604,6 +1665,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, @@ -1679,6 +1741,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, @@ -1692,6 +1755,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, @@ -1715,12 +1779,16 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, @@ -1752,6 +1820,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, @@ -1774,6 +1843,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) }, @@ -1799,19 +1869,28 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },  #if IS_ENABLED(CONFIG_HID_ROCCAT) -	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEXTD) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },  #endif +#if IS_ENABLED(CONFIG_HID_SAITEK)  	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, +#endif  	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, @@ -1821,6 +1900,8 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) }, @@ -2106,6 +2187,7 @@ static const struct hid_device_id hid_ignore_list[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI470X) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_RADIO_SI4713) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) }, @@ -2262,6 +2344,7 @@ static const struct hid_device_id hid_ignore_list[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },  	{ }  }; @@ -2373,15 +2456,6 @@ bool hid_ignore(struct hid_device *hdev)  				hdev->type == HID_TYPE_USBNONE)  			return true;  		break; -	case USB_VENDOR_ID_DWAV: -		/* These are handled by usbtouchscreen. hdev->type is probably -		 * HID_TYPE_USBNONE, but we say !HID_TYPE_USBMOUSE to match -		 * usbtouchscreen. */ -		if ((hdev->product == USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER || -		     hdev->product == USB_DEVICE_ID_DWAV_TOUCHCONTROLLER) && -		    hdev->type != HID_TYPE_USBMOUSE) -			return true; -		break;  	case USB_VENDOR_ID_VELLEMAN:  		/* These are not HID devices.  They are handled by comedi. */  		if ((hdev->product >= USB_DEVICE_ID_VELLEMAN_K8055_FIRST && @@ -2426,6 +2500,14 @@ int hid_add_device(struct hid_device *hdev)  		return -ENODEV;  	/* +	 * Check for the mandatory transport channel. +	 */ +	 if (!hdev->ll_driver->raw_request) { +		hid_err(hdev, "transport driver missing .raw_request()\n"); +		return -EINVAL; +	 } + +	/*  	 * Read the device report descriptor once and use as template  	 * for the driver-specific modifications.  	 */ diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c new file mode 100644 index 00000000000..56be85a9a77 --- /dev/null +++ b/drivers/hid/hid-cp2112.c @@ -0,0 +1,1073 @@ +/* + * hid-cp2112.c - Silicon Labs HID USB to SMBus master bridge + * Copyright (c) 2013,2014 Uplogix, Inc. + * David Barksdale <dbarksdale@uplogix.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + */ + +/* + * The Silicon Labs CP2112 chip is a USB HID device which provides an + * SMBus controller for talking to slave devices and 8 GPIO pins. The + * host communicates with the CP2112 via raw HID reports. + * + * Data Sheet: + *   http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf + * Programming Interface Specification: + *   http://www.silabs.com/Support%20Documents/TechnicalDocs/AN495.pdf + */ + +#include <linux/gpio.h> +#include <linux/hid.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/nls.h> +#include <linux/usb/ch9.h> +#include "hid-ids.h" + +enum { +	CP2112_GPIO_CONFIG		= 0x02, +	CP2112_GPIO_GET			= 0x03, +	CP2112_GPIO_SET			= 0x04, +	CP2112_GET_VERSION_INFO		= 0x05, +	CP2112_SMBUS_CONFIG		= 0x06, +	CP2112_DATA_READ_REQUEST	= 0x10, +	CP2112_DATA_WRITE_READ_REQUEST	= 0x11, +	CP2112_DATA_READ_FORCE_SEND	= 0x12, +	CP2112_DATA_READ_RESPONSE	= 0x13, +	CP2112_DATA_WRITE_REQUEST	= 0x14, +	CP2112_TRANSFER_STATUS_REQUEST	= 0x15, +	CP2112_TRANSFER_STATUS_RESPONSE	= 0x16, +	CP2112_CANCEL_TRANSFER		= 0x17, +	CP2112_LOCK_BYTE		= 0x20, +	CP2112_USB_CONFIG		= 0x21, +	CP2112_MANUFACTURER_STRING	= 0x22, +	CP2112_PRODUCT_STRING		= 0x23, +	CP2112_SERIAL_STRING		= 0x24, +}; + +enum { +	STATUS0_IDLE		= 0x00, +	STATUS0_BUSY		= 0x01, +	STATUS0_COMPLETE	= 0x02, +	STATUS0_ERROR		= 0x03, +}; + +enum { +	STATUS1_TIMEOUT_NACK		= 0x00, +	STATUS1_TIMEOUT_BUS		= 0x01, +	STATUS1_ARBITRATION_LOST	= 0x02, +	STATUS1_READ_INCOMPLETE		= 0x03, +	STATUS1_WRITE_INCOMPLETE	= 0x04, +	STATUS1_SUCCESS			= 0x05, +}; + +struct cp2112_smbus_config_report { +	u8 report;		/* CP2112_SMBUS_CONFIG */ +	__be32 clock_speed;	/* Hz */ +	u8 device_address;	/* Stored in the upper 7 bits */ +	u8 auto_send_read;	/* 1 = enabled, 0 = disabled */ +	__be16 write_timeout;	/* ms, 0 = no timeout */ +	__be16 read_timeout;	/* ms, 0 = no timeout */ +	u8 scl_low_timeout;	/* 1 = enabled, 0 = disabled */ +	__be16 retry_time;	/* # of retries, 0 = no limit */ +} __packed; + +struct cp2112_usb_config_report { +	u8 report;	/* CP2112_USB_CONFIG */ +	__le16 vid;	/* Vendor ID */ +	__le16 pid;	/* Product ID */ +	u8 max_power;	/* Power requested in 2mA units */ +	u8 power_mode;	/* 0x00 = bus powered +			   0x01 = self powered & regulator off +			   0x02 = self powered & regulator on */ +	u8 release_major; +	u8 release_minor; +	u8 mask;	/* What fields to program */ +} __packed; + +struct cp2112_read_req_report { +	u8 report;	/* CP2112_DATA_READ_REQUEST */ +	u8 slave_address; +	__be16 length; +} __packed; + +struct cp2112_write_read_req_report { +	u8 report;	/* CP2112_DATA_WRITE_READ_REQUEST */ +	u8 slave_address; +	__be16 length; +	u8 target_address_length; +	u8 target_address[16]; +} __packed; + +struct cp2112_write_req_report { +	u8 report;	/* CP2112_DATA_WRITE_REQUEST */ +	u8 slave_address; +	u8 length; +	u8 data[61]; +} __packed; + +struct cp2112_force_read_report { +	u8 report;	/* CP2112_DATA_READ_FORCE_SEND */ +	__be16 length; +} __packed; + +struct cp2112_xfer_status_report { +	u8 report;	/* CP2112_TRANSFER_STATUS_RESPONSE */ +	u8 status0;	/* STATUS0_* */ +	u8 status1;	/* STATUS1_* */ +	__be16 retries; +	__be16 length; +} __packed; + +struct cp2112_string_report { +	u8 dummy;		/* force .string to be aligned */ +	u8 report;		/* CP2112_*_STRING */ +	u8 length;		/* length in bytes of everyting after .report */ +	u8 type;		/* USB_DT_STRING */ +	wchar_t string[30];	/* UTF16_LITTLE_ENDIAN string */ +} __packed; + +/* Number of times to request transfer status before giving up waiting for a +   transfer to complete. This may need to be changed if SMBUS clock, retries, +   or read/write/scl_low timeout settings are changed. */ +static const int XFER_STATUS_RETRIES = 10; + +/* Time in ms to wait for a CP2112_DATA_READ_RESPONSE or +   CP2112_TRANSFER_STATUS_RESPONSE. */ +static const int RESPONSE_TIMEOUT = 50; + +static const struct hid_device_id cp2112_devices[] = { +	{ HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) }, +	{ } +}; +MODULE_DEVICE_TABLE(hid, cp2112_devices); + +struct cp2112_device { +	struct i2c_adapter adap; +	struct hid_device *hdev; +	wait_queue_head_t wait; +	u8 read_data[61]; +	u8 read_length; +	int xfer_status; +	atomic_t read_avail; +	atomic_t xfer_avail; +	struct gpio_chip gc; +}; + +static int gpio_push_pull = 0xFF; +module_param(gpio_push_pull, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask"); + +static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ +	struct cp2112_device *dev = container_of(chip, struct cp2112_device, +						 gc); +	struct hid_device *hdev = dev->hdev; +	u8 buf[5]; +	int ret; + +	ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, +				       sizeof(buf), HID_FEATURE_REPORT, +				       HID_REQ_GET_REPORT); +	if (ret != sizeof(buf)) { +		hid_err(hdev, "error requesting GPIO config: %d\n", ret); +		return ret; +	} + +	buf[1] &= ~(1 << offset); +	buf[2] = gpio_push_pull; + +	ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf), +				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); +	if (ret < 0) { +		hid_err(hdev, "error setting GPIO config: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ +	struct cp2112_device *dev = container_of(chip, struct cp2112_device, +						 gc); +	struct hid_device *hdev = dev->hdev; +	u8 buf[3]; +	int ret; + +	buf[0] = CP2112_GPIO_SET; +	buf[1] = value ? 0xff : 0; +	buf[2] = 1 << offset; + +	ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf, sizeof(buf), +				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); +	if (ret < 0) +		hid_err(hdev, "error setting GPIO values: %d\n", ret); +} + +static int cp2112_gpio_get(struct gpio_chip *chip, unsigned offset) +{ +	struct cp2112_device *dev = container_of(chip, struct cp2112_device, +						 gc); +	struct hid_device *hdev = dev->hdev; +	u8 buf[2]; +	int ret; + +	ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf, sizeof(buf), +				       HID_FEATURE_REPORT, HID_REQ_GET_REPORT); +	if (ret != sizeof(buf)) { +		hid_err(hdev, "error requesting GPIO values: %d\n", ret); +		return ret; +	} + +	return (buf[1] >> offset) & 1; +} + +static int cp2112_gpio_direction_output(struct gpio_chip *chip, +					unsigned offset, int value) +{ +	struct cp2112_device *dev = container_of(chip, struct cp2112_device, +						 gc); +	struct hid_device *hdev = dev->hdev; +	u8 buf[5]; +	int ret; + +	cp2112_gpio_set(chip, offset, value); + +	ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, +				       sizeof(buf), HID_FEATURE_REPORT, +				       HID_REQ_GET_REPORT); +	if (ret != sizeof(buf)) { +		hid_err(hdev, "error requesting GPIO config: %d\n", ret); +		return ret; +	} + +	buf[1] |= 1 << offset; +	buf[2] = gpio_push_pull; + +	ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf), +				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT); +	if (ret < 0) { +		hid_err(hdev, "error setting GPIO config: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int cp2112_hid_get(struct hid_device *hdev, unsigned char report_number, +			  u8 *data, size_t count, unsigned char report_type) +{ +	u8 *buf; +	int ret; + +	buf = kmalloc(count, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	ret = hid_hw_raw_request(hdev, report_number, buf, count, +				       report_type, HID_REQ_GET_REPORT); +	memcpy(data, buf, count); +	kfree(buf); +	return ret; +} + +static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count, +			     unsigned char report_type) +{ +	u8 *buf; +	int ret; + +	buf = kmemdup(data, count, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	if (report_type == HID_OUTPUT_REPORT) +		ret = hid_hw_output_report(hdev, buf, count); +	else +		ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type, +				HID_REQ_SET_REPORT); + +	kfree(buf); +	return ret; +} + +static int cp2112_wait(struct cp2112_device *dev, atomic_t *avail) +{ +	int ret = 0; + +	/* We have sent either a CP2112_TRANSFER_STATUS_REQUEST or a +	 * CP2112_DATA_READ_FORCE_SEND and we are waiting for the response to +	 * come in cp2112_raw_event or timeout. There will only be one of these +	 * in flight at any one time. The timeout is extremely large and is a +	 * last resort if the CP2112 has died. If we do timeout we don't expect +	 * to receive the response which would cause data races, it's not like +	 * we can do anything about it anyway. +	 */ +	ret = wait_event_interruptible_timeout(dev->wait, +		atomic_read(avail), msecs_to_jiffies(RESPONSE_TIMEOUT)); +	if (-ERESTARTSYS == ret) +		return ret; +	if (!ret) +		return -ETIMEDOUT; + +	atomic_set(avail, 0); +	return 0; +} + +static int cp2112_xfer_status(struct cp2112_device *dev) +{ +	struct hid_device *hdev = dev->hdev; +	u8 buf[2]; +	int ret; + +	buf[0] = CP2112_TRANSFER_STATUS_REQUEST; +	buf[1] = 0x01; +	atomic_set(&dev->xfer_avail, 0); + +	ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); +	if (ret < 0) { +		hid_warn(hdev, "Error requesting status: %d\n", ret); +		return ret; +	} + +	ret = cp2112_wait(dev, &dev->xfer_avail); +	if (ret) +		return ret; + +	return dev->xfer_status; +} + +static int cp2112_read(struct cp2112_device *dev, u8 *data, size_t size) +{ +	struct hid_device *hdev = dev->hdev; +	struct cp2112_force_read_report report; +	int ret; + +	report.report = CP2112_DATA_READ_FORCE_SEND; +	report.length = cpu_to_be16(size); + +	atomic_set(&dev->read_avail, 0); + +	ret = cp2112_hid_output(hdev, &report.report, sizeof(report), +				HID_OUTPUT_REPORT); +	if (ret < 0) { +		hid_warn(hdev, "Error requesting data: %d\n", ret); +		return ret; +	} + +	ret = cp2112_wait(dev, &dev->read_avail); +	if (ret) +		return ret; + +	hid_dbg(hdev, "read %d of %zd bytes requested\n", +		dev->read_length, size); + +	if (size > dev->read_length) +		size = dev->read_length; + +	memcpy(data, dev->read_data, size); +	return dev->read_length; +} + +static int cp2112_read_req(void *buf, u8 slave_address, u16 length) +{ +	struct cp2112_read_req_report *report = buf; + +	if (length < 1 || length > 512) +		return -EINVAL; + +	report->report = CP2112_DATA_READ_REQUEST; +	report->slave_address = slave_address << 1; +	report->length = cpu_to_be16(length); +	return sizeof(*report); +} + +static int cp2112_write_read_req(void *buf, u8 slave_address, u16 length, +				 u8 command, u8 *data, u8 data_length) +{ +	struct cp2112_write_read_req_report *report = buf; + +	if (length < 1 || length > 512 +	    || data_length > sizeof(report->target_address) - 1) +		return -EINVAL; + +	report->report = CP2112_DATA_WRITE_READ_REQUEST; +	report->slave_address = slave_address << 1; +	report->length = cpu_to_be16(length); +	report->target_address_length = data_length + 1; +	report->target_address[0] = command; +	memcpy(&report->target_address[1], data, data_length); +	return data_length + 6; +} + +static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, +			    u8 data_length) +{ +	struct cp2112_write_req_report *report = buf; + +	if (data_length > sizeof(report->data) - 1) +		return -EINVAL; + +	report->report = CP2112_DATA_WRITE_REQUEST; +	report->slave_address = slave_address << 1; +	report->length = data_length + 1; +	report->data[0] = command; +	memcpy(&report->data[1], data, data_length); +	return data_length + 4; +} + +static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, +		       unsigned short flags, char read_write, u8 command, +		       int size, union i2c_smbus_data *data) +{ +	struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; +	struct hid_device *hdev = dev->hdev; +	u8 buf[64]; +	__be16 word; +	ssize_t count; +	size_t read_length = 0; +	unsigned int retries; +	int ret; + +	hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n", +		read_write == I2C_SMBUS_WRITE ? "write" : "read", +		addr, flags, command, size); + +	switch (size) { +	case I2C_SMBUS_BYTE: +		read_length = 1; + +		if (I2C_SMBUS_READ == read_write) +			count = cp2112_read_req(buf, addr, read_length); +		else +			count = cp2112_write_req(buf, addr, data->byte, NULL, +						 0); +		break; +	case I2C_SMBUS_BYTE_DATA: +		read_length = 1; + +		if (I2C_SMBUS_READ == read_write) +			count = cp2112_write_read_req(buf, addr, read_length, +						      command, NULL, 0); +		else +			count = cp2112_write_req(buf, addr, command, +						 &data->byte, 1); +		break; +	case I2C_SMBUS_WORD_DATA: +		read_length = 2; +		word = cpu_to_be16(data->word); + +		if (I2C_SMBUS_READ == read_write) +			count = cp2112_write_read_req(buf, addr, read_length, +						      command, NULL, 0); +		else +			count = cp2112_write_req(buf, addr, command, +						 (u8 *)&word, 2); +		break; +	case I2C_SMBUS_PROC_CALL: +		size = I2C_SMBUS_WORD_DATA; +		read_write = I2C_SMBUS_READ; +		read_length = 2; +		word = cpu_to_be16(data->word); + +		count = cp2112_write_read_req(buf, addr, read_length, command, +					      (u8 *)&word, 2); +		break; +	case I2C_SMBUS_I2C_BLOCK_DATA: +		size = I2C_SMBUS_BLOCK_DATA; +		/* fallthrough */ +	case I2C_SMBUS_BLOCK_DATA: +		if (I2C_SMBUS_READ == read_write) { +			count = cp2112_write_read_req(buf, addr, +						      I2C_SMBUS_BLOCK_MAX, +						      command, NULL, 0); +		} else { +			count = cp2112_write_req(buf, addr, command, +						 data->block, +						 data->block[0] + 1); +		} +		break; +	case I2C_SMBUS_BLOCK_PROC_CALL: +		size = I2C_SMBUS_BLOCK_DATA; +		read_write = I2C_SMBUS_READ; + +		count = cp2112_write_read_req(buf, addr, I2C_SMBUS_BLOCK_MAX, +					      command, data->block, +					      data->block[0] + 1); +		break; +	default: +		hid_warn(hdev, "Unsupported transaction %d\n", size); +		return -EOPNOTSUPP; +	} + +	if (count < 0) +		return count; + +	ret = hid_hw_power(hdev, PM_HINT_FULLON); +	if (ret < 0) { +		hid_err(hdev, "power management error: %d\n", ret); +		return ret; +	} + +	ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT); +	if (ret < 0) { +		hid_warn(hdev, "Error starting transaction: %d\n", ret); +		goto power_normal; +	} + +	for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) { +		ret = cp2112_xfer_status(dev); +		if (-EBUSY == ret) +			continue; +		if (ret < 0) +			goto power_normal; +		break; +	} + +	if (XFER_STATUS_RETRIES <= retries) { +		hid_warn(hdev, "Transfer timed out, cancelling.\n"); +		buf[0] = CP2112_CANCEL_TRANSFER; +		buf[1] = 0x01; + +		ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); +		if (ret < 0) +			hid_warn(hdev, "Error cancelling transaction: %d\n", +				 ret); + +		ret = -ETIMEDOUT; +		goto power_normal; +	} + +	if (I2C_SMBUS_WRITE == read_write) { +		ret = 0; +		goto power_normal; +	} + +	if (I2C_SMBUS_BLOCK_DATA == size) +		read_length = ret; + +	ret = cp2112_read(dev, buf, read_length); +	if (ret < 0) +		goto power_normal; +	if (ret != read_length) { +		hid_warn(hdev, "short read: %d < %zd\n", ret, read_length); +		ret = -EIO; +		goto power_normal; +	} + +	switch (size) { +	case I2C_SMBUS_BYTE: +	case I2C_SMBUS_BYTE_DATA: +		data->byte = buf[0]; +		break; +	case I2C_SMBUS_WORD_DATA: +		data->word = be16_to_cpup((__be16 *)buf); +		break; +	case I2C_SMBUS_BLOCK_DATA: +		if (read_length > I2C_SMBUS_BLOCK_MAX) { +			ret = -EPROTO; +			goto power_normal; +		} + +		memcpy(data->block, buf, read_length); +		break; +	} + +	ret = 0; +power_normal: +	hid_hw_power(hdev, PM_HINT_NORMAL); +	hid_dbg(hdev, "transfer finished: %d\n", ret); +	return ret; +} + +static u32 cp2112_functionality(struct i2c_adapter *adap) +{ +	return I2C_FUNC_SMBUS_BYTE | +		I2C_FUNC_SMBUS_BYTE_DATA | +		I2C_FUNC_SMBUS_WORD_DATA | +		I2C_FUNC_SMBUS_BLOCK_DATA | +		I2C_FUNC_SMBUS_I2C_BLOCK | +		I2C_FUNC_SMBUS_PROC_CALL | +		I2C_FUNC_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm smbus_algorithm = { +	.smbus_xfer	= cp2112_xfer, +	.functionality	= cp2112_functionality, +}; + +static int cp2112_get_usb_config(struct hid_device *hdev, +				 struct cp2112_usb_config_report *cfg) +{ +	int ret; + +	ret = cp2112_hid_get(hdev, CP2112_USB_CONFIG, (u8 *)cfg, sizeof(*cfg), +			     HID_FEATURE_REPORT); +	if (ret != sizeof(*cfg)) { +		hid_err(hdev, "error reading usb config: %d\n", ret); +		if (ret < 0) +			return ret; +		return -EIO; +	} + +	return 0; +} + +static int cp2112_set_usb_config(struct hid_device *hdev, +				 struct cp2112_usb_config_report *cfg) +{ +	int ret; + +	BUG_ON(cfg->report != CP2112_USB_CONFIG); + +	ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg), +				HID_FEATURE_REPORT); +	if (ret != sizeof(*cfg)) { +		hid_err(hdev, "error writing usb config: %d\n", ret); +		if (ret < 0) +			return ret; +		return -EIO; +	} + +	return 0; +} + +static void chmod_sysfs_attrs(struct hid_device *hdev); + +#define CP2112_CONFIG_ATTR(name, store, format, ...) \ +static ssize_t name##_store(struct device *kdev, \ +			    struct device_attribute *attr, const char *buf, \ +			    size_t count) \ +{ \ +	struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \ +	struct cp2112_usb_config_report cfg; \ +	int ret = cp2112_get_usb_config(hdev, &cfg); \ +	if (ret) \ +		return ret; \ +	store; \ +	ret = cp2112_set_usb_config(hdev, &cfg); \ +	if (ret) \ +		return ret; \ +	chmod_sysfs_attrs(hdev); \ +	return count; \ +} \ +static ssize_t name##_show(struct device *kdev, \ +			   struct device_attribute *attr, char *buf) \ +{ \ +	struct hid_device *hdev = container_of(kdev, struct hid_device, dev); \ +	struct cp2112_usb_config_report cfg; \ +	int ret = cp2112_get_usb_config(hdev, &cfg); \ +	if (ret) \ +		return ret; \ +	return scnprintf(buf, PAGE_SIZE, format, ##__VA_ARGS__); \ +} \ +static DEVICE_ATTR_RW(name); + +CP2112_CONFIG_ATTR(vendor_id, ({ +	u16 vid; + +	if (sscanf(buf, "%hi", &vid) != 1) +		return -EINVAL; + +	cfg.vid = cpu_to_le16(vid); +	cfg.mask = 0x01; +}), "0x%04x\n", le16_to_cpu(cfg.vid)); + +CP2112_CONFIG_ATTR(product_id, ({ +	u16 pid; + +	if (sscanf(buf, "%hi", &pid) != 1) +		return -EINVAL; + +	cfg.pid = cpu_to_le16(pid); +	cfg.mask = 0x02; +}), "0x%04x\n", le16_to_cpu(cfg.pid)); + +CP2112_CONFIG_ATTR(max_power, ({ +	int mA; + +	if (sscanf(buf, "%i", &mA) != 1) +		return -EINVAL; + +	cfg.max_power = (mA + 1) / 2; +	cfg.mask = 0x04; +}), "%u mA\n", cfg.max_power * 2); + +CP2112_CONFIG_ATTR(power_mode, ({ +	if (sscanf(buf, "%hhi", &cfg.power_mode) != 1) +		return -EINVAL; + +	cfg.mask = 0x08; +}), "%u\n", cfg.power_mode); + +CP2112_CONFIG_ATTR(release_version, ({ +	if (sscanf(buf, "%hhi.%hhi", &cfg.release_major, &cfg.release_minor) +	    != 2) +		return -EINVAL; + +	cfg.mask = 0x10; +}), "%u.%u\n", cfg.release_major, cfg.release_minor); + +#undef CP2112_CONFIG_ATTR + +struct cp2112_pstring_attribute { +	struct device_attribute attr; +	unsigned char report; +}; + +static ssize_t pstr_store(struct device *kdev, +			  struct device_attribute *kattr, const char *buf, +			  size_t count) +{ +	struct hid_device *hdev = container_of(kdev, struct hid_device, dev); +	struct cp2112_pstring_attribute *attr = +		container_of(kattr, struct cp2112_pstring_attribute, attr); +	struct cp2112_string_report report; +	int ret; + +	memset(&report, 0, sizeof(report)); + +	ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN, +			      report.string, ARRAY_SIZE(report.string)); +	report.report = attr->report; +	report.length = ret * sizeof(report.string[0]) + 2; +	report.type = USB_DT_STRING; + +	ret = cp2112_hid_output(hdev, &report.report, report.length + 1, +				HID_FEATURE_REPORT); +	if (ret != report.length + 1) { +		hid_err(hdev, "error writing %s string: %d\n", kattr->attr.name, +			ret); +		if (ret < 0) +			return ret; +		return -EIO; +	} + +	chmod_sysfs_attrs(hdev); +	return count; +} + +static ssize_t pstr_show(struct device *kdev, +			 struct device_attribute *kattr, char *buf) +{ +	struct hid_device *hdev = container_of(kdev, struct hid_device, dev); +	struct cp2112_pstring_attribute *attr = +		container_of(kattr, struct cp2112_pstring_attribute, attr); +	struct cp2112_string_report report; +	u8 length; +	int ret; + +	ret = cp2112_hid_get(hdev, attr->report, &report.report, +			     sizeof(report) - 1, HID_FEATURE_REPORT); +	if (ret < 3) { +		hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name, +			ret); +		if (ret < 0) +			return ret; +		return -EIO; +	} + +	if (report.length < 2) { +		hid_err(hdev, "invalid %s string length: %d\n", +			kattr->attr.name, report.length); +		return -EIO; +	} + +	length = report.length > ret - 1 ? ret - 1 : report.length; +	length = (length - 2) / sizeof(report.string[0]); +	ret = utf16s_to_utf8s(report.string, length, UTF16_LITTLE_ENDIAN, buf, +			      PAGE_SIZE - 1); +	buf[ret++] = '\n'; +	return ret; +} + +#define CP2112_PSTR_ATTR(name, _report) \ +static struct cp2112_pstring_attribute dev_attr_##name = { \ +	.attr = __ATTR(name, (S_IWUSR | S_IRUGO), pstr_show, pstr_store), \ +	.report = _report, \ +}; + +CP2112_PSTR_ATTR(manufacturer,	CP2112_MANUFACTURER_STRING); +CP2112_PSTR_ATTR(product,	CP2112_PRODUCT_STRING); +CP2112_PSTR_ATTR(serial,	CP2112_SERIAL_STRING); + +#undef CP2112_PSTR_ATTR + +static const struct attribute_group cp2112_attr_group = { +	.attrs = (struct attribute *[]){ +		&dev_attr_vendor_id.attr, +		&dev_attr_product_id.attr, +		&dev_attr_max_power.attr, +		&dev_attr_power_mode.attr, +		&dev_attr_release_version.attr, +		&dev_attr_manufacturer.attr.attr, +		&dev_attr_product.attr.attr, +		&dev_attr_serial.attr.attr, +		NULL +	} +}; + +/* Chmoding our sysfs attributes is simply a way to expose which fields in the + * PROM have already been programmed. We do not depend on this preventing + * writing to these attributes since the CP2112 will simply ignore writes to + * already-programmed fields. This is why there is no sense in fixing this + * racy behaviour. + */ +static void chmod_sysfs_attrs(struct hid_device *hdev) +{ +	struct attribute **attr; +	u8 buf[2]; +	int ret; + +	ret = cp2112_hid_get(hdev, CP2112_LOCK_BYTE, buf, sizeof(buf), +			     HID_FEATURE_REPORT); +	if (ret != sizeof(buf)) { +		hid_err(hdev, "error reading lock byte: %d\n", ret); +		return; +	} + +	for (attr = cp2112_attr_group.attrs; *attr; ++attr) { +		umode_t mode = (buf[1] & 1) ? S_IWUSR | S_IRUGO : S_IRUGO; +		ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode); +		if (ret < 0) +			hid_err(hdev, "error chmoding sysfs file %s\n", +				(*attr)->name); +		buf[1] >>= 1; +	} +} + +static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ +	struct cp2112_device *dev; +	u8 buf[3]; +	struct cp2112_smbus_config_report config; +	int ret; + +	ret = hid_parse(hdev); +	if (ret) { +		hid_err(hdev, "parse failed\n"); +		return ret; +	} + +	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); +	if (ret) { +		hid_err(hdev, "hw start failed\n"); +		return ret; +	} + +	ret = hid_hw_open(hdev); +	if (ret) { +		hid_err(hdev, "hw open failed\n"); +		goto err_hid_stop; +	} + +	ret = hid_hw_power(hdev, PM_HINT_FULLON); +	if (ret < 0) { +		hid_err(hdev, "power management error: %d\n", ret); +		goto err_hid_close; +	} + +	ret = cp2112_hid_get(hdev, CP2112_GET_VERSION_INFO, buf, sizeof(buf), +			     HID_FEATURE_REPORT); +	if (ret != sizeof(buf)) { +		hid_err(hdev, "error requesting version\n"); +		if (ret >= 0) +			ret = -EIO; +		goto err_power_normal; +	} + +	hid_info(hdev, "Part Number: 0x%02X Device Version: 0x%02X\n", +		 buf[1], buf[2]); + +	ret = cp2112_hid_get(hdev, CP2112_SMBUS_CONFIG, (u8 *)&config, +			     sizeof(config), HID_FEATURE_REPORT); +	if (ret != sizeof(config)) { +		hid_err(hdev, "error requesting SMBus config\n"); +		if (ret >= 0) +			ret = -EIO; +		goto err_power_normal; +	} + +	config.retry_time = cpu_to_be16(1); + +	ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config), +				HID_FEATURE_REPORT); +	if (ret != sizeof(config)) { +		hid_err(hdev, "error setting SMBus config\n"); +		if (ret >= 0) +			ret = -EIO; +		goto err_power_normal; +	} + +	dev = kzalloc(sizeof(*dev), GFP_KERNEL); +	if (!dev) { +		ret = -ENOMEM; +		goto err_power_normal; +	} + +	hid_set_drvdata(hdev, (void *)dev); +	dev->hdev		= hdev; +	dev->adap.owner		= THIS_MODULE; +	dev->adap.class		= I2C_CLASS_HWMON; +	dev->adap.algo		= &smbus_algorithm; +	dev->adap.algo_data	= dev; +	dev->adap.dev.parent	= &hdev->dev; +	snprintf(dev->adap.name, sizeof(dev->adap.name), +		 "CP2112 SMBus Bridge on hiddev%d", hdev->minor); +	init_waitqueue_head(&dev->wait); + +	hid_device_io_start(hdev); +	ret = i2c_add_adapter(&dev->adap); +	hid_device_io_stop(hdev); + +	if (ret) { +		hid_err(hdev, "error registering i2c adapter\n"); +		goto err_free_dev; +	} + +	hid_dbg(hdev, "adapter registered\n"); + +	dev->gc.label			= "cp2112_gpio"; +	dev->gc.direction_input		= cp2112_gpio_direction_input; +	dev->gc.direction_output	= cp2112_gpio_direction_output; +	dev->gc.set			= cp2112_gpio_set; +	dev->gc.get			= cp2112_gpio_get; +	dev->gc.base			= -1; +	dev->gc.ngpio			= 8; +	dev->gc.can_sleep		= 1; +	dev->gc.dev			= &hdev->dev; + +	ret = gpiochip_add(&dev->gc); +	if (ret < 0) { +		hid_err(hdev, "error registering gpio chip\n"); +		goto err_free_i2c; +	} + +	ret = sysfs_create_group(&hdev->dev.kobj, &cp2112_attr_group); +	if (ret < 0) { +		hid_err(hdev, "error creating sysfs attrs\n"); +		goto err_gpiochip_remove; +	} + +	chmod_sysfs_attrs(hdev); +	hid_hw_power(hdev, PM_HINT_NORMAL); + +	return ret; + +err_gpiochip_remove: +	if (gpiochip_remove(&dev->gc) < 0) +		hid_err(hdev, "error removing gpio chip\n"); +err_free_i2c: +	i2c_del_adapter(&dev->adap); +err_free_dev: +	kfree(dev); +err_power_normal: +	hid_hw_power(hdev, PM_HINT_NORMAL); +err_hid_close: +	hid_hw_close(hdev); +err_hid_stop: +	hid_hw_stop(hdev); +	return ret; +} + +static void cp2112_remove(struct hid_device *hdev) +{ +	struct cp2112_device *dev = hid_get_drvdata(hdev); + +	sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group); +	if (gpiochip_remove(&dev->gc)) +		hid_err(hdev, "unable to remove gpio chip\n"); +	i2c_del_adapter(&dev->adap); +	/* i2c_del_adapter has finished removing all i2c devices from our +	 * adapter. Well behaved devices should no longer call our cp2112_xfer +	 * and should have waited for any pending calls to finish. It has also +	 * waited for device_unregister(&adap->dev) to complete. Therefore we +	 * can safely free our struct cp2112_device. +	 */ +	hid_hw_close(hdev); +	hid_hw_stop(hdev); +	kfree(dev); +} + +static int cp2112_raw_event(struct hid_device *hdev, struct hid_report *report, +			    u8 *data, int size) +{ +	struct cp2112_device *dev = hid_get_drvdata(hdev); +	struct cp2112_xfer_status_report *xfer = (void *)data; + +	switch (data[0]) { +	case CP2112_TRANSFER_STATUS_RESPONSE: +		hid_dbg(hdev, "xfer status: %02x %02x %04x %04x\n", +			xfer->status0, xfer->status1, +			be16_to_cpu(xfer->retries), be16_to_cpu(xfer->length)); + +		switch (xfer->status0) { +		case STATUS0_IDLE: +			dev->xfer_status = -EAGAIN; +			break; +		case STATUS0_BUSY: +			dev->xfer_status = -EBUSY; +			break; +		case STATUS0_COMPLETE: +			dev->xfer_status = be16_to_cpu(xfer->length); +			break; +		case STATUS0_ERROR: +			switch (xfer->status1) { +			case STATUS1_TIMEOUT_NACK: +			case STATUS1_TIMEOUT_BUS: +				dev->xfer_status = -ETIMEDOUT; +				break; +			default: +				dev->xfer_status = -EIO; +				break; +			} +			break; +		default: +			dev->xfer_status = -EINVAL; +			break; +		} + +		atomic_set(&dev->xfer_avail, 1); +		break; +	case CP2112_DATA_READ_RESPONSE: +		hid_dbg(hdev, "read response: %02x %02x\n", data[1], data[2]); + +		dev->read_length = data[2]; +		if (dev->read_length > sizeof(dev->read_data)) +			dev->read_length = sizeof(dev->read_data); + +		memcpy(dev->read_data, &data[3], dev->read_length); +		atomic_set(&dev->read_avail, 1); +		break; +	default: +		hid_err(hdev, "unknown report\n"); + +		return 0; +	} + +	wake_up_interruptible(&dev->wait); +	return 1; +} + +static struct hid_driver cp2112_driver = { +	.name		= "cp2112", +	.id_table	= cp2112_devices, +	.probe		= cp2112_probe, +	.remove		= cp2112_remove, +	.raw_event	= cp2112_raw_event, +}; + +module_hid_driver(cp2112_driver); +MODULE_DESCRIPTION("Silicon Labs HID USB to SMBus master bridge"); +MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 8453214ec37..84c3cb15ccd 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -165,6 +165,8 @@ static const struct hid_usage_entry hid_usage_table[] = {      {0, 0x53, "DeviceIndex"},      {0, 0x54, "ContactCount"},      {0, 0x55, "ContactMaximumNumber"}, +    {0, 0x5A, "SecondaryBarrelSwitch"}, +    {0, 0x5B, "TransducerSerialNumber"},    { 15, 0, "PhysicalInterfaceDevice" },      {0, 0x00, "Undefined"},      {0, 0x01, "Physical_Interface_Device"}, @@ -272,6 +274,85 @@ static const struct hid_usage_entry hid_usage_table[] = {      {0, 0xAA, "Shared_Parameter_Blocks"},      {0, 0xAB, "Create_New_Effect_Report"},      {0, 0xAC, "RAM_Pool_Available"}, +  {  0x20, 0, "Sensor" }, +    { 0x20, 0x01, "Sensor" }, +    { 0x20, 0x10, "Biometric" }, +      { 0x20, 0x11, "BiometricHumanPresence" }, +      { 0x20, 0x12, "BiometricHumanProximity" }, +      { 0x20, 0x13, "BiometricHumanTouch" }, +    { 0x20, 0x20, "Electrical" }, +      { 0x20, 0x21, "ElectricalCapacitance" }, +      { 0x20, 0x22, "ElectricalCurrent" }, +      { 0x20, 0x23, "ElectricalPower" }, +      { 0x20, 0x24, "ElectricalInductance" }, +      { 0x20, 0x25, "ElectricalResistance" }, +      { 0x20, 0x26, "ElectricalVoltage" }, +      { 0x20, 0x27, "ElectricalPoteniometer" }, +      { 0x20, 0x28, "ElectricalFrequency" }, +      { 0x20, 0x29, "ElectricalPeriod" }, +    { 0x20, 0x30, "Environmental" }, +      { 0x20, 0x31, "EnvironmentalAtmosphericPressure" }, +      { 0x20, 0x32, "EnvironmentalHumidity" }, +      { 0x20, 0x33, "EnvironmentalTemperature" }, +      { 0x20, 0x34, "EnvironmentalWindDirection" }, +      { 0x20, 0x35, "EnvironmentalWindSpeed" }, +    { 0x20, 0x40, "Light" }, +      { 0x20, 0x41, "LightAmbientLight" }, +      { 0x20, 0x42, "LightConsumerInfrared" }, +    { 0x20, 0x50, "Location" }, +      { 0x20, 0x51, "LocationBroadcast" }, +      { 0x20, 0x52, "LocationDeadReckoning" }, +      { 0x20, 0x53, "LocationGPS" }, +      { 0x20, 0x54, "LocationLookup" }, +      { 0x20, 0x55, "LocationOther" }, +      { 0x20, 0x56, "LocationStatic" }, +      { 0x20, 0x57, "LocationTriangulation" }, +    { 0x20, 0x60, "Mechanical" }, +      { 0x20, 0x61, "MechanicalBooleanSwitch" }, +      { 0x20, 0x62, "MechanicalBooleanSwitchArray" }, +      { 0x20, 0x63, "MechanicalMultivalueSwitch" }, +      { 0x20, 0x64, "MechanicalForce" }, +      { 0x20, 0x65, "MechanicalPressure" }, +      { 0x20, 0x66, "MechanicalStrain" }, +      { 0x20, 0x67, "MechanicalWeight" }, +      { 0x20, 0x68, "MechanicalHapticVibrator" }, +      { 0x20, 0x69, "MechanicalHallEffectSwitch" }, +    { 0x20, 0x70, "Motion" }, +      { 0x20, 0x71, "MotionAccelerometer1D" }, +      { 0x20, 0x72, "MotionAccelerometer2D" }, +      { 0x20, 0x73, "MotionAccelerometer3D" }, +      { 0x20, 0x74, "MotionGyrometer1D" }, +      { 0x20, 0x75, "MotionGyrometer2D" }, +      { 0x20, 0x76, "MotionGyrometer3D" }, +      { 0x20, 0x77, "MotionMotionDetector" }, +      { 0x20, 0x78, "MotionSpeedometer" }, +      { 0x20, 0x79, "MotionAccelerometer" }, +      { 0x20, 0x7A, "MotionGyrometer" }, +    { 0x20, 0x80, "Orientation" }, +      { 0x20, 0x81, "OrientationCompass1D" }, +      { 0x20, 0x82, "OrientationCompass2D" }, +      { 0x20, 0x83, "OrientationCompass3D" }, +      { 0x20, 0x84, "OrientationInclinometer1D" }, +      { 0x20, 0x85, "OrientationInclinometer2D" }, +      { 0x20, 0x86, "OrientationInclinometer3D" }, +      { 0x20, 0x87, "OrientationDistance1D" }, +      { 0x20, 0x88, "OrientationDistance2D" }, +      { 0x20, 0x89, "OrientationDistance3D" }, +      { 0x20, 0x8A, "OrientationDeviceOrientation" }, +      { 0x20, 0x8B, "OrientationCompass" }, +      { 0x20, 0x8C, "OrientationInclinometer" }, +      { 0x20, 0x8D, "OrientationDistance" }, +    { 0x20, 0x90, "Scanner" }, +      { 0x20, 0x91, "ScannerBarcode" }, +      { 0x20, 0x91, "ScannerRFID" }, +      { 0x20, 0x91, "ScannerNFC" }, +    { 0x20, 0xA0, "Time" }, +      { 0x20, 0xA1, "TimeAlarmTimer" }, +      { 0x20, 0xA2, "TimeRealTimeClock" }, +    { 0x20, 0xE0, "Other" }, +      { 0x20, 0xE1, "OtherCustom" }, +      { 0x20, 0xE2, "OtherGeneric" }, +      { 0x20, 0xE3, "OtherGenericEnumerator" },    { 0x84, 0, "Power Device" },      { 0x84, 0x02, "PresentStatus" },      { 0x84, 0x03, "ChangeStatus" }, @@ -768,6 +849,8 @@ static const char *keys[KEY_MAX + 1] = {  	[KEY_ALTERASE] = "AlternateErase",	[KEY_CANCEL] = "Cancel",  	[KEY_BRIGHTNESSDOWN] = "BrightnessDown", [KEY_BRIGHTNESSUP] = "BrightnessUp",  	[KEY_MEDIA] = "Media",			[KEY_UNKNOWN] = "Unknown", +	[BTN_DPAD_UP] = "BtnDPadUp",		[BTN_DPAD_DOWN] = "BtnDPadDown", +	[BTN_DPAD_LEFT] = "BtnDPadLeft",	[BTN_DPAD_RIGHT] = "BtnDPadRight",  	[BTN_0] = "Btn0",			[BTN_1] = "Btn1",  	[BTN_2] = "Btn2",			[BTN_3] = "Btn3",  	[BTN_4] = "Btn4",			[BTN_5] = "Btn5", @@ -797,7 +880,8 @@ static const char *keys[KEY_MAX + 1] = {  	[BTN_TOOL_MOUSE] = "ToolMouse",		[BTN_TOOL_LENS] = "ToolLens",  	[BTN_TOUCH] = "Touch",			[BTN_STYLUS] = "Stylus",  	[BTN_STYLUS2] = "Stylus2",		[BTN_TOOL_DOUBLETAP] = "ToolDoubleTap", -	[BTN_TOOL_TRIPLETAP] = "ToolTripleTap", [BTN_GEAR_DOWN] = "WheelBtn", +	[BTN_TOOL_TRIPLETAP] = "ToolTripleTap",	[BTN_TOOL_QUADTAP] = "ToolQuadrupleTap", +	[BTN_GEAR_DOWN] = "WheelBtn",  	[BTN_GEAR_UP] = "Gear up",		[KEY_OK] = "Ok",  	[KEY_SELECT] = "Select",		[KEY_GOTO] = "Goto",  	[KEY_CLEAR] = "Clear",			[KEY_POWER2] = "Power2", @@ -852,6 +936,16 @@ static const char *keys[KEY_MAX + 1] = {  	[KEY_KBDILLUMDOWN] = "KbdIlluminationDown",  	[KEY_KBDILLUMUP] = "KbdIlluminationUp",  	[KEY_SWITCHVIDEOMODE] = "SwitchVideoMode", +	[KEY_BUTTONCONFIG] = "ButtonConfig", +	[KEY_TASKMANAGER] = "TaskManager", +	[KEY_JOURNAL] = "Journal", +	[KEY_CONTROLPANEL] = "ControlPanel", +	[KEY_APPSELECT] = "AppSelect", +	[KEY_SCREENSAVER] = "ScreenSaver", +	[KEY_VOICECOMMAND] = "VoiceCommand", +	[KEY_BRIGHTNESS_MIN] = "BrightnessMin", +	[KEY_BRIGHTNESS_MAX] = "BrightnessMax", +	[KEY_BRIGHTNESS_AUTO] = "BrightnessAuto",  };  static const char *relatives[REL_MAX + 1] = { diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c index f042a6cf8b1..4e49462870a 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -181,7 +181,40 @@ fail:   */  static bool elo_broken_firmware(struct usb_device *dev)  { -	return use_fw_quirk && le16_to_cpu(dev->descriptor.bcdDevice) == 0x10d; +	struct usb_device *hub = dev->parent; +	struct usb_device *child = NULL; +	u16 fw_lvl = le16_to_cpu(dev->descriptor.bcdDevice); +	u16 child_vid, child_pid; +	int i; +     +	if (!use_fw_quirk) +		return false; +	if (fw_lvl != 0x10d) +		return false; + +	/* iterate sibling devices of the touch controller */ +	usb_hub_for_each_child(hub, i, child) { +		child_vid = le16_to_cpu(child->descriptor.idVendor); +		child_pid = le16_to_cpu(child->descriptor.idProduct); + +		/* +		 * If one of the devices below is present attached as a sibling of  +		 * the touch controller then  this is a newer IBM 4820 monitor that  +		 * does not need the IBM-requested workaround if fw level is +		 * 0x010d - aka 'M'. +		 * No other HW can have this combination. +		 */ +		if (child_vid==0x04b3) { +			switch (child_pid) { +			case 0x4676: /* 4820 21x Video */ +			case 0x4677: /* 4820 51x Video */ +			case 0x4678: /* 4820 2Lx Video */ +			case 0x4679: /* 4820 5Lx Video */ +				return false; +			} +		} +	} +	return true;  }  static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c index 7e6db3cf46f..d60fbd0adc0 100644 --- a/drivers/hid/hid-holtek-mouse.c +++ b/drivers/hid/hid-holtek-mouse.c @@ -27,6 +27,8 @@   * - USB ID 04d9:a067, sold as Sharkoon Drakonia and Perixx MX-2000   * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200   *   and Zalman ZM-GM1 + * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse + * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse   */  static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -39,6 +41,7 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,  		 * 0x2fff, so they don't exceed HID_MAX_USAGES */  		switch (hdev->product) {  		case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067: +		case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072:  			if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f  					&& rdesc[120] == 0xff && rdesc[121] == 0x7f) {  				hid_info(hdev, "Fixing up report descriptor\n"); @@ -46,6 +49,8 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,  			}  			break;  		case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A: +		case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070: +		case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081:  			if (*rsize >= 113 && rdesc[106] == 0xff && rdesc[107] == 0x7f  					&& rdesc[111] == 0xff && rdesc[112] == 0x7f) {  				hid_info(hdev, "Fixing up report descriptor\n"); @@ -61,8 +66,14 @@ static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,  static const struct hid_device_id holtek_mouse_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,  			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, +        { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, +			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT,  			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, +			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, +			USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) },  	{ }  };  MODULE_DEVICE_TABLE(hid, holtek_mouse_devices); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 8fae6d1414c..f52dbcb7133 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -157,6 +157,7 @@ struct mousevsc_dev {  	u32			report_desc_size;  	struct hv_input_dev_info hid_dev_info;  	struct hid_device       *hid_device; +	u8			input_buf[HID_MAX_BUFFER_SIZE];  }; @@ -256,6 +257,7 @@ static void mousevsc_on_receive(struct hv_device *device,  	struct synthhid_msg *hid_msg;  	struct mousevsc_dev *input_dev = hv_get_drvdata(device);  	struct synthhid_input_report *input_report; +	size_t len;  	pipe_msg = (struct pipe_prt_msg *)((unsigned long)packet +  						(packet->offset8 << 3)); @@ -300,9 +302,12 @@ static void mousevsc_on_receive(struct hv_device *device,  			(struct synthhid_input_report *)pipe_msg->data;  		if (!input_dev->init_complete)  			break; -		hid_input_report(input_dev->hid_device, -				HID_INPUT_REPORT, input_report->buffer, -				input_report->header.size, 1); + +		len = min(input_report->header.size, +			  (u32)sizeof(input_dev->input_buf)); +		memcpy(input_dev->input_buf, input_report->buffer, len); +		hid_input_report(input_dev->hid_device, HID_INPUT_REPORT, +				 input_dev->input_buf, len, 1);  		break;  	default:  		pr_err("unsupported hid msg type - type %d len %d", @@ -455,12 +460,22 @@ static void mousevsc_hid_stop(struct hid_device *hid)  {  } +static int mousevsc_hid_raw_request(struct hid_device *hid, +				    unsigned char report_num, +				    __u8 *buf, size_t len, +				    unsigned char rtype, +				    int reqtype) +{ +	return 0; +} +  static struct hid_ll_driver mousevsc_ll_driver = {  	.parse = mousevsc_hid_parse,  	.open = mousevsc_hid_open,  	.close = mousevsc_hid_close,  	.start = mousevsc_hid_start,  	.stop = mousevsc_hid_stop, +	.raw_request = mousevsc_hid_raw_request,  };  static struct hid_driver mousevsc_hid_driver; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e60e8d53069..48b66bbffc9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -67,6 +67,9 @@  #define USB_VENDOR_ID_ALPS		0x0433  #define USB_DEVICE_ID_IBM_GAMEPAD	0x1101 +#define USB_VENDOR_ID_ANTON		0x1130 +#define USB_DEVICE_ID_ANTON_TOUCH_PAD	0x3101 +  #define USB_VENDOR_ID_APPLE		0x05ac  #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304  #define USB_DEVICE_ID_APPLE_MAGICMOUSE	0x030d @@ -135,6 +138,7 @@  #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS   0x023b  #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI  0x0255  #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO   0x0256 +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_JIS   0x0257  #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI	0x0290  #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO	0x0291  #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS	0x0292 @@ -240,6 +244,10 @@  #define USB_VENDOR_ID_CYGNAL		0x10c4  #define USB_DEVICE_ID_CYGNAL_RADIO_SI470X	0x818a +#define USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH	0x81b9 +#define USB_DEVICE_ID_CYGNAL_CP2112	0xea90 + +#define USB_DEVICE_ID_CYGNAL_RADIO_SI4713       0x8244  #define USB_VENDOR_ID_CYPRESS		0x04b4  #define USB_DEVICE_ID_CYPRESS_MOUSE	0x0001 @@ -293,6 +301,9 @@  #define USB_VENDOR_ID_DREAM_CHEEKY	0x1d34 +#define USB_VENDOR_ID_ELITEGROUP	0x03fc +#define USB_DEVICE_ID_ELITEGROUP_05D8	0x05d8 +  #define USB_VENDOR_ID_ELO		0x04E7  #define USB_DEVICE_ID_ELO_TS2515	0x0022  #define USB_DEVICE_ID_ELO_TS2700	0x0020 @@ -312,6 +323,7 @@  #define USB_VENDOR_ID_ETURBOTOUCH	0x22b9  #define USB_DEVICE_ID_ETURBOTOUCH	0x0006 +#define USB_DEVICE_ID_ETURBOTOUCH_2968	0x2968  #define USB_VENDOR_ID_EZKEY		0x0518  #define USB_DEVICE_ID_BTC_8193		0x0002 @@ -332,6 +344,11 @@  #define USB_VENDOR_ID_GENERAL_TOUCH	0x0dfc  #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003  #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101 0x0101 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102 0x0102 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106 0x0106 +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A 0x010a +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100  #define USB_VENDOR_ID_GLAB		0x06c2  #define USB_DEVICE_ID_4_PHIDGETSERVO_30	0x0038 @@ -440,6 +457,15 @@  #define USB_VENDOR_ID_ILITEK		0x222a  #define USB_DEVICE_ID_ILITEK_MULTITOUCH	0x0001 +#define USB_VENDOR_ID_INTEL_0		0x8086 +#define USB_VENDOR_ID_INTEL_1		0x8087 +#define USB_DEVICE_ID_INTEL_HID_SENSOR_0	0x09fa +#define USB_DEVICE_ID_INTEL_HID_SENSOR_1	0x0a04 + +#define USB_VENDOR_ID_STM_0             0x0483 +#define USB_DEVICE_ID_STM_HID_SENSOR    0x91d1 +#define USB_DEVICE_ID_STM_HID_SENSOR_1  0x9100 +  #define USB_VENDOR_ID_ION		0x15e4  #define USB_DEVICE_ID_ICADE		0x0132 @@ -448,8 +474,11 @@  #define USB_VENDOR_ID_HOLTEK_ALT		0x04d9  #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD	0xa055 -#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067	0xa067  #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A	0xa04a +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067	0xa067 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070	0xa070 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072	0xa072 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081	0xa081  #define USB_VENDOR_ID_IMATION		0x0718  #define USB_DEVICE_ID_DISC_STAKKA	0xd000 @@ -482,6 +511,7 @@  #define USB_VENDOR_ID_KYE		0x0458  #define USB_DEVICE_ID_KYE_ERGO_525V	0x0087  #define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE	0x0138 +#define USB_DEVICE_ID_GENIUS_MANTICORE	0x0153  #define USB_DEVICE_ID_GENIUS_GX_IMPERATOR	0x4018  #define USB_DEVICE_ID_KYE_GPEN_560	0x5003  #define USB_DEVICE_ID_KYE_EASYPEN_I405X	0x5010 @@ -544,6 +574,7 @@  #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD	0xc20a  #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD	0xc211  #define USB_DEVICE_ID_LOGITECH_EXTREME_3D	0xc215 +#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION	0xc216  #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2	0xc218  #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2	0xc219  #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D	0xc283 @@ -570,6 +601,7 @@  #define USB_DEVICE_ID_DINOVO_EDGE	0xc714  #define USB_DEVICE_ID_DINOVO_MINI	0xc71f  #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2	0xca03 +#define USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL	0xca04  #define USB_VENDOR_ID_LUMIO		0x202e  #define USB_DEVICE_ID_CRYSTALTOUCH	0x0006 @@ -594,6 +626,7 @@  #define USB_VENDOR_ID_MICROSOFT		0x045e  #define USB_DEVICE_ID_SIDEWINDER_GV	0x003b +#define USB_DEVICE_ID_MS_OFFICE_KB	0x0048  #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d  #define USB_DEVICE_ID_MS_NE4K		0x00db  #define USB_DEVICE_ID_MS_NE4K_JP	0x00dc @@ -602,6 +635,9 @@  #define USB_DEVICE_ID_MS_PRESENTER_8K_USB	0x0713  #define USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K	0x0730  #define USB_DEVICE_ID_MS_COMFORT_MOUSE_4500	0x076c +#define USB_DEVICE_ID_MS_SURFACE_PRO_2   0x0799 +#define USB_DEVICE_ID_MS_TOUCH_COVER_2   0x07a7 +#define USB_DEVICE_ID_MS_TYPE_COVER_2    0x07a9  #define USB_VENDOR_ID_MOJO		0x8282  #define USB_DEVICE_ID_RETRO_ADAPTER	0x3201 @@ -627,6 +663,7 @@  #define USB_VENDOR_ID_NEXIO		0x1870  #define USB_DEVICE_ID_NEXIO_MULTITOUCH_420	0x010d +#define USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750	0x0110  #define USB_VENDOR_ID_NEXTWINDOW	0x1926  #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN	0x0003 @@ -679,6 +716,8 @@  #define USB_VENDOR_ID_PENMOUNT		0x14e1  #define USB_DEVICE_ID_PENMOUNT_PCI	0x3500 +#define USB_DEVICE_ID_PENMOUNT_1610	0x1610 +#define USB_DEVICE_ID_PENMOUNT_1640	0x1640  #define USB_VENDOR_ID_PETALYNX		0x18b1  #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE	0x0037 @@ -724,16 +763,24 @@  #define USB_DEVICE_ID_ROCCAT_LUA	0x2c2e  #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED	0x2c24  #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS	0x2cf6 +#define USB_DEVICE_ID_ROCCAT_RYOS_MK	0x3138 +#define USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW	0x31ce +#define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO	0x3232  #define USB_DEVICE_ID_ROCCAT_SAVU	0x2d5a  #define USB_VENDOR_ID_SAITEK		0x06a3  #define USB_DEVICE_ID_SAITEK_RUMBLEPAD	0xff17  #define USB_DEVICE_ID_SAITEK_PS1000	0x0621 +#define USB_DEVICE_ID_SAITEK_RAT7	0x0cd7 +#define USB_DEVICE_ID_SAITEK_MMO7	0x0cd0  #define USB_VENDOR_ID_SAMSUNG		0x0419  #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE	0x0001  #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE	0x0600 +#define USB_VENDOR_ID_SEMICO			0x1a2c +#define USB_DEVICE_ID_SEMICO_USB_KEYKOARD	0x0023 +  #define USB_VENDOR_ID_SENNHEISER	0x1395  #define USB_DEVICE_ID_SENNHEISER_BTD500USB	0x002c @@ -743,6 +790,12 @@  #define USB_VENDOR_ID_SIGMATEL		0x066F  #define USB_DEVICE_ID_SIGMATEL_STMP3780	0x3780 +#define USB_VENDOR_ID_SIS_TOUCH		0x0457 +#define USB_DEVICE_ID_SIS9200_TOUCH	0x9200 +#define USB_DEVICE_ID_SIS817_TOUCH	0x0817 +#define USB_DEVICE_ID_SIS_TS		0x1013 +#define USB_DEVICE_ID_SIS1030_TOUCH	0x1030 +  #define USB_VENDOR_ID_SKYCABLE			0x1223  #define	USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER	0x3F07 @@ -751,6 +804,7 @@  #define USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE	0x0374  #define USB_DEVICE_ID_SONY_PS3_BDREMOTE		0x0306  #define USB_DEVICE_ID_SONY_PS3_CONTROLLER	0x0268 +#define USB_DEVICE_ID_SONY_PS4_CONTROLLER	0x05c4  #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER	0x042f  #define USB_DEVICE_ID_SONY_BUZZ_CONTROLLER		0x0002  #define USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER	0x1000 @@ -791,6 +845,14 @@  #define USB_DEVICE_ID_SYNAPTICS_COMP_TP	0x0009  #define USB_DEVICE_ID_SYNAPTICS_WTP	0x0010  #define USB_DEVICE_ID_SYNAPTICS_DPAD	0x0013 +#define USB_DEVICE_ID_SYNAPTICS_LTS1	0x0af8 +#define USB_DEVICE_ID_SYNAPTICS_LTS2	0x1d10 +#define USB_DEVICE_ID_SYNAPTICS_HD	0x0ac3 +#define USB_DEVICE_ID_SYNAPTICS_QUAD_HD	0x1ac3 +#define USB_DEVICE_ID_SYNAPTICS_TP_V103	0x5710 + +#define USB_VENDOR_ID_TEXAS_INSTRUMENTS	0x2047 +#define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA	0x0855  #define USB_VENDOR_ID_THINGM		0x27b8  #define USB_DEVICE_ID_BLINK1		0x01ed @@ -884,6 +946,9 @@  #define USB_DEVICE_ID_SUPER_DUAL_BOX_PRO 0x8802  #define USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO 0x8804 +#define USB_VENDOR_ID_WISTRON		0x0fb8 +#define USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH		0x1109 +  #define USB_VENDOR_ID_X_TENSIONS               0x1ae7  #define USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE    0x9001 @@ -918,4 +983,8 @@  #define USB_VENDOR_ID_PRIMAX	0x0461  #define USB_DEVICE_ID_PRIMAX_KEYBOARD	0x4e05 + +#define USB_VENDOR_ID_RISO_KAGAKU	0x1294	/* Riso Kagaku Corp. */ +#define USB_DEVICE_ID_RI_KA_WEBMAIL	0x1320	/* Webmail Notifier */ +  #endif diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8741d953dcc..2619f7f4517 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -192,6 +192,7 @@ static int hidinput_setkeycode(struct input_dev *dev,  	return -EINVAL;  } +  /**   * hidinput_calc_abs_res - calculate an absolute axis resolution   * @field: the HID report field to calculate resolution for @@ -234,23 +235,17 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)  	case ABS_MT_TOOL_Y:  	case ABS_MT_TOUCH_MAJOR:  	case ABS_MT_TOUCH_MINOR: -		if (field->unit & 0xffffff00)		/* Not a length */ -			return 0; -		unit_exponent += hid_snto32(field->unit >> 4, 4) - 1; -		switch (field->unit & 0xf) { -		case 0x1:				/* If centimeters */ +		if (field->unit == 0x11) {		/* If centimeters */  			/* Convert to millimeters */  			unit_exponent += 1; -			break; -		case 0x3:				/* If inches */ +		} else if (field->unit == 0x13) {	/* If inches */  			/* Convert to millimeters */  			prev = physical_extents;  			physical_extents *= 254;  			if (physical_extents < prev)  				return 0;  			unit_exponent -= 1; -			break; -		default: +		} else {  			return 0;  		}  		break; @@ -355,9 +350,9 @@ static int hidinput_get_battery_property(struct power_supply *psy,  			ret = -ENOMEM;  			break;  		} -		ret = dev->hid_get_raw_report(dev, dev->battery_report_id, -					      buf, 2, -					      dev->battery_report_type); +		ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, +					 dev->battery_report_type, +					 HID_REQ_GET_REPORT);  		if (ret != 2) {  			ret = -ENODATA; @@ -689,9 +684,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  			break;  		case 0x46: /* TabletPick */ +		case 0x5a: /* SecondaryBarrelSwitch */  			map_key_clear(BTN_STYLUS2);  			break; +		case 0x5b: /* TransducerSerialNumber */ +			set_bit(MSC_SERIAL, input->mscbit); +			break; +  		default:  goto unknown;  		}  		break; @@ -726,6 +726,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x06c: map_key_clear(KEY_YELLOW);		break;  		case 0x06d: map_key_clear(KEY_ZOOM);		break; +		case 0x06f: map_key_clear(KEY_BRIGHTNESSUP);		break; +		case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN);		break; +		case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE);	break; +		case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN);		break; +		case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX);		break; +		case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO);		break; +  		case 0x082: map_key_clear(KEY_VIDEO_NEXT);	break;  		case 0x083: map_key_clear(KEY_LAST);		break;  		case 0x084: map_key_clear(KEY_ENTER);		break; @@ -766,6 +773,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x0bf: map_key_clear(KEY_SLOW);		break;  		case 0x0cd: map_key_clear(KEY_PLAYPAUSE);	break; +		case 0x0cf: map_key_clear(KEY_VOICECOMMAND);	break;  		case 0x0e0: map_abs_clear(ABS_VOLUME);		break;  		case 0x0e2: map_key_clear(KEY_MUTE);		break;  		case 0x0e5: map_key_clear(KEY_BASSBOOST);	break; @@ -773,6 +781,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x0ea: map_key_clear(KEY_VOLUMEDOWN);	break;  		case 0x0f5: map_key_clear(KEY_SLOW);		break; +		case 0x181: map_key_clear(KEY_BUTTONCONFIG);	break;  		case 0x182: map_key_clear(KEY_BOOKMARKS);	break;  		case 0x183: map_key_clear(KEY_CONFIG);		break;  		case 0x184: map_key_clear(KEY_WORDPROCESSOR);	break; @@ -786,6 +795,8 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x18c: map_key_clear(KEY_VOICEMAIL);	break;  		case 0x18d: map_key_clear(KEY_ADDRESSBOOK);	break;  		case 0x18e: map_key_clear(KEY_CALENDAR);	break; +		case 0x18f: map_key_clear(KEY_TASKMANAGER);	break; +		case 0x190: map_key_clear(KEY_JOURNAL);		break;  		case 0x191: map_key_clear(KEY_FINANCE);		break;  		case 0x192: map_key_clear(KEY_CALC);		break;  		case 0x193: map_key_clear(KEY_PLAYER);		break; @@ -794,10 +805,16 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x199: map_key_clear(KEY_CHAT);		break;  		case 0x19c: map_key_clear(KEY_LOGOFF);		break;  		case 0x19e: map_key_clear(KEY_COFFEE);		break; +		case 0x19f: map_key_clear(KEY_CONTROLPANEL);		break; +		case 0x1a2: map_key_clear(KEY_APPSELECT);		break; +		case 0x1a3: map_key_clear(KEY_NEXT);		break; +		case 0x1a4: map_key_clear(KEY_PREVIOUS);	break;  		case 0x1a6: map_key_clear(KEY_HELP);		break;  		case 0x1a7: map_key_clear(KEY_DOCUMENTS);	break;  		case 0x1ab: map_key_clear(KEY_SPELLCHECK);	break;  		case 0x1ae: map_key_clear(KEY_KEYBOARD);	break; +		case 0x1b1: map_key_clear(KEY_SCREENSAVER);		break; +		case 0x1b4: map_key_clear(KEY_FILE);		break;  		case 0x1b6: map_key_clear(KEY_IMAGES);		break;  		case 0x1b7: map_key_clear(KEY_AUDIO);		break;  		case 0x1b8: map_key_clear(KEY_VIDEO);		break; @@ -1155,7 +1172,7 @@ static void hidinput_led_worker(struct work_struct *work)  					      led_work);  	struct hid_field *field;  	struct hid_report *report; -	int len; +	int len, ret;  	__u8 *buf;  	field = hidinput_get_led_field(hid); @@ -1183,13 +1200,16 @@ static void hidinput_led_worker(struct work_struct *work)  	/* fall back to generic raw-output-report */  	len = ((report->size - 1) >> 3) + 1 + (report->id > 0); -	buf = kmalloc(len, GFP_KERNEL); +	buf = hid_alloc_report_buf(report, GFP_KERNEL);  	if (!buf)  		return;  	hid_output_report(report, buf);  	/* synchronous output report */ -	hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); +	ret = hid_hw_output_report(hid, buf, len); +	if (ret == -ENOSYS) +		hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT, +				HID_REQ_SET_REPORT);  	kfree(buf);  } @@ -1268,10 +1288,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)  	}  	input_set_drvdata(input_dev, hid); -	if (hid->ll_driver->hidinput_input_event) -		input_dev->event = hid->ll_driver->hidinput_input_event; -	else if (hid->ll_driver->request || hid->hid_output_raw_report) -		input_dev->event = hidinput_input_event; +	input_dev->event = hidinput_input_event;  	input_dev->open = hidinput_open;  	input_dev->close = hidinput_close;  	input_dev->setkeycode = hidinput_setkeycode; @@ -1284,7 +1301,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)  	input_dev->id.vendor  = hid->vendor;  	input_dev->id.product = hid->product;  	input_dev->id.version = hid->version; -	input_dev->dev.parent = hid->dev.parent; +	input_dev->dev.parent = &hid->dev;  	hidinput->input = input_dev;  	list_add_tail(&hidinput->list, &hid->inputs); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 73845120295..e7769636759 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -342,6 +342,10 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,  		rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 83,  					"Genius Gx Imperator Keyboard");  		break; +	case USB_DEVICE_ID_GENIUS_MANTICORE: +		rdesc = kye_consumer_control_fixup(hdev, rdesc, rsize, 104, +					"Genius Manticore Keyboard"); +		break;  	}  	return rdesc;  } @@ -418,6 +422,14 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)  			goto enabling_err;  		}  		break; +	case USB_DEVICE_ID_GENIUS_MANTICORE: +		/* +		 * The manticore keyboard needs to have all the interfaces +		 * opened at least once to be fully functional. +		 */ +		if (hid_hw_open(hdev)) +			hid_hw_close(hdev); +		break;  	}  	return 0; @@ -439,6 +451,8 @@ static const struct hid_device_id kye_devices[] = {  				USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,  				USB_DEVICE_ID_GENIUS_GX_IMPERATOR) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, +				USB_DEVICE_ID_GENIUS_MANTICORE) },  	{ }  };  MODULE_DEVICE_TABLE(hid, kye_devices); diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c index 31cf29a6ba1..2d25b6cbbc0 100644 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ b/drivers/hid/hid-lenovo-tpkbd.c @@ -14,11 +14,9 @@  #include <linux/module.h>  #include <linux/sysfs.h>  #include <linux/device.h> -#include <linux/usb.h>  #include <linux/hid.h>  #include <linux/input.h>  #include <linux/leds.h> -#include "usbhid/usbhid.h"  #include "hid-ids.h" @@ -41,10 +39,9 @@ static int tpkbd_input_mapping(struct hid_device *hdev,  		struct hid_input *hi, struct hid_field *field,  		struct hid_usage *usage, unsigned long **bit, int *max)  { -	struct usbhid_device *uhdev; - -	uhdev = (struct usbhid_device *) hdev->driver_data; -	if (uhdev->ifnum == 1 && usage->hid == (HID_UP_BUTTON | 0x0010)) { +	if (usage->hid == (HID_UP_BUTTON | 0x0010)) { +		/* mark the device as pointer */ +		hid_set_drvdata(hdev, (void *)1);  		map_key_clear(KEY_MICMUTE);  		return 1;  	} @@ -339,7 +336,7 @@ static int tpkbd_probe_tp(struct hid_device *hdev)  	struct tpkbd_data_pointer *data_pointer;  	size_t name_sz = strlen(dev_name(dev)) + 16;  	char *name_mute, *name_micmute; -	int i, ret; +	int i;  	/* Validate required reports. */  	for (i = 0; i < 4; i++) { @@ -354,7 +351,9 @@ static int tpkbd_probe_tp(struct hid_device *hdev)  		hid_warn(hdev, "Could not create sysfs group\n");  	} -	data_pointer = kzalloc(sizeof(struct tpkbd_data_pointer), GFP_KERNEL); +	data_pointer = devm_kzalloc(&hdev->dev, +				    sizeof(struct tpkbd_data_pointer), +				    GFP_KERNEL);  	if (data_pointer == NULL) {  		hid_err(hdev, "Could not allocate memory for driver data\n");  		return -ENOMEM; @@ -364,20 +363,13 @@ static int tpkbd_probe_tp(struct hid_device *hdev)  	data_pointer->sensitivity = 0xa0;  	data_pointer->press_speed = 0x38; -	name_mute = kzalloc(name_sz, GFP_KERNEL); -	if (name_mute == NULL) { +	name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); +	name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL); +	if (name_mute == NULL || name_micmute == NULL) {  		hid_err(hdev, "Could not allocate memory for led data\n"); -		ret = -ENOMEM; -		goto err; +		return -ENOMEM;  	}  	snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev)); - -	name_micmute = kzalloc(name_sz, GFP_KERNEL); -	if (name_micmute == NULL) { -		hid_err(hdev, "Could not allocate memory for led data\n"); -		ret = -ENOMEM; -		goto err2; -	}  	snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));  	hid_set_drvdata(hdev, data_pointer); @@ -397,19 +389,12 @@ static int tpkbd_probe_tp(struct hid_device *hdev)  	tpkbd_features_set(hdev);  	return 0; - -err2: -	kfree(name_mute); -err: -	kfree(data_pointer); -	return ret;  }  static int tpkbd_probe(struct hid_device *hdev,  		const struct hid_device_id *id)  {  	int ret; -	struct usbhid_device *uhdev;  	ret = hid_parse(hdev);  	if (ret) { @@ -423,9 +408,8 @@ static int tpkbd_probe(struct hid_device *hdev,  		goto err;  	} -	uhdev = (struct usbhid_device *) hdev->driver_data; - -	if (uhdev->ifnum == 1) { +	if (hid_get_drvdata(hdev)) { +		hid_set_drvdata(hdev, NULL);  		ret = tpkbd_probe_tp(hdev);  		if (ret)  			goto err_hid; @@ -449,17 +433,11 @@ static void tpkbd_remove_tp(struct hid_device *hdev)  	led_classdev_unregister(&data_pointer->led_mute);  	hid_set_drvdata(hdev, NULL); -	kfree(data_pointer->led_micmute.name); -	kfree(data_pointer->led_mute.name); -	kfree(data_pointer);  }  static void tpkbd_remove(struct hid_device *hdev)  { -	struct usbhid_device *uhdev; - -	uhdev = (struct usbhid_device *) hdev->driver_data; -	if (uhdev->ifnum == 1) +	if (hid_get_drvdata(hdev))  		tpkbd_remove_tp(hdev);  	hid_hw_stop(hdev); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 6f12ecd36c8..a976f48263f 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -45,7 +45,9 @@  /* Size of the original descriptors of the Driving Force (and Pro) wheels */  #define DF_RDESC_ORIG_SIZE	130  #define DFP_RDESC_ORIG_SIZE	97 +#define FV_RDESC_ORIG_SIZE	130  #define MOMO_RDESC_ORIG_SIZE	87 +#define MOMO2_RDESC_ORIG_SIZE	87  /* Fixed report descriptors for Logitech Driving Force (and Pro)   * wheel controllers @@ -170,6 +172,73 @@ static __u8 dfp_rdesc_fixed[] = {  0xC0                /*  End Collection                          */  }; +static __u8 fv_rdesc_fixed[] = { +0x05, 0x01,         /*  Usage Page (Desktop),                   */ +0x09, 0x04,         /*  Usage (Joystik),                        */ +0xA1, 0x01,         /*  Collection (Application),               */ +0xA1, 0x02,         /*      Collection (Logical),               */ +0x95, 0x01,         /*          Report Count (1),               */ +0x75, 0x0A,         /*          Report Size (10),               */ +0x15, 0x00,         /*          Logical Minimum (0),            */ +0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */ +0x35, 0x00,         /*          Physical Minimum (0),           */ +0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */ +0x09, 0x30,         /*          Usage (X),                      */ +0x81, 0x02,         /*          Input (Variable),               */ +0x95, 0x0C,         /*          Report Count (12),              */ +0x75, 0x01,         /*          Report Size (1),                */ +0x25, 0x01,         /*          Logical Maximum (1),            */ +0x45, 0x01,         /*          Physical Maximum (1),           */ +0x05, 0x09,         /*          Usage Page (Button),            */ +0x19, 0x01,         /*          Usage Minimum (01h),            */ +0x29, 0x0C,         /*          Usage Maximum (0Ch),            */ +0x81, 0x02,         /*          Input (Variable),               */ +0x95, 0x02,         /*          Report Count (2),               */ +0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */ +0x09, 0x01,         /*          Usage (01h),                    */ +0x81, 0x02,         /*          Input (Variable),               */ +0x09, 0x02,         /*          Usage (02h),                    */ +0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */ +0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */ +0x95, 0x01,         /*          Report Count (1),               */ +0x75, 0x08,         /*          Report Size (8),                */ +0x81, 0x02,         /*          Input (Variable),               */ +0x05, 0x01,         /*          Usage Page (Desktop),           */ +0x25, 0x07,         /*          Logical Maximum (7),            */ +0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */ +0x75, 0x04,         /*          Report Size (4),                */ +0x65, 0x14,         /*          Unit (Degrees),                 */ +0x09, 0x39,         /*          Usage (Hat Switch),             */ +0x81, 0x42,         /*          Input (Variable, Null State),   */ +0x75, 0x01,         /*          Report Size (1),                */ +0x95, 0x04,         /*          Report Count (4),               */ +0x65, 0x00,         /*          Unit,                           */ +0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */ +0x09, 0x01,         /*          Usage (01h),                    */ +0x25, 0x01,         /*          Logical Maximum (1),            */ +0x45, 0x01,         /*          Physical Maximum (1),           */ +0x81, 0x02,         /*          Input (Variable),               */ +0x05, 0x01,         /*          Usage Page (Desktop),           */ +0x95, 0x01,         /*          Report Count (1),               */ +0x75, 0x08,         /*          Report Size (8),                */ +0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */ +0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */ +0x09, 0x31,         /*          Usage (Y),                      */ +0x81, 0x02,         /*          Input (Variable),               */ +0x09, 0x32,         /*          Usage (Z),                      */ +0x81, 0x02,         /*          Input (Variable),               */ +0xC0,               /*      End Collection,                     */ +0xA1, 0x02,         /*      Collection (Logical),               */ +0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */ +0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */ +0x95, 0x07,         /*          Report Count (7),               */ +0x75, 0x08,         /*          Report Size (8),                */ +0x09, 0x03,         /*          Usage (03h),                    */ +0x91, 0x02,         /*          Output (Variable),              */ +0xC0,               /*      End Collection,                     */ +0xC0                /*  End Collection                          */ +}; +  static __u8 momo_rdesc_fixed[] = {  0x05, 0x01,         /*  Usage Page (Desktop),               */  0x09, 0x04,         /*  Usage (Joystik),                    */ @@ -216,6 +285,54 @@ static __u8 momo_rdesc_fixed[] = {  0xC0                /*  End Collection                      */  }; +static __u8 momo2_rdesc_fixed[] = { +0x05, 0x01,         /*  Usage Page (Desktop),               */ +0x09, 0x04,         /*  Usage (Joystik),                    */ +0xA1, 0x01,         /*  Collection (Application),           */ +0xA1, 0x02,         /*      Collection (Logical),           */ +0x95, 0x01,         /*          Report Count (1),           */ +0x75, 0x0A,         /*          Report Size (10),           */ +0x15, 0x00,         /*          Logical Minimum (0),        */ +0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */ +0x35, 0x00,         /*          Physical Minimum (0),       */ +0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */ +0x09, 0x30,         /*          Usage (X),                  */ +0x81, 0x02,         /*          Input (Variable),           */ +0x95, 0x0A,         /*          Report Count (10),          */ +0x75, 0x01,         /*          Report Size (1),            */ +0x25, 0x01,         /*          Logical Maximum (1),        */ +0x45, 0x01,         /*          Physical Maximum (1),       */ +0x05, 0x09,         /*          Usage Page (Button),        */ +0x19, 0x01,         /*          Usage Minimum (01h),        */ +0x29, 0x0A,         /*          Usage Maximum (0Ah),        */ +0x81, 0x02,         /*          Input (Variable),           */ +0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */ +0x09, 0x00,         /*          Usage (00h),                */ +0x95, 0x04,         /*          Report Count (4),           */ +0x81, 0x02,         /*          Input (Variable),           */ +0x95, 0x01,         /*          Report Count (1),           */ +0x75, 0x08,         /*          Report Size (8),            */ +0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */ +0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */ +0x09, 0x01,         /*          Usage (01h),                */ +0x81, 0x02,         /*          Input (Variable),           */ +0x05, 0x01,         /*          Usage Page (Desktop),       */ +0x09, 0x31,         /*          Usage (Y),                  */ +0x81, 0x02,         /*          Input (Variable),           */ +0x09, 0x32,         /*          Usage (Z),                  */ +0x81, 0x02,         /*          Input (Variable),           */ +0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */ +0x09, 0x00,         /*          Usage (00h),                */ +0x81, 0x02,         /*          Input (Variable),           */ +0xC0,               /*      End Collection,                 */ +0xA1, 0x02,         /*      Collection (Logical),           */ +0x09, 0x02,         /*          Usage (02h),                */ +0x95, 0x07,         /*          Report Count (7),           */ +0x91, 0x02,         /*          Output (Variable),          */ +0xC0,               /*      End Collection,                 */ +0xC0                /*  End Collection                      */ +}; +  /*   * Certain Logitech keyboards send in report #3 keys which are far   * above the logical maximum described in descriptor. This extends @@ -275,6 +392,24 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,  		}  		break; +	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: +		if (*rsize == MOMO2_RDESC_ORIG_SIZE) { +			hid_info(hdev, +				"fixing up Logitech Momo Racing Force (Black) report descriptor\n"); +			rdesc = momo2_rdesc_fixed; +			*rsize = sizeof(momo2_rdesc_fixed); +		} +		break; + +	case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL: +		if (*rsize == FV_RDESC_ORIG_SIZE) { +			hid_info(hdev, +				"fixing up Logitech Formula Vibration report descriptor\n"); +			rdesc = fv_rdesc_fixed; +			*rsize = sizeof(fv_rdesc_fixed); +		} +		break; +  	case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:  		if (*rsize == DFP_RDESC_ORIG_SIZE) {  			hid_info(hdev, @@ -492,6 +627,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,  		case USB_DEVICE_ID_LOGITECH_G27_WHEEL:  		case USB_DEVICE_ID_LOGITECH_WII_WHEEL:  		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: +		case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:  			field->application = HID_GD_MULTIAXIS;  			break;  		default: @@ -556,7 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)  	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {  		unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -		ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); +		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), +					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  		if (ret >= 0) {  			/* insert a little delay of 10 jiffies ~ 40ms */ @@ -568,7 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)  			buf[1] = 0xB2;  			get_random_bytes(&buf[2], 2); -			ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); +			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), +					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  		}  	} @@ -622,6 +760,8 @@ static const struct hid_device_id lg_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),  		.driver_data = LG_NOGET }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION), +		.driver_data = LG_NOGET },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),  		.driver_data = LG_NOGET | LG_FF4 }, @@ -639,6 +779,8 @@ static const struct hid_device_id lg_devices[] = {  		.driver_data = LG_NOGET | LG_FF4 },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),  		.driver_data = LG_FF4 }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL), +		.driver_data = LG_FF2 },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),  		.driver_data = LG_FF4 },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL), diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index 1a42eaa6ca0..0e3fb1a7e42 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -95,7 +95,7 @@ int lg2ff_init(struct hid_device *hid)  	hid_hw_request(hid, report, HID_REQ_SET_REPORT); -	hid_info(hid, "Force feedback for Logitech RumblePad/Rumblepad 2 by Anssi Hannula <anssi.hannula@gmail.com>\n"); +	hid_info(hid, "Force feedback for Logitech variant 2 rumble devices by Anssi Hannula <anssi.hannula@gmail.com>\n");  	return 0;  } diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 8782fe1aaa0..cc2bd202219 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -43,6 +43,7 @@  #define G25_REV_MIN 0x22  #define G27_REV_MAJ 0x12  #define G27_REV_MIN 0x38 +#define G27_2_REV_MIN 0x39  #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev) @@ -51,7 +52,7 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);  static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);  static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); -static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); +static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_range_store);  struct lg4ff_device_entry {  	__u32 product_id; @@ -130,6 +131,7 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {  	{DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},	/* Driving Force Pro */  	{G25_REV_MAJ,  G25_REV_MIN,  &native_g25},	/* G25 */  	{G27_REV_MAJ,  G27_REV_MIN,  &native_g27},	/* G27 */ +	{G27_REV_MAJ,  G27_2_REV_MIN,  &native_g27},	/* G27 v2 */  };  /* Recalculates X axis value accordingly to currently selected range */ @@ -196,6 +198,21 @@ static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *e  	case FF_CONSTANT:  		x = effect->u.ramp.start_level + 0x80;	/* 0x80 is no force */  		CLAMP(x); + +		if (x == 0x80) { +			/* De-activate force in slot-1*/ +			value[0] = 0x13; +			value[1] = 0x00; +			value[2] = 0x00; +			value[3] = 0x00; +			value[4] = 0x00; +			value[5] = 0x00; +			value[6] = 0x00; + +			hid_hw_request(hid, report, HID_REQ_SET_REPORT); +			return 0; +		} +  		value[0] = 0x11;	/* Slot 1 */  		value[1] = 0x08;  		value[2] = x; @@ -218,12 +235,70 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud  	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;  	struct hid_report *report = list_entry(report_list->next, struct hid_report, list);  	__s32 *value = report->field[0]->value; +	__u32 expand_a, expand_b; +	struct lg4ff_device_entry *entry; +	struct lg_drv_data *drv_data; + +	drv_data = hid_get_drvdata(hid); +	if (!drv_data) { +		hid_err(hid, "Private driver data not found!\n"); +		return; +	} + +	entry = drv_data->device_props; +	if (!entry) { +		hid_err(hid, "Device properties not found!\n"); +		return; +	} + +	/* De-activate Auto-Center */ +	if (magnitude == 0) { +		value[0] = 0xf5; +		value[1] = 0x00; +		value[2] = 0x00; +		value[3] = 0x00; +		value[4] = 0x00; +		value[5] = 0x00; +		value[6] = 0x00; + +		hid_hw_request(hid, report, HID_REQ_SET_REPORT); +		return; +	} + +	if (magnitude <= 0xaaaa) { +		expand_a = 0x0c * magnitude; +		expand_b = 0x80 * magnitude; +	} else { +		expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa); +		expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa); +	} + +	/* Adjust for non-MOMO wheels */ +	switch (entry->product_id) { +	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: +	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: +		break; +	default: +		expand_a = expand_a >> 1; +		break; +	}  	value[0] = 0xfe;  	value[1] = 0x0d; -	value[2] = magnitude >> 13; -	value[3] = magnitude >> 13; -	value[4] = magnitude >> 8; +	value[2] = expand_a / 0xaaaa; +	value[3] = expand_a / 0xaaaa; +	value[4] = expand_b / 0xaaaa; +	value[5] = 0x00; +	value[6] = 0x00; + +	hid_hw_request(hid, report, HID_REQ_SET_REPORT); + +	/* Activate Auto-Center */ +	value[0] = 0x14; +	value[1] = 0x00; +	value[2] = 0x00; +	value[3] = 0x00; +	value[4] = 0x00;  	value[5] = 0x00;  	value[6] = 0x00; @@ -540,17 +615,6 @@ int lg4ff_init(struct hid_device *hid)  	if (error)  		return error; -	/* Check if autocentering is available and -	 * set the centering force to zero by default */ -	if (test_bit(FF_AUTOCENTER, dev->ffbit)) { -		if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)	/* Formula Force EX expects different autocentering command */ -			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; -		else -			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; - -		dev->ff->set_autocenter(dev, 0); -	} -  	/* Get private driver data */  	drv_data = hid_get_drvdata(hid);  	if (!drv_data) { @@ -571,6 +635,17 @@ int lg4ff_init(struct hid_device *hid)  	entry->max_range = lg4ff_devices[i].max_range;  	entry->set_range = lg4ff_devices[i].set_range; +	/* Check if autocentering is available and +	 * set the centering force to zero by default */ +	if (test_bit(FF_AUTOCENTER, dev->ffbit)) { +		if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)	/* Formula Force EX expects different autocentering command */ +			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex; +		else +			dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default; + +		dev->ff->set_autocenter(dev, 0); +	} +  	/* Create sysfs interface */  	error = device_create_file(&hid->dev, &dev_attr_range);  	if (error) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 2e5302462ef..486dbde2ba2 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -44,14 +44,6 @@ static const char kbd_descriptor[] = {  	0x19, 0xE0,		/*   USAGE_MINIMUM (Left Control)   */  	0x29, 0xE7,		/*   USAGE_MAXIMUM (Right GUI)      */  	0x81, 0x02,		/*   INPUT (Data,Var,Abs)       */ -	0x95, 0x05,		/*   REPORT COUNT (5)           */ -	0x05, 0x08,		/*   USAGE PAGE (LED page)      */ -	0x19, 0x01,		/*   USAGE MINIMUM (1)          */ -	0x29, 0x05,		/*   USAGE MAXIMUM (5)          */ -	0x91, 0x02,		/*   OUTPUT (Data, Variable, Absolute)  */ -	0x95, 0x01,		/*   REPORT COUNT (1)           */ -	0x75, 0x03,		/*   REPORT SIZE (3)            */ -	0x91, 0x01,		/*   OUTPUT (Constant)          */  	0x95, 0x06,		/*   REPORT_COUNT (6)           */  	0x75, 0x08,		/*   REPORT_SIZE (8)            */  	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */ @@ -60,6 +52,18 @@ static const char kbd_descriptor[] = {  	0x19, 0x00,		/*   USAGE_MINIMUM (no event)       */  	0x2A, 0xFF, 0x00,	/*   USAGE_MAXIMUM (reserved)       */  	0x81, 0x00,		/*   INPUT (Data,Ary,Abs)       */ +	0x85, 0x0e,		/* REPORT_ID (14)               */ +	0x05, 0x08,		/*   USAGE PAGE (LED page)      */ +	0x95, 0x05,		/*   REPORT COUNT (5)           */ +	0x75, 0x01,		/*   REPORT SIZE (1)            */ +	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */ +	0x25, 0x01,		/*   LOGICAL_MAXIMUM (1)        */ +	0x19, 0x01,		/*   USAGE MINIMUM (1)          */ +	0x29, 0x05,		/*   USAGE MAXIMUM (5)          */ +	0x91, 0x02,		/*   OUTPUT (Data, Variable, Absolute)  */ +	0x95, 0x01,		/*   REPORT COUNT (1)           */ +	0x75, 0x03,		/*   REPORT SIZE (3)            */ +	0x91, 0x01,		/*   OUTPUT (Constant)          */  	0xC0  }; @@ -189,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {  static struct hid_ll_driver logi_dj_ll_driver; -static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, -					size_t count, -					unsigned char report_type);  static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);  static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, @@ -258,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,  	}  	dj_hiddev->ll_driver = &logi_dj_ll_driver; -	dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report;  	dj_hiddev->dev.parent = &djrcv_hdev->dev;  	dj_hiddev->bus = BUS_USB; @@ -516,6 +516,14 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,  	dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout;  	retval = logi_dj_recv_send_report(djrcv_dev, dj_report);  	kfree(dj_report); + +	/* +	 * Ugly sleep to work around a USB 3.0 bug when the receiver is still +	 * processing the "switch-to-dj" command while we send an other command. +	 * 50 msec should gives enough time to the receiver to be ready. +	 */ +	msleep(50); +  	return retval;  } @@ -532,19 +540,40 @@ static void logi_dj_ll_close(struct hid_device *hid)  	dbg_hid("%s:%s\n", __func__, hid->phys);  } -static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, -					size_t count, -					unsigned char report_type) +static int logi_dj_ll_raw_request(struct hid_device *hid, +				  unsigned char reportnum, __u8 *buf, +				  size_t count, unsigned char report_type, +				  int reqtype)  { -	/* Called by hid raw to send data */ -	dbg_hid("%s\n", __func__); +	struct dj_device *djdev = hid->driver_data; +	struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev; +	u8 *out_buf; +	int ret; -	return 0; +	if (buf[0] != REPORT_TYPE_LEDS) +		return -EINVAL; + +	out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC); +	if (!out_buf) +		return -ENOMEM; + +	if (count < DJREPORT_SHORT_LENGTH - 2) +		count = DJREPORT_SHORT_LENGTH - 2; + +	out_buf[0] = REPORT_ID_DJ_SHORT; +	out_buf[1] = djdev->device_index; +	memcpy(out_buf + 2, buf, count); + +	ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf, +		DJREPORT_SHORT_LENGTH, report_type, reqtype); + +	kfree(out_buf); +	return ret;  } -static void rdcat(char **rdesc, unsigned int *rsize, const char *data, unsigned int size) +static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)  { -	memcpy(*rdesc + *rsize, data, size); +	memcpy(rdesc + *rsize, data, size);  	*rsize += size;  } @@ -567,31 +596,31 @@ static int logi_dj_ll_parse(struct hid_device *hid)  	if (djdev->reports_supported & STD_KEYBOARD) {  		dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",  			__func__, djdev->reports_supported); -		rdcat(&rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); +		rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));  	}  	if (djdev->reports_supported & STD_MOUSE) {  		dbg_hid("%s: sending a mouse descriptor, reports_supported: "  			"%x\n", __func__, djdev->reports_supported); -		rdcat(&rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor)); +		rdcat(rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor));  	}  	if (djdev->reports_supported & MULTIMEDIA) {  		dbg_hid("%s: sending a multimedia report descriptor: %x\n",  			__func__, djdev->reports_supported); -		rdcat(&rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor)); +		rdcat(rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));  	}  	if (djdev->reports_supported & POWER_KEYS) {  		dbg_hid("%s: sending a power keys report descriptor: %x\n",  			__func__, djdev->reports_supported); -		rdcat(&rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor)); +		rdcat(rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));  	}  	if (djdev->reports_supported & MEDIA_CENTER) {  		dbg_hid("%s: sending a media center report descriptor: %x\n",  			__func__, djdev->reports_supported); -		rdcat(&rdesc, &rsize, media_descriptor, sizeof(media_descriptor)); +		rdcat(rdesc, &rsize, media_descriptor, sizeof(media_descriptor));  	}  	if (djdev->reports_supported & KBD_LEDS) { @@ -605,58 +634,6 @@ static int logi_dj_ll_parse(struct hid_device *hid)  	return retval;  } -static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, -				  unsigned int code, int value) -{ -	/* Sent by the input layer to handle leds and Force Feedback */ -	struct hid_device *dj_hiddev = input_get_drvdata(dev); -	struct dj_device *dj_dev = dj_hiddev->driver_data; - -	struct dj_receiver_dev *djrcv_dev = -	    dev_get_drvdata(dj_hiddev->dev.parent); -	struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev; -	struct hid_report_enum *output_report_enum; - -	struct hid_field *field; -	struct hid_report *report; -	unsigned char *data; -	int offset; - -	dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", -		__func__, dev->phys, type, code, value); - -	if (type != EV_LED) -		return -1; - -	offset = hidinput_find_field(dj_hiddev, type, code, &field); - -	if (offset == -1) { -		dev_warn(&dev->dev, "event field not found\n"); -		return -1; -	} -	hid_set_field(field, offset, value); - -	data = hid_alloc_report_buf(field->report, GFP_ATOMIC); -	if (!data) { -		dev_warn(&dev->dev, "failed to allocate report buf memory\n"); -		return -1; -	} - -	hid_output_report(field->report, &data[0]); - -	output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; -	report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; -	hid_set_field(report->field[0], 0, dj_dev->device_index); -	hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS); -	hid_set_field(report->field[0], 2, data[1]); - -	hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - -	kfree(data); - -	return 0; -} -  static int logi_dj_ll_start(struct hid_device *hid)  {  	dbg_hid("%s\n", __func__); @@ -675,7 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = {  	.stop = logi_dj_ll_stop,  	.open = logi_dj_ll_open,  	.close = logi_dj_ll_close, -	.hidinput_input_event = logi_dj_ll_input_event, +	.raw_request = logi_dj_ll_raw_request,  }; diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 3b43d1cfa93..ecc2cbf300c 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev,  	 * but there seems to be no other way of switching the mode.  	 * Thus the super-ugly hacky success check below.  	 */ -	ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), -			HID_FEATURE_REPORT); +	ret = hid_hw_raw_request(hdev, feature[0], feature, sizeof(feature), +				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  	if (ret != -EIO && ret != sizeof(feature)) {  		hid_err(hdev, "unable to request touch data (%d)\n", ret);  		goto err_stop_hw; diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 551795b7da1..8ba17a946f2 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -62,9 +62,48 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,  {  	struct input_dev *input = hi->input; +	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) { +		switch (usage->hid & HID_USAGE) { +		/* +		 * Microsoft uses these 2 reserved usage ids for 2 keys on +		 * the MS office kb labelled "Office Home" and "Task Pane". +		 */ +		case 0x29d: +			ms_map_key_clear(KEY_PROG1); +			return 1; +		case 0x29e: +			ms_map_key_clear(KEY_PROG2); +			return 1; +		} +		return 0; +	} + +	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) +		return 0; +  	switch (usage->hid & HID_USAGE) {  	case 0xfd06: ms_map_key_clear(KEY_CHAT);	break;  	case 0xfd07: ms_map_key_clear(KEY_PHONE);	break; +	case 0xff00: +		/* Special keypad keys */ +		ms_map_key_clear(KEY_KPEQUAL); +		set_bit(KEY_KPLEFTPAREN, input->keybit); +		set_bit(KEY_KPRIGHTPAREN, input->keybit); +		break; +	case 0xff01: +		/* Scroll wheel */ +		hid_map_usage_clear(hi, usage, bit, max, EV_REL, REL_WHEEL); +		break; +	case 0xff02: +		/* +		 * This byte contains a copy of the modifier keys byte of a +		 * standard hid keyboard report, as send by interface 0 +		 * (this usage is found on interface 1). +		 * +		 * This byte only gets send when another key in the same report +		 * changes state, and as such is useless, ignore it. +		 */ +		return -1;  	case 0xff05:  		set_bit(EV_REP, input->evbit);  		ms_map_key_clear(KEY_F13); @@ -73,6 +112,7 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,  		set_bit(KEY_F16, input->keybit);  		set_bit(KEY_F17, input->keybit);  		set_bit(KEY_F18, input->keybit); +		break;  	default:  		return 0;  	} @@ -82,6 +122,9 @@ static int ms_ergonomy_kb_quirk(struct hid_input *hi, struct hid_usage *usage,  static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage,  		unsigned long **bit, int *max)  { +	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) +		return 0; +  	set_bit(EV_REP, hi->input->evbit);  	switch (usage->hid & HID_USAGE) {  	case 0xfd08: ms_map_key_clear(KEY_FORWARD);	break; @@ -101,9 +144,6 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi,  {  	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); -	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) -		return 0; -  	if (quirks & MS_ERGONOMY) {  		int ret = ms_ergonomy_kb_quirk(hi, usage, bit, max);  		if (ret) @@ -133,14 +173,39 @@ static int ms_event(struct hid_device *hdev, struct hid_field *field,  		struct hid_usage *usage, __s32 value)  {  	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); +	struct input_dev *input;  	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||  			!usage->type)  		return 0; +	input = field->hidinput->input; +  	/* Handling MS keyboards special buttons */ +	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff00)) { +		/* Special keypad keys */ +		input_report_key(input, KEY_KPEQUAL, value & 0x01); +		input_report_key(input, KEY_KPLEFTPAREN, value & 0x02); +		input_report_key(input, KEY_KPRIGHTPAREN, value & 0x04); +		return 1; +	} + +	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff01)) { +		/* Scroll wheel */ +		int step = ((value & 0x60) >> 5) + 1; + +		switch (value & 0x1f) { +		case 0x01: +			input_report_rel(input, REL_WHEEL, step); +			break; +		case 0x1f: +			input_report_rel(input, REL_WHEEL, -step); +			break; +		} +		return 1; +	} +  	if (quirks & MS_ERGONOMY && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { -		struct input_dev *input = field->hidinput->input;  		static unsigned int last_key = 0;  		unsigned int key = 0;  		switch (value) { @@ -193,6 +258,8 @@ err_free:  static const struct hid_device_id ms_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV),  		.driver_data = MS_HIDINPUT }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB), +		.driver_data = MS_ERGONOMY },  	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K),  		.driver_data = MS_ERGONOMY },  	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_NE4K_JP), diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 5e5fe1b8eeb..51e25b9407f 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -68,6 +68,9 @@ MODULE_LICENSE("GPL");  #define MT_QUIRK_HOVERING		(1 << 11)  #define MT_QUIRK_CONTACT_CNT_ACCURATE	(1 << 12) +#define MT_INPUTMODE_TOUCHSCREEN	0x02 +#define MT_INPUTMODE_TOUCHPAD		0x03 +  struct mt_slot {  	__s32 x, y, cx, cy, p, w, h;  	__s32 contactid;	/* the device ContactID assigned to this slot */ @@ -84,6 +87,7 @@ struct mt_class {  	__s32 sn_pressure;	/* Signal/noise ratio for pressure events */  	__u8 maxcontacts;  	bool is_indirect;	/* true for touchpads */ +	bool export_all_inputs;	/* do not ignore mouse, keyboards, etc... */  };  struct mt_fields { @@ -100,11 +104,11 @@ struct mt_device {  	int cc_value_index;	/* contact count value index in the field */  	unsigned last_slot_field;	/* the last field of a slot */  	unsigned mt_report_id;	/* the report ID of the multitouch device */ -	unsigned pen_report_id;	/* the report ID of the pen device */  	__s16 inputmode;	/* InputMode HID feature, -1 if non-existent */  	__s16 inputmode_index;	/* InputMode HID feature index in the report */  	__s16 maxcontact_report_id;	/* Maximum Contact Number HID feature,  				   -1 if non-existent */ +	__u8 inputmode_value;  /* InputMode HID feature value */  	__u8 num_received;	/* how many contacts we received */  	__u8 num_expected;	/* expected last contact index */  	__u8 maxcontacts; @@ -128,16 +132,17 @@ static void mt_post_parse(struct mt_device *td);  #define MT_CLS_CONFIDENCE_MINUS_ONE		0x0005  #define MT_CLS_DUAL_INRANGE_CONTACTID		0x0006  #define MT_CLS_DUAL_INRANGE_CONTACTNUMBER	0x0007 -#define MT_CLS_DUAL_NSMU_CONTACTID		0x0008 +/* reserved					0x0008 */  #define MT_CLS_INRANGE_CONTACTNUMBER		0x0009  #define MT_CLS_NSMU				0x000a -#define MT_CLS_DUAL_CONTACT_NUMBER		0x0010 -#define MT_CLS_DUAL_CONTACT_ID			0x0011 +/* reserved					0x0010 */ +/* reserved					0x0011 */  #define MT_CLS_WIN_8				0x0012 +#define MT_CLS_EXPORT_ALL_INPUTS		0x0013  /* vendor specific classes */  #define MT_CLS_3M				0x0101 -#define MT_CLS_CYPRESS				0x0102 +/* reserved					0x0102 */  #define MT_CLS_EGALAX				0x0103  #define MT_CLS_EGALAX_SERIAL			0x0104  #define MT_CLS_TOPSEED				0x0105 @@ -189,28 +194,18 @@ static struct mt_class mt_classes[] = {  		.quirks = MT_QUIRK_VALID_IS_INRANGE |  			MT_QUIRK_SLOT_IS_CONTACTNUMBER,  		.maxcontacts = 2 }, -	{ .name = MT_CLS_DUAL_NSMU_CONTACTID, -		.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | -			MT_QUIRK_SLOT_IS_CONTACTID, -		.maxcontacts = 2 },  	{ .name = MT_CLS_INRANGE_CONTACTNUMBER,  		.quirks = MT_QUIRK_VALID_IS_INRANGE |  			MT_QUIRK_SLOT_IS_CONTACTNUMBER }, -	{ .name = MT_CLS_DUAL_CONTACT_NUMBER, -		.quirks = MT_QUIRK_ALWAYS_VALID | -			MT_QUIRK_CONTACT_CNT_ACCURATE | -			MT_QUIRK_SLOT_IS_CONTACTNUMBER, -		.maxcontacts = 2 }, -	{ .name = MT_CLS_DUAL_CONTACT_ID, -		.quirks = MT_QUIRK_ALWAYS_VALID | -			MT_QUIRK_CONTACT_CNT_ACCURATE | -			MT_QUIRK_SLOT_IS_CONTACTID, -		.maxcontacts = 2 },  	{ .name = MT_CLS_WIN_8,  		.quirks = MT_QUIRK_ALWAYS_VALID |  			MT_QUIRK_IGNORE_DUPLICATES |  			MT_QUIRK_HOVERING |  			MT_QUIRK_CONTACT_CNT_ACCURATE }, +	{ .name = MT_CLS_EXPORT_ALL_INPUTS, +		.quirks = MT_QUIRK_ALWAYS_VALID | +			MT_QUIRK_CONTACT_CNT_ACCURATE, +		.export_all_inputs = true },  	/*  	 * vendor specific classes @@ -223,10 +218,6 @@ static struct mt_class mt_classes[] = {  		.sn_height = 128,  		.maxcontacts = 60,  	}, -	{ .name = MT_CLS_CYPRESS, -		.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | -			MT_QUIRK_CYPRESS, -		.maxcontacts = 10 },  	{ .name = MT_CLS_EGALAX,  		.quirks =  MT_QUIRK_SLOT_IS_CONTACTID |  			MT_QUIRK_VALID_IS_INRANGE, @@ -250,12 +241,12 @@ static struct mt_class mt_classes[] = {  	{ .name	= MT_CLS_GENERALTOUCH_TWOFINGERS,  		.quirks	= MT_QUIRK_NOT_SEEN_MEANS_UP |  			MT_QUIRK_VALID_IS_INRANGE | -			MT_QUIRK_SLOT_IS_CONTACTNUMBER, +			MT_QUIRK_SLOT_IS_CONTACTID,  		.maxcontacts = 2  	},  	{ .name	= MT_CLS_GENERALTOUCH_PWT_TENFINGERS,  		.quirks	= MT_QUIRK_NOT_SEEN_MEANS_UP | -			MT_QUIRK_SLOT_IS_CONTACTNUMBER +			MT_QUIRK_SLOT_IS_CONTACTID  	},  	{ .name = MT_CLS_FLATFROG, @@ -360,45 +351,6 @@ static void mt_store_field(struct hid_usage *usage, struct mt_device *td,  	f->usages[f->length++] = usage->hid;  } -static int mt_pen_input_mapping(struct hid_device *hdev, struct hid_input *hi, -		struct hid_field *field, struct hid_usage *usage, -		unsigned long **bit, int *max) -{ -	struct mt_device *td = hid_get_drvdata(hdev); - -	td->pen_report_id = field->report->id; - -	return 0; -} - -static int mt_pen_input_mapped(struct hid_device *hdev, struct hid_input *hi, -		struct hid_field *field, struct hid_usage *usage, -		unsigned long **bit, int *max) -{ -	return 0; -} - -static int mt_pen_event(struct hid_device *hid, struct hid_field *field, -				struct hid_usage *usage, __s32 value) -{ -	/* let hid-input handle it */ -	return 0; -} - -static void mt_pen_report(struct hid_device *hid, struct hid_report *report) -{ -	struct hid_field *field = report->field[0]; - -	input_sync(field->hidinput->input); -} - -static void mt_pen_input_configured(struct hid_device *hdev, -					struct hid_input *hi) -{ -	/* force BTN_STYLUS to allow tablet matching in udev */ -	__set_bit(BTN_STYLUS, hi->input->keybit); -} -  static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,  		struct hid_field *field, struct hid_usage *usage,  		unsigned long **bit, int *max) @@ -415,8 +367,10 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,  	 * Model touchscreens providing buttons as touchpads.  	 */  	if (field->application == HID_DG_TOUCHPAD || -	    (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) +	    (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) {  		td->mt_flags |= INPUT_MT_POINTER; +		td->inputmode_value = MT_INPUTMODE_TOUCHPAD; +	}  	if (usage->usage_index)  		prev_usage = &field->usage[usage->usage_index - 1]; @@ -776,28 +730,52 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,  		struct hid_field *field, struct hid_usage *usage,  		unsigned long **bit, int *max)  { -	/* Only map fields from TouchScreen or TouchPad collections. -	* We need to ignore fields that belong to other collections -	* such as Mouse that might have the same GenericDesktop usages. */ -	if (field->application != HID_DG_TOUCHSCREEN && +	struct mt_device *td = hid_get_drvdata(hdev); + +	/* +	 * If mtclass.export_all_inputs is not set, only map fields from +	 * TouchScreen or TouchPad collections. We need to ignore fields +	 * that belong to other collections such as Mouse that might have +	 * the same GenericDesktop usages. +	 */ +	if (!td->mtclass.export_all_inputs && +	    field->application != HID_DG_TOUCHSCREEN &&  	    field->application != HID_DG_PEN &&  	    field->application != HID_DG_TOUCHPAD)  		return -1; +	/* +	 * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" +	 * for the stylus. +	 */  	if (field->physical == HID_DG_STYLUS) -		return mt_pen_input_mapping(hdev, hi, field, usage, bit, max); +		return 0; + +	if (field->application == HID_DG_TOUCHSCREEN || +	    field->application == HID_DG_TOUCHPAD) +		return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); -	return mt_touch_input_mapping(hdev, hi, field, usage, bit, max); +	/* let hid-core decide for the others */ +	return 0;  }  static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,  		struct hid_field *field, struct hid_usage *usage,  		unsigned long **bit, int *max)  { +	/* +	 * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" +	 * for the stylus. +	 */  	if (field->physical == HID_DG_STYLUS) -		return mt_pen_input_mapped(hdev, hi, field, usage, bit, max); +		return 0; -	return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); +	if (field->application == HID_DG_TOUCHSCREEN || +	    field->application == HID_DG_TOUCHPAD) +		return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); + +	/* let hid-core decide for the others */ +	return 0;  }  static int mt_event(struct hid_device *hid, struct hid_field *field, @@ -808,25 +786,22 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,  	if (field->report->id == td->mt_report_id)  		return mt_touch_event(hid, field, usage, value); -	if (field->report->id == td->pen_report_id) -		return mt_pen_event(hid, field, usage, value); - -	/* ignore other reports */ -	return 1; +	return 0;  }  static void mt_report(struct hid_device *hid, struct hid_report *report)  {  	struct mt_device *td = hid_get_drvdata(hid); +	struct hid_field *field = report->field[0];  	if (!(hid->claimed & HID_CLAIMED_INPUT))  		return;  	if (report->id == td->mt_report_id) -		mt_touch_report(hid, report); +		return mt_touch_report(hid, report); -	if (report->id == td->pen_report_id) -		mt_pen_report(hid, report); +	if (field && field->hidinput && field->hidinput->input) +		input_sync(field->hidinput->input);  }  static void mt_set_input_mode(struct hid_device *hdev) @@ -841,7 +816,7 @@ static void mt_set_input_mode(struct hid_device *hdev)  	re = &(hdev->report_enum[HID_FEATURE_REPORT]);  	r = re->report_id_hash[td->inputmode];  	if (r) { -		r->field[0]->value[td->inputmode_index] = 0x02; +		r->field[0]->value[td->inputmode_index] = td->inputmode_value;  		hid_hw_request(hdev, r, HID_REQ_SET_REPORT);  	}  } @@ -907,13 +882,49 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)  	struct mt_device *td = hid_get_drvdata(hdev);  	char *name;  	const char *suffix = NULL; +	struct hid_field *field = hi->report->field[0];  	if (hi->report->id == td->mt_report_id)  		mt_touch_input_configured(hdev, hi); +	/* +	 * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" +	 * for the stylus. Check this first, and then rely on the application +	 * field. +	 */  	if (hi->report->field[0]->physical == HID_DG_STYLUS) {  		suffix = "Pen"; -		mt_pen_input_configured(hdev, hi); +		/* force BTN_STYLUS to allow tablet matching in udev */ +		__set_bit(BTN_STYLUS, hi->input->keybit); +	} else { +		switch (field->application) { +		case HID_GD_KEYBOARD: +			suffix = "Keyboard"; +			break; +		case HID_GD_KEYPAD: +			suffix = "Keypad"; +			break; +		case HID_GD_MOUSE: +			suffix = "Mouse"; +			break; +		case HID_DG_STYLUS: +			suffix = "Pen"; +			/* force BTN_STYLUS to allow tablet matching in udev */ +			__set_bit(BTN_STYLUS, hi->input->keybit); +			break; +		case HID_DG_TOUCHSCREEN: +			/* we do not set suffix = "Touchscreen" */ +			break; +		case HID_GD_SYSTEM_CONTROL: +			suffix = "System Control"; +			break; +		case HID_CP_CONSUMER_CONTROL: +			suffix = "Consumer Control"; +			break; +		default: +			suffix = "UNKNOWN"; +			break; +		}  	}  	if (suffix) { @@ -973,9 +984,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)  	td->mtclass = *mtclass;  	td->inputmode = -1;  	td->maxcontact_report_id = -1; +	td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;  	td->cc_index = -1;  	td->mt_report_id = -1; -	td->pen_report_id = -1;  	hid_set_drvdata(hdev, td);  	td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields), @@ -1034,6 +1045,12 @@ static void mt_remove(struct hid_device *hdev)  	hid_hw_stop(hdev);  } +/* + * This list contains only: + * - VID/PID of products not working with the default multitouch handling + * - 2 generic rules. + * So there is no point in adding here any device with MT_CLS_DEFAULT. + */  static const struct hid_device_id mt_devices[] = {  	/* 3M panels */ @@ -1047,33 +1064,25 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_3M,  			USB_DEVICE_ID_3M3266) }, -	/* ActionStar panels */ -	{ .driver_data = MT_CLS_NSMU, -		MT_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, -			USB_DEVICE_ID_ACTIONSTAR_1011) }, +	/* Anton devices */ +	{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS, +		MT_USB_DEVICE(USB_VENDOR_ID_ANTON, +			USB_DEVICE_ID_ANTON_TOUCH_PAD) },  	/* Atmel panels */  	{ .driver_data = MT_CLS_SERIAL,  		MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, -			USB_DEVICE_ID_ATMEL_MULTITOUCH) }, -	{ .driver_data = MT_CLS_SERIAL, -		MT_USB_DEVICE(USB_VENDOR_ID_ATMEL,  			USB_DEVICE_ID_ATMEL_MXT_DIGITIZER) },  	/* Baanto multitouch devices */  	{ .driver_data = MT_CLS_NSMU,  		MT_USB_DEVICE(USB_VENDOR_ID_BAANTO,  			USB_DEVICE_ID_BAANTO_MT_190W2) }, +  	/* Cando panels */  	{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,  		MT_USB_DEVICE(USB_VENDOR_ID_CANDO,  			USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, -	{ .driver_data = MT_CLS_DUAL_CONTACT_NUMBER, -		MT_USB_DEVICE(USB_VENDOR_ID_CANDO, -			USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, -	{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, -		MT_USB_DEVICE(USB_VENDOR_ID_CANDO, -			USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },  	{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,  		MT_USB_DEVICE(USB_VENDOR_ID_CANDO,  			USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, @@ -1088,16 +1097,6 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_CVTOUCH,  			USB_DEVICE_ID_CVTOUCH_SCREEN) }, -	/* Cypress panel */ -	{ .driver_data = MT_CLS_CYPRESS, -		HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, -			USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, - -	/* Data Modul easyMaxTouch */ -	{ .driver_data = MT_CLS_DEFAULT, -		MT_USB_DEVICE(USB_VENDOR_ID_DATA_MODUL, -			USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH) }, -  	/* eGalax devices (resistive) */  	{ .driver_data = MT_CLS_EGALAX,  		MT_USB_DEVICE(USB_VENDOR_ID_DWAV, @@ -1156,16 +1155,21 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_DWAV,  			USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, -	/* Elo TouchSystems IntelliTouch Plus panel */ -	{ .driver_data = MT_CLS_DUAL_CONTACT_ID, -		MT_USB_DEVICE(USB_VENDOR_ID_ELO, -			USB_DEVICE_ID_ELO_TS2515) }, +	/* Elitegroup panel */ +	{ .driver_data = MT_CLS_SERIAL, +		MT_USB_DEVICE(USB_VENDOR_ID_ELITEGROUP, +			USB_DEVICE_ID_ELITEGROUP_05D8) },  	/* Flatfrog Panels */  	{ .driver_data = MT_CLS_FLATFROG,  		MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG,  			USB_DEVICE_ID_MULTITOUCH_3200) }, +	/* FocalTech Panels */ +	{ .driver_data = MT_CLS_SERIAL, +		MT_USB_DEVICE(USB_VENDOR_ID_CYGNAL, +			USB_DEVICE_ID_FOCALTECH_FTXXXX_MULTITOUCH) }, +  	/* GeneralTouch panel */  	{ .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,  		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, @@ -1173,6 +1177,21 @@ static const struct hid_device_id mt_devices[] = {  	{ .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,  		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,  			USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) }, +	{ .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS, +		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, +			USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0101) }, +	{ .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, +		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, +			USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0102) }, +	{ .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, +		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, +			USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_0106) }, +	{ .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, +		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, +			USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_010A) }, +	{ .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS, +		MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, +			USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100) },  	/* Gametel game controller */  	{ .driver_data = MT_CLS_NSMU, @@ -1189,37 +1208,11 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT,  			USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, -	/* Ideacom panel */ -	{ .driver_data = MT_CLS_SERIAL, -		MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, -			USB_DEVICE_ID_IDEACOM_IDC6650) }, -	{ .driver_data = MT_CLS_SERIAL, -		MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, -			USB_DEVICE_ID_IDEACOM_IDC6651) }, -  	/* Ilitek dual touch panel */  	{  .driver_data = MT_CLS_NSMU,  		MT_USB_DEVICE(USB_VENDOR_ID_ILITEK,  			USB_DEVICE_ID_ILITEK_MULTITOUCH) }, -	/* IRTOUCH panels */ -	{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, -		MT_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, -			USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, - -	/* LG Display panels */ -	{ .driver_data = MT_CLS_DEFAULT, -		MT_USB_DEVICE(USB_VENDOR_ID_LG, -			USB_DEVICE_ID_LG_MULTITOUCH) }, - -	/* Lumio panels */ -	{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, -		MT_USB_DEVICE(USB_VENDOR_ID_LUMIO, -			USB_DEVICE_ID_CRYSTALTOUCH) }, -	{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, -		MT_USB_DEVICE(USB_VENDOR_ID_LUMIO, -			USB_DEVICE_ID_CRYSTALTOUCH_DUAL) }, -  	/* MosArt panels */  	{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE,  		MT_USB_DEVICE(USB_VENDOR_ID_ASUS, @@ -1231,11 +1224,6 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_TURBOX,  			USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) }, -	/* Nexio panels */ -	{ .driver_data = MT_CLS_DEFAULT, -		MT_USB_DEVICE(USB_VENDOR_ID_NEXIO, -			USB_DEVICE_ID_NEXIO_MULTITOUCH_420)}, -  	/* Panasonic panels */  	{ .driver_data = MT_CLS_PANASONIC,  		MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, @@ -1249,11 +1237,6 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_NOVATEK,  			USB_DEVICE_ID_NOVATEK_PCT) }, -	/* PenMount panels */ -	{ .driver_data = MT_CLS_CONFIDENCE, -		MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, -			USB_DEVICE_ID_PENMOUNT_PCI) }, -  	/* PixArt optical touch screen */  	{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,  		MT_USB_DEVICE(USB_VENDOR_ID_PIXART, @@ -1267,33 +1250,18 @@ static const struct hid_device_id mt_devices[] = {  	/* PixCir-based panels */  	{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, -		MT_USB_DEVICE(USB_VENDOR_ID_HANVON, -			USB_DEVICE_ID_HANVON_MULTITOUCH) }, -	{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID,  		MT_USB_DEVICE(USB_VENDOR_ID_CANDO,  			USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },  	/* Quanta-based panels */  	{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID,  		MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, -			USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, -	{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, -		MT_USB_DEVICE(USB_VENDOR_ID_QUANTA,  			USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) }, -	{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, -		MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, -			USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },  	/* Stantum panels */  	{ .driver_data = MT_CLS_CONFIDENCE, -		MT_USB_DEVICE(USB_VENDOR_ID_STANTUM, -			USB_DEVICE_ID_MTP)}, -	{ .driver_data = MT_CLS_CONFIDENCE,  		MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,  			USB_DEVICE_ID_MTP_STM)}, -	{ .driver_data = MT_CLS_DEFAULT, -		MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, -			USB_DEVICE_ID_MTP_SITRONIX)},  	/* TopSeed panels */  	{ .driver_data = MT_CLS_TOPSEED, @@ -1312,6 +1280,12 @@ static const struct hid_device_id mt_devices[] = {  	{ .driver_data = MT_CLS_NSMU,  		MT_USB_DEVICE(USB_VENDOR_ID_UNITEC,  			USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, + +	/* Wistron panels */ +	{ .driver_data = MT_CLS_NSMU, +		MT_USB_DEVICE(USB_VENDOR_ID_WISTRON, +			USB_DEVICE_ID_WISTRON_OPTICAL_TOUCH) }, +  	/* XAT */  	{ .driver_data = MT_CLS_NSMU,  		MT_USB_DEVICE(USB_VENDOR_ID_XAT, @@ -1346,11 +1320,6 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,  			USB_DEVICE_ID_XIROKU_CSR2) }, -	/* Zytronic panels */ -	{ .driver_data = MT_CLS_SERIAL, -		MT_USB_DEVICE(USB_VENDOR_ID_ZYTRONIC, -			USB_DEVICE_ID_ZYTRONIC_ZXY100) }, -  	/* Generic MT device */  	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c index 59d5eb1e742..cf1a9f1c121 100644 --- a/drivers/hid/hid-picolcd_cir.c +++ b/drivers/hid/hid-picolcd_cir.c @@ -114,7 +114,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)  	rdev->priv             = data;  	rdev->driver_type      = RC_DRIVER_IR_RAW; -	rdev->allowed_protos   = RC_BIT_ALL; +	rc_set_allowed_protocols(rdev, RC_BIT_ALL);  	rdev->open             = picolcd_cir_open;  	rdev->close            = picolcd_cir_close;  	rdev->input_name       = data->hdev->name; diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index c930ab8554e..7f965e23143 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -501,7 +501,7 @@ static ssize_t picolcd_fb_update_rate_store(struct device *dev,  	return count;  } -static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show, +static DEVICE_ATTR(fb_update_rate, 0664, picolcd_fb_update_rate_show,  		picolcd_fb_update_rate_store);  /* initialize Framebuffer device */ diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index 7ed82805641..91fab975063 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -624,7 +624,8 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm)  	/* Setup sound card */ -	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card); +	err = snd_card_new(&pm->pk->hdev->dev, index[dev], id[dev], +			   THIS_MODULE, 0, &card);  	if (err < 0) {  		pk_error("failed to create pc-midi sound card\n");  		err = -ENOMEM; @@ -660,8 +661,6 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm)  	snd_rawmidi_set_ops(rwmidi, SNDRV_RAWMIDI_STREAM_INPUT,  		&pcmidi_in_ops); -	snd_card_set_dev(card, &pm->pk->hdev->dev); -  	/* create sysfs variables */  	err = device_create_file(&pm->pk->hdev->dev,  				 sysfs_device_attr_channel); diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c new file mode 100644 index 00000000000..578bbe65902 --- /dev/null +++ b/drivers/hid/hid-rmi.c @@ -0,0 +1,922 @@ +/* + *  Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com> + *  Copyright (c) 2013 Synaptics Incorporated + *  Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com> + *  Copyright (c) 2014 Red Hat, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/kernel.h> +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include "hid-ids.h" + +#define RMI_MOUSE_REPORT_ID		0x01 /* Mouse emulation Report */ +#define RMI_WRITE_REPORT_ID		0x09 /* Output Report */ +#define RMI_READ_ADDR_REPORT_ID		0x0a /* Output Report */ +#define RMI_READ_DATA_REPORT_ID		0x0b /* Input Report */ +#define RMI_ATTN_REPORT_ID		0x0c /* Input Report */ +#define RMI_SET_RMI_MODE_REPORT_ID	0x0f /* Feature Report */ + +/* flags */ +#define RMI_READ_REQUEST_PENDING	BIT(0) +#define RMI_READ_DATA_PENDING		BIT(1) +#define RMI_STARTED			BIT(2) + +enum rmi_mode_type { +	RMI_MODE_OFF			= 0, +	RMI_MODE_ATTN_REPORTS		= 1, +	RMI_MODE_NO_PACKED_ATTN_REPORTS	= 2, +}; + +struct rmi_function { +	unsigned page;			/* page of the function */ +	u16 query_base_addr;		/* base address for queries */ +	u16 command_base_addr;		/* base address for commands */ +	u16 control_base_addr;		/* base address for controls */ +	u16 data_base_addr;		/* base address for datas */ +	unsigned int interrupt_base;	/* cross-function interrupt number +					 * (uniq in the device)*/ +	unsigned int interrupt_count;	/* number of interrupts */ +	unsigned int report_size;	/* size of a report */ +	unsigned long irq_mask;		/* mask of the interrupts +					 * (to be applied against ATTN IRQ) */ +}; + +/** + * struct rmi_data - stores information for hid communication + * + * @page_mutex: Locks current page to avoid changing pages in unexpected ways. + * @page: Keeps track of the current virtual page + * + * @wait: Used for waiting for read data + * + * @writeReport: output buffer when writing RMI registers + * @readReport: input buffer when reading RMI registers + * + * @input_report_size: size of an input report (advertised by HID) + * @output_report_size: size of an output report (advertised by HID) + * + * @flags: flags for the current device (started, reading, etc...) + * + * @f11: placeholder of internal RMI function F11 description + * @f30: placeholder of internal RMI function F30 description + * + * @max_fingers: maximum finger count reported by the device + * @max_x: maximum x value reported by the device + * @max_y: maximum y value reported by the device + * + * @gpio_led_count: count of GPIOs + LEDs reported by F30 + * @button_count: actual physical buttons count + * @button_mask: button mask used to decode GPIO ATTN reports + * @button_state_mask: pull state of the buttons + * + * @input: pointer to the kernel input device + * + * @reset_work: worker which will be called in case of a mouse report + * @hdev: pointer to the struct hid_device + */ +struct rmi_data { +	struct mutex page_mutex; +	int page; + +	wait_queue_head_t wait; + +	u8 *writeReport; +	u8 *readReport; + +	int input_report_size; +	int output_report_size; + +	unsigned long flags; + +	struct rmi_function f11; +	struct rmi_function f30; + +	unsigned int max_fingers; +	unsigned int max_x; +	unsigned int max_y; +	unsigned int x_size_mm; +	unsigned int y_size_mm; + +	unsigned int gpio_led_count; +	unsigned int button_count; +	unsigned long button_mask; +	unsigned long button_state_mask; + +	struct input_dev *input; + +	struct work_struct reset_work; +	struct hid_device *hdev; +}; + +#define RMI_PAGE(addr) (((addr) >> 8) & 0xff) + +static int rmi_write_report(struct hid_device *hdev, u8 *report, int len); + +/** + * rmi_set_page - Set RMI page + * @hdev: The pointer to the hid_device struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the physical + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct hid_device *hdev, u8 page) +{ +	struct rmi_data *data = hid_get_drvdata(hdev); +	int retval; + +	data->writeReport[0] = RMI_WRITE_REPORT_ID; +	data->writeReport[1] = 1; +	data->writeReport[2] = 0xFF; +	data->writeReport[4] = page; + +	retval = rmi_write_report(hdev, data->writeReport, +			data->output_report_size); +	if (retval != data->output_report_size) { +		dev_err(&hdev->dev, +			"%s: set page failed: %d.", __func__, retval); +		return retval; +	} + +	data->page = page; +	return 0; +} + +static int rmi_set_mode(struct hid_device *hdev, u8 mode) +{ +	int ret; +	u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode}; + +	ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf, +			sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT); +	if (ret < 0) { +		dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode, +			ret); +		return ret; +	} + +	return 0; +} + +static int rmi_write_report(struct hid_device *hdev, u8 *report, int len) +{ +	int ret; + +	ret = hid_hw_output_report(hdev, (void *)report, len); +	if (ret < 0) { +		dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret); +		return ret; +	} + +	return ret; +} + +static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf, +		const int len) +{ +	struct rmi_data *data = hid_get_drvdata(hdev); +	int ret; +	int bytes_read; +	int bytes_needed; +	int retries; +	int read_input_count; + +	mutex_lock(&data->page_mutex); + +	if (RMI_PAGE(addr) != data->page) { +		ret = rmi_set_page(hdev, RMI_PAGE(addr)); +		if (ret < 0) +			goto exit; +	} + +	for (retries = 5; retries > 0; retries--) { +		data->writeReport[0] = RMI_READ_ADDR_REPORT_ID; +		data->writeReport[1] = 0; /* old 1 byte read count */ +		data->writeReport[2] = addr & 0xFF; +		data->writeReport[3] = (addr >> 8) & 0xFF; +		data->writeReport[4] = len  & 0xFF; +		data->writeReport[5] = (len >> 8) & 0xFF; + +		set_bit(RMI_READ_REQUEST_PENDING, &data->flags); + +		ret = rmi_write_report(hdev, data->writeReport, +						data->output_report_size); +		if (ret != data->output_report_size) { +			clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); +			dev_err(&hdev->dev, +				"failed to write request output report (%d)\n", +				ret); +			goto exit; +		} + +		bytes_read = 0; +		bytes_needed = len; +		while (bytes_read < len) { +			if (!wait_event_timeout(data->wait, +				test_bit(RMI_READ_DATA_PENDING, &data->flags), +					msecs_to_jiffies(1000))) { +				hid_warn(hdev, "%s: timeout elapsed\n", +					 __func__); +				ret = -EAGAIN; +				break; +			} + +			read_input_count = data->readReport[1]; +			memcpy(buf + bytes_read, &data->readReport[2], +				read_input_count < bytes_needed ? +					read_input_count : bytes_needed); + +			bytes_read += read_input_count; +			bytes_needed -= read_input_count; +			clear_bit(RMI_READ_DATA_PENDING, &data->flags); +		} + +		if (ret >= 0) { +			ret = 0; +			break; +		} +	} + +exit: +	clear_bit(RMI_READ_REQUEST_PENDING, &data->flags); +	mutex_unlock(&data->page_mutex); +	return ret; +} + +static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf) +{ +	return rmi_read_block(hdev, addr, buf, 1); +} + +static void rmi_f11_process_touch(struct rmi_data *hdata, int slot, +		u8 finger_state, u8 *touch_data) +{ +	int x, y, wx, wy; +	int wide, major, minor; +	int z; + +	input_mt_slot(hdata->input, slot); +	input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER, +			finger_state == 0x01); +	if (finger_state == 0x01) { +		x = (touch_data[0] << 4) | (touch_data[2] & 0x0F); +		y = (touch_data[1] << 4) | (touch_data[2] >> 4); +		wx = touch_data[3] & 0x0F; +		wy = touch_data[3] >> 4; +		wide = (wx > wy); +		major = max(wx, wy); +		minor = min(wx, wy); +		z = touch_data[4]; + +		/* y is inverted */ +		y = hdata->max_y - y; + +		input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x); +		input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y); +		input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide); +		input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z); +		input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); +		input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); +	} +} + +static void rmi_reset_work(struct work_struct *work) +{ +	struct rmi_data *hdata = container_of(work, struct rmi_data, +						reset_work); + +	/* switch the device to RMI if we receive a generic mouse report */ +	rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS); +} + +static inline int rmi_schedule_reset(struct hid_device *hdev) +{ +	struct rmi_data *hdata = hid_get_drvdata(hdev); +	return schedule_work(&hdata->reset_work); +} + +static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data, +		int size) +{ +	struct rmi_data *hdata = hid_get_drvdata(hdev); +	int offset; +	int i; + +	if (size < hdata->f11.report_size) +		return 0; + +	if (!(irq & hdata->f11.irq_mask)) +		return 0; + +	offset = (hdata->max_fingers >> 2) + 1; +	for (i = 0; i < hdata->max_fingers; i++) { +		int fs_byte_position = i >> 2; +		int fs_bit_position = (i & 0x3) << 1; +		int finger_state = (data[fs_byte_position] >> fs_bit_position) & +					0x03; + +		rmi_f11_process_touch(hdata, i, finger_state, +				&data[offset + 5 * i]); +	} +	input_mt_sync_frame(hdata->input); +	input_sync(hdata->input); +	return hdata->f11.report_size; +} + +static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data, +		int size) +{ +	struct rmi_data *hdata = hid_get_drvdata(hdev); +	int i; +	int button = 0; +	bool value; + +	if (!(irq & hdata->f30.irq_mask)) +		return 0; + +	for (i = 0; i < hdata->gpio_led_count; i++) { +		if (test_bit(i, &hdata->button_mask)) { +			value = (data[i / 8] >> (i & 0x07)) & BIT(0); +			if (test_bit(i, &hdata->button_state_mask)) +				value = !value; +			input_event(hdata->input, EV_KEY, BTN_LEFT + button++, +					value); +		} +	} +	return hdata->f30.report_size; +} + +static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) +{ +	struct rmi_data *hdata = hid_get_drvdata(hdev); +	unsigned long irq_mask = 0; +	unsigned index = 2; + +	if (!(test_bit(RMI_STARTED, &hdata->flags))) +		return 0; + +	irq_mask |= hdata->f11.irq_mask; +	irq_mask |= hdata->f30.irq_mask; + +	if (data[1] & ~irq_mask) +		hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", +			data[1] & ~irq_mask, __FILE__, __LINE__); + +	if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { +		index += rmi_f11_input_event(hdev, data[1], &data[index], +				size - index); +		index += rmi_f30_input_event(hdev, data[1], &data[index], +				size - index); +	} else { +		index += rmi_f30_input_event(hdev, data[1], &data[index], +				size - index); +		index += rmi_f11_input_event(hdev, data[1], &data[index], +				size - index); +	} + +	return 1; +} + +static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) +{ +	struct rmi_data *hdata = hid_get_drvdata(hdev); + +	if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { +		hid_err(hdev, "no read request pending\n"); +		return 0; +	} + +	memcpy(hdata->readReport, data, size < hdata->input_report_size ? +			size : hdata->input_report_size); +	set_bit(RMI_READ_DATA_PENDING, &hdata->flags); +	wake_up(&hdata->wait); + +	return 1; +} + +static int rmi_raw_event(struct hid_device *hdev, +		struct hid_report *report, u8 *data, int size) +{ +	switch (data[0]) { +	case RMI_READ_DATA_REPORT_ID: +		return rmi_read_data_event(hdev, data, size); +	case RMI_ATTN_REPORT_ID: +		return rmi_input_event(hdev, data, size); +	case RMI_MOUSE_REPORT_ID: +		rmi_schedule_reset(hdev); +		break; +	} + +	return 0; +} + +#ifdef CONFIG_PM +static int rmi_post_reset(struct hid_device *hdev) +{ +	return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +} + +static int rmi_post_resume(struct hid_device *hdev) +{ +	return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +} +#endif /* CONFIG_PM */ + +#define RMI4_MAX_PAGE 0xff +#define RMI4_PAGE_SIZE 0x0100 + +#define PDT_START_SCAN_LOCATION 0x00e9 +#define PDT_END_SCAN_LOCATION	0x0005 +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) + +struct pdt_entry { +	u8 query_base_addr:8; +	u8 command_base_addr:8; +	u8 control_base_addr:8; +	u8 data_base_addr:8; +	u8 interrupt_source_count:3; +	u8 bits3and4:2; +	u8 function_version:2; +	u8 bit7:1; +	u8 function_number:8; +} __attribute__((__packed__)); + +static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count) +{ +	return GENMASK(irq_count + irq_base - 1, irq_base); +} + +static void rmi_register_function(struct rmi_data *data, +	struct pdt_entry *pdt_entry, int page, unsigned interrupt_count) +{ +	struct rmi_function *f = NULL; +	u16 page_base = page << 8; + +	switch (pdt_entry->function_number) { +	case 0x11: +		f = &data->f11; +		break; +	case 0x30: +		f = &data->f30; +		break; +	} + +	if (f) { +		f->page = page; +		f->query_base_addr = page_base | pdt_entry->query_base_addr; +		f->command_base_addr = page_base | pdt_entry->command_base_addr; +		f->control_base_addr = page_base | pdt_entry->control_base_addr; +		f->data_base_addr = page_base | pdt_entry->data_base_addr; +		f->interrupt_base = interrupt_count; +		f->interrupt_count = pdt_entry->interrupt_source_count; +		f->irq_mask = rmi_gen_mask(f->interrupt_base, +						f->interrupt_count); +	} +} + +static int rmi_scan_pdt(struct hid_device *hdev) +{ +	struct rmi_data *data = hid_get_drvdata(hdev); +	struct pdt_entry entry; +	int page; +	bool page_has_function; +	int i; +	int retval; +	int interrupt = 0; +	u16 page_start, pdt_start , pdt_end; + +	hid_info(hdev, "Scanning PDT...\n"); + +	for (page = 0; (page <= RMI4_MAX_PAGE); page++) { +		page_start = RMI4_PAGE_SIZE * page; +		pdt_start = page_start + PDT_START_SCAN_LOCATION; +		pdt_end = page_start + PDT_END_SCAN_LOCATION; + +		page_has_function = false; +		for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) { +			retval = rmi_read_block(hdev, i, &entry, sizeof(entry)); +			if (retval) { +				hid_err(hdev, +					"Read of PDT entry at %#06x failed.\n", +					i); +				goto error_exit; +			} + +			if (RMI4_END_OF_PDT(entry.function_number)) +				break; + +			page_has_function = true; + +			hid_info(hdev, "Found F%02X on page %#04x\n", +					entry.function_number, page); + +			rmi_register_function(data, &entry, page, interrupt); +			interrupt += entry.interrupt_source_count; +		} + +		if (!page_has_function) +			break; +	} + +	hid_info(hdev, "%s: Done with PDT scan.\n", __func__); +	retval = 0; + +error_exit: +	return retval; +} + +static int rmi_populate_f11(struct hid_device *hdev) +{ +	struct rmi_data *data = hid_get_drvdata(hdev); +	u8 buf[20]; +	int ret; +	bool has_query9; +	bool has_query10; +	bool has_query11; +	bool has_query12; +	bool has_physical_props; +	unsigned x_size, y_size; +	u16 query12_offset; + +	if (!data->f11.query_base_addr) { +		hid_err(hdev, "No 2D sensor found, giving up.\n"); +		return -ENODEV; +	} + +	/* query 0 contains some useful information */ +	ret = rmi_read(hdev, data->f11.query_base_addr, buf); +	if (ret) { +		hid_err(hdev, "can not get query 0: %d.\n", ret); +		return ret; +	} +	has_query9 = !!(buf[0] & BIT(3)); +	has_query11 = !!(buf[0] & BIT(4)); +	has_query12 = !!(buf[0] & BIT(5)); + +	/* query 1 to get the max number of fingers */ +	ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf); +	if (ret) { +		hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret); +		return ret; +	} +	data->max_fingers = (buf[0] & 0x07) + 1; +	if (data->max_fingers > 5) +		data->max_fingers = 10; + +	data->f11.report_size = data->max_fingers * 5 + +				DIV_ROUND_UP(data->max_fingers, 4); + +	if (!(buf[0] & BIT(4))) { +		hid_err(hdev, "No absolute events, giving up.\n"); +		return -ENODEV; +	} + +	/* query 8 to find out if query 10 exists */ +	ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); +	if (ret) { +		hid_err(hdev, "can not read gesture information: %d.\n", ret); +		return ret; +	} +	has_query10 = !!(buf[0] & BIT(2)); + +	/* +	 * At least 8 queries are guaranteed to be present in F11 +	 * +1 for query12. +	 */ +	query12_offset = 9; + +	if (has_query9) +		++query12_offset; + +	if (has_query10) +		++query12_offset; + +	if (has_query11) +		++query12_offset; + +	/* query 12 to know if the physical properties are reported */ +	if (has_query12) { +		ret = rmi_read(hdev, data->f11.query_base_addr +				+ query12_offset, buf); +		if (ret) { +			hid_err(hdev, "can not get query 12: %d.\n", ret); +			return ret; +		} +		has_physical_props = !!(buf[0] & BIT(5)); + +		if (has_physical_props) { +			ret = rmi_read_block(hdev, +					data->f11.query_base_addr +						+ query12_offset + 1, buf, 4); +			if (ret) { +				hid_err(hdev, "can not read query 15-18: %d.\n", +					ret); +				return ret; +			} + +			x_size = buf[0] | (buf[1] << 8); +			y_size = buf[2] | (buf[3] << 8); + +			data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10); +			data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10); + +			hid_info(hdev, "%s: size in mm: %d x %d\n", +				 __func__, data->x_size_mm, data->y_size_mm); +		} +	} + +	/* +	 * retrieve the ctrl registers +	 * the ctrl register has a size of 20 but a fw bug split it into 16 + 4, +	 * and there is no way to know if the first 20 bytes are here or not. +	 * We use only the first 10 bytes, so get only them. +	 */ +	ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 10); +	if (ret) { +		hid_err(hdev, "can not read ctrl block of size 10: %d.\n", ret); +		return ret; +	} + +	data->max_x = buf[6] | (buf[7] << 8); +	data->max_y = buf[8] | (buf[9] << 8); + +	return 0; +} + +static int rmi_populate_f30(struct hid_device *hdev) +{ +	struct rmi_data *data = hid_get_drvdata(hdev); +	u8 buf[20]; +	int ret; +	bool has_gpio, has_led; +	unsigned bytes_per_ctrl; +	u8 ctrl2_addr; +	int ctrl2_3_length; +	int i; + +	/* function F30 is for physical buttons */ +	if (!data->f30.query_base_addr) { +		hid_err(hdev, "No GPIO/LEDs found, giving up.\n"); +		return -ENODEV; +	} + +	ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2); +	if (ret) { +		hid_err(hdev, "can not get F30 query registers: %d.\n", ret); +		return ret; +	} + +	has_gpio = !!(buf[0] & BIT(3)); +	has_led = !!(buf[0] & BIT(2)); +	data->gpio_led_count = buf[1] & 0x1f; + +	/* retrieve ctrl 2 & 3 registers */ +	bytes_per_ctrl = (data->gpio_led_count + 7) / 8; +	/* Ctrl0 is present only if both has_gpio and has_led are set*/ +	ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0; +	/* Ctrl1 is always be present */ +	ctrl2_addr += bytes_per_ctrl; +	ctrl2_3_length = 2 * bytes_per_ctrl; + +	data->f30.report_size = bytes_per_ctrl; + +	ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr, +				buf, ctrl2_3_length); +	if (ret) { +		hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n", +			ctrl2_3_length, ret); +		return ret; +	} + +	for (i = 0; i < data->gpio_led_count; i++) { +		int byte_position = i >> 3; +		int bit_position = i & 0x07; +		u8 dir_byte = buf[byte_position]; +		u8 data_byte = buf[byte_position + bytes_per_ctrl]; +		bool dir = (dir_byte >> bit_position) & BIT(0); +		bool dat = (data_byte >> bit_position) & BIT(0); + +		if (dir == 0) { +			/* input mode */ +			if (dat) { +				/* actual buttons have pull up resistor */ +				data->button_count++; +				set_bit(i, &data->button_mask); +				set_bit(i, &data->button_state_mask); +			} +		} + +	} + +	return 0; +} + +static int rmi_populate(struct hid_device *hdev) +{ +	int ret; + +	ret = rmi_scan_pdt(hdev); +	if (ret) { +		hid_err(hdev, "PDT scan failed with code %d.\n", ret); +		return ret; +	} + +	ret = rmi_populate_f11(hdev); +	if (ret) { +		hid_err(hdev, "Error while initializing F11 (%d).\n", ret); +		return ret; +	} + +	ret = rmi_populate_f30(hdev); +	if (ret) +		hid_warn(hdev, "Error while initializing F30 (%d).\n", ret); + +	return 0; +} + +static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) +{ +	struct rmi_data *data = hid_get_drvdata(hdev); +	struct input_dev *input = hi->input; +	int ret; +	int res_x, res_y, i; + +	data->input = input; + +	hid_dbg(hdev, "Opening low level driver\n"); +	ret = hid_hw_open(hdev); +	if (ret) +		return; + +	/* Allow incoming hid reports */ +	hid_device_io_start(hdev); + +	ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS); +	if (ret < 0) { +		dev_err(&hdev->dev, "failed to set rmi mode\n"); +		goto exit; +	} + +	ret = rmi_set_page(hdev, 0); +	if (ret < 0) { +		dev_err(&hdev->dev, "failed to set page select to 0.\n"); +		goto exit; +	} + +	ret = rmi_populate(hdev); +	if (ret) +		goto exit; + +	__set_bit(EV_ABS, input->evbit); +	input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0); +	input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0); + +	if (data->x_size_mm && data->y_size_mm) { +		res_x = (data->max_x - 1) / data->x_size_mm; +		res_y = (data->max_y - 1) / data->y_size_mm; + +		input_abs_set_res(input, ABS_MT_POSITION_X, res_x); +		input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); +	} + +	input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); +	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); +	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); +	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + +	input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + +	if (data->button_count) { +		__set_bit(EV_KEY, input->evbit); +		for (i = 0; i < data->button_count; i++) +			__set_bit(BTN_LEFT + i, input->keybit); + +		if (data->button_count == 1) +			__set_bit(INPUT_PROP_BUTTONPAD, input->propbit); +	} + +	set_bit(RMI_STARTED, &data->flags); + +exit: +	hid_device_io_stop(hdev); +	hid_hw_close(hdev); +} + +static int rmi_input_mapping(struct hid_device *hdev, +		struct hid_input *hi, struct hid_field *field, +		struct hid_usage *usage, unsigned long **bit, int *max) +{ +	/* we want to make HID ignore the advertised HID collection */ +	return -1; +} + +static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ +	struct rmi_data *data = NULL; +	int ret; +	size_t alloc_size; + +	data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); +	if (!data) +		return -ENOMEM; + +	INIT_WORK(&data->reset_work, rmi_reset_work); +	data->hdev = hdev; + +	hid_set_drvdata(hdev, data); + +	hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; + +	ret = hid_parse(hdev); +	if (ret) { +		hid_err(hdev, "parse failed\n"); +		return ret; +	} + +	data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] +		.report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) +		+ 1 /* report id */; +	data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] +		.report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) +		+ 1 /* report id */; + +	alloc_size = data->output_report_size + data->input_report_size; + +	data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL); +	if (!data->writeReport) { +		ret = -ENOMEM; +		return ret; +	} + +	data->readReport = data->writeReport + data->output_report_size; + +	init_waitqueue_head(&data->wait); + +	mutex_init(&data->page_mutex); + +	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); +	if (ret) { +		hid_err(hdev, "hw start failed\n"); +		return ret; +	} + +	if (!test_bit(RMI_STARTED, &data->flags)) { +		hid_hw_stop(hdev); +		return -EIO; +	} + +	return 0; +} + +static void rmi_remove(struct hid_device *hdev) +{ +	struct rmi_data *hdata = hid_get_drvdata(hdev); + +	clear_bit(RMI_STARTED, &hdata->flags); + +	hid_hw_stop(hdev); +} + +static const struct hid_device_id rmi_id[] = { +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) }, +	{ } +}; +MODULE_DEVICE_TABLE(hid, rmi_id); + +static struct hid_driver rmi_driver = { +	.name = "hid-rmi", +	.id_table		= rmi_id, +	.probe			= rmi_probe, +	.remove			= rmi_remove, +	.raw_event		= rmi_raw_event, +	.input_mapping		= rmi_input_mapping, +	.input_configured	= rmi_input_configured, +#ifdef CONFIG_PM +	.resume			= rmi_post_resume, +	.reset_resume		= rmi_post_reset, +#endif +}; + +module_hid_driver(rmi_driver); + +MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>"); +MODULE_DESCRIPTION("RMI HID driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-roccat-common.c b/drivers/hid/hid-roccat-common.c index 74f70403262..02e28e9f4ea 100644 --- a/drivers/hid/hid-roccat-common.c +++ b/drivers/hid/hid-roccat-common.c @@ -65,10 +65,11 @@ int roccat_common2_send(struct usb_device *usb_dev, uint report_id,  EXPORT_SYMBOL_GPL(roccat_common2_send);  enum roccat_common2_control_states { -	ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD = 0, +	ROCCAT_COMMON_CONTROL_STATUS_CRITICAL = 0,  	ROCCAT_COMMON_CONTROL_STATUS_OK = 1,  	ROCCAT_COMMON_CONTROL_STATUS_INVALID = 2, -	ROCCAT_COMMON_CONTROL_STATUS_WAIT = 3, +	ROCCAT_COMMON_CONTROL_STATUS_BUSY = 3, +	ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW = 4,  };  static int roccat_common2_receive_control_status(struct usb_device *usb_dev) @@ -88,13 +89,12 @@ static int roccat_common2_receive_control_status(struct usb_device *usb_dev)  		switch (control.value) {  		case ROCCAT_COMMON_CONTROL_STATUS_OK:  			return 0; -		case ROCCAT_COMMON_CONTROL_STATUS_WAIT: +		case ROCCAT_COMMON_CONTROL_STATUS_BUSY:  			msleep(500);  			continue;  		case ROCCAT_COMMON_CONTROL_STATUS_INVALID: - -		case ROCCAT_COMMON_CONTROL_STATUS_OVERLOAD: -			/* seems to be critical - replug necessary */ +		case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL: +		case ROCCAT_COMMON_CONTROL_STATUS_CRITICAL_NEW:  			return -EINVAL;  		default:  			dev_err(&usb_dev->dev, @@ -122,6 +122,59 @@ int roccat_common2_send_with_status(struct usb_device *usb_dev,  }  EXPORT_SYMBOL_GPL(roccat_common2_send_with_status); +int roccat_common2_device_init_struct(struct usb_device *usb_dev, +		struct roccat_common2_device *dev) +{ +	mutex_init(&dev->lock); +	return 0; +} +EXPORT_SYMBOL_GPL(roccat_common2_device_init_struct); + +ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, +		char *buf, loff_t off, size_t count, +		size_t real_size, uint command) +{ +	struct device *dev = +			container_of(kobj, struct device, kobj)->parent->parent; +	struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); +	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); +	int retval; + +	if (off >= real_size) +		return 0; + +	if (off != 0 || count != real_size) +		return -EINVAL; + +	mutex_lock(&roccat_dev->lock); +	retval = roccat_common2_receive(usb_dev, command, buf, real_size); +	mutex_unlock(&roccat_dev->lock); + +	return retval ? retval : real_size; +} +EXPORT_SYMBOL_GPL(roccat_common2_sysfs_read); + +ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, +		void const *buf, loff_t off, size_t count, +		size_t real_size, uint command) +{ +	struct device *dev = +			container_of(kobj, struct device, kobj)->parent->parent; +	struct roccat_common2_device *roccat_dev = hid_get_drvdata(dev_get_drvdata(dev)); +	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); +	int retval; + +	if (off != 0 || count != real_size) +		return -EINVAL; + +	mutex_lock(&roccat_dev->lock); +	retval = roccat_common2_send_with_status(usb_dev, command, buf, real_size); +	mutex_unlock(&roccat_dev->lock); + +	return retval ? retval : real_size; +} +EXPORT_SYMBOL_GPL(roccat_common2_sysfs_write); +  MODULE_AUTHOR("Stefan Achatz");  MODULE_DESCRIPTION("USB Roccat common driver");  MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h index a97746a63b7..eaa56eb7d5d 100644 --- a/drivers/hid/hid-roccat-common.h +++ b/drivers/hid/hid-roccat-common.h @@ -32,4 +32,66 @@ int roccat_common2_send(struct usb_device *usb_dev, uint report_id,  int roccat_common2_send_with_status(struct usb_device *usb_dev,  		uint command, void const *buf, uint size); +struct roccat_common2_device { +	int roccat_claimed; +	int chrdev_minor; +	struct mutex lock; +}; + +int roccat_common2_device_init_struct(struct usb_device *usb_dev, +		struct roccat_common2_device *dev); +ssize_t roccat_common2_sysfs_read(struct file *fp, struct kobject *kobj, +		char *buf, loff_t off, size_t count, +		size_t real_size, uint command); +ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, +		void const *buf, loff_t off, size_t count, +		size_t real_size, uint command); + +#define ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \ +static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \ +		struct kobject *kobj, struct bin_attribute *attr, char *buf, \ +		loff_t off, size_t count) \ +{ \ +	return roccat_common2_sysfs_write(fp, kobj, buf, off, count, \ +			SIZE, COMMAND); \ +} + +#define ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) \ +static ssize_t roccat_common2_sysfs_read_ ## thingy(struct file *fp, \ +		struct kobject *kobj, struct bin_attribute *attr, char *buf, \ +		loff_t off, size_t count) \ +{ \ +	return roccat_common2_sysfs_read(fp, kobj, buf, off, count, \ +			SIZE, COMMAND); \ +} + +#define ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) + +#define ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE); \ +static struct bin_attribute bin_attr_ ## thingy = { \ +	.attr = { .name = #thingy, .mode = 0660 }, \ +	.size = SIZE, \ +	.read = roccat_common2_sysfs_read_ ## thingy, \ +	.write = roccat_common2_sysfs_write_ ## thingy \ +} + +#define ROCCAT_COMMON2_BIN_ATTRIBUTE_R(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE); \ +static struct bin_attribute bin_attr_ ## thingy = { \ +	.attr = { .name = #thingy, .mode = 0440 }, \ +	.size = SIZE, \ +	.read = roccat_common2_sysfs_read_ ## thingy, \ +} + +#define ROCCAT_COMMON2_BIN_ATTRIBUTE_W(thingy, COMMAND, SIZE) \ +ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE); \ +static struct bin_attribute bin_attr_ ## thingy = { \ +	.attr = { .name = #thingy, .mode = 0220 }, \ +	.size = SIZE, \ +	.write = roccat_common2_sysfs_write_ ## thingy \ +} +  #endif diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 602c188e9d8..6101816a7dd 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -382,7 +382,7 @@ static ssize_t kone_sysfs_write_profilex(struct file *fp,  }  #define PROFILE_ATTR(number)					\  static struct bin_attribute bin_attr_profile##number = {	\ -	.attr = { .name = "profile##number", .mode = 0660 },	\ +	.attr = { .name = "profile" #number, .mode = 0660 },	\  	.size = sizeof(struct kone_profile),			\  	.read = kone_sysfs_read_profilex,			\  	.write = kone_sysfs_write_profilex,			\ diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 5ddf605b6b8..5e99fcdc71b 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -229,13 +229,13 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp,  #define PROFILE_ATTR(number)						\  static struct bin_attribute bin_attr_profile##number##_settings = {	\ -	.attr = { .name = "profile##number##_settings", .mode = 0440 },	\ +	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\  	.size = KONEPLUS_SIZE_PROFILE_SETTINGS,				\  	.read = koneplus_sysfs_read_profilex_settings,			\  	.private = &profile_numbers[number-1],				\  };									\  static struct bin_attribute bin_attr_profile##number##_buttons = {	\ -	.attr = { .name = "profile##number##_buttons", .mode = 0440 },	\ +	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\  	.size = KONEPLUS_SIZE_PROFILE_BUTTONS,				\  	.read = koneplus_sysfs_read_profilex_buttons,			\  	.private = &profile_numbers[number-1],				\ diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index 99a605ebb66..07de2f9014c 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -15,6 +15,7 @@   * Roccat KonePure is a smaller version of KoneXTD with less buttons and lights.   */ +#include <linux/types.h>  #include <linux/device.h>  #include <linux/input.h>  #include <linux/hid.h> @@ -23,128 +24,50 @@  #include <linux/hid-roccat.h>  #include "hid-ids.h"  #include "hid-roccat-common.h" -#include "hid-roccat-konepure.h" -static struct class *konepure_class; - -static ssize_t konepure_sysfs_read(struct file *fp, struct kobject *kobj, -		char *buf, loff_t off, size_t count, -		size_t real_size, uint command) -{ -	struct device *dev = -			container_of(kobj, struct device, kobj)->parent->parent; -	struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev)); -	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); -	int retval; - -	if (off >= real_size) -		return 0; - -	if (off != 0 || count != real_size) -		return -EINVAL; - -	mutex_lock(&konepure->konepure_lock); -	retval = roccat_common2_receive(usb_dev, command, buf, real_size); -	mutex_unlock(&konepure->konepure_lock); - -	return retval ? retval : real_size; -} - -static ssize_t konepure_sysfs_write(struct file *fp, struct kobject *kobj, -		void const *buf, loff_t off, size_t count, -		size_t real_size, uint command) -{ -	struct device *dev = -			container_of(kobj, struct device, kobj)->parent->parent; -	struct konepure_device *konepure = hid_get_drvdata(dev_get_drvdata(dev)); -	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); -	int retval; - -	if (off != 0 || count != real_size) -		return -EINVAL; - -	mutex_lock(&konepure->konepure_lock); -	retval = roccat_common2_send_with_status(usb_dev, command, -			(void *)buf, real_size); -	mutex_unlock(&konepure->konepure_lock); - -	return retval ? retval : real_size; -} - -#define KONEPURE_SYSFS_W(thingy, THINGY) \ -static ssize_t konepure_sysfs_write_ ## thingy(struct file *fp, \ -		struct kobject *kobj, struct bin_attribute *attr, char *buf, \ -		loff_t off, size_t count) \ -{ \ -	return konepure_sysfs_write(fp, kobj, buf, off, count, \ -			KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ -} - -#define KONEPURE_SYSFS_R(thingy, THINGY) \ -static ssize_t konepure_sysfs_read_ ## thingy(struct file *fp, \ -		struct kobject *kobj, struct bin_attribute *attr, char *buf, \ -		loff_t off, size_t count) \ -{ \ -	return konepure_sysfs_read(fp, kobj, buf, off, count, \ -			KONEPURE_SIZE_ ## THINGY, KONEPURE_COMMAND_ ## THINGY); \ -} +enum { +	KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; -#define KONEPURE_SYSFS_RW(thingy, THINGY) \ -KONEPURE_SYSFS_W(thingy, THINGY) \ -KONEPURE_SYSFS_R(thingy, THINGY) - -#define KONEPURE_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -KONEPURE_SYSFS_RW(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ -	.attr = { .name = #thingy, .mode = 0660 }, \ -	.size = KONEPURE_SIZE_ ## THINGY, \ -	.read = konepure_sysfs_read_ ## thingy, \ -	.write = konepure_sysfs_write_ ## thingy \ -} +struct konepure_mouse_report_button { +	uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */ +	uint8_t zero; +	uint8_t type; +	uint8_t data1; +	uint8_t data2; +	uint8_t zero2; +	uint8_t unknown[2]; +} __packed; -#define KONEPURE_BIN_ATTRIBUTE_R(thingy, THINGY) \ -KONEPURE_SYSFS_R(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ -	.attr = { .name = #thingy, .mode = 0440 }, \ -	.size = KONEPURE_SIZE_ ## THINGY, \ -	.read = konepure_sysfs_read_ ## thingy, \ -} - -#define KONEPURE_BIN_ATTRIBUTE_W(thingy, THINGY) \ -KONEPURE_SYSFS_W(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ -	.attr = { .name = #thingy, .mode = 0220 }, \ -	.size = KONEPURE_SIZE_ ## THINGY, \ -	.write = konepure_sysfs_write_ ## thingy \ -} +static struct class *konepure_class; -KONEPURE_BIN_ATTRIBUTE_RW(actual_profile, ACTUAL_PROFILE); -KONEPURE_BIN_ATTRIBUTE_RW(info, INFO); -KONEPURE_BIN_ATTRIBUTE_RW(sensor, SENSOR); -KONEPURE_BIN_ATTRIBUTE_RW(tcu, TCU); -KONEPURE_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); -KONEPURE_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); -KONEPURE_BIN_ATTRIBUTE_W(control, CONTROL); -KONEPURE_BIN_ATTRIBUTE_W(talk, TALK); -KONEPURE_BIN_ATTRIBUTE_W(macro, MACRO); -KONEPURE_BIN_ATTRIBUTE_R(tcu_image, TCU_IMAGE); - -static struct bin_attribute *konepure_bin_attributes[] = { +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(actual_profile, 0x05, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_settings, 0x06, 0x1f); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile_buttons, 0x07, 0x3b); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(macro, 0x08, 0x0822); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x09, 0x06); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(tcu, 0x0c, 0x04); +ROCCAT_COMMON2_BIN_ATTRIBUTE_R(tcu_image, 0x0c, 0x0404); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0x0f, 0x06); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x10, 0x10); + +static struct bin_attribute *konepure_bin_attrs[] = {  	&bin_attr_actual_profile, +	&bin_attr_control,  	&bin_attr_info, +	&bin_attr_talk, +	&bin_attr_macro,  	&bin_attr_sensor,  	&bin_attr_tcu, +	&bin_attr_tcu_image,  	&bin_attr_profile_settings,  	&bin_attr_profile_buttons, -	&bin_attr_control, -	&bin_attr_talk, -	&bin_attr_macro, -	&bin_attr_tcu_image,  	NULL,  };  static const struct attribute_group konepure_group = { -	.bin_attrs = konepure_bin_attributes, +	.bin_attrs = konepure_bin_attrs,  };  static const struct attribute_group *konepure_groups[] = { @@ -152,20 +75,11 @@ static const struct attribute_group *konepure_groups[] = {  	NULL,  }; - -static int konepure_init_konepure_device_struct(struct usb_device *usb_dev, -		struct konepure_device *konepure) -{ -	mutex_init(&konepure->konepure_lock); - -	return 0; -} -  static int konepure_init_specials(struct hid_device *hdev)  {  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);  	struct usb_device *usb_dev = interface_to_usbdev(intf); -	struct konepure_device *konepure; +	struct roccat_common2_device *konepure;  	int retval;  	if (intf->cur_altsetting->desc.bInterfaceProtocol @@ -181,9 +95,9 @@ static int konepure_init_specials(struct hid_device *hdev)  	}  	hid_set_drvdata(hdev, konepure); -	retval = konepure_init_konepure_device_struct(usb_dev, konepure); +	retval = roccat_common2_device_init_struct(usb_dev, konepure);  	if (retval) { -		hid_err(hdev, "couldn't init struct konepure_device\n"); +		hid_err(hdev, "couldn't init KonePure device\n");  		goto exit_free;  	} @@ -205,7 +119,7 @@ exit_free:  static void konepure_remove_specials(struct hid_device *hdev)  {  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); -	struct konepure_device *konepure; +	struct roccat_common2_device *konepure;  	if (intf->cur_altsetting->desc.bInterfaceProtocol  			!= USB_INTERFACE_PROTOCOL_MOUSE) @@ -258,7 +172,7 @@ static int konepure_raw_event(struct hid_device *hdev,  		struct hid_report *report, u8 *data, int size)  {  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); -	struct konepure_device *konepure = hid_get_drvdata(hdev); +	struct roccat_common2_device *konepure = hid_get_drvdata(hdev);  	if (intf->cur_altsetting->desc.bInterfaceProtocol  			!= USB_INTERFACE_PROTOCOL_MOUSE) diff --git a/drivers/hid/hid-roccat-konepure.h b/drivers/hid/hid-roccat-konepure.h deleted file mode 100644 index 2cd24e93dfd..00000000000 --- a/drivers/hid/hid-roccat-konepure.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef __HID_ROCCAT_KONEPURE_H -#define __HID_ROCCAT_KONEPURE_H - -/* - * Copyright (c) 2012 Stefan Achatz <erazor_de@users.sourceforge.net> - */ - -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - */ - -#include <linux/types.h> - -enum { -	KONEPURE_SIZE_ACTUAL_PROFILE = 0x03, -	KONEPURE_SIZE_CONTROL = 0x03, -	KONEPURE_SIZE_FIRMWARE_WRITE = 0x0402, -	KONEPURE_SIZE_INFO = 0x06, -	KONEPURE_SIZE_MACRO = 0x0822, -	KONEPURE_SIZE_PROFILE_SETTINGS = 0x1f, -	KONEPURE_SIZE_PROFILE_BUTTONS = 0x3b, -	KONEPURE_SIZE_SENSOR = 0x06, -	KONEPURE_SIZE_TALK = 0x10, -	KONEPURE_SIZE_TCU = 0x04, -	KONEPURE_SIZE_TCU_IMAGE = 0x0404, -}; - -enum konepure_control_requests { -	KONEPURE_CONTROL_REQUEST_GENERAL = 0x80, -	KONEPURE_CONTROL_REQUEST_BUTTONS = 0x90, -}; - -enum konepure_commands { -	KONEPURE_COMMAND_CONTROL = 0x04, -	KONEPURE_COMMAND_ACTUAL_PROFILE = 0x05, -	KONEPURE_COMMAND_PROFILE_SETTINGS = 0x06, -	KONEPURE_COMMAND_PROFILE_BUTTONS = 0x07, -	KONEPURE_COMMAND_MACRO = 0x08, -	KONEPURE_COMMAND_INFO = 0x09, -	KONEPURE_COMMAND_TCU = 0x0c, -	KONEPURE_COMMAND_TCU_IMAGE = 0x0c, -	KONEPURE_COMMAND_E = 0x0e, -	KONEPURE_COMMAND_SENSOR = 0x0f, -	KONEPURE_COMMAND_TALK = 0x10, -	KONEPURE_COMMAND_FIRMWARE_WRITE = 0x1b, -	KONEPURE_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, -}; - -enum { -	KONEPURE_MOUSE_REPORT_NUMBER_BUTTON = 3, -}; - -struct konepure_mouse_report_button { -	uint8_t report_number; /* always KONEPURE_MOUSE_REPORT_NUMBER_BUTTON */ -	uint8_t zero; -	uint8_t type; -	uint8_t data1; -	uint8_t data2; -	uint8_t zero2; -	uint8_t unknown[2]; -} __packed; - -struct konepure_device { -	int roccat_claimed; -	int chrdev_minor; -	struct mutex konepure_lock; -}; - -#endif diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 515bc03136c..966047711fb 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -257,13 +257,13 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,  #define PROFILE_ATTR(number)						\  static struct bin_attribute bin_attr_profile##number##_settings = {	\ -	.attr = { .name = "profile##number##_settings", .mode = 0440 },	\ +	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\  	.size = KOVAPLUS_SIZE_PROFILE_SETTINGS,				\  	.read = kovaplus_sysfs_read_profilex_settings,			\  	.private = &profile_numbers[number-1],				\  };									\  static struct bin_attribute bin_attr_profile##number##_buttons = {	\ -	.attr = { .name = "profile##number##_buttons", .mode = 0440 },	\ +	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\  	.size = KOVAPLUS_SIZE_PROFILE_BUTTONS,				\  	.read = kovaplus_sysfs_read_profilex_buttons,			\  	.private = &profile_numbers[number-1],				\ @@ -554,9 +554,13 @@ static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,  		break;  	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:  		kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1); +		break;  	case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:  		kovaplus->actual_x_sensitivity = button_report->data1;  		kovaplus->actual_y_sensitivity = button_report->data2; +		break; +	default: +		break;  	}  } diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 5a6dbbeee79..1a07e07d99a 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -225,13 +225,13 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,  #define PROFILE_ATTR(number)						\  static struct bin_attribute bin_attr_profile##number##_settings = {	\ -	.attr = { .name = "profile##number##_settings", .mode = 0440 },	\ +	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\  	.size = PYRA_SIZE_PROFILE_SETTINGS,				\  	.read = pyra_sysfs_read_profilex_settings,			\  	.private = &profile_numbers[number-1],				\  };									\  static struct bin_attribute bin_attr_profile##number##_buttons = {	\ -	.attr = { .name = "profile##number##_buttons", .mode = 0440 },	\ +	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\  	.size = PYRA_SIZE_PROFILE_BUTTONS,				\  	.read = pyra_sysfs_read_profilex_buttons,			\  	.private = &profile_numbers[number-1],				\ diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c new file mode 100644 index 00000000000..47cc8f30ff6 --- /dev/null +++ b/drivers/hid/hid-roccat-ryos.c @@ -0,0 +1,241 @@ +/* + * Roccat Ryos driver for Linux + * + * Copyright (c) 2013 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/types.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/hid-roccat.h> +#include "hid-ids.h" +#include "hid-roccat-common.h" + +enum { +	RYOS_REPORT_NUMBER_SPECIAL = 3, +	RYOS_USB_INTERFACE_PROTOCOL = 0, +}; + +struct ryos_report_special { +	uint8_t number; /* RYOS_REPORT_NUMBER_SPECIAL */ +	uint8_t data[4]; +} __packed; + +static struct class *ryos_class; + +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x05, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_primary, 0x06, 0x7d); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_function, 0x07, 0x5f); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_macro, 0x08, 0x23); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_thumbster, 0x09, 0x17); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_extra, 0x0a, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_easyzone, 0x0b, 0x126); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(key_mask, 0x0c, 0x06); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light, 0x0d, 0x10); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x0e, 0x7d2); +ROCCAT_COMMON2_BIN_ATTRIBUTE_R(info, 0x0f, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(reset, 0x11, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(light_control, 0x13, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x16, 0x10); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(stored_lights, 0x17, 0x0566); +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(custom_lights, 0x18, 0x14); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light_macro, 0x19, 0x07d2); + +static struct bin_attribute *ryos_bin_attrs[] = { +	&bin_attr_control, +	&bin_attr_profile, +	&bin_attr_keys_primary, +	&bin_attr_keys_function, +	&bin_attr_keys_macro, +	&bin_attr_keys_thumbster, +	&bin_attr_keys_extra, +	&bin_attr_keys_easyzone, +	&bin_attr_key_mask, +	&bin_attr_light, +	&bin_attr_macro, +	&bin_attr_info, +	&bin_attr_reset, +	&bin_attr_light_control, +	&bin_attr_talk, +	&bin_attr_stored_lights, +	&bin_attr_custom_lights, +	&bin_attr_light_macro, +	NULL, +}; + +static const struct attribute_group ryos_group = { +	.bin_attrs = ryos_bin_attrs, +}; + +static const struct attribute_group *ryos_groups[] = { +	&ryos_group, +	NULL, +}; + +static int ryos_init_specials(struct hid_device *hdev) +{ +	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); +	struct usb_device *usb_dev = interface_to_usbdev(intf); +	struct roccat_common2_device *ryos; +	int retval; + +	if (intf->cur_altsetting->desc.bInterfaceProtocol +			!= RYOS_USB_INTERFACE_PROTOCOL) { +		hid_set_drvdata(hdev, NULL); +		return 0; +	} + +	ryos = kzalloc(sizeof(*ryos), GFP_KERNEL); +	if (!ryos) { +		hid_err(hdev, "can't alloc device descriptor\n"); +		return -ENOMEM; +	} +	hid_set_drvdata(hdev, ryos); + +	retval = roccat_common2_device_init_struct(usb_dev, ryos); +	if (retval) { +		hid_err(hdev, "couldn't init Ryos device\n"); +		goto exit_free; +	} + +	retval = roccat_connect(ryos_class, hdev, +			sizeof(struct ryos_report_special)); +	if (retval < 0) { +		hid_err(hdev, "couldn't init char dev\n"); +	} else { +		ryos->chrdev_minor = retval; +		ryos->roccat_claimed = 1; +	} + +	return 0; +exit_free: +	kfree(ryos); +	return retval; +} + +static void ryos_remove_specials(struct hid_device *hdev) +{ +	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); +	struct roccat_common2_device *ryos; + +	if (intf->cur_altsetting->desc.bInterfaceProtocol +			!= RYOS_USB_INTERFACE_PROTOCOL) +		return; + +	ryos = hid_get_drvdata(hdev); +	if (ryos->roccat_claimed) +		roccat_disconnect(ryos->chrdev_minor); +	kfree(ryos); +} + +static int ryos_probe(struct hid_device *hdev, +		const struct hid_device_id *id) +{ +	int retval; + +	retval = hid_parse(hdev); +	if (retval) { +		hid_err(hdev, "parse failed\n"); +		goto exit; +	} + +	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); +	if (retval) { +		hid_err(hdev, "hw start failed\n"); +		goto exit; +	} + +	retval = ryos_init_specials(hdev); +	if (retval) { +		hid_err(hdev, "couldn't install mouse\n"); +		goto exit_stop; +	} + +	return 0; + +exit_stop: +	hid_hw_stop(hdev); +exit: +	return retval; +} + +static void ryos_remove(struct hid_device *hdev) +{ +	ryos_remove_specials(hdev); +	hid_hw_stop(hdev); +} + +static int ryos_raw_event(struct hid_device *hdev, +		struct hid_report *report, u8 *data, int size) +{ +	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); +	struct roccat_common2_device *ryos = hid_get_drvdata(hdev); + +	if (intf->cur_altsetting->desc.bInterfaceProtocol +			!= RYOS_USB_INTERFACE_PROTOCOL) +		return 0; + +	if (data[0] != RYOS_REPORT_NUMBER_SPECIAL) +		return 0; + +	if (ryos != NULL && ryos->roccat_claimed) +		roccat_report_event(ryos->chrdev_minor, data); + +	return 0; +} + +static const struct hid_device_id ryos_devices[] = { +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) }, +	{ } +}; + +MODULE_DEVICE_TABLE(hid, ryos_devices); + +static struct hid_driver ryos_driver = { +		.name = "ryos", +		.id_table = ryos_devices, +		.probe = ryos_probe, +		.remove = ryos_remove, +		.raw_event = ryos_raw_event +}; + +static int __init ryos_init(void) +{ +	int retval; + +	ryos_class = class_create(THIS_MODULE, "ryos"); +	if (IS_ERR(ryos_class)) +		return PTR_ERR(ryos_class); +	ryos_class->dev_groups = ryos_groups; + +	retval = hid_register_driver(&ryos_driver); +	if (retval) +		class_destroy(ryos_class); +	return retval; +} + +static void __exit ryos_exit(void) +{ +	hid_unregister_driver(&ryos_driver); +	class_destroy(ryos_class); +} + +module_init(ryos_init); +module_exit(ryos_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Ryos MK/Glow/Pro driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 0332267199d..6dbf6e04dce 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -27,98 +27,15 @@  static struct class *savu_class; -static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, -		char *buf, loff_t off, size_t count, -		size_t real_size, uint command) -{ -	struct device *dev = -			container_of(kobj, struct device, kobj)->parent->parent; -	struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); -	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); -	int retval; - -	if (off >= real_size) -		return 0; - -	if (off != 0 || count != real_size) -		return -EINVAL; - -	mutex_lock(&savu->savu_lock); -	retval = roccat_common2_receive(usb_dev, command, buf, real_size); -	mutex_unlock(&savu->savu_lock); - -	return retval ? retval : real_size; -} - -static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, -		void const *buf, loff_t off, size_t count, -		size_t real_size, uint command) -{ -	struct device *dev = -			container_of(kobj, struct device, kobj)->parent->parent; -	struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); -	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); -	int retval; - -	if (off != 0 || count != real_size) -		return -EINVAL; - -	mutex_lock(&savu->savu_lock); -	retval = roccat_common2_send_with_status(usb_dev, command, -			(void *)buf, real_size); -	mutex_unlock(&savu->savu_lock); - -	return retval ? retval : real_size; -} - -#define SAVU_SYSFS_W(thingy, THINGY) \ -static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \ -		struct kobject *kobj, struct bin_attribute *attr, char *buf, \ -		loff_t off, size_t count) \ -{ \ -	return savu_sysfs_write(fp, kobj, buf, off, count, \ -			SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ -} - -#define SAVU_SYSFS_R(thingy, THINGY) \ -static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \ -		struct kobject *kobj, struct bin_attribute *attr, char *buf, \ -		loff_t off, size_t count) \ -{ \ -	return savu_sysfs_read(fp, kobj, buf, off, count, \ -			SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ -} - -#define SAVU_SYSFS_RW(thingy, THINGY) \ -SAVU_SYSFS_W(thingy, THINGY) \ -SAVU_SYSFS_R(thingy, THINGY) - -#define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \ -SAVU_SYSFS_RW(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ -	.attr = { .name = #thingy, .mode = 0660 }, \ -	.size = SAVU_SIZE_ ## THINGY, \ -	.read = savu_sysfs_read_ ## thingy, \ -	.write = savu_sysfs_write_ ## thingy \ -} - -#define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \ -SAVU_SYSFS_W(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ -	.attr = { .name = #thingy, .mode = 0220 }, \ -	.size = SAVU_SIZE_ ## THINGY, \ -	.write = savu_sysfs_write_ ## thingy \ -} - -SAVU_BIN_ATTRIBUTE_W(control, CONTROL); -SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE); -SAVU_BIN_ATTRIBUTE_RW(general, GENERAL); -SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS); -SAVU_BIN_ATTRIBUTE_RW(macro, MACRO); -SAVU_BIN_ATTRIBUTE_RW(info, INFO); -SAVU_BIN_ATTRIBUTE_RW(sensor, SENSOR); - -static struct bin_attribute *savu_bin_attributes[] = { +ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x4, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x5, 0x03); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(general, 0x6, 0x10); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(buttons, 0x7, 0x2f); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08); +ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04); + +static struct bin_attribute *savu_bin_attrs[] = {  	&bin_attr_control,  	&bin_attr_profile,  	&bin_attr_general, @@ -130,7 +47,7 @@ static struct bin_attribute *savu_bin_attributes[] = {  };  static const struct attribute_group savu_group = { -	.bin_attrs = savu_bin_attributes, +	.bin_attrs = savu_bin_attrs,  };  static const struct attribute_group *savu_groups[] = { @@ -138,19 +55,11 @@ static const struct attribute_group *savu_groups[] = {  	NULL,  }; -static int savu_init_savu_device_struct(struct usb_device *usb_dev, -		struct savu_device *savu) -{ -	mutex_init(&savu->savu_lock); - -	return 0; -} -  static int savu_init_specials(struct hid_device *hdev)  {  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);  	struct usb_device *usb_dev = interface_to_usbdev(intf); -	struct savu_device *savu; +	struct roccat_common2_device *savu;  	int retval;  	if (intf->cur_altsetting->desc.bInterfaceProtocol @@ -166,9 +75,9 @@ static int savu_init_specials(struct hid_device *hdev)  	}  	hid_set_drvdata(hdev, savu); -	retval = savu_init_savu_device_struct(usb_dev, savu); +	retval = roccat_common2_device_init_struct(usb_dev, savu);  	if (retval) { -		hid_err(hdev, "couldn't init struct savu_device\n"); +		hid_err(hdev, "couldn't init Savu device\n");  		goto exit_free;  	} @@ -190,7 +99,7 @@ exit_free:  static void savu_remove_specials(struct hid_device *hdev)  {  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); -	struct savu_device *savu; +	struct roccat_common2_device *savu;  	if (intf->cur_altsetting->desc.bInterfaceProtocol  			!= USB_INTERFACE_PROTOCOL_MOUSE) @@ -239,7 +148,7 @@ static void savu_remove(struct hid_device *hdev)  	hid_hw_stop(hdev);  } -static void savu_report_to_chrdev(struct savu_device const *savu, +static void savu_report_to_chrdev(struct roccat_common2_device const *savu,  		u8 const *data)  {  	struct savu_roccat_report roccat_report; @@ -261,7 +170,7 @@ static int savu_raw_event(struct hid_device *hdev,  		struct hid_report *report, u8 *data, int size)  {  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); -	struct savu_device *savu = hid_get_drvdata(hdev); +	struct roccat_common2_device *savu = hid_get_drvdata(hdev);  	if (intf->cur_altsetting->desc.bInterfaceProtocol  			!= USB_INTERFACE_PROTOCOL_MOUSE) diff --git a/drivers/hid/hid-roccat-savu.h b/drivers/hid/hid-roccat-savu.h index 9120ba72087..d23217bd2b8 100644 --- a/drivers/hid/hid-roccat-savu.h +++ b/drivers/hid/hid-roccat-savu.h @@ -14,31 +14,6 @@  #include <linux/types.h> -enum { -	SAVU_SIZE_CONTROL = 0x03, -	SAVU_SIZE_PROFILE = 0x03, -	SAVU_SIZE_GENERAL = 0x10, -	SAVU_SIZE_BUTTONS = 0x2f, -	SAVU_SIZE_MACRO = 0x0823, -	SAVU_SIZE_INFO = 0x08, -	SAVU_SIZE_SENSOR = 0x04, -}; - -enum savu_control_requests { -	SAVU_CONTROL_REQUEST_GENERAL = 0x80, -	SAVU_CONTROL_REQUEST_BUTTONS = 0x90, -}; - -enum savu_commands { -	SAVU_COMMAND_CONTROL = 0x4, -	SAVU_COMMAND_PROFILE = 0x5, -	SAVU_COMMAND_GENERAL = 0x6, -	SAVU_COMMAND_BUTTONS = 0x7, -	SAVU_COMMAND_MACRO = 0x8, -	SAVU_COMMAND_INFO = 0x9, -	SAVU_COMMAND_SENSOR = 0xc, -}; -  struct savu_mouse_report_special {  	uint8_t report_number; /* always 3 */  	uint8_t zero; @@ -77,11 +52,4 @@ struct savu_roccat_report {  	uint8_t data[2];  } __packed; -struct savu_device { -	int roccat_claimed; -	int chrdev_minor; - -	struct mutex savu_lock; -}; -  #endif diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 37961c7e397..69cca1476a0 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -1,10 +1,17 @@  /* - *  HID driver for Saitek devices, currently only the PS1000 (USB gamepad). + *  HID driver for Saitek devices. + * + *  PS1000 (USB gamepad):   *  Fixes the HID report descriptor by removing a non-existent axis and   *  clearing the constant bit on the input reports for buttons and d-pad.   *  (This module is based on "hid-ortek".) - *   *  Copyright (c) 2012 Andreas Hübner + * + *  R.A.T.7, M.M.O.7 (USB gaming mice): + *  Fixes the mode button which cycles through three constantly pressed + *  buttons. All three press events are mapped to one button and the + *  missing release event is generated immediately. + *   */  /* @@ -21,12 +28,57 @@  #include "hid-ids.h" +#define SAITEK_FIX_PS1000	0x0001 +#define SAITEK_RELEASE_MODE_RAT7	0x0002 +#define SAITEK_RELEASE_MODE_MMO7	0x0004 + +struct saitek_sc { +	unsigned long quirks; +	int mode; +}; + +static int saitek_probe(struct hid_device *hdev, +		const struct hid_device_id *id) +{ +	unsigned long quirks = id->driver_data; +	struct saitek_sc *ssc; +	int ret; + +	ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL); +	if (ssc == NULL) { +		hid_err(hdev, "can't alloc saitek descriptor\n"); +		return -ENOMEM; +	} + +	ssc->quirks = quirks; +	ssc->mode = -1; + +	hid_set_drvdata(hdev, ssc); + +	ret = hid_parse(hdev); +	if (ret) { +		hid_err(hdev, "parse failed\n"); +		return ret; +	} + +	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); +	if (ret) { +		hid_err(hdev, "hw start failed\n"); +		return ret; +	} + +	return 0; +} +  static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,  		unsigned int *rsize)  { -	if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33 -			&& rdesc[94] == 0x81 && rdesc[95] == 0x03 -			&& rdesc[110] == 0x81 && rdesc[111] == 0x03) { +	struct saitek_sc *ssc = hid_get_drvdata(hdev); + +	if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 && +			rdesc[20] == 0x09 && rdesc[21] == 0x33 && +			rdesc[94] == 0x81 && rdesc[95] == 0x03 && +			rdesc[110] == 0x81 && rdesc[111] == 0x03) {  		hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n"); @@ -42,8 +94,93 @@ static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,  	return rdesc;  } +static int saitek_raw_event(struct hid_device *hdev, +		struct hid_report *report, u8 *raw_data, int size) +{ +	struct saitek_sc *ssc = hid_get_drvdata(hdev); + +	if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) { +		/* R.A.T.7 uses bits 13, 14, 15 for the mode */ +		int mode = -1; +		if (raw_data[1] & 0x01) +			mode = 0; +		else if (raw_data[1] & 0x02) +			mode = 1; +		else if (raw_data[1] & 0x04) +			mode = 2; + +		/* clear mode bits */ +		raw_data[1] &= ~0x07; + +		if (mode != ssc->mode) { +			hid_dbg(hdev, "entered mode %d\n", mode); +			if (ssc->mode != -1) { +				/* use bit 13 as the mode button */ +				raw_data[1] |= 0x04; +			} +			ssc->mode = mode; +		} +	} else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) { + +		/* M.M.O.7 uses bits 8, 22, 23 for the mode */ +		int mode = -1; +		if (raw_data[1] & 0x80) +			mode = 0; +		else if (raw_data[2] & 0x01) +			mode = 1; +		else if (raw_data[2] & 0x02) +			mode = 2; + +		/* clear mode bits */ +		raw_data[1] &= ~0x80; +		raw_data[2] &= ~0x03; + +		if (mode != ssc->mode) { +			hid_dbg(hdev, "entered mode %d\n", mode); +			if (ssc->mode != -1) { +				/* use bit 8 as the mode button, bits 22 +				 * and 23 do not represent buttons +				 * according to the HID report descriptor +				 */ +				raw_data[1] |= 0x80; +			} +			ssc->mode = mode; +		} +	} + +	return 0; +} + +static int saitek_event(struct hid_device *hdev, struct hid_field *field, +		struct hid_usage *usage, __s32 value) +{ +	struct saitek_sc *ssc = hid_get_drvdata(hdev); +	struct input_dev *input = field->hidinput->input; + +	if (usage->type == EV_KEY && value && +			(((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) && +			  usage->code - BTN_MOUSE == 10) || +			((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) && +			 usage->code - BTN_MOUSE == 15))) { + +		input_report_key(input, usage->code, 1); + +		/* report missing release event */ +		input_report_key(input, usage->code, 0); + +		return 1; +	} + +	return 0; +} +  static const struct hid_device_id saitek_devices[] = { -	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000)}, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), +		.driver_data = SAITEK_FIX_PS1000 }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), +		.driver_data = SAITEK_RELEASE_MODE_RAT7 }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), +		.driver_data = SAITEK_RELEASE_MODE_MMO7 },  	{ }  }; @@ -52,7 +189,10 @@ MODULE_DEVICE_TABLE(hid, saitek_devices);  static struct hid_driver saitek_driver = {  	.name = "saitek",  	.id_table = saitek_devices, -	.report_fixup = saitek_report_fixup +	.probe = saitek_probe, +	.report_fixup = saitek_report_fixup, +	.raw_event = saitek_raw_event, +	.event = saitek_event,  };  module_hid_driver(saitek_driver); diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 10e1581022c..e244e449cbb 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -26,6 +26,8 @@  #include <linux/hid-sensor-hub.h>  #include "hid-ids.h" +#define HID_SENSOR_HUB_ENUM_QUIRK	0x01 +  /**   * struct sensor_hub_pending - Synchronous read pending information   * @status:		Pending status true/false. @@ -54,9 +56,9 @@ struct sensor_hub_pending {   * @dyn_callback_lock:	spin lock to protect callback list   * @hid_sensor_hub_client_devs:	Stores all MFD cells for a hub instance.   * @hid_sensor_client_cnt: Number of MFD cells, (no of sensors attached). + * @ref_cnt:		Number of MFD clients have opened this device   */  struct sensor_hub_data { -	struct hid_sensor_hub_device *hsdev;  	struct mutex mutex;  	spinlock_t lock;  	struct sensor_hub_pending pending; @@ -64,6 +66,8 @@ struct sensor_hub_data {  	spinlock_t dyn_callback_lock;  	struct mfd_cell *hid_sensor_hub_client_devs;  	int hid_sensor_client_cnt; +	unsigned long quirks; +	int ref_cnt;  };  /** @@ -76,6 +80,7 @@ struct sensor_hub_data {  struct hid_sensor_hub_callbacks_list {  	struct list_head list;  	u32 usage_id; +	struct hid_sensor_hub_device *hsdev;  	struct hid_sensor_hub_callbacks *usage_callback;  	void *priv;  }; @@ -94,44 +99,52 @@ static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,  	return NULL;  } -static int sensor_hub_get_physical_device_count( -				struct hid_report_enum *report_enum) +static int sensor_hub_get_physical_device_count(struct hid_device *hdev)  { -	struct hid_report *report; -	struct hid_field *field; -	int cnt = 0; +	int i; +	int count = 0; -	list_for_each_entry(report, &report_enum->report_list, list) { -		field = report->field[0]; -		if (report->maxfield && field && field->physical) -			cnt++; +	for (i = 0; i < hdev->maxcollection; ++i) { +		struct hid_collection *collection = &hdev->collection[i]; +		if (collection->type == HID_COLLECTION_PHYSICAL) +			++count;  	} -	return cnt; +	return count;  }  static void sensor_hub_fill_attr_info(  		struct hid_sensor_hub_attribute_info *info, -		s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size) +		s32 index, s32 report_id, struct hid_field *field)  {  	info->index = index;  	info->report_id = report_id; -	info->units = units; -	info->unit_expo = unit_expo; -	info->size = size/8; +	info->units = field->unit; +	info->unit_expo = field->unit_exponent; +	info->size = (field->report_size * field->report_count)/8; +	info->logical_minimum = field->logical_minimum; +	info->logical_maximum = field->logical_maximum;  }  static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(  					struct hid_device *hdev, -					u32 usage_id, void **priv) +					u32 usage_id, +					int collection_index, +					struct hid_sensor_hub_device **hsdev, +					void **priv)  {  	struct hid_sensor_hub_callbacks_list *callback;  	struct sensor_hub_data *pdata = hid_get_drvdata(hdev);  	spin_lock(&pdata->dyn_callback_lock);  	list_for_each_entry(callback, &pdata->dyn_callback_list, list) -		if (callback->usage_id == usage_id) { +		if (callback->usage_id == usage_id && +			(collection_index >= +				callback->hsdev->start_collection_index) && +			(collection_index < +				callback->hsdev->end_collection_index)) {  			*priv = callback->priv; +			*hsdev = callback->hsdev;  			spin_unlock(&pdata->dyn_callback_lock);  			return callback->usage_callback;  		} @@ -146,23 +159,26 @@ int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,  {  	struct hid_sensor_hub_callbacks_list *callback;  	struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); +	unsigned long flags; -	spin_lock(&pdata->dyn_callback_lock); +	spin_lock_irqsave(&pdata->dyn_callback_lock, flags);  	list_for_each_entry(callback, &pdata->dyn_callback_list, list) -		if (callback->usage_id == usage_id) { -			spin_unlock(&pdata->dyn_callback_lock); +		if (callback->usage_id == usage_id && +						callback->hsdev == hsdev) { +			spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);  			return -EINVAL;  		}  	callback = kzalloc(sizeof(*callback), GFP_ATOMIC);  	if (!callback) { -		spin_unlock(&pdata->dyn_callback_lock); +		spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);  		return -ENOMEM;  	} +	callback->hsdev = hsdev;  	callback->usage_callback = usage_callback;  	callback->usage_id = usage_id;  	callback->priv = NULL;  	list_add_tail(&callback->list, &pdata->dyn_callback_list); -	spin_unlock(&pdata->dyn_callback_lock); +	spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);  	return 0;  } @@ -173,15 +189,17 @@ int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,  {  	struct hid_sensor_hub_callbacks_list *callback;  	struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev); +	unsigned long flags; -	spin_lock(&pdata->dyn_callback_lock); +	spin_lock_irqsave(&pdata->dyn_callback_lock, flags);  	list_for_each_entry(callback, &pdata->dyn_callback_list, list) -		if (callback->usage_id == usage_id) { +		if (callback->usage_id == usage_id && +						callback->hsdev == hsdev) {  			list_del(&callback->list);  			kfree(callback);  			break;  		} -	spin_unlock(&pdata->dyn_callback_lock); +	spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);  	return 0;  } @@ -255,13 +273,12 @@ int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,  	spin_lock_irqsave(&data->lock, flags);  	data->pending.status = true; +	spin_unlock_irqrestore(&data->lock, flags);  	report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT); -	if (!report) { -		spin_unlock_irqrestore(&data->lock, flags); +	if (!report)  		goto err_free; -	} +  	hid_hw_request(hsdev->hdev, report, HID_REQ_GET_REPORT); -	spin_unlock_irqrestore(&data->lock, flags);  	wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);  	switch (data->pending.raw_size) {  	case 1: @@ -286,6 +303,28 @@ err_free:  }  EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value); +int hid_sensor_get_usage_index(struct hid_sensor_hub_device *hsdev, +				u32 report_id, int field_index, u32 usage_id) +{ +	struct hid_report *report; +	struct hid_field *field; +	int i; + +	report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT); +	if (!report || (field_index >= report->maxfield)) +		goto done_proc; + +	field = report->field[field_index]; +	for (i = 0; i < field->maxusage; ++i) { +		if (field->usage[i].hid == usage_id) +			return field->usage[i].usage_index; +	} + +done_proc: +	return -EINVAL; +} +EXPORT_SYMBOL_GPL(hid_sensor_get_usage_index); +  int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,  				u8 type,  				u32 usage_id, @@ -293,8 +332,7 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,  				struct hid_sensor_hub_attribute_info *info)  {  	int ret = -1; -	int i, j; -	int collection_index = -1; +	int i;  	struct hid_report *report;  	struct hid_field *field;  	struct hid_report_enum *report_enum; @@ -308,48 +346,31 @@ int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,  	info->units = -1;  	info->unit_expo = -1; -	for (i = 0; i < hdev->maxcollection; ++i) { -		struct hid_collection *collection = &hdev->collection[i]; -		if (usage_id == collection->usage) { -			collection_index = i; -			break; -		} -	} -	if (collection_index == -1) -		goto err_ret; -  	report_enum = &hdev->report_enum[type];  	list_for_each_entry(report, &report_enum->report_list, list) {  		for (i = 0; i < report->maxfield; ++i) {  			field = report->field[i]; -			if (field->physical == usage_id && -				field->logical == attr_usage_id) { -				sensor_hub_fill_attr_info(info, i, report->id, -					field->unit, field->unit_exponent, -					field->report_size); -				ret = 0; -			} else { -				for (j = 0; j < field->maxusage; ++j) { -					if (field->usage[j].hid == -					attr_usage_id && -					field->usage[j].collection_index == -					collection_index) { -						sensor_hub_fill_attr_info(info, -							i, report->id, -							field->unit, -							field->unit_exponent, -							field->report_size); -						ret = 0; -						break; -					} +			if (field->maxusage) { +				if (field->physical == usage_id && +					(field->logical == attr_usage_id || +					field->usage[0].hid == +							attr_usage_id) && +					(field->usage[0].collection_index >= +					hsdev->start_collection_index) && +					(field->usage[0].collection_index < +					hsdev->end_collection_index)) { + +					sensor_hub_fill_attr_info(info, i, +								report->id, +								field); +					ret = 0; +					break;  				}  			} -			if (ret == 0) -				break;  		} +  	} -err_ret:  	return ret;  }  EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info); @@ -359,15 +380,16 @@ static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)  {  	struct sensor_hub_data *pdata = hid_get_drvdata(hdev);  	struct hid_sensor_hub_callbacks_list *callback; +	unsigned long flags;  	hid_dbg(hdev, " sensor_hub_suspend\n"); -	spin_lock(&pdata->dyn_callback_lock); +	spin_lock_irqsave(&pdata->dyn_callback_lock, flags);  	list_for_each_entry(callback, &pdata->dyn_callback_list, list) {  		if (callback->usage_callback->suspend)  			callback->usage_callback->suspend( -					pdata->hsdev, callback->priv); +					callback->hsdev, callback->priv);  	} -	spin_unlock(&pdata->dyn_callback_lock); +	spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);  	return 0;  } @@ -376,15 +398,16 @@ static int sensor_hub_resume(struct hid_device *hdev)  {  	struct sensor_hub_data *pdata = hid_get_drvdata(hdev);  	struct hid_sensor_hub_callbacks_list *callback; +	unsigned long flags;  	hid_dbg(hdev, " sensor_hub_resume\n"); -	spin_lock(&pdata->dyn_callback_lock); +	spin_lock_irqsave(&pdata->dyn_callback_lock, flags);  	list_for_each_entry(callback, &pdata->dyn_callback_list, list) {  		if (callback->usage_callback->resume)  			callback->usage_callback->resume( -					pdata->hsdev, callback->priv); +					callback->hsdev, callback->priv);  	} -	spin_unlock(&pdata->dyn_callback_lock); +	spin_unlock_irqrestore(&pdata->dyn_callback_lock, flags);  	return 0;  } @@ -409,6 +432,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev,  	struct hid_sensor_hub_callbacks *callback = NULL;  	struct hid_collection *collection = NULL;  	void *priv = NULL; +	struct hid_sensor_hub_device *hsdev = NULL;  	hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",  			 report->id, size, report->type); @@ -425,9 +449,10 @@ static int sensor_hub_raw_event(struct hid_device *hdev,  		hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n",  				i, report->field[i]->usage->collection_index,  				report->field[i]->usage->hid, -				report->field[i]->report_size/8); - -		sz = report->field[i]->report_size/8; +				(report->field[i]->report_size * +					report->field[i]->report_count)/8); +		sz = (report->field[i]->report_size * +					report->field[i]->report_count)/8;  		if (pdata->pending.status && pdata->pending.attr_usage_id ==  				report->field[i]->usage->hid) {  			hid_dbg(hdev, "data was pending ...\n"); @@ -442,29 +467,99 @@ static int sensor_hub_raw_event(struct hid_device *hdev,  				report->field[i]->usage->collection_index];  		hid_dbg(hdev, "collection->usage %x\n",  					collection->usage); -		callback = sensor_hub_get_callback(pdata->hsdev->hdev, -						report->field[i]->physical, -							&priv); + +		callback = sensor_hub_get_callback(hdev, +				report->field[i]->physical, +				report->field[i]->usage[0].collection_index, +				&hsdev, &priv); +  		if (callback && callback->capture_sample) {  			if (report->field[i]->logical) -				callback->capture_sample(pdata->hsdev, +				callback->capture_sample(hsdev,  					report->field[i]->logical, sz, ptr,  					callback->pdev);  			else -				callback->capture_sample(pdata->hsdev, +				callback->capture_sample(hsdev,  					report->field[i]->usage->hid, sz, ptr,  					callback->pdev);  		}  		ptr += sz;  	}  	if (callback && collection && callback->send_event) -		callback->send_event(pdata->hsdev, collection->usage, +		callback->send_event(hsdev, collection->usage,  				callback->pdev);  	spin_unlock_irqrestore(&pdata->lock, flags);  	return 1;  } +int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev) +{ +	int ret = 0; +	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev); + +	mutex_lock(&data->mutex); +	if (!data->ref_cnt) { +		ret = hid_hw_open(hsdev->hdev); +		if (ret) { +			hid_err(hsdev->hdev, "failed to open hid device\n"); +			mutex_unlock(&data->mutex); +			return ret; +		} +	} +	data->ref_cnt++; +	mutex_unlock(&data->mutex); + +	return ret; +} +EXPORT_SYMBOL_GPL(sensor_hub_device_open); + +void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev) +{ +	struct sensor_hub_data *data =  hid_get_drvdata(hsdev->hdev); + +	mutex_lock(&data->mutex); +	data->ref_cnt--; +	if (!data->ref_cnt) +		hid_hw_close(hsdev->hdev); +	mutex_unlock(&data->mutex); +} +EXPORT_SYMBOL_GPL(sensor_hub_device_close); + +static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, +		unsigned int *rsize) +{ +	int index; +	struct sensor_hub_data *sd =  hid_get_drvdata(hdev); +	unsigned char report_block[] = { +				0x0a,  0x16, 0x03, 0x15, 0x00, 0x25, 0x05}; +	unsigned char power_block[] = { +				0x0a,  0x19, 0x03, 0x15, 0x00, 0x25, 0x05}; + +	if (!(sd->quirks & HID_SENSOR_HUB_ENUM_QUIRK)) { +		hid_dbg(hdev, "No Enum quirks\n"); +		return rdesc; +	} + +	/* Looks for power and report state usage id and force to 1 */ +	for (index = 0; index < *rsize; ++index) { +		if (((*rsize - index) > sizeof(report_block)) && +			!memcmp(&rdesc[index], report_block, +						sizeof(report_block))) { +			rdesc[index + 4] = 0x01; +			index += sizeof(report_block); +		} +		if (((*rsize - index) > sizeof(power_block)) && +			!memcmp(&rdesc[index], power_block, +						sizeof(power_block))) { +			rdesc[index + 4] = 0x01; +			index += sizeof(power_block); +		} +	} + +	return rdesc; +} +  static int sensor_hub_probe(struct hid_device *hdev,  				const struct hid_device_id *id)  { @@ -472,25 +567,19 @@ static int sensor_hub_probe(struct hid_device *hdev,  	struct sensor_hub_data *sd;  	int i;  	char *name; -	struct hid_report *report; -	struct hid_report_enum *report_enum; -	struct hid_field *field;  	int dev_cnt; +	struct hid_sensor_hub_device *hsdev; +	struct hid_sensor_hub_device *last_hsdev = NULL;  	sd = devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL);  	if (!sd) {  		hid_err(hdev, "cannot allocate Sensor data\n");  		return -ENOMEM;  	} -	sd->hsdev = devm_kzalloc(&hdev->dev, sizeof(*sd->hsdev), GFP_KERNEL); -	if (!sd->hsdev) { -		hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); -		return -ENOMEM; -	} +  	hid_set_drvdata(hdev, sd); -	sd->hsdev->hdev = hdev; -	sd->hsdev->vendor_id = hdev->vendor; -	sd->hsdev->product_id = hdev->product; +	sd->quirks = id->driver_data; +  	spin_lock_init(&sd->lock);  	spin_lock_init(&sd->dyn_callback_lock);  	mutex_init(&sd->mutex); @@ -506,21 +595,14 @@ static int sensor_hub_probe(struct hid_device *hdev,  		hid_err(hdev, "hw start failed\n");  		return ret;  	} -	ret = hid_hw_open(hdev); -	if (ret) { -		hid_err(hdev, "failed to open input interrupt pipe\n"); -		goto err_stop_hw; -	} -  	INIT_LIST_HEAD(&sd->dyn_callback_list);  	sd->hid_sensor_client_cnt = 0; -	report_enum = &hdev->report_enum[HID_INPUT_REPORT]; -	dev_cnt = sensor_hub_get_physical_device_count(report_enum); +	dev_cnt = sensor_hub_get_physical_device_count(hdev);  	if (dev_cnt > HID_MAX_PHY_DEVICES) {  		hid_err(hdev, "Invalid Physical device count\n");  		ret = -EINVAL; -		goto err_close; +		goto err_stop_hw;  	}  	sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt *  						sizeof(struct mfd_cell), @@ -528,45 +610,67 @@ static int sensor_hub_probe(struct hid_device *hdev,  	if (sd->hid_sensor_hub_client_devs == NULL) {  		hid_err(hdev, "Failed to allocate memory for mfd cells\n");  			ret = -ENOMEM; -			goto err_close; +			goto err_stop_hw;  	} -	list_for_each_entry(report, &report_enum->report_list, list) { -		hid_dbg(hdev, "Report id:%x\n", report->id); -		field = report->field[0]; -		if (report->maxfield && field && -					field->physical) { + +	for (i = 0; i < hdev->maxcollection; ++i) { +		struct hid_collection *collection = &hdev->collection[i]; + +		if (collection->type == HID_COLLECTION_PHYSICAL) { + +			hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); +			if (!hsdev) { +				hid_err(hdev, "cannot allocate hid_sensor_hub_device\n"); +				ret = -ENOMEM; +				goto err_no_mem; +			} +			hsdev->hdev = hdev; +			hsdev->vendor_id = hdev->vendor; +			hsdev->product_id = hdev->product; +			hsdev->start_collection_index = i; +			if (last_hsdev) +				last_hsdev->end_collection_index = i; +			last_hsdev = hsdev;  			name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x", -						field->physical); +					collection->usage);  			if (name == NULL) {  				hid_err(hdev, "Failed MFD device name\n");  					ret = -ENOMEM; -					goto err_free_names; +					kfree(hsdev); +					goto err_no_mem;  			}  			sd->hid_sensor_hub_client_devs[ +				sd->hid_sensor_client_cnt].id = +							PLATFORM_DEVID_AUTO; +			sd->hid_sensor_hub_client_devs[  				sd->hid_sensor_client_cnt].name = name;  			sd->hid_sensor_hub_client_devs[  				sd->hid_sensor_client_cnt].platform_data = -						sd->hsdev; +							hsdev;  			sd->hid_sensor_hub_client_devs[  				sd->hid_sensor_client_cnt].pdata_size = -						sizeof(*sd->hsdev); -			hid_dbg(hdev, "Adding %s:%p\n", name, sd); +							sizeof(*hsdev); +			hid_dbg(hdev, "Adding %s:%d\n", name, +					hsdev->start_collection_index);  			sd->hid_sensor_client_cnt++;  		}  	} +	if (last_hsdev) +		last_hsdev->end_collection_index = i; +  	ret = mfd_add_devices(&hdev->dev, 0, sd->hid_sensor_hub_client_devs,  		sd->hid_sensor_client_cnt, NULL, 0, NULL);  	if (ret < 0) -		goto err_free_names; +		goto err_no_mem;  	return ret; -err_free_names: -	for (i = 0; i < sd->hid_sensor_client_cnt ; ++i) +err_no_mem: +	for (i = 0; i < sd->hid_sensor_client_cnt; ++i) {  		kfree(sd->hid_sensor_hub_client_devs[i].name); +		kfree(sd->hid_sensor_hub_client_devs[i].platform_data); +	}  	kfree(sd->hid_sensor_hub_client_devs); -err_close: -	hid_hw_close(hdev);  err_stop_hw:  	hid_hw_stop(hdev); @@ -587,14 +691,40 @@ static void sensor_hub_remove(struct hid_device *hdev)  		complete(&data->pending.ready);  	spin_unlock_irqrestore(&data->lock, flags);  	mfd_remove_devices(&hdev->dev); -	for (i = 0; i < data->hid_sensor_client_cnt ; ++i) +	for (i = 0; i < data->hid_sensor_client_cnt; ++i) {  		kfree(data->hid_sensor_hub_client_devs[i].name); +		kfree(data->hid_sensor_hub_client_devs[i].platform_data); +	}  	kfree(data->hid_sensor_hub_client_devs);  	hid_set_drvdata(hdev, NULL);  	mutex_destroy(&data->mutex);  }  static const struct hid_device_id sensor_hub_devices[] = { +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_0, +			USB_DEVICE_ID_INTEL_HID_SENSOR_0), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1, +			USB_DEVICE_ID_INTEL_HID_SENSOR_0), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1, +			USB_DEVICE_ID_INTEL_HID_SENSOR_1), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, +			USB_DEVICE_ID_MS_SURFACE_PRO_2), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, +			USB_DEVICE_ID_MS_TOUCH_COVER_2), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, +			USB_DEVICE_ID_MS_TYPE_COVER_2), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, +			USB_DEVICE_ID_STM_HID_SENSOR_1), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, +	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS, +			USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA), +			.driver_data = HID_SENSOR_HUB_ENUM_QUIRK},  	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID,  		     HID_ANY_ID) },  	{ } @@ -607,6 +737,7 @@ static struct hid_driver sensor_hub_driver = {  	.probe = sensor_hub_probe,  	.remove = sensor_hub_remove,  	.raw_event = sensor_hub_raw_event, +	.report_fixup = sensor_hub_report_fixup,  #ifdef CONFIG_PM  	.suspend = sensor_hub_suspend,  	.resume = sensor_hub_resume, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index b18320db5f7..2259eaa8b98 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -17,7 +17,8 @@   * any later version.   */ -/* NOTE: in order for the Sony PS3 BD Remote Control to be found by +/* + * NOTE: in order for the Sony PS3 BD Remote Control to be found by   * a Bluetooth host, the key combination Start+Enter has to be kept pressed   * for about 7 seconds with the Bluetooth Host Controller in discovering mode.   * @@ -28,16 +29,32 @@  #include <linux/hid.h>  #include <linux/module.h>  #include <linux/slab.h> -#include <linux/usb.h>  #include <linux/leds.h> +#include <linux/power_supply.h> +#include <linux/spinlock.h> +#include <linux/list.h> +#include <linux/idr.h> +#include <linux/input/mt.h>  #include "hid-ids.h" -#define VAIO_RDESC_CONSTANT     (1 << 0) -#define SIXAXIS_CONTROLLER_USB  (1 << 1) -#define SIXAXIS_CONTROLLER_BT   (1 << 2) -#define BUZZ_CONTROLLER         (1 << 3) -#define PS3REMOTE		(1 << 4) +#define VAIO_RDESC_CONSTANT       BIT(0) +#define SIXAXIS_CONTROLLER_USB    BIT(1) +#define SIXAXIS_CONTROLLER_BT     BIT(2) +#define BUZZ_CONTROLLER           BIT(3) +#define PS3REMOTE                 BIT(4) +#define DUALSHOCK4_CONTROLLER_USB BIT(5) +#define DUALSHOCK4_CONTROLLER_BT  BIT(6) + +#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) +#define DUALSHOCK4_CONTROLLER (DUALSHOCK4_CONTROLLER_USB |\ +				DUALSHOCK4_CONTROLLER_BT) +#define SONY_LED_SUPPORT (SIXAXIS_CONTROLLER | BUZZ_CONTROLLER |\ +				DUALSHOCK4_CONTROLLER) +#define SONY_BATTERY_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER) +#define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER) + +#define MAX_LEDS 4  static const u8 sixaxis_rdesc_fixup[] = {  	0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, @@ -67,6 +84,477 @@ static const u8 sixaxis_rdesc_fixup2[] = {  	0xb1, 0x02, 0xc0, 0xc0,  }; +/* + * The default descriptor doesn't provide mapping for the accelerometers + * or orientation sensors.  This fixed descriptor maps the accelerometers + * to usage values 0x40, 0x41 and 0x42 and maps the orientation sensors + * to usage values 0x43, 0x44 and 0x45. + */ +static u8 dualshock4_usb_rdesc[] = { +	0x05, 0x01,         /*  Usage Page (Desktop),               */ +	0x09, 0x05,         /*  Usage (Gamepad),                    */ +	0xA1, 0x01,         /*  Collection (Application),           */ +	0x85, 0x01,         /*      Report ID (1),                  */ +	0x09, 0x30,         /*      Usage (X),                      */ +	0x09, 0x31,         /*      Usage (Y),                      */ +	0x09, 0x32,         /*      Usage (Z),                      */ +	0x09, 0x35,         /*      Usage (Rz),                     */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x04,         /*      Report Count (4),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x39,         /*      Usage (Hat Switch),             */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x25, 0x07,         /*      Logical Maximum (7),            */ +	0x35, 0x00,         /*      Physical Minimum (0),           */ +	0x46, 0x3B, 0x01,   /*      Physical Maximum (315),         */ +	0x65, 0x14,         /*      Unit (Degrees),                 */ +	0x75, 0x04,         /*      Report Size (4),                */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0x81, 0x42,         /*      Input (Variable, Null State),   */ +	0x65, 0x00,         /*      Unit,                           */ +	0x05, 0x09,         /*      Usage Page (Button),            */ +	0x19, 0x01,         /*      Usage Minimum (01h),            */ +	0x29, 0x0E,         /*      Usage Maximum (0Eh),            */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x25, 0x01,         /*      Logical Maximum (1),            */ +	0x75, 0x01,         /*      Report Size (1),                */ +	0x95, 0x0E,         /*      Report Count (14),              */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */ +	0x09, 0x20,         /*      Usage (20h),                    */ +	0x75, 0x06,         /*      Report Size (6),                */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x25, 0x7F,         /*      Logical Maximum (127),          */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x05, 0x01,         /*      Usage Page (Desktop),           */ +	0x09, 0x33,         /*      Usage (Rx),                     */ +	0x09, 0x34,         /*      Usage (Ry),                     */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x02,         /*      Report Count (2),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x05, 0x01,         /*      Usage Page (Desktop),           */ +	0x19, 0x40,         /*      Usage Minimum (40h),            */ +	0x29, 0x42,         /*      Usage Maximum (42h),            */ +	0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */ +	0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */ +	0x75, 0x10,         /*      Report Size (16),               */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x19, 0x43,         /*      Usage Minimum (43h),            */ +	0x29, 0x45,         /*      Usage Maximum (45h),            */ +	0x16, 0xFF, 0xBF,   /*      Logical Minimum (-16385),       */ +	0x26, 0x00, 0x40,   /*      Logical Maximum (16384),        */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x25, 0xFF,         /*      Logical Maximum (255),          */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x27,         /*      Report Count (39),              */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x85, 0x05,         /*      Report ID (5),                  */ +	0x09, 0x22,         /*      Usage (22h),                    */ +	0x95, 0x1F,         /*      Report Count (31),              */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x04,         /*      Report ID (4),                  */ +	0x09, 0x23,         /*      Usage (23h),                    */ +	0x95, 0x24,         /*      Report Count (36),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x02,         /*      Report ID (2),                  */ +	0x09, 0x24,         /*      Usage (24h),                    */ +	0x95, 0x24,         /*      Report Count (36),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x08,         /*      Report ID (8),                  */ +	0x09, 0x25,         /*      Usage (25h),                    */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x10,         /*      Report ID (16),                 */ +	0x09, 0x26,         /*      Usage (26h),                    */ +	0x95, 0x04,         /*      Report Count (4),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x11,         /*      Report ID (17),                 */ +	0x09, 0x27,         /*      Usage (27h),                    */ +	0x95, 0x02,         /*      Report Count (2),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x12,         /*      Report ID (18),                 */ +	0x06, 0x02, 0xFF,   /*      Usage Page (FF02h),             */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x95, 0x0F,         /*      Report Count (15),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x13,         /*      Report ID (19),                 */ +	0x09, 0x22,         /*      Usage (22h),                    */ +	0x95, 0x16,         /*      Report Count (22),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x14,         /*      Report ID (20),                 */ +	0x06, 0x05, 0xFF,   /*      Usage Page (FF05h),             */ +	0x09, 0x20,         /*      Usage (20h),                    */ +	0x95, 0x10,         /*      Report Count (16),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x15,         /*      Report ID (21),                 */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x95, 0x2C,         /*      Report Count (44),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x06, 0x80, 0xFF,   /*      Usage Page (FF80h),             */ +	0x85, 0x80,         /*      Report ID (128),                */ +	0x09, 0x20,         /*      Usage (20h),                    */ +	0x95, 0x06,         /*      Report Count (6),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x81,         /*      Report ID (129),                */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x95, 0x06,         /*      Report Count (6),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x82,         /*      Report ID (130),                */ +	0x09, 0x22,         /*      Usage (22h),                    */ +	0x95, 0x05,         /*      Report Count (5),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x83,         /*      Report ID (131),                */ +	0x09, 0x23,         /*      Usage (23h),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x84,         /*      Report ID (132),                */ +	0x09, 0x24,         /*      Usage (24h),                    */ +	0x95, 0x04,         /*      Report Count (4),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x85,         /*      Report ID (133),                */ +	0x09, 0x25,         /*      Usage (25h),                    */ +	0x95, 0x06,         /*      Report Count (6),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x86,         /*      Report ID (134),                */ +	0x09, 0x26,         /*      Usage (26h),                    */ +	0x95, 0x06,         /*      Report Count (6),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x87,         /*      Report ID (135),                */ +	0x09, 0x27,         /*      Usage (27h),                    */ +	0x95, 0x23,         /*      Report Count (35),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x88,         /*      Report ID (136),                */ +	0x09, 0x28,         /*      Usage (28h),                    */ +	0x95, 0x22,         /*      Report Count (34),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x89,         /*      Report ID (137),                */ +	0x09, 0x29,         /*      Usage (29h),                    */ +	0x95, 0x02,         /*      Report Count (2),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x90,         /*      Report ID (144),                */ +	0x09, 0x30,         /*      Usage (30h),                    */ +	0x95, 0x05,         /*      Report Count (5),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x91,         /*      Report ID (145),                */ +	0x09, 0x31,         /*      Usage (31h),                    */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x92,         /*      Report ID (146),                */ +	0x09, 0x32,         /*      Usage (32h),                    */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x93,         /*      Report ID (147),                */ +	0x09, 0x33,         /*      Usage (33h),                    */ +	0x95, 0x0C,         /*      Report Count (12),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA0,         /*      Report ID (160),                */ +	0x09, 0x40,         /*      Usage (40h),                    */ +	0x95, 0x06,         /*      Report Count (6),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA1,         /*      Report ID (161),                */ +	0x09, 0x41,         /*      Usage (41h),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA2,         /*      Report ID (162),                */ +	0x09, 0x42,         /*      Usage (42h),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA3,         /*      Report ID (163),                */ +	0x09, 0x43,         /*      Usage (43h),                    */ +	0x95, 0x30,         /*      Report Count (48),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA4,         /*      Report ID (164),                */ +	0x09, 0x44,         /*      Usage (44h),                    */ +	0x95, 0x0D,         /*      Report Count (13),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA5,         /*      Report ID (165),                */ +	0x09, 0x45,         /*      Usage (45h),                    */ +	0x95, 0x15,         /*      Report Count (21),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA6,         /*      Report ID (166),                */ +	0x09, 0x46,         /*      Usage (46h),                    */ +	0x95, 0x15,         /*      Report Count (21),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xF0,         /*      Report ID (240),                */ +	0x09, 0x47,         /*      Usage (47h),                    */ +	0x95, 0x3F,         /*      Report Count (63),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xF1,         /*      Report ID (241),                */ +	0x09, 0x48,         /*      Usage (48h),                    */ +	0x95, 0x3F,         /*      Report Count (63),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xF2,         /*      Report ID (242),                */ +	0x09, 0x49,         /*      Usage (49h),                    */ +	0x95, 0x0F,         /*      Report Count (15),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA7,         /*      Report ID (167),                */ +	0x09, 0x4A,         /*      Usage (4Ah),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA8,         /*      Report ID (168),                */ +	0x09, 0x4B,         /*      Usage (4Bh),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA9,         /*      Report ID (169),                */ +	0x09, 0x4C,         /*      Usage (4Ch),                    */ +	0x95, 0x08,         /*      Report Count (8),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xAA,         /*      Report ID (170),                */ +	0x09, 0x4E,         /*      Usage (4Eh),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xAB,         /*      Report ID (171),                */ +	0x09, 0x4F,         /*      Usage (4Fh),                    */ +	0x95, 0x39,         /*      Report Count (57),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xAC,         /*      Report ID (172),                */ +	0x09, 0x50,         /*      Usage (50h),                    */ +	0x95, 0x39,         /*      Report Count (57),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xAD,         /*      Report ID (173),                */ +	0x09, 0x51,         /*      Usage (51h),                    */ +	0x95, 0x0B,         /*      Report Count (11),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xAE,         /*      Report ID (174),                */ +	0x09, 0x52,         /*      Usage (52h),                    */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xAF,         /*      Report ID (175),                */ +	0x09, 0x53,         /*      Usage (53h),                    */ +	0x95, 0x02,         /*      Report Count (2),               */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xB0,         /*      Report ID (176),                */ +	0x09, 0x54,         /*      Usage (54h),                    */ +	0x95, 0x3F,         /*      Report Count (63),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0xC0                /*  End Collection                      */ +}; + +/* + * The default behavior of the Dualshock 4 is to send reports using report + * type 1 when running over Bluetooth. However, as soon as it receives a + * report of type 17 to set the LEDs or rumble it starts returning it's state + * in report 17 instead of 1.  Since report 17 is undefined in the default HID + * descriptor the button and axis definitions must be moved to report 17 or + * the HID layer won't process the received input once a report is sent. + */ +static u8 dualshock4_bt_rdesc[] = { +	0x05, 0x01,         /*  Usage Page (Desktop),               */ +	0x09, 0x05,         /*  Usage (Gamepad),                    */ +	0xA1, 0x01,         /*  Collection (Application),           */ +	0x85, 0x01,         /*      Report ID (1),                  */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x0A,         /*      Report Count (9),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x06, 0x04, 0xFF,   /*      Usage Page (FF04h),             */ +	0x85, 0x02,         /*      Report ID (2),                  */ +	0x09, 0x24,         /*      Usage (24h),                    */ +	0x95, 0x24,         /*      Report Count (36),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA3,         /*      Report ID (163),                */ +	0x09, 0x25,         /*      Usage (25h),                    */ +	0x95, 0x30,         /*      Report Count (48),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x05,         /*      Report ID (5),                  */ +	0x09, 0x26,         /*      Usage (26h),                    */ +	0x95, 0x28,         /*      Report Count (40),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x06,         /*      Report ID (6),                  */ +	0x09, 0x27,         /*      Usage (27h),                    */ +	0x95, 0x34,         /*      Report Count (52),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x07,         /*      Report ID (7),                  */ +	0x09, 0x28,         /*      Usage (28h),                    */ +	0x95, 0x30,         /*      Report Count (48),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x08,         /*      Report ID (8),                  */ +	0x09, 0x29,         /*      Usage (29h),                    */ +	0x95, 0x2F,         /*      Report Count (47),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x06, 0x03, 0xFF,   /*      Usage Page (FF03h),             */ +	0x85, 0x03,         /*      Report ID (3),                  */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x95, 0x26,         /*      Report Count (38),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x04,         /*      Report ID (4),                  */ +	0x09, 0x22,         /*      Usage (22h),                    */ +	0x95, 0x2E,         /*      Report Count (46),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xF0,         /*      Report ID (240),                */ +	0x09, 0x47,         /*      Usage (47h),                    */ +	0x95, 0x3F,         /*      Report Count (63),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xF1,         /*      Report ID (241),                */ +	0x09, 0x48,         /*      Usage (48h),                    */ +	0x95, 0x3F,         /*      Report Count (63),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xF2,         /*      Report ID (242),                */ +	0x09, 0x49,         /*      Usage (49h),                    */ +	0x95, 0x0F,         /*      Report Count (15),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x11,         /*      Report ID (17),                 */ +	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */ +	0x09, 0x20,         /*      Usage (20h),                    */ +	0x95, 0x02,         /*      Report Count (2),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x05, 0x01,         /*      Usage Page (Desktop),           */ +	0x09, 0x30,         /*      Usage (X),                      */ +	0x09, 0x31,         /*      Usage (Y),                      */ +	0x09, 0x32,         /*      Usage (Z),                      */ +	0x09, 0x35,         /*      Usage (Rz),                     */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x04,         /*      Report Count (4),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x39,         /*      Usage (Hat Switch),             */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x25, 0x07,         /*      Logical Maximum (7),            */ +	0x75, 0x04,         /*      Report Size (4),                */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0x81, 0x42,         /*      Input (Variable, Null State),   */ +	0x05, 0x09,         /*      Usage Page (Button),            */ +	0x19, 0x01,         /*      Usage Minimum (01h),            */ +	0x29, 0x0E,         /*      Usage Maximum (0Eh),            */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x25, 0x01,         /*      Logical Maximum (1),            */ +	0x75, 0x01,         /*      Report Size (1),                */ +	0x95, 0x0E,         /*      Report Count (14),              */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x75, 0x06,         /*      Report Size (6),                */ +	0x95, 0x01,         /*      Report Count (1),               */ +	0x81, 0x01,         /*      Input (Constant),               */ +	0x05, 0x01,         /*      Usage Page (Desktop),           */ +	0x09, 0x33,         /*      Usage (Rx),                     */ +	0x09, 0x34,         /*      Usage (Ry),                     */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x02,         /*      Report Count (2),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */ +	0x09, 0x20,         /*      Usage (20h),                    */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x05, 0x01,         /*      Usage Page (Desktop),           */ +	0x19, 0x40,         /*      Usage Minimum (40h),            */ +	0x29, 0x42,         /*      Usage Maximum (42h),            */ +	0x16, 0x00, 0x80,   /*      Logical Minimum (-32768),       */ +	0x26, 0x00, 0x7F,   /*      Logical Maximum (32767),        */ +	0x75, 0x10,         /*      Report Size (16),               */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x19, 0x43,         /*      Usage Minimum (43h),            */ +	0x29, 0x45,         /*      Usage Maximum (45h),            */ +	0x16, 0xFF, 0xBF,   /*      Logical Minimum (-16385),       */ +	0x26, 0x00, 0x40,   /*      Logical Maximum (16384),        */ +	0x95, 0x03,         /*      Report Count (3),               */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x06, 0x00, 0xFF,   /*      Usage Page (FF00h),             */ +	0x09, 0x20,         /*      Usage (20h),                    */ +	0x15, 0x00,         /*      Logical Minimum (0),            */ +	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x31,         /*      Report Count (51),              */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x21,         /*      Usage (21h),                    */ +	0x75, 0x08,         /*      Report Size (8),                */ +	0x95, 0x4D,         /*      Report Count (77),              */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x12,         /*      Report ID (18),                 */ +	0x09, 0x22,         /*      Usage (22h),                    */ +	0x95, 0x8D,         /*      Report Count (141),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x23,         /*      Usage (23h),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x13,         /*      Report ID (19),                 */ +	0x09, 0x24,         /*      Usage (24h),                    */ +	0x95, 0xCD,         /*      Report Count (205),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x25,         /*      Usage (25h),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x14,         /*      Report ID (20),                 */ +	0x09, 0x26,         /*      Usage (26h),                    */ +	0x96, 0x0D, 0x01,   /*      Report Count (269),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x27,         /*      Usage (27h),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x15,         /*      Report ID (21),                 */ +	0x09, 0x28,         /*      Usage (28h),                    */ +	0x96, 0x4D, 0x01,   /*      Report Count (333),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x29,         /*      Usage (29h),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x16,         /*      Report ID (22),                 */ +	0x09, 0x2A,         /*      Usage (2Ah),                    */ +	0x96, 0x8D, 0x01,   /*      Report Count (397),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x2B,         /*      Usage (2Bh),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x17,         /*      Report ID (23),                 */ +	0x09, 0x2C,         /*      Usage (2Ch),                    */ +	0x96, 0xCD, 0x01,   /*      Report Count (461),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x2D,         /*      Usage (2Dh),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x18,         /*      Report ID (24),                 */ +	0x09, 0x2E,         /*      Usage (2Eh),                    */ +	0x96, 0x0D, 0x02,   /*      Report Count (525),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x2F,         /*      Usage (2Fh),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x85, 0x19,         /*      Report ID (25),                 */ +	0x09, 0x30,         /*      Usage (30h),                    */ +	0x96, 0x22, 0x02,   /*      Report Count (546),             */ +	0x81, 0x02,         /*      Input (Variable),               */ +	0x09, 0x31,         /*      Usage (31h),                    */ +	0x91, 0x02,         /*      Output (Variable),              */ +	0x06, 0x80, 0xFF,   /*      Usage Page (FF80h),             */ +	0x85, 0x82,         /*      Report ID (130),                */ +	0x09, 0x22,         /*      Usage (22h),                    */ +	0x95, 0x3F,         /*      Report Count (63),              */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x83,         /*      Report ID (131),                */ +	0x09, 0x23,         /*      Usage (23h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x84,         /*      Report ID (132),                */ +	0x09, 0x24,         /*      Usage (24h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x90,         /*      Report ID (144),                */ +	0x09, 0x30,         /*      Usage (30h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x91,         /*      Report ID (145),                */ +	0x09, 0x31,         /*      Usage (31h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x92,         /*      Report ID (146),                */ +	0x09, 0x32,         /*      Usage (32h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0x93,         /*      Report ID (147),                */ +	0x09, 0x33,         /*      Usage (33h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA0,         /*      Report ID (160),                */ +	0x09, 0x40,         /*      Usage (40h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0x85, 0xA4,         /*      Report ID (164),                */ +	0x09, 0x44,         /*      Usage (44h),                    */ +	0xB1, 0x02,         /*      Feature (Variable),             */ +	0xC0                /*  End Collection                      */ +}; +  static __u8 ps3remote_rdesc[] = {  	0x05, 0x01,          /* GUsagePage Generic Desktop */  	0x09, 0x05,          /* LUsage 0x05 [Game Pad] */ @@ -184,7 +672,8 @@ static const unsigned int ps3remote_keymap_remote_buttons[] = {  };  static const unsigned int buzz_keymap[] = { -	/* The controller has 4 remote buzzers, each with one LED and 5 +	/* +	 * The controller has 4 remote buzzers, each with one LED and 5  	 * buttons.  	 *   	 * We use the mapping chosen by the controller, which is: @@ -222,15 +711,71 @@ static const unsigned int buzz_keymap[] = {  	[20] = BTN_TRIGGER_HAPPY20,  }; -struct sony_sc { -	unsigned long quirks; +static enum power_supply_property sony_battery_props[] = { +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_SCOPE, +	POWER_SUPPLY_PROP_STATUS, +}; -	void *extra; +struct sixaxis_led { +	__u8 time_enabled; /* the total time the led is active (0xff means forever) */ +	__u8 duty_length;  /* how long a cycle is in deciseconds (0 means "really fast") */ +	__u8 enabled; +	__u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */ +	__u8 duty_on;  /* % of duty_length the led is on (0xff mean 100%) */ +} __packed; + +struct sixaxis_rumble { +	__u8 padding; +	__u8 right_duration; /* Right motor duration (0xff means forever) */ +	__u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */ +	__u8 left_duration;    /* Left motor duration (0xff means forever) */ +	__u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */ +} __packed; + +struct sixaxis_output_report { +	__u8 report_id; +	struct sixaxis_rumble rumble; +	__u8 padding[4]; +	__u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */ +	struct sixaxis_led led[4];    /* LEDx at (4 - x) */ +	struct sixaxis_led _reserved; /* LED5, not actually soldered */ +} __packed; + +union sixaxis_output_report_01 { +	struct sixaxis_output_report data; +	__u8 buf[36];  }; -struct buzz_extra { -	int led_state; -	struct led_classdev *leds[4]; +static spinlock_t sony_dev_list_lock; +static LIST_HEAD(sony_device_list); +static DEFINE_IDA(sony_device_id_allocator); + +struct sony_sc { +	spinlock_t lock; +	struct list_head list_node; +	struct hid_device *hdev; +	struct led_classdev *leds[MAX_LEDS]; +	unsigned long quirks; +	struct work_struct state_worker; +	struct power_supply battery; +	int device_id; + +#ifdef CONFIG_SONY_FF +	__u8 left; +	__u8 right; +#endif + +	__u8 mac_address[6]; +	__u8 worker_initialized; +	__u8 cable_state; +	__u8 battery_charging; +	__u8 battery_capacity; +	__u8 led_state[MAX_LEDS]; +	__u8 led_delay_on[MAX_LEDS]; +	__u8 led_delay_off[MAX_LEDS]; +	__u8 led_count;  };  static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -297,6 +842,21 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,  		rdesc[55] = 0x06;  	} +	/* +	 * The default Dualshock 4 USB descriptor doesn't assign +	 * the gyroscope values to corresponding axes so we need a +	 * modified one. +	 */ +	if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && *rsize == 467) { +		hid_info(hdev, "Using modified Dualshock 4 report descriptor with gyroscope axes\n"); +		rdesc = dualshock4_usb_rdesc; +		*rsize = sizeof(dualshock4_usb_rdesc); +	} else if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) && *rsize == 357) { +		hid_info(hdev, "Using modified Dualshock 4 Bluetooth report descriptor\n"); +		rdesc = dualshock4_bt_rdesc; +		*rsize = sizeof(dualshock4_bt_rdesc); +	} +  	/* The HID descriptor exposed over BT has a trailing zero byte */  	if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) ||  			((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) && @@ -318,20 +878,127 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc,  	return rdesc;  } +static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size) +{ +	static const __u8 sixaxis_battery_capacity[] = { 0, 1, 25, 50, 75, 100 }; +	unsigned long flags; +	__u8 cable_state, battery_capacity, battery_charging; + +	/* +	 * The sixaxis is charging if the battery value is 0xee +	 * and it is fully charged if the value is 0xef. +	 * It does not report the actual level while charging so it +	 * is set to 100% while charging is in progress. +	 */ +	if (rd[30] >= 0xee) { +		battery_capacity = 100; +		battery_charging = !(rd[30] & 0x01); +	} else { +		__u8 index = rd[30] <= 5 ? rd[30] : 5; +		battery_capacity = sixaxis_battery_capacity[index]; +		battery_charging = 0; +	} +	cable_state = !(rd[31] & 0x04); + +	spin_lock_irqsave(&sc->lock, flags); +	sc->cable_state = cable_state; +	sc->battery_capacity = battery_capacity; +	sc->battery_charging = battery_charging; +	spin_unlock_irqrestore(&sc->lock, flags); +} + +static void dualshock4_parse_report(struct sony_sc *sc, __u8 *rd, int size) +{ +	struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, +						struct hid_input, list); +	struct input_dev *input_dev = hidinput->input; +	unsigned long flags; +	int n, offset; +	__u8 cable_state, battery_capacity, battery_charging; + +	/* +	 * Battery and touchpad data starts at byte 30 in the USB report and +	 * 32 in Bluetooth report. +	 */ +	offset = (sc->quirks & DUALSHOCK4_CONTROLLER_USB) ? 30 : 32; + +	/* +	 * The lower 4 bits of byte 30 contain the battery level +	 * and the 5th bit contains the USB cable state. +	 */ +	cable_state = (rd[offset] >> 4) & 0x01; +	battery_capacity = rd[offset] & 0x0F; + +	/* +	 * When a USB power source is connected the battery level ranges from +	 * 0 to 10, and when running on battery power it ranges from 0 to 9. +	 * A battery level above 10 when plugged in means charge completed. +	 */ +	if (!cable_state || battery_capacity > 10) +		battery_charging = 0; +	else +		battery_charging = 1; + +	if (!cable_state) +		battery_capacity++; +	if (battery_capacity > 10) +		battery_capacity = 10; + +	battery_capacity *= 10; + +	spin_lock_irqsave(&sc->lock, flags); +	sc->cable_state = cable_state; +	sc->battery_capacity = battery_capacity; +	sc->battery_charging = battery_charging; +	spin_unlock_irqrestore(&sc->lock, flags); + +	offset += 5; + +	/* +	 * The Dualshock 4 multi-touch trackpad data starts at offset 35 on USB +	 * and 37 on Bluetooth. +	 * The first 7 bits of the first byte is a counter and bit 8 is a touch +	 * indicator that is 0 when pressed and 1 when not pressed. +	 * The next 3 bytes are two 12 bit touch coordinates, X and Y. +	 * The data for the second touch is in the same format and immediatly +	 * follows the data for the first. +	 */ +	for (n = 0; n < 2; n++) { +		__u16 x, y; + +		x = rd[offset+1] | ((rd[offset+2] & 0xF) << 8); +		y = ((rd[offset+2] & 0xF0) >> 4) | (rd[offset+3] << 4); + +		input_mt_slot(input_dev, n); +		input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, +					!(rd[offset] >> 7)); +		input_report_abs(input_dev, ABS_MT_POSITION_X, x); +		input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + +		offset += 4; +	} +} +  static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,  		__u8 *rd, int size)  {  	struct sony_sc *sc = hid_get_drvdata(hdev); -	/* Sixaxis HID report has acclerometers/gyro with MSByte first, this +	/* +	 * Sixaxis HID report has acclerometers/gyro with MSByte first, this  	 * has to be BYTE_SWAPPED before passing up to joystick interface  	 */ -	if ((sc->quirks & (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)) && -			rd[0] == 0x01 && size == 49) { +	if ((sc->quirks & SIXAXIS_CONTROLLER) && rd[0] == 0x01 && size == 49) {  		swap(rd[41], rd[42]);  		swap(rd[43], rd[44]);  		swap(rd[45], rd[46]);  		swap(rd[47], rd[48]); + +		sixaxis_parse_report(sc, rd, size); +	} else if (((sc->quirks & DUALSHOCK4_CONTROLLER_USB) && rd[0] == 0x01 && +			size == 64) || ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) +			&& rd[0] == 0x11 && size == 78)) { +		dualshock4_parse_report(sc, rd, size);  	}  	return 0; @@ -374,66 +1041,21 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,  }  /* - * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP - * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() - * so we need to override that forcing HID Output Reports on the Control EP. - * - * There is also another issue about HID Output Reports via USB, the Sixaxis - * does not want the report_id as part of the data packet, so we have to - * discard buf[0] when sending the actual control message, even for numbered - * reports, humpf! - */ -static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, -		size_t count, unsigned char report_type) -{ -	struct usb_interface *intf = to_usb_interface(hid->dev.parent); -	struct usb_device *dev = interface_to_usbdev(intf); -	struct usb_host_interface *interface = intf->cur_altsetting; -	int report_id = buf[0]; -	int ret; - -	if (report_type == HID_OUTPUT_REPORT) { -		/* Don't send the Report ID */ -		buf++; -		count--; -	} - -	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), -		HID_REQ_SET_REPORT, -		USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, -		((report_type + 1) << 8) | report_id, -		interface->desc.bInterfaceNumber, buf, count, -		USB_CTRL_SET_TIMEOUT); - -	/* Count also the Report ID, in case of an Output report. */ -	if (ret > 0 && report_type == HID_OUTPUT_REPORT) -		ret++; - -	return ret; -} - -/*   * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller   * to "operational".  Without this, the ps3 controller will not report any   * events.   */  static int sixaxis_set_operational_usb(struct hid_device *hdev)  { -	struct usb_interface *intf = to_usb_interface(hdev->dev.parent); -	struct usb_device *dev = interface_to_usbdev(intf); -	__u16 ifnum = intf->cur_altsetting->desc.bInterfaceNumber;  	int ret;  	char *buf = kmalloc(18, GFP_KERNEL);  	if (!buf)  		return -ENOMEM; -	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), -				 HID_REQ_GET_REPORT, -				 USB_DIR_IN | USB_TYPE_CLASS | -				 USB_RECIP_INTERFACE, -				 (3 << 8) | 0xf2, ifnum, buf, 17, -				 USB_CTRL_GET_TIMEOUT); +	ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT, +				 HID_REQ_GET_REPORT); +  	if (ret < 0)  		hid_err(hdev, "can't set operational mode\n"); @@ -445,10 +1067,69 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)  static int sixaxis_set_operational_bt(struct hid_device *hdev)  {  	unsigned char buf[] = { 0xf4,  0x42, 0x03, 0x00, 0x00 }; -	return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); +	return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), +				  HID_FEATURE_REPORT, HID_REQ_SET_REPORT); +} + +/* + * Requesting feature report 0x02 in Bluetooth mode changes the state of the + * controller so that it sends full input reports of type 0x11. + */ +static int dualshock4_set_operational_bt(struct hid_device *hdev) +{ +	__u8 buf[37] = { 0 }; + +	return hid_hw_raw_request(hdev, 0x02, buf, sizeof(buf), +				HID_FEATURE_REPORT, HID_REQ_GET_REPORT); +} + +static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS]) +{ +	static const __u8 sixaxis_leds[10][4] = { +				{ 0x01, 0x00, 0x00, 0x00 }, +				{ 0x00, 0x01, 0x00, 0x00 }, +				{ 0x00, 0x00, 0x01, 0x00 }, +				{ 0x00, 0x00, 0x00, 0x01 }, +				{ 0x01, 0x00, 0x00, 0x01 }, +				{ 0x00, 0x01, 0x00, 0x01 }, +				{ 0x00, 0x00, 0x01, 0x01 }, +				{ 0x01, 0x00, 0x01, 0x01 }, +				{ 0x00, 0x01, 0x01, 0x01 }, +				{ 0x01, 0x01, 0x01, 0x01 } +	}; + +	BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0])); + +	if (id < 0) +		return; + +	id %= 10; +	memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));  } -static void buzz_set_leds(struct hid_device *hdev, int leds) +static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS]) +{ +	/* The first 4 color/index entries match what the PS4 assigns */ +	static const __u8 color_code[7][3] = { +			/* Blue   */	{ 0x00, 0x00, 0x01 }, +			/* Red	  */	{ 0x01, 0x00, 0x00 }, +			/* Green  */	{ 0x00, 0x01, 0x00 }, +			/* Pink   */	{ 0x02, 0x00, 0x01 }, +			/* Orange */	{ 0x02, 0x01, 0x00 }, +			/* Teal   */	{ 0x00, 0x01, 0x01 }, +			/* White  */	{ 0x01, 0x01, 0x01 } +	}; + +	BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0])); + +	if (id < 0) +		return; + +	id %= 7; +	memcpy(values, color_code[id], sizeof(color_code[id])); +} + +static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)  {  	struct list_head *report_list =  		&hdev->report_enum[HID_OUTPUT_REPORT].report_list; @@ -457,168 +1138,671 @@ static void buzz_set_leds(struct hid_device *hdev, int leds)  	__s32 *value = report->field[0]->value;  	value[0] = 0x00; -	value[1] = (leds & 1) ? 0xff : 0x00; -	value[2] = (leds & 2) ? 0xff : 0x00; -	value[3] = (leds & 4) ? 0xff : 0x00; -	value[4] = (leds & 8) ? 0xff : 0x00; +	value[1] = leds[0] ? 0xff : 0x00; +	value[2] = leds[1] ? 0xff : 0x00; +	value[3] = leds[2] ? 0xff : 0x00; +	value[4] = leds[3] ? 0xff : 0x00;  	value[5] = 0x00;  	value[6] = 0x00;  	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);  } -static void buzz_led_set_brightness(struct led_classdev *led, +static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count) +{ +	int n; + +	BUG_ON(count > MAX_LEDS); + +	if (sc->quirks & BUZZ_CONTROLLER && count == 4) { +		buzz_set_leds(sc->hdev, leds); +	} else { +		for (n = 0; n < count; n++) +			sc->led_state[n] = leds[n]; +		schedule_work(&sc->state_worker); +	} +} + +static void sony_led_set_brightness(struct led_classdev *led,  				    enum led_brightness value)  {  	struct device *dev = led->dev->parent;  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);  	struct sony_sc *drv_data; -	struct buzz_extra *buzz;  	int n; +	int force_update;  	drv_data = hid_get_drvdata(hdev); -	if (!drv_data || !drv_data->extra) { +	if (!drv_data) {  		hid_err(hdev, "No device data\n");  		return;  	} -	buzz = drv_data->extra; -	for (n = 0; n < 4; n++) { -		if (led == buzz->leds[n]) { -			int on = !! (buzz->led_state & (1 << n)); -			if (value == LED_OFF && on) { -				buzz->led_state &= ~(1 << n); -				buzz_set_leds(hdev, buzz->led_state); -			} else if (value != LED_OFF && !on) { -				buzz->led_state |= (1 << n); -				buzz_set_leds(hdev, buzz->led_state); -			} +	/* +	 * The Sixaxis on USB will override any LED settings sent to it +	 * and keep flashing all of the LEDs until the PS button is pressed. +	 * Updates, even if redundant, must be always be sent to the +	 * controller to avoid having to toggle the state of an LED just to +	 * stop the flashing later on. +	 */ +	force_update = !!(drv_data->quirks & SIXAXIS_CONTROLLER_USB); + +	for (n = 0; n < drv_data->led_count; n++) { +		if (led == drv_data->leds[n] && (force_update || +			(value != drv_data->led_state[n] || +			drv_data->led_delay_on[n] || +			drv_data->led_delay_off[n]))) { + +			drv_data->led_state[n] = value; + +			/* Setting the brightness stops the blinking */ +			drv_data->led_delay_on[n] = 0; +			drv_data->led_delay_off[n] = 0; + +			sony_set_leds(drv_data, drv_data->led_state, +					drv_data->led_count);  			break;  		}  	}  } -static enum led_brightness buzz_led_get_brightness(struct led_classdev *led) +static enum led_brightness sony_led_get_brightness(struct led_classdev *led)  {  	struct device *dev = led->dev->parent;  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);  	struct sony_sc *drv_data; -	struct buzz_extra *buzz;  	int n; -	int on = 0;  	drv_data = hid_get_drvdata(hdev); -	if (!drv_data || !drv_data->extra) { +	if (!drv_data) {  		hid_err(hdev, "No device data\n");  		return LED_OFF;  	} -	buzz = drv_data->extra; -	for (n = 0; n < 4; n++) { -		if (led == buzz->leds[n]) { -			on = !! (buzz->led_state & (1 << n)); +	for (n = 0; n < drv_data->led_count; n++) { +		if (led == drv_data->leds[n]) +			return drv_data->led_state[n]; +	} + +	return LED_OFF; +} + +static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, +				unsigned long *delay_off) +{ +	struct device *dev = led->dev->parent; +	struct hid_device *hdev = container_of(dev, struct hid_device, dev); +	struct sony_sc *drv_data = hid_get_drvdata(hdev); +	int n; +	__u8 new_on, new_off; + +	if (!drv_data) { +		hid_err(hdev, "No device data\n"); +		return -EINVAL; +	} + +	/* Max delay is 255 deciseconds or 2550 milliseconds */ +	if (*delay_on > 2550) +		*delay_on = 2550; +	if (*delay_off > 2550) +		*delay_off = 2550; + +	/* Blink at 1 Hz if both values are zero */ +	if (!*delay_on && !*delay_off) +		*delay_on = *delay_off = 500; + +	new_on = *delay_on / 10; +	new_off = *delay_off / 10; + +	for (n = 0; n < drv_data->led_count; n++) { +		if (led == drv_data->leds[n])  			break; -		}  	} -	return on ? LED_FULL : LED_OFF; +	/* This LED is not registered on this device */ +	if (n >= drv_data->led_count) +		return -EINVAL; + +	/* Don't schedule work if the values didn't change */ +	if (new_on != drv_data->led_delay_on[n] || +		new_off != drv_data->led_delay_off[n]) { +		drv_data->led_delay_on[n] = new_on; +		drv_data->led_delay_off[n] = new_off; +		schedule_work(&drv_data->state_worker); +	} + +	return 0;  } -static int buzz_init(struct hid_device *hdev) +static void sony_leds_remove(struct sony_sc *sc)  { -	struct sony_sc *drv_data; -	struct buzz_extra *buzz; +	struct led_classdev *led; +	int n; + +	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); + +	for (n = 0; n < sc->led_count; n++) { +		led = sc->leds[n]; +		sc->leds[n] = NULL; +		if (!led) +			continue; +		led_classdev_unregister(led); +		kfree(led); +	} + +	sc->led_count = 0; +} + +static int sony_leds_init(struct sony_sc *sc) +{ +	struct hid_device *hdev = sc->hdev;  	int n, ret = 0; +	int use_ds4_names;  	struct led_classdev *led;  	size_t name_sz;  	char *name; +	size_t name_len; +	const char *name_fmt; +	static const char * const ds4_name_str[] = { "red", "green", "blue", +						  "global" }; +	__u8 initial_values[MAX_LEDS] = { 0 }; +	__u8 max_brightness[MAX_LEDS] = { 1 }; +	__u8 use_hw_blink[MAX_LEDS] = { 0 }; -	drv_data = hid_get_drvdata(hdev); -	BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); - -	/* Validate expected report characteristics. */ -	if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7)) -		return -ENODEV; +	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); -	buzz = kzalloc(sizeof(*buzz), GFP_KERNEL); -	if (!buzz) { -		hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); -		return -ENOMEM; +	if (sc->quirks & BUZZ_CONTROLLER) { +		sc->led_count = 4; +		use_ds4_names = 0; +		name_len = strlen("::buzz#"); +		name_fmt = "%s::buzz%d"; +		/* Validate expected report characteristics. */ +		if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7)) +			return -ENODEV; +	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) { +		dualshock4_set_leds_from_id(sc->device_id, initial_values); +		initial_values[3] = 1; +		sc->led_count = 4; +		memset(max_brightness, 255, 3); +		use_hw_blink[3] = 1; +		use_ds4_names = 1; +		name_len = 0; +		name_fmt = "%s:%s"; +	} else { +		sixaxis_set_leds_from_id(sc->device_id, initial_values); +		sc->led_count = 4; +		memset(use_hw_blink, 1, 4); +		use_ds4_names = 0; +		name_len = strlen("::sony#"); +		name_fmt = "%s::sony%d";  	} -	drv_data->extra = buzz; -	/* Clear LEDs as we have no way of reading their initial state. This is +	/* +	 * Clear LEDs as we have no way of reading their initial state. This is  	 * only relevant if the driver is loaded after somebody actively set the -	 * LEDs to on */ -	buzz_set_leds(hdev, 0x00); +	 * LEDs to on +	 */ +	sony_set_leds(sc, initial_values, sc->led_count); -	name_sz = strlen(dev_name(&hdev->dev)) + strlen("::buzz#") + 1; +	name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1; + +	for (n = 0; n < sc->led_count; n++) { + +		if (use_ds4_names) +			name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2; -	for (n = 0; n < 4; n++) {  		led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);  		if (!led) {  			hid_err(hdev, "Couldn't allocate memory for LED %d\n", n); +			ret = -ENOMEM;  			goto error_leds;  		}  		name = (void *)(&led[1]); -		snprintf(name, name_sz, "%s::buzz%d", dev_name(&hdev->dev), n + 1); +		if (use_ds4_names) +			snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), +			ds4_name_str[n]); +		else +			snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);  		led->name = name; -		led->brightness = 0; -		led->max_brightness = 1; -		led->brightness_get = buzz_led_get_brightness; -		led->brightness_set = buzz_led_set_brightness; +		led->brightness = initial_values[n]; +		led->max_brightness = max_brightness[n]; +		led->brightness_get = sony_led_get_brightness; +		led->brightness_set = sony_led_set_brightness; + +		if (use_hw_blink[n]) +			led->blink_set = sony_led_blink_set; -		if (led_classdev_register(&hdev->dev, led)) { +		sc->leds[n] = led; + +		ret = led_classdev_register(&hdev->dev, led); +		if (ret) {  			hid_err(hdev, "Failed to register LED %d\n", n); +			sc->leds[n] = NULL;  			kfree(led);  			goto error_leds;  		} - -		buzz->leds[n] = led;  	}  	return ret;  error_leds: -	for (n = 0; n < 4; n++) { -		led = buzz->leds[n]; -		buzz->leds[n] = NULL; -		if (!led) -			continue; -		led_classdev_unregister(led); -		kfree(led); -	} +	sony_leds_remove(sc); -	kfree(drv_data->extra); -	drv_data->extra = NULL;  	return ret;  } -static void buzz_remove(struct hid_device *hdev) +static void sixaxis_state_worker(struct work_struct *work)  { -	struct sony_sc *drv_data; -	struct buzz_extra *buzz; -	struct led_classdev *led; +	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);  	int n; +	union sixaxis_output_report_01 report = { +		.buf = { +			0x01, +			0x00, 0xff, 0x00, 0xff, 0x00, +			0x00, 0x00, 0x00, 0x00, 0x00, +			0xff, 0x27, 0x10, 0x00, 0x32, +			0xff, 0x27, 0x10, 0x00, 0x32, +			0xff, 0x27, 0x10, 0x00, 0x32, +			0xff, 0x27, 0x10, 0x00, 0x32, +			0x00, 0x00, 0x00, 0x00, 0x00 +		} +	}; -	drv_data = hid_get_drvdata(hdev); -	BUG_ON(!(drv_data->quirks & BUZZ_CONTROLLER)); +#ifdef CONFIG_SONY_FF +	report.data.rumble.right_motor_on = sc->right ? 1 : 0; +	report.data.rumble.left_motor_force = sc->left; +#endif -	buzz = drv_data->extra; +	report.data.leds_bitmap |= sc->led_state[0] << 1; +	report.data.leds_bitmap |= sc->led_state[1] << 2; +	report.data.leds_bitmap |= sc->led_state[2] << 3; +	report.data.leds_bitmap |= sc->led_state[3] << 4; +	/* Set flag for all leds off, required for 3rd party INTEC controller */ +	if ((report.data.leds_bitmap & 0x1E) == 0) +		report.data.leds_bitmap |= 0x20; + +	/* +	 * The LEDs in the report are indexed in reverse order to their +	 * corresponding light on the controller. +	 * Index 0 = LED 4, index 1 = LED 3, etc... +	 * +	 * In the case of both delay values being zero (blinking disabled) the +	 * default report values should be used or the controller LED will be +	 * always off. +	 */  	for (n = 0; n < 4; n++) { -		led = buzz->leds[n]; -		buzz->leds[n] = NULL; -		if (!led) -			continue; -		led_classdev_unregister(led); -		kfree(led); +		if (sc->led_delay_on[n] || sc->led_delay_off[n]) { +			report.data.led[3 - n].duty_off = sc->led_delay_off[n]; +			report.data.led[3 - n].duty_on = sc->led_delay_on[n]; +		} +	} + +	hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf, +			sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); +} + +static void dualshock4_state_worker(struct work_struct *work) +{ +	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); +	struct hid_device *hdev = sc->hdev; +	int offset; + +	__u8 buf[78] = { 0 }; + +	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { +		buf[0] = 0x05; +		buf[1] = 0xFF; +		offset = 4; +	} else { +		buf[0] = 0x11; +		buf[1] = 0xB0; +		buf[3] = 0x0F; +		offset = 6; +	} + +#ifdef CONFIG_SONY_FF +	buf[offset++] = sc->right; +	buf[offset++] = sc->left; +#else +	offset += 2; +#endif + +	/* LED 3 is the global control */ +	if (sc->led_state[3]) { +		buf[offset++] = sc->led_state[0]; +		buf[offset++] = sc->led_state[1]; +		buf[offset++] = sc->led_state[2]; +	} else { +		offset += 3; +	} + +	/* If both delay values are zero the DualShock 4 disables blinking. */ +	buf[offset++] = sc->led_delay_on[3]; +	buf[offset++] = sc->led_delay_off[3]; + +	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) +		hid_hw_output_report(hdev, buf, 32); +	else +		hid_hw_raw_request(hdev, 0x11, buf, 78, +				HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); +} + +#ifdef CONFIG_SONY_FF +static int sony_play_effect(struct input_dev *dev, void *data, +			    struct ff_effect *effect) +{ +	struct hid_device *hid = input_get_drvdata(dev); +	struct sony_sc *sc = hid_get_drvdata(hid); + +	if (effect->type != FF_RUMBLE) +		return 0; + +	sc->left = effect->u.rumble.strong_magnitude / 256; +	sc->right = effect->u.rumble.weak_magnitude / 256; + +	schedule_work(&sc->state_worker); +	return 0; +} + +static int sony_init_ff(struct sony_sc *sc) +{ +	struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, +						struct hid_input, list); +	struct input_dev *input_dev = hidinput->input; + +	input_set_capability(input_dev, EV_FF, FF_RUMBLE); +	return input_ff_create_memless(input_dev, NULL, sony_play_effect); +} + +#else +static int sony_init_ff(struct sony_sc *sc) +{ +	return 0; +} + +#endif + +static int sony_battery_get_property(struct power_supply *psy, +				     enum power_supply_property psp, +				     union power_supply_propval *val) +{ +	struct sony_sc *sc = container_of(psy, struct sony_sc, battery); +	unsigned long flags; +	int ret = 0; +	u8 battery_charging, battery_capacity, cable_state; + +	spin_lock_irqsave(&sc->lock, flags); +	battery_charging = sc->battery_charging; +	battery_capacity = sc->battery_capacity; +	cable_state = sc->cable_state; +	spin_unlock_irqrestore(&sc->lock, flags); + +	switch (psp) { +	case POWER_SUPPLY_PROP_PRESENT: +		val->intval = 1; +		break; +	case POWER_SUPPLY_PROP_SCOPE: +		val->intval = POWER_SUPPLY_SCOPE_DEVICE; +		break; +	case POWER_SUPPLY_PROP_CAPACITY: +		val->intval = battery_capacity; +		break; +	case POWER_SUPPLY_PROP_STATUS: +		if (battery_charging) +			val->intval = POWER_SUPPLY_STATUS_CHARGING; +		else +			if (battery_capacity == 100 && cable_state) +				val->intval = POWER_SUPPLY_STATUS_FULL; +			else +				val->intval = POWER_SUPPLY_STATUS_DISCHARGING; +		break; +	default: +		ret = -EINVAL; +		break; +	} +	return ret; +} + +static int sony_battery_probe(struct sony_sc *sc) +{ +	struct hid_device *hdev = sc->hdev; +	int ret; + +	/* +	 * Set the default battery level to 100% to avoid low battery warnings +	 * if the battery is polled before the first device report is received. +	 */ +	sc->battery_capacity = 100; + +	sc->battery.properties = sony_battery_props; +	sc->battery.num_properties = ARRAY_SIZE(sony_battery_props); +	sc->battery.get_property = sony_battery_get_property; +	sc->battery.type = POWER_SUPPLY_TYPE_BATTERY; +	sc->battery.use_for_apm = 0; +	sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR", +				     sc->mac_address); +	if (!sc->battery.name) +		return -ENOMEM; + +	ret = power_supply_register(&hdev->dev, &sc->battery); +	if (ret) { +		hid_err(hdev, "Unable to register battery device\n"); +		goto err_free; +	} + +	power_supply_powers(&sc->battery, &hdev->dev); +	return 0; + +err_free: +	kfree(sc->battery.name); +	sc->battery.name = NULL; +	return ret; +} + +static void sony_battery_remove(struct sony_sc *sc) +{ +	if (!sc->battery.name) +		return; + +	power_supply_unregister(&sc->battery); +	kfree(sc->battery.name); +	sc->battery.name = NULL; +} + +static int sony_register_touchpad(struct sony_sc *sc, int touch_count, +					int w, int h) +{ +	struct hid_input *hidinput = list_entry(sc->hdev->inputs.next, +						struct hid_input, list); +	struct input_dev *input_dev = hidinput->input; +	int ret; + +	ret = input_mt_init_slots(input_dev, touch_count, 0); +	if (ret < 0) { +		hid_err(sc->hdev, "Unable to initialize multi-touch slots\n"); +		return ret; +	} + +	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, w, 0, 0); +	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, h, 0, 0); + +	return 0; +} + +/* + * If a controller is plugged in via USB while already connected via Bluetooth + * it will show up as two devices. A global list of connected controllers and + * their MAC addresses is maintained to ensure that a device is only connected + * once. + */ +static int sony_check_add_dev_list(struct sony_sc *sc) +{ +	struct sony_sc *entry; +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&sony_dev_list_lock, flags); + +	list_for_each_entry(entry, &sony_device_list, list_node) { +		ret = memcmp(sc->mac_address, entry->mac_address, +				sizeof(sc->mac_address)); +		if (!ret) { +			ret = -EEXIST; +			hid_info(sc->hdev, "controller with MAC address %pMR already connected\n", +				sc->mac_address); +			goto unlock; +		}  	} -	kfree(drv_data->extra); -	drv_data->extra = NULL; +	ret = 0; +	list_add(&(sc->list_node), &sony_device_list); + +unlock: +	spin_unlock_irqrestore(&sony_dev_list_lock, flags); +	return ret; +} + +static void sony_remove_dev_list(struct sony_sc *sc) +{ +	unsigned long flags; + +	if (sc->list_node.next) { +		spin_lock_irqsave(&sony_dev_list_lock, flags); +		list_del(&(sc->list_node)); +		spin_unlock_irqrestore(&sony_dev_list_lock, flags); +	} +} + +static int sony_get_bt_devaddr(struct sony_sc *sc) +{ +	int ret; + +	/* HIDP stores the device MAC address as a string in the uniq field. */ +	ret = strlen(sc->hdev->uniq); +	if (ret != 17) +		return -EINVAL; + +	ret = sscanf(sc->hdev->uniq, +		"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", +		&sc->mac_address[5], &sc->mac_address[4], &sc->mac_address[3], +		&sc->mac_address[2], &sc->mac_address[1], &sc->mac_address[0]); + +	if (ret != 6) +		return -EINVAL; + +	return 0; +} + +static int sony_check_add(struct sony_sc *sc) +{ +	int n, ret; + +	if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) || +	    (sc->quirks & SIXAXIS_CONTROLLER_BT)) { +		/* +		 * sony_get_bt_devaddr() attempts to parse the Bluetooth MAC +		 * address from the uniq string where HIDP stores it. +		 * As uniq cannot be guaranteed to be a MAC address in all cases +		 * a failure of this function should not prevent the connection. +		 */ +		if (sony_get_bt_devaddr(sc) < 0) { +			hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n"); +			return 0; +		} +	} else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { +		__u8 buf[7]; + +		/* +		 * The MAC address of a DS4 controller connected via USB can be +		 * retrieved with feature report 0x81. The address begins at +		 * offset 1. +		 */ +		ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf), +				HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + +		if (ret != 7) { +			hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n"); +			return ret < 0 ? ret : -EINVAL; +		} + +		memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address)); +	} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) { +		__u8 buf[18]; + +		/* +		 * The MAC address of a Sixaxis controller connected via USB can +		 * be retrieved with feature report 0xf2. The address begins at +		 * offset 4. +		 */ +		ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf), +				HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + +		if (ret != 18) { +			hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n"); +			return ret < 0 ? ret : -EINVAL; +		} + +		/* +		 * The Sixaxis device MAC in the report is big-endian and must +		 * be byte-swapped. +		 */ +		for (n = 0; n < 6; n++) +			sc->mac_address[5-n] = buf[4+n]; +	} else { +		return 0; +	} + +	return sony_check_add_dev_list(sc); +} + +static int sony_set_device_id(struct sony_sc *sc) +{ +	int ret; + +	/* +	 * Only DualShock 4 or Sixaxis controllers get an id. +	 * All others are set to -1. +	 */ +	if ((sc->quirks & SIXAXIS_CONTROLLER) || +	    (sc->quirks & DUALSHOCK4_CONTROLLER)) { +		ret = ida_simple_get(&sony_device_id_allocator, 0, 0, +					GFP_KERNEL); +		if (ret < 0) { +			sc->device_id = -1; +			return ret; +		} +		sc->device_id = ret; +	} else { +		sc->device_id = -1; +	} + +	return 0; +} + +static void sony_release_device_id(struct sony_sc *sc) +{ +	if (sc->device_id >= 0) { +		ida_simple_remove(&sony_device_id_allocator, sc->device_id); +		sc->device_id = -1; +	} +} + +static inline void sony_init_work(struct sony_sc *sc, +					void (*worker)(struct work_struct *)) +{ +	if (!sc->worker_initialized) +		INIT_WORK(&sc->state_worker, worker); + +	sc->worker_initialized = 1; +} + +static inline void sony_cancel_work_sync(struct sony_sc *sc) +{ +	if (sc->worker_initialized) +		cancel_work_sync(&sc->state_worker);  }  static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -636,6 +1820,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  	sc->quirks = quirks;  	hid_set_drvdata(hdev, sc); +	sc->hdev = hdev;  	ret = hid_parse(hdev);  	if (ret) { @@ -656,22 +1841,104 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  		return ret;  	} +	ret = sony_set_device_id(sc); +	if (ret < 0) { +		hid_err(hdev, "failed to allocate the device id\n"); +		goto err_stop; +	} +  	if (sc->quirks & SIXAXIS_CONTROLLER_USB) { -		hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; +		/* +		 * The Sony Sixaxis does not handle HID Output Reports on the +		 * Interrupt EP like it could, so we need to force HID Output +		 * Reports to use HID_REQ_SET_REPORT on the Control EP. +		 * +		 * There is also another issue about HID Output Reports via USB, +		 * the Sixaxis does not want the report_id as part of the data +		 * packet, so we have to discard buf[0] when sending the actual +		 * control message, even for numbered reports, humpf! +		 */ +		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; +		hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;  		ret = sixaxis_set_operational_usb(hdev); -	} -	else if (sc->quirks & SIXAXIS_CONTROLLER_BT) +		sony_init_work(sc, sixaxis_state_worker); +	} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) { +		/* +		 * The Sixaxis wants output reports sent on the ctrl endpoint +		 * when connected via Bluetooth. +		 */ +		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;  		ret = sixaxis_set_operational_bt(hdev); -	else if (sc->quirks & BUZZ_CONTROLLER) -		ret = buzz_init(hdev); -	else +		sony_init_work(sc, sixaxis_state_worker); +	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) { +		if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) { +			/* +			 * The DualShock 4 wants output reports sent on the ctrl +			 * endpoint when connected via Bluetooth. +			 */ +			hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; +			ret = dualshock4_set_operational_bt(hdev); +			if (ret < 0) { +				hid_err(hdev, "failed to set the Dualshock 4 operational mode\n"); +				goto err_stop; +			} +		} +		/* +		 * The Dualshock 4 touchpad supports 2 touches and has a +		 * resolution of 1920x940. +		 */ +		ret = sony_register_touchpad(sc, 2, 1920, 940); +		if (ret < 0) +			goto err_stop; + +		sony_init_work(sc, dualshock4_state_worker); +	} else {  		ret = 0; +	}  	if (ret < 0)  		goto err_stop; +	ret = sony_check_add(sc); +	if (ret < 0) +		goto err_stop; + +	if (sc->quirks & SONY_LED_SUPPORT) { +		ret = sony_leds_init(sc); +		if (ret < 0) +			goto err_stop; +	} + +	if (sc->quirks & SONY_BATTERY_SUPPORT) { +		ret = sony_battery_probe(sc); +		if (ret < 0) +			goto err_stop; + +		/* Open the device to receive reports with battery info */ +		ret = hid_hw_open(hdev); +		if (ret < 0) { +			hid_err(hdev, "hw open failed\n"); +			goto err_stop; +		} +	} + +	if (sc->quirks & SONY_FF_SUPPORT) { +		ret = sony_init_ff(sc); +		if (ret < 0) +			goto err_close; +	} +  	return 0; +err_close: +	hid_hw_close(hdev);  err_stop: +	if (sc->quirks & SONY_LED_SUPPORT) +		sony_leds_remove(sc); +	if (sc->quirks & SONY_BATTERY_SUPPORT) +		sony_battery_remove(sc); +	sony_cancel_work_sync(sc); +	sony_remove_dev_list(sc); +	sony_release_device_id(sc);  	hid_hw_stop(hdev);  	return ret;  } @@ -680,8 +1947,19 @@ static void sony_remove(struct hid_device *hdev)  {  	struct sony_sc *sc = hid_get_drvdata(hdev); -	if (sc->quirks & BUZZ_CONTROLLER) -		buzz_remove(hdev); +	if (sc->quirks & SONY_LED_SUPPORT) +		sony_leds_remove(sc); + +	if (sc->quirks & SONY_BATTERY_SUPPORT) { +		hid_hw_close(hdev); +		sony_battery_remove(sc); +	} + +	sony_cancel_work_sync(sc); + +	sony_remove_dev_list(sc); + +	sony_release_device_id(sc);  	hid_hw_stop(hdev);  } @@ -709,6 +1987,11 @@ static const struct hid_device_id sony_devices[] = {  	/* Logitech Harmony Adapter for PS3 */  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3),  		.driver_data = PS3REMOTE }, +	/* Sony Dualshock 4 controllers for PS4 */ +	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), +		.driver_data = DUALSHOCK4_CONTROLLER_USB }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), +		.driver_data = DUALSHOCK4_CONTROLLER_BT },  	{ }  };  MODULE_DEVICE_TABLE(hid, sony_devices); @@ -722,6 +2005,22 @@ static struct hid_driver sony_driver = {  	.report_fixup  = sony_report_fixup,  	.raw_event     = sony_raw_event  }; -module_hid_driver(sony_driver); + +static int __init sony_init(void) +{ +	dbg_hid("Sony:%s\n", __func__); + +	return hid_register_driver(&sony_driver); +} + +static void __exit sony_exit(void) +{ +	dbg_hid("Sony:%s\n", __func__); + +	ida_destroy(&sony_device_id_allocator); +	hid_unregister_driver(&sony_driver); +} +module_init(sony_init); +module_exit(sony_exit);  MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index 99342cfa0ea..134be89b15e 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -1,7 +1,7 @@  /*   * ThingM blink(1) USB RGB LED driver   * - * Copyright 2013 Savoir-faire Linux Inc. + * Copyright 2013-2014 Savoir-faire Linux Inc.   *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>   *   * This program is free software; you can redistribute it and/or @@ -10,244 +10,285 @@   */  #include <linux/hid.h> +#include <linux/hidraw.h>  #include <linux/leds.h>  #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/workqueue.h>  #include "hid-ids.h" -#define BLINK1_CMD_SIZE		9 +#define REPORT_ID	1 +#define REPORT_SIZE	9 -#define blink1_rgb_to_r(rgb)	((rgb & 0xFF0000) >> 16) -#define blink1_rgb_to_g(rgb)	((rgb & 0x00FF00) >> 8) -#define blink1_rgb_to_b(rgb)	((rgb & 0x0000FF) >> 0) +/* Firmware major number of supported devices */ +#define THINGM_MAJOR_MK1	'1' +#define THINGM_MAJOR_MK2	'2' -/** - * struct blink1_data - blink(1) device specific data - * @hdev:		HID device. - * @led_cdev:		LED class instance. - * @rgb:		8-bit per channel RGB notation. - * @fade:		fade time in hundredths of a second. - * @brightness:		brightness coefficient. - * @play:		play/pause in-memory patterns. - */ -struct blink1_data { +struct thingm_fwinfo { +	char major; +	unsigned numrgb; +	unsigned first; +}; + +static const struct thingm_fwinfo thingm_fwinfo[] = { +	{ +		.major = THINGM_MAJOR_MK1, +		.numrgb = 1, +		.first = 0, +	}, { +		.major = THINGM_MAJOR_MK2, +		.numrgb = 2, +		.first = 1, +	} +}; + +/* A red, green or blue channel, part of an RGB chip */ +struct thingm_led { +	struct thingm_rgb *rgb; +	struct led_classdev ldev; +	char name[32]; +}; + +/* Basically a WS2812 5050 RGB LED chip */ +struct thingm_rgb { +	struct thingm_device *tdev; +	struct thingm_led red; +	struct thingm_led green; +	struct thingm_led blue; +	struct work_struct work; +	u8 num; +}; + +struct thingm_device {  	struct hid_device *hdev; -	struct led_classdev led_cdev; -	u32 rgb; -	u16 fade; -	u8 brightness; -	bool play; +	struct { +		char major; +		char minor; +	} version; +	const struct thingm_fwinfo *fwinfo; +	struct mutex lock; +	struct thingm_rgb *rgb;  }; -static int blink1_send_command(struct blink1_data *data, -		u8 buf[BLINK1_CMD_SIZE]) +static int thingm_send(struct thingm_device *tdev, u8 buf[REPORT_SIZE])  {  	int ret; -	hid_dbg(data->hdev, "command: %d%c%.2x%.2x%.2x%.2x%.2x%.2x%.2x\n", +	hid_dbg(tdev->hdev, "-> %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n",  			buf[0], buf[1], buf[2], buf[3], buf[4],  			buf[5], buf[6], buf[7], buf[8]); -	ret = data->hdev->hid_output_raw_report(data->hdev, buf, -			BLINK1_CMD_SIZE, HID_FEATURE_REPORT); +	ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, +			HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  	return ret < 0 ? ret : 0;  } -static int blink1_update_color(struct blink1_data *data) +static int thingm_recv(struct thingm_device *tdev, u8 buf[REPORT_SIZE])  { -	u8 buf[BLINK1_CMD_SIZE] = { 1, 'n', 0, 0, 0, 0, 0, 0, 0 }; - -	if (data->brightness) { -		unsigned int coef = DIV_ROUND_CLOSEST(255, data->brightness); +	int ret; -		buf[2] = DIV_ROUND_CLOSEST(blink1_rgb_to_r(data->rgb), coef); -		buf[3] = DIV_ROUND_CLOSEST(blink1_rgb_to_g(data->rgb), coef); -		buf[4] = DIV_ROUND_CLOSEST(blink1_rgb_to_b(data->rgb), coef); -	} +	ret = hid_hw_raw_request(tdev->hdev, buf[0], buf, REPORT_SIZE, +			HID_FEATURE_REPORT, HID_REQ_GET_REPORT); +	if (ret < 0) +		return ret; -	if (data->fade) { -		buf[1] = 'c'; -		buf[5] = (data->fade & 0xFF00) >> 8; -		buf[6] = (data->fade & 0x00FF); -	} +	hid_dbg(tdev->hdev, "<- %d %c %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx %02hhx\n", +			buf[0], buf[1], buf[2], buf[3], buf[4], +			buf[5], buf[6], buf[7], buf[8]); -	return blink1_send_command(data, buf); +	return 0;  } -static void blink1_led_set(struct led_classdev *led_cdev, -		enum led_brightness brightness) +static int thingm_version(struct thingm_device *tdev)  { -	struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); +	u8 buf[REPORT_SIZE] = { REPORT_ID, 'v', 0, 0, 0, 0, 0, 0, 0 }; +	int err; -	data->brightness = brightness; -	if (blink1_update_color(data)) -		hid_err(data->hdev, "failed to update color\n"); -} +	err = thingm_send(tdev, buf); +	if (err) +		return err; -static enum led_brightness blink1_led_get(struct led_classdev *led_cdev) -{ -	struct blink1_data *data = dev_get_drvdata(led_cdev->dev->parent); +	err = thingm_recv(tdev, buf); +	if (err) +		return err; -	return data->brightness; +	tdev->version.major = buf[3]; +	tdev->version.minor = buf[4]; + +	return 0;  } -static ssize_t blink1_show_rgb(struct device *dev, -		struct device_attribute *attr, char *buf) +static int thingm_write_color(struct thingm_rgb *rgb)  { -	struct blink1_data *data = dev_get_drvdata(dev->parent); +	u8 buf[REPORT_SIZE] = { REPORT_ID, 'c', 0, 0, 0, 0, 0, rgb->num, 0 }; -	return sprintf(buf, "%.6X\n", data->rgb); +	buf[2] = rgb->red.ldev.brightness; +	buf[3] = rgb->green.ldev.brightness; +	buf[4] = rgb->blue.ldev.brightness; + +	return thingm_send(rgb->tdev, buf);  } -static ssize_t blink1_store_rgb(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t count) +static void thingm_work(struct work_struct *work)  { -	struct blink1_data *data = dev_get_drvdata(dev->parent); -	long unsigned int rgb; -	int ret; +	struct thingm_rgb *rgb = container_of(work, struct thingm_rgb, work); -	ret = kstrtoul(buf, 16, &rgb); -	if (ret) -		return ret; - -	/* RGB triplet notation is 24-bit hexadecimal */ -	if (rgb > 0xFFFFFF) -		return -EINVAL; +	mutex_lock(&rgb->tdev->lock); -	data->rgb = rgb; -	ret = blink1_update_color(data); +	if (thingm_write_color(rgb)) +		hid_err(rgb->tdev->hdev, "failed to write color\n"); -	return ret ? ret : count; +	mutex_unlock(&rgb->tdev->lock);  } -static DEVICE_ATTR(rgb, S_IRUGO | S_IWUSR, blink1_show_rgb, blink1_store_rgb); - -static ssize_t blink1_show_fade(struct device *dev, -		struct device_attribute *attr, char *buf) +static void thingm_led_set(struct led_classdev *ldev, +		enum led_brightness brightness)  { -	struct blink1_data *data = dev_get_drvdata(dev->parent); +	struct thingm_led *led = container_of(ldev, struct thingm_led, ldev); -	return sprintf(buf, "%d\n", data->fade * 10); +	/* the ledclass has already stored the brightness value */ +	schedule_work(&led->rgb->work);  } -static ssize_t blink1_store_fade(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t count) +static int thingm_init_rgb(struct thingm_rgb *rgb)  { -	struct blink1_data *data = dev_get_drvdata(dev->parent); -	long unsigned int fade; -	int ret; +	const int minor = ((struct hidraw *) rgb->tdev->hdev->hidraw)->minor; +	int err; + +	/* Register the red diode */ +	snprintf(rgb->red.name, sizeof(rgb->red.name), +			"thingm%d:red:led%d", minor, rgb->num); +	rgb->red.ldev.name = rgb->red.name; +	rgb->red.ldev.max_brightness = 255; +	rgb->red.ldev.brightness_set = thingm_led_set; +	rgb->red.rgb = rgb; + +	err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->red.ldev); +	if (err) +		return err; + +	/* Register the green diode */ +	snprintf(rgb->green.name, sizeof(rgb->green.name), +			"thingm%d:green:led%d", minor, rgb->num); +	rgb->green.ldev.name = rgb->green.name; +	rgb->green.ldev.max_brightness = 255; +	rgb->green.ldev.brightness_set = thingm_led_set; +	rgb->green.rgb = rgb; + +	err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->green.ldev); +	if (err) +		goto unregister_red; + +	/* Register the blue diode */ +	snprintf(rgb->blue.name, sizeof(rgb->blue.name), +			"thingm%d:blue:led%d", minor, rgb->num); +	rgb->blue.ldev.name = rgb->blue.name; +	rgb->blue.ldev.max_brightness = 255; +	rgb->blue.ldev.brightness_set = thingm_led_set; +	rgb->blue.rgb = rgb; + +	err = led_classdev_register(&rgb->tdev->hdev->dev, &rgb->blue.ldev); +	if (err) +		goto unregister_green; + +	INIT_WORK(&rgb->work, thingm_work); -	ret = kstrtoul(buf, 10, &fade); -	if (ret) -		return ret; +	return 0; -	/* blink(1) accepts 16-bit fade time, number of 10ms ticks */ -	fade = DIV_ROUND_CLOSEST(fade, 10); -	if (fade > 65535) -		return -EINVAL; +unregister_green: +	led_classdev_unregister(&rgb->green.ldev); -	data->fade = fade; +unregister_red: +	led_classdev_unregister(&rgb->red.ldev); -	return count; +	return err;  } -static DEVICE_ATTR(fade, S_IRUGO | S_IWUSR, -		blink1_show_fade, blink1_store_fade); - -static ssize_t blink1_show_play(struct device *dev, -		struct device_attribute *attr, char *buf) +static void thingm_remove_rgb(struct thingm_rgb *rgb)  { -	struct blink1_data *data = dev_get_drvdata(dev->parent); - -	return sprintf(buf, "%d\n", data->play); +	flush_work(&rgb->work); +	led_classdev_unregister(&rgb->red.ldev); +	led_classdev_unregister(&rgb->green.ldev); +	led_classdev_unregister(&rgb->blue.ldev);  } -static ssize_t blink1_store_play(struct device *dev, -		struct device_attribute *attr, const char *buf, size_t count) +static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id)  { -	struct blink1_data *data = dev_get_drvdata(dev->parent); -	u8 cmd[BLINK1_CMD_SIZE] = { 1, 'p', 0, 0, 0, 0, 0, 0, 0 }; -	long unsigned int play; -	int ret; +	struct thingm_device *tdev; +	int i, err; -	ret = kstrtoul(buf, 10, &play); -	if (ret) -		return ret; +	tdev = devm_kzalloc(&hdev->dev, sizeof(struct thingm_device), +			GFP_KERNEL); +	if (!tdev) +		return -ENOMEM; -	data->play = !!play; -	cmd[2] = data->play; -	ret = blink1_send_command(data, cmd); +	tdev->hdev = hdev; +	hid_set_drvdata(hdev, tdev); -	return ret ? ret : count; -} - -static DEVICE_ATTR(play, S_IRUGO | S_IWUSR, -		blink1_show_play, blink1_store_play); +	err = hid_parse(hdev); +	if (err) +		goto error; -static const struct attribute_group blink1_sysfs_group = { -	.attrs = (struct attribute *[]) { -		&dev_attr_rgb.attr, -		&dev_attr_fade.attr, -		&dev_attr_play.attr, -		NULL -	}, -}; +	err = hid_hw_start(hdev, HID_CONNECT_HIDRAW); +	if (err) +		goto error; -static int thingm_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ -	struct blink1_data *data; -	struct led_classdev *led; -	char led_name[13]; -	int ret; +	mutex_init(&tdev->lock); -	data = devm_kzalloc(&hdev->dev, sizeof(struct blink1_data), GFP_KERNEL); -	if (!data) -		return -ENOMEM; +	err = thingm_version(tdev); +	if (err) +		goto stop; -	hid_set_drvdata(hdev, data); -	data->hdev = hdev; -	data->rgb = 0xFFFFFF; /* set a default white color */ +	hid_dbg(hdev, "firmware version: %c.%c\n", +			tdev->version.major, tdev->version.minor); -	ret = hid_parse(hdev); -	if (ret) -		goto error; +	for (i = 0; i < ARRAY_SIZE(thingm_fwinfo) && !tdev->fwinfo; ++i) +		if (thingm_fwinfo[i].major == tdev->version.major) +			tdev->fwinfo = &thingm_fwinfo[i]; -	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); -	if (ret) -		goto error; +	if (!tdev->fwinfo) { +		hid_err(hdev, "unsupported firmware %c\n", tdev->version.major); +		goto stop; +	} -	/* blink(1) serial numbers range is 0x1A001000 to 0x1A002FFF */ -	led = &data->led_cdev; -	snprintf(led_name, sizeof(led_name), "blink1::%s", hdev->uniq + 4); -	led->name = led_name; -	led->brightness_set = blink1_led_set; -	led->brightness_get = blink1_led_get; -	ret = led_classdev_register(&hdev->dev, led); -	if (ret) +	tdev->rgb = devm_kzalloc(&hdev->dev, +			sizeof(struct thingm_rgb) * tdev->fwinfo->numrgb, +			GFP_KERNEL); +	if (!tdev->rgb) { +		err = -ENOMEM;  		goto stop; +	} -	ret = sysfs_create_group(&led->dev->kobj, &blink1_sysfs_group); -	if (ret) -		goto remove_led; +	for (i = 0; i < tdev->fwinfo->numrgb; ++i) { +		struct thingm_rgb *rgb = tdev->rgb + i; + +		rgb->tdev = tdev; +		rgb->num = tdev->fwinfo->first + i; +		err = thingm_init_rgb(rgb); +		if (err) { +			while (--i >= 0) +				thingm_remove_rgb(tdev->rgb + i); +			goto stop; +		} +	}  	return 0; - -remove_led: -	led_classdev_unregister(led);  stop:  	hid_hw_stop(hdev);  error: -	return ret; +	return err;  }  static void thingm_remove(struct hid_device *hdev)  { -	struct blink1_data *data = hid_get_drvdata(hdev); -	struct led_classdev *led = &data->led_cdev; +	struct thingm_device *tdev = hid_get_drvdata(hdev); +	int i; + +	for (i = 0; i < tdev->fwinfo->numrgb; ++i) +		thingm_remove_rgb(tdev->rgb + i); -	sysfs_remove_group(&led->dev->kobj, &blink1_sysfs_group); -	led_classdev_unregister(led);  	hid_hw_stop(hdev);  } diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 60c75dcbbdb..902013ec041 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -128,8 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,  	rep_data[0] = WAC_CMD_ICON_START_STOP;  	rep_data[1] = 0; -	ret = hdev->hid_output_raw_report(hdev, rep_data, 2, -				HID_FEATURE_REPORT); +	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, +				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  	if (ret < 0)  		goto err; @@ -143,15 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image,  			rep_data[j + 3] = p[(i << 6) + j];  		rep_data[2] = i; -		ret = hdev->hid_output_raw_report(hdev, rep_data, 67, -					HID_FEATURE_REPORT); +		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67, +					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  	}  	rep_data[0] = WAC_CMD_ICON_START_STOP;  	rep_data[1] = 0; -	ret = hdev->hid_output_raw_report(hdev, rep_data, 2, -				HID_FEATURE_REPORT); +	ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, +				 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  err:  	return; @@ -183,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,  		buf[3] = value;  		/* use fixed brightness for OLEDs */  		buf[4] = 0x08; -		hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); +		hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT, +				   HID_REQ_SET_REPORT);  		kfree(buf);  	} @@ -339,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)  		rep_data[0] = 0x03 ; rep_data[1] = 0x00;  		limit = 3;  		do { -			ret = hdev->hid_output_raw_report(hdev, rep_data, 2, -					HID_FEATURE_REPORT); +			ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, +					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  		} while (ret < 0 && limit-- > 0);  		if (ret >= 0) { @@ -352,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)  			rep_data[1] = 0x00;  			limit = 3;  			do { -				ret = hdev->hid_output_raw_report(hdev, -					rep_data, 2, HID_FEATURE_REPORT); +				ret = hid_hw_raw_request(hdev, rep_data[0], +					rep_data, 2, HID_FEATURE_REPORT, +					HID_REQ_SET_REPORT);  			} while (ret < 0 && limit-- > 0);  			if (ret >= 0) { @@ -378,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed)  		rep_data[0] = 0x03;  		rep_data[1] = wdata->features; -		ret = hdev->hid_output_raw_report(hdev, rep_data, 2, -					HID_FEATURE_REPORT); +		ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, +				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);  		if (ret >= 0)  			wdata->high_speed = speed;  		break; diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index abb20db2b44..d00391418d1 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,  	__u8 *buf;  	int ret; -	if (!hdev->hid_output_raw_report) +	if (!hdev->ll_driver->output_report)  		return -ENODEV;  	buf = kmemdup(buffer, count, GFP_KERNEL);  	if (!buf)  		return -ENOMEM; -	ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); +	ret = hid_hw_output_report(hdev, buf, count);  	kfree(buf);  	return ret; diff --git a/drivers/hid/hid-wiimote-modules.c b/drivers/hid/hid-wiimote-modules.c index 2e7d644dba1..6b61f01e01e 100644 --- a/drivers/hid/hid-wiimote-modules.c +++ b/drivers/hid/hid-wiimote-modules.c @@ -119,12 +119,22 @@ static const struct wiimod_ops wiimod_keys = {   * the rumble motor, this flag shouldn't be set.   */ +/* used by wiimod_rumble and wiipro_rumble */ +static void wiimod_rumble_worker(struct work_struct *work) +{ +	struct wiimote_data *wdata = container_of(work, struct wiimote_data, +						  rumble_worker); + +	spin_lock_irq(&wdata->state.lock); +	wiiproto_req_rumble(wdata, wdata->state.cache_rumble); +	spin_unlock_irq(&wdata->state.lock); +} +  static int wiimod_rumble_play(struct input_dev *dev, void *data,  			      struct ff_effect *eff)  {  	struct wiimote_data *wdata = input_get_drvdata(dev);  	__u8 value; -	unsigned long flags;  	/*  	 * The wiimote supports only a single rumble motor so if any magnitude @@ -137,9 +147,10 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data,  	else  		value = 0; -	spin_lock_irqsave(&wdata->state.lock, flags); -	wiiproto_req_rumble(wdata, value); -	spin_unlock_irqrestore(&wdata->state.lock, flags); +	/* Locking state.lock here might deadlock with input_event() calls. +	 * schedule_work acts as barrier. Merging multiple changes is fine. */ +	wdata->state.cache_rumble = value; +	schedule_work(&wdata->rumble_worker);  	return 0;  } @@ -147,6 +158,8 @@ static int wiimod_rumble_play(struct input_dev *dev, void *data,  static int wiimod_rumble_probe(const struct wiimod_ops *ops,  			       struct wiimote_data *wdata)  { +	INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker); +  	set_bit(FF_RUMBLE, wdata->input->ffbit);  	if (input_ff_create_memless(wdata->input, NULL, wiimod_rumble_play))  		return -ENOMEM; @@ -159,6 +172,8 @@ static void wiimod_rumble_remove(const struct wiimod_ops *ops,  {  	unsigned long flags; +	cancel_work_sync(&wdata->rumble_worker); +  	spin_lock_irqsave(&wdata->state.lock, flags);  	wiiproto_req_rumble(wdata, 0);  	spin_unlock_irqrestore(&wdata->state.lock, flags); @@ -1640,10 +1655,39 @@ static void wiimod_pro_in_ext(struct wiimote_data *wdata, const __u8 *ext)  	ly = (ext[4] & 0xff) | ((ext[5] & 0x0f) << 8);  	ry = (ext[6] & 0xff) | ((ext[7] & 0x0f) << 8); -	input_report_abs(wdata->extension.input, ABS_X, lx - 0x800); -	input_report_abs(wdata->extension.input, ABS_Y, ly - 0x800); -	input_report_abs(wdata->extension.input, ABS_RX, rx - 0x800); -	input_report_abs(wdata->extension.input, ABS_RY, ry - 0x800); +	/* zero-point offsets */ +	lx -= 0x800; +	ly = 0x800 - ly; +	rx -= 0x800; +	ry = 0x800 - ry; + +	/* Trivial automatic calibration. We don't know any calibration data +	 * in the EEPROM so we must use the first report to calibrate the +	 * null-position of the analog sticks. Users can retrigger calibration +	 * via sysfs, or set it explicitly. If data is off more than abs(500), +	 * we skip calibration as the sticks are likely to be moved already. */ +	if (!(wdata->state.flags & WIIPROTO_FLAG_PRO_CALIB_DONE)) { +		wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE; +		if (abs(lx) < 500) +			wdata->state.calib_pro_sticks[0] = -lx; +		if (abs(ly) < 500) +			wdata->state.calib_pro_sticks[1] = -ly; +		if (abs(rx) < 500) +			wdata->state.calib_pro_sticks[2] = -rx; +		if (abs(ry) < 500) +			wdata->state.calib_pro_sticks[3] = -ry; +	} + +	/* apply calibration data */ +	lx += wdata->state.calib_pro_sticks[0]; +	ly += wdata->state.calib_pro_sticks[1]; +	rx += wdata->state.calib_pro_sticks[2]; +	ry += wdata->state.calib_pro_sticks[3]; + +	input_report_abs(wdata->extension.input, ABS_X, lx); +	input_report_abs(wdata->extension.input, ABS_Y, ly); +	input_report_abs(wdata->extension.input, ABS_RX, rx); +	input_report_abs(wdata->extension.input, ABS_RY, ry);  	input_report_key(wdata->extension.input,  			 wiimod_pro_map[WIIMOD_PRO_KEY_RIGHT], @@ -1731,7 +1775,6 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,  {  	struct wiimote_data *wdata = input_get_drvdata(dev);  	__u8 value; -	unsigned long flags;  	/*  	 * The wiimote supports only a single rumble motor so if any magnitude @@ -1744,17 +1787,78 @@ static int wiimod_pro_play(struct input_dev *dev, void *data,  	else  		value = 0; -	spin_lock_irqsave(&wdata->state.lock, flags); -	wiiproto_req_rumble(wdata, value); -	spin_unlock_irqrestore(&wdata->state.lock, flags); +	/* Locking state.lock here might deadlock with input_event() calls. +	 * schedule_work acts as barrier. Merging multiple changes is fine. */ +	wdata->state.cache_rumble = value; +	schedule_work(&wdata->rumble_worker);  	return 0;  } +static ssize_t wiimod_pro_calib_show(struct device *dev, +				     struct device_attribute *attr, +				     char *out) +{ +	struct wiimote_data *wdata = dev_to_wii(dev); +	int r; + +	r = 0; +	r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[0]); +	r += sprintf(&out[r], "%+06hd ", wdata->state.calib_pro_sticks[1]); +	r += sprintf(&out[r], "%+06hd:", wdata->state.calib_pro_sticks[2]); +	r += sprintf(&out[r], "%+06hd\n", wdata->state.calib_pro_sticks[3]); + +	return r; +} + +static ssize_t wiimod_pro_calib_store(struct device *dev, +				      struct device_attribute *attr, +				      const char *buf, size_t count) +{ +	struct wiimote_data *wdata = dev_to_wii(dev); +	int r; +	s16 x1, y1, x2, y2; + +	if (!strncmp(buf, "scan\n", 5)) { +		spin_lock_irq(&wdata->state.lock); +		wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE; +		spin_unlock_irq(&wdata->state.lock); +	} else { +		r = sscanf(buf, "%hd:%hd %hd:%hd", &x1, &y1, &x2, &y2); +		if (r != 4) +			return -EINVAL; + +		spin_lock_irq(&wdata->state.lock); +		wdata->state.flags |= WIIPROTO_FLAG_PRO_CALIB_DONE; +		spin_unlock_irq(&wdata->state.lock); + +		wdata->state.calib_pro_sticks[0] = x1; +		wdata->state.calib_pro_sticks[1] = y1; +		wdata->state.calib_pro_sticks[2] = x2; +		wdata->state.calib_pro_sticks[3] = y2; +	} + +	return strnlen(buf, PAGE_SIZE); +} + +static DEVICE_ATTR(pro_calib, S_IRUGO|S_IWUSR|S_IWGRP, wiimod_pro_calib_show, +		   wiimod_pro_calib_store); +  static int wiimod_pro_probe(const struct wiimod_ops *ops,  			    struct wiimote_data *wdata)  {  	int ret, i; +	unsigned long flags; + +	INIT_WORK(&wdata->rumble_worker, wiimod_rumble_worker); +	wdata->state.calib_pro_sticks[0] = 0; +	wdata->state.calib_pro_sticks[1] = 0; +	wdata->state.calib_pro_sticks[2] = 0; +	wdata->state.calib_pro_sticks[3] = 0; + +	spin_lock_irqsave(&wdata->state.lock, flags); +	wdata->state.flags &= ~WIIPROTO_FLAG_PRO_CALIB_DONE; +	spin_unlock_irqrestore(&wdata->state.lock, flags);  	wdata->extension.input = input_allocate_device();  	if (!wdata->extension.input) @@ -1769,6 +1873,13 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,  		goto err_free;  	} +	ret = device_create_file(&wdata->hdev->dev, +				 &dev_attr_pro_calib); +	if (ret) { +		hid_err(wdata->hdev, "cannot create sysfs attribute\n"); +		goto err_free; +	} +  	wdata->extension.input->open = wiimod_pro_open;  	wdata->extension.input->close = wiimod_pro_close;  	wdata->extension.input->dev.parent = &wdata->hdev->dev; @@ -1789,20 +1900,23 @@ static int wiimod_pro_probe(const struct wiimod_ops *ops,  	set_bit(ABS_RX, wdata->extension.input->absbit);  	set_bit(ABS_RY, wdata->extension.input->absbit);  	input_set_abs_params(wdata->extension.input, -			     ABS_X, -0x800, 0x800, 2, 4); +			     ABS_X, -0x400, 0x400, 4, 100);  	input_set_abs_params(wdata->extension.input, -			     ABS_Y, -0x800, 0x800, 2, 4); +			     ABS_Y, -0x400, 0x400, 4, 100);  	input_set_abs_params(wdata->extension.input, -			     ABS_RX, -0x800, 0x800, 2, 4); +			     ABS_RX, -0x400, 0x400, 4, 100);  	input_set_abs_params(wdata->extension.input, -			     ABS_RY, -0x800, 0x800, 2, 4); +			     ABS_RY, -0x400, 0x400, 4, 100);  	ret = input_register_device(wdata->extension.input);  	if (ret) -		goto err_free; +		goto err_file;  	return 0; +err_file: +	device_remove_file(&wdata->hdev->dev, +			   &dev_attr_pro_calib);  err_free:  	input_free_device(wdata->extension.input);  	wdata->extension.input = NULL; @@ -1817,12 +1931,15 @@ static void wiimod_pro_remove(const struct wiimod_ops *ops,  	if (!wdata->extension.input)  		return; +	input_unregister_device(wdata->extension.input); +	wdata->extension.input = NULL; +	cancel_work_sync(&wdata->rumble_worker); +	device_remove_file(&wdata->hdev->dev, +			   &dev_attr_pro_calib); +  	spin_lock_irqsave(&wdata->state.lock, flags);  	wiiproto_req_rumble(wdata, 0);  	spin_unlock_irqrestore(&wdata->state.lock, flags); - -	input_unregister_device(wdata->extension.input); -	wdata->extension.input = NULL;  }  static const struct wiimod_ops wiimod_pro = { diff --git a/drivers/hid/hid-wiimote.h b/drivers/hid/hid-wiimote.h index f1474f372c0..10934aa129f 100644 --- a/drivers/hid/hid-wiimote.h +++ b/drivers/hid/hid-wiimote.h @@ -46,6 +46,7 @@  #define WIIPROTO_FLAG_DRM_LOCKED	0x8000  #define WIIPROTO_FLAG_BUILTIN_MP	0x010000  #define WIIPROTO_FLAG_NO_MP		0x020000 +#define WIIPROTO_FLAG_PRO_CALIB_DONE	0x040000  #define WIIPROTO_FLAGS_LEDS (WIIPROTO_FLAG_LED1 | WIIPROTO_FLAG_LED2 | \  					WIIPROTO_FLAG_LED3 | WIIPROTO_FLAG_LED4) @@ -133,13 +134,16 @@ struct wiimote_state {  	__u8 *cmd_read_buf;  	__u8 cmd_read_size; -	/* calibration data */ +	/* calibration/cache data */  	__u16 calib_bboard[4][3]; +	__s16 calib_pro_sticks[4]; +	__u8 cache_rumble;  };  struct wiimote_data {  	struct hid_device *hdev;  	struct input_dev *input; +	struct work_struct rumble_worker;  	struct led_classdev *leds[4];  	struct input_dev *accel;  	struct input_dev *ir; @@ -325,7 +329,7 @@ static inline void wiimote_cmd_acquire_noint(struct wiimote_data *wdata)  static inline void wiimote_cmd_set(struct wiimote_data *wdata, int cmd,  								__u32 opt)  { -	INIT_COMPLETION(wdata->state.ready); +	reinit_completion(&wdata->state.ready);  	wdata->state.cmd = cmd;  	wdata->state.opt = opt;  } diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 8918dd12bb6..9c2d7c23f29 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -6,7 +6,7 @@   * to work on raw hid events as they want to, and avoids a need to   * use a transport-specific userspace libhid/libusb libraries.   * - *  Copyright (c) 2007 Jiri Kosina + *  Copyright (c) 2007-2014 Jiri Kosina   */  /* @@ -104,8 +104,11 @@ out:  	return ret;  } -/* The first byte is expected to be a report number. - * This function is to be called with the minors_lock mutex held */ +/* + * 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)  {  	unsigned int minor = iminor(file_inode(file)); @@ -120,10 +123,6 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,  	dev = hidraw_table[minor]->hid; -	if (!dev->hid_output_raw_report) { -		ret = -ENODEV; -		goto out; -	}  	if (count > HID_MAX_BUFFER_SIZE) {  		hid_warn(dev, "pid %d passed too large report\n", @@ -150,14 +149,27 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,  		goto out_free;  	} -	ret = dev->hid_output_raw_report(dev, buf, count, report_type); +	if ((report_type == HID_OUTPUT_REPORT) && +	    !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { +		ret = hid_hw_output_report(dev, buf, count); +		/* +		 * compatibility with old implementation of USB-HID and I2C-HID: +		 * if the device does not support receiving output reports, +		 * on an interrupt endpoint, fallback to SET_REPORT HID command. +		 */ +		if (ret != -ENOSYS) +			goto out_free; +	} + +	ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, +				HID_REQ_SET_REPORT); +  out_free:  	kfree(buf);  out:  	return ret;  } -/* the first byte is expected to be a report number */  static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)  {  	ssize_t ret; @@ -168,12 +180,15 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t  } -/* This function performs a Get_Report transfer over the control endpoint +/* + * This function performs a Get_Report transfer over the control endpoint   * per section 7.2.1 of the HID specification, version 1.1.  The first byte   * 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. */ + * 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)  {  	unsigned int minor = iminor(file_inode(file)); @@ -184,7 +199,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t  	dev = hidraw_table[minor]->hid; -	if (!dev->hid_get_raw_report) { +	if (!dev->ll_driver->raw_request) {  		ret = -ENODEV;  		goto out;  	} @@ -209,14 +224,17 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t  		goto out;  	} -	/* Read the first byte from the user. This is the report number, -	 * which is passed to dev->hid_get_raw_report(). */ +	/* +	 * Read the first byte from the user. This is the report number, +	 * which is passed to hid_hw_raw_request(). +	 */  	if (copy_from_user(&report_number, buffer, 1)) {  		ret = -EFAULT;  		goto out_free;  	} -	ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); +	ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, +				 HID_REQ_GET_REPORT);  	if (ret < 0)  		goto out_free; @@ -308,18 +326,25 @@ static int hidraw_fasync(int fd, struct file *file, int on)  static void drop_ref(struct hidraw *hidraw, int exists_bit)  {  	if (exists_bit) { -		hid_hw_close(hidraw->hid);  		hidraw->exist = 0; -		if (hidraw->open) +		if (hidraw->open) { +			hid_hw_close(hidraw->hid);  			wake_up_interruptible(&hidraw->wait); +		} +		device_destroy(hidraw_class, +			       MKDEV(hidraw_major, hidraw->minor));  	} else {  		--hidraw->open;  	} - -	if (!hidraw->open && !hidraw->exist) { -		device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); -		hidraw_table[hidraw->minor] = NULL; -		kfree(hidraw); +	if (!hidraw->open) { +		if (!hidraw->exist) { +			hidraw_table[hidraw->minor] = NULL; +			kfree(hidraw); +		} else { +			/* close device for last reader */ +			hid_hw_power(hidraw->hid, PM_HINT_NORMAL); +			hid_hw_close(hidraw->hid); +		}  	}  } @@ -491,7 +516,7 @@ int hidraw_connect(struct hid_device *hid)  	int minor, result;  	struct hidraw *dev; -	/* we accept any HID device, no matter the applications */ +	/* we accept any HID device, all applications */  	dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);  	if (!dev) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index c1336193b04..21aafc8f48c 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -25,6 +25,7 @@  #include <linux/delay.h>  #include <linux/slab.h>  #include <linux/pm.h> +#include <linux/pm_runtime.h>  #include <linux/device.h>  #include <linux/wait.h>  #include <linux/err.h> @@ -256,18 +257,27 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType,  	return 0;  } -static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, -		u8 reportID, unsigned char *buf, size_t data_len) +/** + * i2c_hid_set_or_send_report: forward an incoming report to the device + * @client: the i2c_client of the device + * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT + * @reportID: the report ID + * @buf: the actual data to transfer, without the report ID + * @len: size of buf + * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report + */ +static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, +		u8 reportID, unsigned char *buf, size_t data_len, bool use_data)  {  	struct i2c_hid *ihid = i2c_get_clientdata(client);  	u8 *args = ihid->argsbuf; -	const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd; +	const struct i2c_hid_cmd *hidcmd;  	int ret;  	u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister);  	u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister);  	u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); -	/* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ +	/* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */  	u16 size =	2			/* size */ +  			(reportID ? 1 : 0)	/* reportID */ +  			data_len		/* buf */; @@ -278,6 +288,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,  	i2c_hid_dbg(ihid, "%s\n", __func__); +	if (!use_data && maxOutputLength == 0) +		return -ENOSYS; +  	if (reportID >= 0x0F) {  		args[index++] = reportID;  		reportID = 0x0F; @@ -287,9 +300,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType,  	 * use the data register for feature reports or if the device does not  	 * support the output register  	 */ -	if (reportType == 0x03 || maxOutputLength == 0) { +	if (use_data) {  		args[index++] = dataRegister & 0xFF;  		args[index++] = dataRegister >> 8; +		hidcmd = &hid_set_report_cmd;  	} else {  		args[index++] = outputRegister & 0xFF;  		args[index++] = outputRegister >> 8; @@ -454,14 +468,18 @@ static void i2c_hid_init_reports(struct hid_device *hid)  		return;  	} -	list_for_each_entry(report, -		&hid->report_enum[HID_INPUT_REPORT].report_list, list) -		i2c_hid_init_report(report, inbuf, ihid->bufsize); +	/* +	 * The device must be powered on while we fetch initial reports +	 * from it. +	 */ +	pm_runtime_get_sync(&client->dev);  	list_for_each_entry(report,  		&hid->report_enum[HID_FEATURE_REPORT].report_list, list)  		i2c_hid_init_report(report, inbuf, ihid->bufsize); +	pm_runtime_put(&client->dev); +  	kfree(inbuf);  } @@ -554,7 +572,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid,  }  static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, -		size_t count, unsigned char report_type) +		size_t count, unsigned char report_type, bool use_data)  {  	struct i2c_client *client = hid->driver_data;  	int report_id = buf[0]; @@ -568,9 +586,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,  		count--;  	} -	ret = i2c_hid_set_report(client, +	ret = i2c_hid_set_or_send_report(client,  				report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, -				report_id, buf, count); +				report_id, buf, count, use_data);  	if (report_id && ret >= 0)  		ret++; /* add report_id to the number of transfered bytes */ @@ -578,34 +596,27 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf,  	return ret;  } -static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, -		int reqtype) +static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf, +		size_t count)  { -	struct i2c_client *client = hid->driver_data; -	char *buf; -	int ret; -	int len = i2c_hid_get_report_length(rep) - 2; - -	buf = kzalloc(len, GFP_KERNEL); -	if (!buf) -		return; +	return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT, +			false); +} +static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum, +			       __u8 *buf, size_t len, unsigned char rtype, +			       int reqtype) +{  	switch (reqtype) {  	case HID_REQ_GET_REPORT: -		ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type); -		if (ret < 0) -			dev_err(&client->dev, "%s: unable to get report: %d\n", -				__func__, ret); -		else -			hid_input_report(hid, rep->type, buf, ret, 0); -		break; +		return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype);  	case HID_REQ_SET_REPORT: -		hid_output_report(rep, buf); -		i2c_hid_output_raw_report(hid, buf, len, rep->type); -		break; +		if (buf[0] != reportnum) +			return -EINVAL; +		return i2c_hid_output_raw_report(hid, buf, len, rtype, true); +	default: +		return -EIO;  	} - -	kfree(buf);  }  static int i2c_hid_parse(struct hid_device *hid) @@ -707,8 +718,8 @@ static int i2c_hid_open(struct hid_device *hid)  	mutex_lock(&i2c_hid_open_mut);  	if (!hid->open++) { -		ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); -		if (ret) { +		ret = pm_runtime_get_sync(&client->dev); +		if (ret < 0) {  			hid->open--;  			goto done;  		} @@ -716,7 +727,7 @@ static int i2c_hid_open(struct hid_device *hid)  	}  done:  	mutex_unlock(&i2c_hid_open_mut); -	return ret; +	return ret < 0 ? ret : 0;  }  static void i2c_hid_close(struct hid_device *hid) @@ -733,7 +744,7 @@ static void i2c_hid_close(struct hid_device *hid)  		clear_bit(I2C_HID_STARTED, &ihid->flags);  		/* Save some power */ -		i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); +		pm_runtime_put(&client->dev);  	}  	mutex_unlock(&i2c_hid_open_mut);  } @@ -742,19 +753,18 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)  {  	struct i2c_client *client = hid->driver_data;  	struct i2c_hid *ihid = i2c_get_clientdata(client); -	int ret = 0;  	i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);  	switch (lvl) {  	case PM_HINT_FULLON: -		ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); +		pm_runtime_get_sync(&client->dev);  		break;  	case PM_HINT_NORMAL: -		ret = i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); +		pm_runtime_put(&client->dev);  		break;  	} -	return ret; +	return 0;  }  static struct hid_ll_driver i2c_hid_ll_driver = { @@ -764,7 +774,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = {  	.open = i2c_hid_open,  	.close = i2c_hid_close,  	.power = i2c_hid_power, -	.request = i2c_hid_request, +	.output_report = i2c_hid_output_report, +	.raw_request = i2c_hid_raw_request,  };  static int i2c_hid_init_irq(struct i2c_client *client) @@ -796,34 +807,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)  	unsigned int dsize;  	int ret; -	/* Fetch the length of HID description, retrieve the 4 first bytes: -	 * bytes 0-1 -> length -	 * bytes 2-3 -> bcdVersion (has to be 1.00) */ -	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, 4); - -	i2c_hid_dbg(ihid, "%s, ihid->hdesc_buffer: %4ph\n", __func__, -			ihid->hdesc_buffer); - +	/* i2c hid fetch using a fixed descriptor size (30 bytes) */ +	i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); +	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, +				sizeof(struct i2c_hid_desc));  	if (ret) { -		dev_err(&client->dev, -			"unable to fetch the size of HID descriptor (ret=%d)\n", -			ret); -		return -ENODEV; -	} - -	dsize = le16_to_cpu(hdesc->wHIDDescLength); -	/* -	 * the size of the HID descriptor should at least contain -	 * its size and the bcdVersion (4 bytes), and should not be greater -	 * than sizeof(struct i2c_hid_desc) as we directly fill this struct -	 * through i2c_hid_command. -	 */ -	if (dsize < 4 || dsize > sizeof(struct i2c_hid_desc)) { -		dev_err(&client->dev, "weird size of HID descriptor (%u)\n", -			dsize); +		dev_err(&client->dev, "hid_descr_cmd failed\n");  		return -ENODEV;  	} +	/* Validate the length of HID descriptor, the 4 first bytes: +	 * bytes 0-1 -> length +	 * bytes 2-3 -> bcdVersion (has to be 1.00) */  	/* check bcdVersion == 1.0 */  	if (le16_to_cpu(hdesc->bcdVersion) != 0x0100) {  		dev_err(&client->dev, @@ -832,17 +827,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)  		return -ENODEV;  	} -	i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); - -	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, -				dsize); -	if (ret) { -		dev_err(&client->dev, "hid_descr_cmd Fail\n"); +	/* Descriptor length should be 30 bytes as per the specification */ +	dsize = le16_to_cpu(hdesc->wHIDDescLength); +	if (dsize != sizeof(struct i2c_hid_desc)) { +		dev_err(&client->dev, "weird size of HID descriptor (%u)\n", +			dsize);  		return -ENODEV;  	} -  	i2c_hid_dbg(ihid, "HID Descriptor: %*ph\n", dsize, ihid->hdesc_buffer); -  	return 0;  } @@ -854,9 +846,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,  		0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,  		0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,  	}; -	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; -	union acpi_object params[4], *obj; -	struct acpi_object_list input; +	union acpi_object *obj;  	struct acpi_device *adev;  	acpi_handle handle; @@ -864,36 +854,16 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,  	if (!handle || acpi_bus_get_device(handle, &adev))  		return -ENODEV; -	input.count = ARRAY_SIZE(params); -	input.pointer = params; - -	params[0].type = ACPI_TYPE_BUFFER; -	params[0].buffer.length = sizeof(i2c_hid_guid); -	params[0].buffer.pointer = i2c_hid_guid; -	params[1].type = ACPI_TYPE_INTEGER; -	params[1].integer.value = 1; -	params[2].type = ACPI_TYPE_INTEGER; -	params[2].integer.value = 1; /* HID function */ -	params[3].type = ACPI_TYPE_PACKAGE; -	params[3].package.count = 0; -	params[3].package.elements = NULL; - -	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) { +	obj = acpi_evaluate_dsm_typed(handle, i2c_hid_guid, 1, 1, NULL, +				      ACPI_TYPE_INTEGER); +	if (!obj) {  		dev_err(&client->dev, "device _DSM execution failed\n");  		return -ENODEV;  	} -	obj = (union acpi_object *)buf.pointer; -	if (obj->type != ACPI_TYPE_INTEGER) { -		dev_err(&client->dev, "device _DSM returned invalid type: %d\n", -			obj->type); -		kfree(buf.pointer); -		return -EINVAL; -	} -  	pdata->hid_descriptor_address = obj->integer.value; +	ACPI_FREE(obj); -	kfree(buf.pointer);  	return 0;  } @@ -999,13 +969,17 @@ static int i2c_hid_probe(struct i2c_client *client,  	if (ret < 0)  		goto err; +	pm_runtime_get_noresume(&client->dev); +	pm_runtime_set_active(&client->dev); +	pm_runtime_enable(&client->dev); +  	ret = i2c_hid_fetch_hid_descriptor(ihid);  	if (ret < 0) -		goto err; +		goto err_pm;  	ret = i2c_hid_init_irq(client);  	if (ret < 0) -		goto err; +		goto err_pm;  	hid = hid_allocate_device();  	if (IS_ERR(hid)) { @@ -1017,10 +991,8 @@ static int i2c_hid_probe(struct i2c_client *client,  	hid->driver_data = client;  	hid->ll_driver = &i2c_hid_ll_driver; -	hid->hid_get_raw_report = i2c_hid_get_raw_report; -	hid->hid_output_raw_report = i2c_hid_output_raw_report;  	hid->dev.parent = &client->dev; -	ACPI_HANDLE_SET(&hid->dev, ACPI_HANDLE(&client->dev)); +	ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev));  	hid->bus = BUS_I2C;  	hid->version = le16_to_cpu(ihid->hdesc.bcdVersion);  	hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); @@ -1036,6 +1008,7 @@ static int i2c_hid_probe(struct i2c_client *client,  		goto err_mem_free;  	} +	pm_runtime_put(&client->dev);  	return 0;  err_mem_free: @@ -1044,6 +1017,10 @@ err_mem_free:  err_irq:  	free_irq(client->irq, ihid); +err_pm: +	pm_runtime_put_noidle(&client->dev); +	pm_runtime_disable(&client->dev); +  err:  	i2c_hid_free_buffers(ihid);  	kfree(ihid); @@ -1055,6 +1032,11 @@ static int i2c_hid_remove(struct i2c_client *client)  	struct i2c_hid *ihid = i2c_get_clientdata(client);  	struct hid_device *hid; +	pm_runtime_get_sync(&client->dev); +	pm_runtime_disable(&client->dev); +	pm_runtime_set_suspended(&client->dev); +	pm_runtime_put_noidle(&client->dev); +  	hid = ihid->hid;  	hid_destroy_device(hid); @@ -1073,6 +1055,7 @@ static int i2c_hid_suspend(struct device *dev)  {  	struct i2c_client *client = to_i2c_client(dev); +	disable_irq(client->irq);  	if (device_may_wakeup(&client->dev))  		enable_irq_wake(client->irq); @@ -1087,6 +1070,7 @@ static int i2c_hid_resume(struct device *dev)  	int ret;  	struct i2c_client *client = to_i2c_client(dev); +	enable_irq(client->irq);  	ret = i2c_hid_hwreset(client);  	if (ret)  		return ret; @@ -1098,7 +1082,31 @@ static int i2c_hid_resume(struct device *dev)  }  #endif -static SIMPLE_DEV_PM_OPS(i2c_hid_pm, i2c_hid_suspend, i2c_hid_resume); +#ifdef CONFIG_PM_RUNTIME +static int i2c_hid_runtime_suspend(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); +	disable_irq(client->irq); +	return 0; +} + +static int i2c_hid_runtime_resume(struct device *dev) +{ +	struct i2c_client *client = to_i2c_client(dev); + +	enable_irq(client->irq); +	i2c_hid_set_power(client, I2C_HID_PWR_ON); +	return 0; +} +#endif + +static const struct dev_pm_ops i2c_hid_pm = { +	SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume) +	SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume, +			   NULL) +};  static const struct i2c_device_id i2c_hid_id_table[] = {  	{ "hid", 0 }, diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 5bf2fb78584..0cb92e34725 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -244,12 +244,35 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count,  	return count;  } +static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, +				  size_t count) +{ +	return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); +} + +static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum, +			    __u8 *buf, size_t len, unsigned char rtype, +			    int reqtype) +{ +	switch (reqtype) { +	case HID_REQ_GET_REPORT: +		return uhid_hid_get_raw(hid, reportnum, buf, len, rtype); +	case HID_REQ_SET_REPORT: +		/* TODO: implement proper SET_REPORT functionality */ +		return -ENOSYS; +	default: +		return -EIO; +	} +} +  static struct hid_ll_driver uhid_hid_driver = {  	.start = uhid_hid_start,  	.stop = uhid_hid_stop,  	.open = uhid_hid_open,  	.close = uhid_hid_close,  	.parse = uhid_hid_parse, +	.output_report = uhid_hid_output_report, +	.raw_request = uhid_raw_request,  };  #ifdef CONFIG_COMPAT @@ -287,7 +310,7 @@ static int uhid_event_from_user(const char __user *buffer, size_t len,  			 */  			struct uhid_create_req_compat *compat; -			compat = kmalloc(sizeof(*compat), GFP_KERNEL); +			compat = kzalloc(sizeof(*compat), GFP_KERNEL);  			if (!compat)  				return -ENOMEM; @@ -377,8 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid,  	hid->uniq[63] = 0;  	hid->ll_driver = &uhid_hid_driver; -	hid->hid_get_raw_report = uhid_hid_get_raw; -	hid->hid_output_raw_report = uhid_hid_output_raw;  	hid->bus = ev->u.create.bus;  	hid->vendor = ev->u.create.vendor;  	hid->product = ev->u.create.product; @@ -407,6 +428,66 @@ err_free:  	return ret;  } +static int uhid_dev_create2(struct uhid_device *uhid, +			    const struct uhid_event *ev) +{ +	struct hid_device *hid; +	int ret; + +	if (uhid->running) +		return -EALREADY; + +	uhid->rd_size = ev->u.create2.rd_size; +	if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE) +		return -EINVAL; + +	uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size, +				GFP_KERNEL); +	if (!uhid->rd_data) +		return -ENOMEM; + +	hid = hid_allocate_device(); +	if (IS_ERR(hid)) { +		ret = PTR_ERR(hid); +		goto err_free; +	} + +	strncpy(hid->name, ev->u.create2.name, 127); +	hid->name[127] = 0; +	strncpy(hid->phys, ev->u.create2.phys, 63); +	hid->phys[63] = 0; +	strncpy(hid->uniq, ev->u.create2.uniq, 63); +	hid->uniq[63] = 0; + +	hid->ll_driver = &uhid_hid_driver; +	hid->bus = ev->u.create2.bus; +	hid->vendor = ev->u.create2.vendor; +	hid->product = ev->u.create2.product; +	hid->version = ev->u.create2.version; +	hid->country = ev->u.create2.country; +	hid->driver_data = uhid; +	hid->dev.parent = uhid_misc.this_device; + +	uhid->hid = hid; +	uhid->running = true; + +	ret = hid_add_device(hid); +	if (ret) { +		hid_err(hid, "Cannot register HID device\n"); +		goto err_hid; +	} + +	return 0; + +err_hid: +	hid_destroy_device(hid); +	uhid->hid = NULL; +	uhid->running = false; +err_free: +	kfree(uhid->rd_data); +	return ret; +} +  static int uhid_dev_destroy(struct uhid_device *uhid)  {  	if (!uhid->running) @@ -435,6 +516,17 @@ static int uhid_dev_input(struct uhid_device *uhid, struct uhid_event *ev)  	return 0;  } +static int uhid_dev_input2(struct uhid_device *uhid, struct uhid_event *ev) +{ +	if (!uhid->running) +		return -EINVAL; + +	hid_input_report(uhid->hid, HID_INPUT_REPORT, ev->u.input2.data, +			 min_t(size_t, ev->u.input2.size, UHID_DATA_MAX), 0); + +	return 0; +} +  static int uhid_dev_feature_answer(struct uhid_device *uhid,  				   struct uhid_event *ev)  { @@ -571,12 +663,18 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer,  	case UHID_CREATE:  		ret = uhid_dev_create(uhid, &uhid->input_buf);  		break; +	case UHID_CREATE2: +		ret = uhid_dev_create2(uhid, &uhid->input_buf); +		break;  	case UHID_DESTROY:  		ret = uhid_dev_destroy(uhid);  		break;  	case UHID_INPUT:  		ret = uhid_dev_input(uhid, &uhid->input_buf);  		break; +	case UHID_INPUT2: +		ret = uhid_dev_input2(uhid, &uhid->input_buf); +		break;  	case UHID_FEATURE_ANSWER:  		ret = uhid_dev_feature_answer(uhid, &uhid->input_buf);  		break; @@ -615,7 +713,7 @@ static const struct file_operations uhid_fops = {  static struct miscdevice uhid_misc = {  	.fops		= &uhid_fops, -	.minor		= MISC_DYNAMIC_MINOR, +	.minor		= UHID_MINOR,  	.name		= UHID_NAME,  }; @@ -634,4 +732,5 @@ module_exit(uhid_exit);  MODULE_LICENSE("GPL");  MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");  MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem"); +MODULE_ALIAS_MISCDEV(UHID_MINOR);  MODULE_ALIAS("devname:" UHID_NAME); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 44df131d390..7b88f4cb990 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -884,52 +884,66 @@ static int usbhid_get_raw_report(struct hid_device *hid,  	return ret;  } -static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, -		unsigned char report_type) +static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, +				 __u8 *buf, size_t count, unsigned char rtype)  {  	struct usbhid_device *usbhid = hid->driver_data;  	struct usb_device *dev = hid_to_usb_dev(hid);  	struct usb_interface *intf = usbhid->intf;  	struct usb_host_interface *interface = intf->cur_altsetting; -	int ret; +	int ret, skipped_report_id = 0; -	if (usbhid->urbout && report_type != HID_FEATURE_REPORT) { -		int actual_length; -		int skipped_report_id = 0; +	/* Byte 0 is the report number. Report data starts at byte 1.*/ +	if ((rtype == HID_OUTPUT_REPORT) && +	    (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID)) +		buf[0] = 0; +	else +		buf[0] = reportnum; + +	if (buf[0] == 0x0) { +		/* Don't send the Report ID */ +		buf++; +		count--; +		skipped_report_id = 1; +	} -		if (buf[0] == 0x0) { -			/* Don't send the Report ID */ -			buf++; -			count--; -			skipped_report_id = 1; -		} -		ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, -			buf, count, &actual_length, -			USB_CTRL_SET_TIMEOUT); -		/* return the number of bytes transferred */ -		if (ret == 0) { -			ret = actual_length; -			/* count also the report id */ -			if (skipped_report_id) -				ret++; -		} -	} else { -		int skipped_report_id = 0; -		int report_id = buf[0]; -		if (buf[0] == 0x0) { -			/* Don't send the Report ID */ -			buf++; -			count--; -			skipped_report_id = 1; -		} -		ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), +	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),  			HID_REQ_SET_REPORT,  			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, -			((report_type + 1) << 8) | report_id, +			((rtype + 1) << 8) | reportnum,  			interface->desc.bInterfaceNumber, buf, count,  			USB_CTRL_SET_TIMEOUT); -		/* count also the report id, if this was a numbered report. */ -		if (ret > 0 && skipped_report_id) +	/* count also the report id, if this was a numbered report. */ +	if (ret > 0 && skipped_report_id) +		ret++; + +	return ret; +} + +static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) +{ +	struct usbhid_device *usbhid = hid->driver_data; +	struct usb_device *dev = hid_to_usb_dev(hid); +	int actual_length, skipped_report_id = 0, ret; + +	if (!usbhid->urbout) +		return -ENOSYS; + +	if (buf[0] == 0x0) { +		/* Don't send the Report ID */ +		buf++; +		count--; +		skipped_report_id = 1; +	} + +	ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, +				buf, count, &actual_length, +				USB_CTRL_SET_TIMEOUT); +	/* return the number of bytes transferred */ +	if (ret == 0) { +		ret = actual_length; +		/* count also the report id */ +		if (skipped_report_id)  			ret++;  	} @@ -1200,6 +1214,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r  	}  } +static int usbhid_raw_request(struct hid_device *hid, unsigned char reportnum, +			      __u8 *buf, size_t len, unsigned char rtype, +			      int reqtype) +{ +	switch (reqtype) { +	case HID_REQ_GET_REPORT: +		return usbhid_get_raw_report(hid, reportnum, buf, len, rtype); +	case HID_REQ_SET_REPORT: +		return usbhid_set_raw_report(hid, reportnum, buf, len, rtype); +	default: +		return -EIO; +	} +} +  static int usbhid_idle(struct hid_device *hid, int report, int idle,  		int reqtype)  { @@ -1223,6 +1251,8 @@ static struct hid_ll_driver usb_hid_driver = {  	.power = usbhid_power,  	.request = usbhid_request,  	.wait = usbhid_wait_io, +	.raw_request = usbhid_raw_request, +	.output_report = usbhid_output_report,  	.idle = usbhid_idle,  }; @@ -1253,8 +1283,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *  	usb_set_intfdata(intf, hid);  	hid->ll_driver = &usb_hid_driver; -	hid->hid_get_raw_report = usbhid_get_raw_report; -	hid->hid_output_raw_report = usbhid_output_raw_report;  	hid->ff_init = hid_pidff_init;  #ifdef CONFIG_USB_HIDDEV  	hid->hiddev_connect = hiddev_connect; diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 07345521f42..31e6727cd00 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -49,6 +49,7 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, +	{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },  	{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, @@ -74,7 +75,10 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET }, +	{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS }, @@ -84,6 +88,10 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },  	{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET }, +	{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH, HID_QUIRK_NOGET }, +	{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH, HID_QUIRK_NOGET }, +	{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS_TS, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET }, @@ -110,6 +118,12 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, +	{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },  	{ 0, 0 }  }; diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 796086980f4..9a332e683db 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -146,7 +146,7 @@ static void usb_kbd_irq(struct urb *urb)  				input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);  			else  				hid_info(urb->dev, -					 "Unknown key (scancode %#x) released.\n", +					 "Unknown key (scancode %#x) pressed.\n",  					 kbd->new[i]);  		}  	}  | 
