diff options
Diffstat (limited to 'drivers/hid')
| -rw-r--r-- | drivers/hid/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/hid/Makefile | 1 | ||||
| -rw-r--r-- | drivers/hid/hid-core.c | 29 | ||||
| -rw-r--r-- | drivers/hid/hid-debug.c | 91 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 16 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 19 | ||||
| -rw-r--r-- | drivers/hid/hid-multitouch.c | 5 | ||||
| -rw-r--r-- | drivers/hid/hid-rmi.c | 920 | ||||
| -rw-r--r-- | drivers/hid/hid-saitek.c | 154 | ||||
| -rw-r--r-- | drivers/hid/hid-sensor-hub.c | 14 | ||||
| -rw-r--r-- | drivers/hid/hid-sony.c | 434 | ||||
| -rw-r--r-- | drivers/hid/hid-thingm.c | 361 | ||||
| -rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 45 | ||||
| -rw-r--r-- | drivers/hid/uhid.c | 5 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 2 | 
15 files changed, 1808 insertions, 301 deletions
| diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7af9d0b5dea1..800c8b60f7a2 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -608,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" @@ -657,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 diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fc712dde02a4..a6fa6baf368e 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -97,6 +97,7 @@ 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-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-core.c b/drivers/hid/hid-core.c index 10a2c0866459..8ed66fd1ea87 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -779,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;  } @@ -842,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; @@ -1253,7 +1271,8 @@ EXPORT_SYMBOL_GPL(hid_output_report);  static int hid_report_len(struct hid_report *report)  { -	return ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; +	/* equivalent to DIV_ROUND_UP(report->size, 8) + !!(report->id > 0) */ +	return ((report->size - 1) >> 3) + 1 + (report->id > 0);  }  /* @@ -1266,7 +1285,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags)  	 * of implement() working on 8 byte chunks  	 */ -	int len = hid_report_len(report); +	int len = hid_report_len(report) + 7;  	return kmalloc(len, flags);  } @@ -1867,7 +1886,11 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ 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) }, diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 53b771d5683c..84c3cb15ccdd 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" }, @@ -855,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-ids.h b/drivers/hid/hid-ids.h index c8af7202c28d..6d00bb9366fa 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -301,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 @@ -460,6 +463,7 @@  #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 @@ -630,6 +634,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 @@ -761,11 +768,16 @@  #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 @@ -834,6 +846,10 @@  #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 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e7e8b19a9284..2619f7f4517a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -684,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; @@ -721,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; @@ -761,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; @@ -768,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; @@ -781,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; @@ -789,12 +805,15 @@ 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; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 35278e43c7a4..51e25b9407f2 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1155,6 +1155,11 @@ static const struct hid_device_id mt_devices[] = {  		MT_USB_DEVICE(USB_VENDOR_ID_DWAV,  			USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, +	/* 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, diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c new file mode 100644 index 000000000000..2451c7e5febd --- /dev/null +++ b/drivers/hid/hid-rmi.c @@ -0,0 +1,920 @@ +/* + *  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; +} + +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); +} + +#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-saitek.c b/drivers/hid/hid-saitek.c index 37961c7e397d..69cca1476a0c 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 af8244b1c1f4..a8d5c8faf8cf 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -705,8 +705,20 @@ static const struct hid_device_id sensor_hub_devices[] = {  	{ 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), +			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) }, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 908de2789219..2259eaa8b988 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -33,6 +33,7 @@  #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" @@ -717,8 +718,39 @@ static enum power_supply_property sony_battery_props[] = {  	POWER_SUPPLY_PROP_STATUS,  }; +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]; +}; +  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; @@ -728,6 +760,7 @@ struct sony_sc {  	unsigned long quirks;  	struct work_struct state_worker;  	struct power_supply battery; +	int device_id;  #ifdef CONFIG_SONY_FF  	__u8 left; @@ -740,6 +773,8 @@ struct sony_sc {  	__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;  }; @@ -1048,6 +1083,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)  				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 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 = @@ -1066,19 +1147,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)  	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);  } -static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count) +static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)  { -	struct sony_sc *drv_data = hid_get_drvdata(hdev);  	int n;  	BUG_ON(count > MAX_LEDS); -	if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) { -		buzz_set_leds(hdev, leds); +	if (sc->quirks & BUZZ_CONTROLLER && count == 4) { +		buzz_set_leds(sc->hdev, leds);  	} else {  		for (n = 0; n < count; n++) -			drv_data->led_state[n] = leds[n]; -		schedule_work(&drv_data->state_worker); +			sc->led_state[n] = leds[n]; +		schedule_work(&sc->state_worker);  	}  } @@ -1090,6 +1170,7 @@ static void sony_led_set_brightness(struct led_classdev *led,  	struct sony_sc *drv_data;  	int n; +	int force_update;  	drv_data = hid_get_drvdata(hdev);  	if (!drv_data) { @@ -1097,12 +1178,29 @@ static void sony_led_set_brightness(struct led_classdev *led,  		return;  	} +	/* +	 * 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]) { -			if (value != drv_data->led_state[n]) { -				drv_data->led_state[n] = value; -				sony_set_leds(hdev, drv_data->led_state, drv_data->led_count); -			} +		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;  		}  	} @@ -1130,63 +1228,112 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)  	return LED_OFF;  } -static void sony_leds_remove(struct hid_device *hdev) +static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, +				unsigned long *delay_off)  { -	struct sony_sc *drv_data; -	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 = hid_get_drvdata(hdev);  	int n; +	__u8 new_on, new_off; -	drv_data = hid_get_drvdata(hdev); -	BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT)); +	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++) { -		led = drv_data->leds[n]; -		drv_data->leds[n] = NULL; +		if (led == drv_data->leds[n]) +			break; +	} + +	/* 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 void sony_leds_remove(struct sony_sc *sc) +{ +	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);  	} -	drv_data->led_count = 0; +	sc->led_count = 0;  } -static int sony_leds_init(struct hid_device *hdev) +static int sony_leds_init(struct sony_sc *sc)  { -	struct sony_sc *drv_data; +	struct hid_device *hdev = sc->hdev;  	int n, ret = 0; -	int max_brightness; -	int use_colors; +	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 color_str[] = { "red", "green", "blue" }; -	static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 }; +	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 & SONY_LED_SUPPORT)); +	BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); -	if (drv_data->quirks & BUZZ_CONTROLLER) { -		drv_data->led_count = 4; -		max_brightness = 1; -		use_colors = 0; +	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 (drv_data->quirks & DUALSHOCK4_CONTROLLER) { -		drv_data->led_count = 3; -		max_brightness = 255; -		use_colors = 1; +	} 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 { -		drv_data->led_count = 4; -		max_brightness = 1; -		use_colors = 0; +		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";  	} @@ -1196,14 +1343,14 @@ static int sony_leds_init(struct hid_device *hdev)  	 * only relevant if the driver is loaded after somebody actively set the  	 * LEDs to on  	 */ -	sony_set_leds(hdev, initial_values, drv_data->led_count); +	sony_set_leds(sc, initial_values, sc->led_count);  	name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1; -	for (n = 0; n < drv_data->led_count; n++) { +	for (n = 0; n < sc->led_count; n++) { -		if (use_colors) -			name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2; +		if (use_ds4_names) +			name_sz = strlen(dev_name(&hdev->dev)) + strlen(ds4_name_str[n]) + 2;  		led = kzalloc(sizeof(struct led_classdev) + name_sz, GFP_KERNEL);  		if (!led) { @@ -1213,30 +1360,35 @@ static int sony_leds_init(struct hid_device *hdev)  		}  		name = (void *)(&led[1]); -		if (use_colors) -			snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), color_str[n]); +		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 = max_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; + +		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;  		} - -		drv_data->leds[n] = led;  	}  	return ret;  error_leds: -	sony_leds_remove(hdev); +	sony_leds_remove(sc);  	return ret;  } @@ -1244,29 +1396,52 @@ error_leds:  static void sixaxis_state_worker(struct work_struct *work)  {  	struct sony_sc *sc = container_of(work, struct sony_sc, state_worker); -	unsigned char 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 +	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 +		}  	};  #ifdef CONFIG_SONY_FF -	buf[3] = sc->right ? 1 : 0; -	buf[5] = sc->left; +	report.data.rumble.right_motor_on = sc->right ? 1 : 0; +	report.data.rumble.left_motor_force = sc->left;  #endif -	buf[10] |= sc->led_state[0] << 1; -	buf[10] |= sc->led_state[1] << 2; -	buf[10] |= sc->led_state[2] << 3; -	buf[10] |= sc->led_state[3] << 4; +	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; -	hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT, -			HID_REQ_SET_REPORT); +	/* +	 * 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++) { +		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) @@ -1279,7 +1454,7 @@ static void dualshock4_state_worker(struct work_struct *work)  	if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {  		buf[0] = 0x05; -		buf[1] = 0x03; +		buf[1] = 0xFF;  		offset = 4;  	} else {  		buf[0] = 0x11; @@ -1295,9 +1470,18 @@ static void dualshock4_state_worker(struct work_struct *work)  	offset += 2;  #endif -	buf[offset++] = sc->led_state[0]; -	buf[offset++] = sc->led_state[1]; -	buf[offset++] = sc->led_state[2]; +	/* 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); @@ -1323,9 +1507,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,  	return 0;  } -static int sony_init_ff(struct hid_device *hdev) +static int sony_init_ff(struct sony_sc *sc)  { -	struct hid_input *hidinput = list_entry(hdev->inputs.next, +	struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,  						struct hid_input, list);  	struct input_dev *input_dev = hidinput->input; @@ -1334,7 +1518,7 @@ static int sony_init_ff(struct hid_device *hdev)  }  #else -static int sony_init_ff(struct hid_device *hdev) +static int sony_init_ff(struct sony_sc *sc)  {  	return 0;  } @@ -1384,8 +1568,6 @@ static int sony_battery_get_property(struct power_supply *psy,  static int sony_battery_probe(struct sony_sc *sc)  { -	static atomic_t power_id_seq = ATOMIC_INIT(0); -	unsigned long power_id;  	struct hid_device *hdev = sc->hdev;  	int ret; @@ -1395,15 +1577,13 @@ static int sony_battery_probe(struct sony_sc *sc)  	 */  	sc->battery_capacity = 100; -	power_id = (unsigned long)atomic_inc_return(&power_id_seq); -  	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_%lu", -				     power_id); +	sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR", +				     sc->mac_address);  	if (!sc->battery.name)  		return -ENOMEM; @@ -1578,6 +1758,52 @@ static int sony_check_add(struct sony_sc *sc)  	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)  { @@ -1615,6 +1841,12 @@ 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) {  		/*  		 * The Sony Sixaxis does not handle HID Output Reports on the @@ -1629,8 +1861,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;  		hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;  		ret = sixaxis_set_operational_usb(hdev); -		sc->worker_initialized = 1; -		INIT_WORK(&sc->state_worker, sixaxis_state_worker); +		sony_init_work(sc, sixaxis_state_worker);  	} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {  		/*  		 * The Sixaxis wants output reports sent on the ctrl endpoint @@ -1638,8 +1869,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  		 */  		hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;  		ret = sixaxis_set_operational_bt(hdev); -		sc->worker_initialized = 1; -		INIT_WORK(&sc->state_worker, sixaxis_state_worker); +		sony_init_work(sc, sixaxis_state_worker);  	} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {  		if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {  			/* @@ -1661,8 +1891,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  		if (ret < 0)  			goto err_stop; -		sc->worker_initialized = 1; -		INIT_WORK(&sc->state_worker, dualshock4_state_worker); +		sony_init_work(sc, dualshock4_state_worker);  	} else {  		ret = 0;  	} @@ -1675,7 +1904,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  		goto err_stop;  	if (sc->quirks & SONY_LED_SUPPORT) { -		ret = sony_leds_init(hdev); +		ret = sony_leds_init(sc);  		if (ret < 0)  			goto err_stop;  	} @@ -1694,7 +1923,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)  	}  	if (sc->quirks & SONY_FF_SUPPORT) { -		ret = sony_init_ff(hdev); +		ret = sony_init_ff(sc);  		if (ret < 0)  			goto err_close;  	} @@ -1704,12 +1933,12 @@ err_close:  	hid_hw_close(hdev);  err_stop:  	if (sc->quirks & SONY_LED_SUPPORT) -		sony_leds_remove(hdev); +		sony_leds_remove(sc);  	if (sc->quirks & SONY_BATTERY_SUPPORT)  		sony_battery_remove(sc); -	if (sc->worker_initialized) -		cancel_work_sync(&sc->state_worker); +	sony_cancel_work_sync(sc);  	sony_remove_dev_list(sc); +	sony_release_device_id(sc);  	hid_hw_stop(hdev);  	return ret;  } @@ -1719,18 +1948,19 @@ static void sony_remove(struct hid_device *hdev)  	struct sony_sc *sc = hid_get_drvdata(hdev);  	if (sc->quirks & SONY_LED_SUPPORT) -		sony_leds_remove(hdev); +		sony_leds_remove(sc);  	if (sc->quirks & SONY_BATTERY_SUPPORT) {  		hid_hw_close(hdev);  		sony_battery_remove(sc);  	} -	if (sc->worker_initialized) -		cancel_work_sync(&sc->state_worker); +	sony_cancel_work_sync(sc);  	sony_remove_dev_list(sc); +	sony_release_device_id(sc); +  	hid_hw_stop(hdev);  } @@ -1775,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 a97c78845f7b..134be89b15ea 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 = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE, -				 HID_FEATURE_REPORT, HID_REQ_SET_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/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index b50860db92f1..21aafc8f48c8 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -807,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, @@ -843,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;  } diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 0d078c32db4f..0cb92e347258 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -441,12 +441,11 @@ static int uhid_dev_create2(struct uhid_device *uhid,  	if (uhid->rd_size <= 0 || uhid->rd_size > HID_MAX_DESCRIPTOR_SIZE)  		return -EINVAL; -	uhid->rd_data = kmalloc(uhid->rd_size, GFP_KERNEL); +	uhid->rd_data = kmemdup(ev->u.create2.rd_data, uhid->rd_size, +				GFP_KERNEL);  	if (!uhid->rd_data)  		return -ENOMEM; -	memcpy(uhid->rd_data, ev->u.create2.rd_data, uhid->rd_size); -  	hid = hid_allocate_device();  	if (IS_ERR(hid)) {  		ret = PTR_ERR(hid); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index dbd83878ff99..59badc10a08c 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -115,10 +115,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 }  }; | 
