diff options
Diffstat (limited to 'drivers/hid/hid-input.c')
| -rw-r--r-- | drivers/hid/hid-input.c | 684 | 
1 files changed, 620 insertions, 64 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index bb0b3659437..2619f7f4517 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -32,6 +32,8 @@  #include <linux/hid.h>  #include <linux/hid-debug.h> +#include "hid-ids.h" +  #define unk	KEY_UNKNOWN  static const unsigned char hid_keyboard[256] = { @@ -44,11 +46,11 @@ static const unsigned char hid_keyboard[256] = {  	 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,  	191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,  	115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk, -	122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, +	122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk,  	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,  	unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk,  	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, -	unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, +	unk,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk,unk,unk,unk,unk,  	 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,  	150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk  }; @@ -174,7 +176,7 @@ static int hidinput_setkeycode(struct input_dev *dev,  		clear_bit(*old_keycode, dev->keybit);  		set_bit(usage->code, dev->keybit); -		dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", +		dbg_hid("Assigned keycode %d to HID usage code %x\n",  			usage->code, usage->hid);  		/* @@ -203,10 +205,10 @@ static int hidinput_setkeycode(struct input_dev *dev,   *   * as seen in the HID specification v1.11 6.2.2.7 Global Items.   * - * Only exponent 1 length units are processed. Centimeters are converted to - * inches. Degrees are converted to radians. + * Only exponent 1 length units are processed. Centimeters and inches are + * converted to millimeters. Degrees are converted to radians.   */ -static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) +__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)  {  	__s32 unit_exponent = field->unit_exponent;  	__s32 logical_extents = field->logical_maximum - @@ -223,18 +225,36 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)  	 * Verify and convert units.  	 * See HID specification v1.11 6.2.2.7 Global Items for unit decoding  	 */ -	if (code == ABS_X || code == ABS_Y || code == ABS_Z) { +	switch (code) { +	case ABS_X: +	case ABS_Y: +	case ABS_Z: +	case ABS_MT_POSITION_X: +	case ABS_MT_POSITION_Y: +	case ABS_MT_TOOL_X: +	case ABS_MT_TOOL_Y: +	case ABS_MT_TOUCH_MAJOR: +	case ABS_MT_TOUCH_MINOR:  		if (field->unit == 0x11) {		/* If centimeters */ -			/* Convert to inches */ -			prev = logical_extents; -			logical_extents *= 254; -			if (logical_extents < prev) +			/* Convert to millimeters */ +			unit_exponent += 1; +		} else if (field->unit == 0x13) {	/* If inches */ +			/* Convert to millimeters */ +			prev = physical_extents; +			physical_extents *= 254; +			if (physical_extents < prev)  				return 0; -			unit_exponent += 2; -		} else if (field->unit != 0x13) {	/* If not inches */ +			unit_exponent -= 1; +		} else {  			return 0;  		} -	} else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) { +		break; + +	case ABS_RX: +	case ABS_RY: +	case ABS_RZ: +	case ABS_TILT_X: +	case ABS_TILT_Y:  		if (field->unit == 0x14) {		/* If degrees */  			/* Convert to radians */  			prev = logical_extents; @@ -245,7 +265,9 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)  		} else if (field->unit != 0x12) {	/* If not radians */  			return 0;  		} -	} else { +		break; + +	default:  		return 0;  	} @@ -265,9 +287,186 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)  	}  	/* Calculate resolution */ -	return logical_extents / physical_extents; +	return DIV_ROUND_CLOSEST(logical_extents, physical_extents); +} +EXPORT_SYMBOL_GPL(hidinput_calc_abs_res); + +#ifdef CONFIG_HID_BATTERY_STRENGTH +static enum power_supply_property hidinput_battery_props[] = { +	POWER_SUPPLY_PROP_PRESENT, +	POWER_SUPPLY_PROP_ONLINE, +	POWER_SUPPLY_PROP_CAPACITY, +	POWER_SUPPLY_PROP_MODEL_NAME, +	POWER_SUPPLY_PROP_STATUS, +	POWER_SUPPLY_PROP_SCOPE, +}; + +#define HID_BATTERY_QUIRK_PERCENT	(1 << 0) /* always reports percent */ +#define HID_BATTERY_QUIRK_FEATURE	(1 << 1) /* ask for feature report */ + +static const struct hid_device_id hid_battery_quirks[] = { +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, +			USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), +	HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, +			       USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), +	  HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, +		USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), +	  HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, +	{} +}; + +static unsigned find_battery_quirk(struct hid_device *hdev) +{ +	unsigned quirks = 0; +	const struct hid_device_id *match; + +	match = hid_match_id(hdev, hid_battery_quirks); +	if (match != NULL) +		quirks = match->driver_data; + +	return quirks; +} + +static int hidinput_get_battery_property(struct power_supply *psy, +					 enum power_supply_property prop, +					 union power_supply_propval *val) +{ +	struct hid_device *dev = container_of(psy, struct hid_device, battery); +	int ret = 0; +	__u8 *buf; + +	switch (prop) { +	case POWER_SUPPLY_PROP_PRESENT: +	case POWER_SUPPLY_PROP_ONLINE: +		val->intval = 1; +		break; + +	case POWER_SUPPLY_PROP_CAPACITY: + +		buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); +		if (!buf) { +			ret = -ENOMEM; +			break; +		} +		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; +			kfree(buf); +			break; +		} +		ret = 0; + +		if (dev->battery_min < dev->battery_max && +		    buf[1] >= dev->battery_min && +		    buf[1] <= dev->battery_max) +			val->intval = (100 * (buf[1] - dev->battery_min)) / +				(dev->battery_max - dev->battery_min); +		kfree(buf); +		break; + +	case POWER_SUPPLY_PROP_MODEL_NAME: +		val->strval = dev->name; +		break; + +	case POWER_SUPPLY_PROP_STATUS: +		val->intval = POWER_SUPPLY_STATUS_DISCHARGING; +		break; + +	case POWER_SUPPLY_PROP_SCOPE: +		val->intval = POWER_SUPPLY_SCOPE_DEVICE; +		break; + +	default: +		ret = -EINVAL; +		break; +	} + +	return ret;  } +static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) +{ +	struct power_supply *battery = &dev->battery; +	int ret; +	unsigned quirks; +	s32 min, max; + +	if (field->usage->hid != HID_DC_BATTERYSTRENGTH) +		return false;	/* no match */ + +	if (battery->name != NULL) +		goto out;	/* already initialized? */ + +	battery->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); +	if (battery->name == NULL) +		goto out; + +	battery->type = POWER_SUPPLY_TYPE_BATTERY; +	battery->properties = hidinput_battery_props; +	battery->num_properties = ARRAY_SIZE(hidinput_battery_props); +	battery->use_for_apm = 0; +	battery->get_property = hidinput_get_battery_property; + +	quirks = find_battery_quirk(dev); + +	hid_dbg(dev, "device %x:%x:%x %d quirks %d\n", +		dev->bus, dev->vendor, dev->product, dev->version, quirks); + +	min = field->logical_minimum; +	max = field->logical_maximum; + +	if (quirks & HID_BATTERY_QUIRK_PERCENT) { +		min = 0; +		max = 100; +	} + +	if (quirks & HID_BATTERY_QUIRK_FEATURE) +		report_type = HID_FEATURE_REPORT; + +	dev->battery_min = min; +	dev->battery_max = max; +	dev->battery_report_type = report_type; +	dev->battery_report_id = field->report->id; + +	ret = power_supply_register(&dev->dev, battery); +	if (ret != 0) { +		hid_warn(dev, "can't register power supply: %d\n", ret); +		kfree(battery->name); +		battery->name = NULL; +	} + +	power_supply_powers(battery, &dev->dev); + +out: +	return true; +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ +	if (!dev->battery.name) +		return; + +	power_supply_unregister(&dev->battery); +	kfree(dev->battery.name); +	dev->battery.name = NULL; +} +#else  /* !CONFIG_HID_BATTERY_STRENGTH */ +static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, +				   struct hid_field *field) +{ +	return false; +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ +} +#endif	/* CONFIG_HID_BATTERY_STRENGTH */ +  static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,  				     struct hid_usage *usage)  { @@ -281,6 +480,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  	if (field->flags & HID_MAIN_ITEM_CONSTANT)  		goto ignore; +	/* Ignore if report count is out of bounds. */ +	if (field->report_count < 1) +		goto ignore; +  	/* only LED usages are supported in output fields */  	if (field->report_type == HID_OUTPUT_REPORT &&  			(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { @@ -316,21 +519,26 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		switch (field->application) {  		case HID_GD_MOUSE: -		case HID_GD_POINTER:  code += 0x110; break; +		case HID_GD_POINTER:  code += BTN_MOUSE; break;  		case HID_GD_JOYSTICK:  				if (code <= 0xf)  					code += BTN_JOYSTICK;  				else -					code += BTN_TRIGGER_HAPPY; +					code += BTN_TRIGGER_HAPPY - 0x10; +				break; +		case HID_GD_GAMEPAD: +				if (code <= 0xf) +					code += BTN_GAMEPAD; +				else +					code += BTN_TRIGGER_HAPPY - 0x10;  				break; -		case HID_GD_GAMEPAD:  code += 0x130; break;  		default:  			switch (field->physical) {  			case HID_GD_MOUSE: -			case HID_GD_POINTER:  code += 0x110; break; -			case HID_GD_JOYSTICK: code += 0x120; break; -			case HID_GD_GAMEPAD:  code += 0x130; break; -			default:              code += 0x100; +			case HID_GD_POINTER:  code += BTN_MOUSE; break; +			case HID_GD_JOYSTICK: code += BTN_JOYSTICK; break; +			case HID_GD_GAMEPAD:  code += BTN_GAMEPAD; break; +			default:              code += BTN_MISC;  			}  		} @@ -354,6 +562,18 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  			case 0x1: map_key_clear(KEY_POWER);  break;  			case 0x2: map_key_clear(KEY_SLEEP);  break;  			case 0x3: map_key_clear(KEY_WAKEUP); break; +			case 0x4: map_key_clear(KEY_CONTEXT_MENU); break; +			case 0x5: map_key_clear(KEY_MENU); break; +			case 0x6: map_key_clear(KEY_PROG1); break; +			case 0x7: map_key_clear(KEY_HELP); break; +			case 0x8: map_key_clear(KEY_EXIT); break; +			case 0x9: map_key_clear(KEY_SELECT); break; +			case 0xa: map_key_clear(KEY_RIGHT); break; +			case 0xb: map_key_clear(KEY_LEFT); break; +			case 0xc: map_key_clear(KEY_UP); break; +			case 0xd: map_key_clear(KEY_DOWN); break; +			case 0xe: map_key_clear(KEY_POWER2); break; +			case 0xf: map_key_clear(KEY_RESTART); break;  			default: goto unknown;  			}  			break; @@ -444,6 +664,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  			map_key_clear(BTN_TOOL_RUBBER);  			break; +		case 0x3d: /* X Tilt */ +			map_abs_clear(ABS_TILT_X); +			break; + +		case 0x3e: /* Y Tilt */ +			map_abs_clear(ABS_TILT_Y); +			break; +  		case 0x33: /* Touch */  		case 0x42: /* TipSwitch */  		case 0x43: /* TipSwitch2 */ @@ -456,23 +684,58 @@ 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; -	case HID_UP_CONSUMER:	/* USB HUT v1.1, pages 56-62 */ +	case HID_UP_CONSUMER:	/* USB HUT v1.12, pages 75-84 */  		switch (usage->hid & HID_USAGE) {  		case 0x000: goto ignore; +		case 0x030: map_key_clear(KEY_POWER);		break; +		case 0x031: map_key_clear(KEY_RESTART);		break; +		case 0x032: map_key_clear(KEY_SLEEP);		break;  		case 0x034: map_key_clear(KEY_SLEEP);		break; +		case 0x035: map_key_clear(KEY_KBDILLUMTOGGLE);	break;  		case 0x036: map_key_clear(BTN_MISC);		break; -		case 0x040: map_key_clear(KEY_MENU);		break; -		case 0x045: map_key_clear(KEY_RADIO);		break; - +		case 0x040: map_key_clear(KEY_MENU);		break; /* Menu */ +		case 0x041: map_key_clear(KEY_SELECT);		break; /* Menu Pick */ +		case 0x042: map_key_clear(KEY_UP);		break; /* Menu Up */ +		case 0x043: map_key_clear(KEY_DOWN);		break; /* Menu Down */ +		case 0x044: map_key_clear(KEY_LEFT);		break; /* Menu Left */ +		case 0x045: map_key_clear(KEY_RIGHT);		break; /* Menu Right */ +		case 0x046: map_key_clear(KEY_ESC);		break; /* Menu Escape */ +		case 0x047: map_key_clear(KEY_KPPLUS);		break; /* Menu Value Increase */ +		case 0x048: map_key_clear(KEY_KPMINUS);		break; /* Menu Value Decrease */ + +		case 0x060: map_key_clear(KEY_INFO);		break; /* Data On Screen */ +		case 0x061: map_key_clear(KEY_SUBTITLE);	break; /* Closed Caption */ +		case 0x063: map_key_clear(KEY_VCR);		break; /* VCR/TV */ +		case 0x065: map_key_clear(KEY_CAMERA);		break; /* Snapshot */ +		case 0x069: map_key_clear(KEY_RED);		break; +		case 0x06a: map_key_clear(KEY_GREEN);		break; +		case 0x06b: map_key_clear(KEY_BLUE);		break; +		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;  		case 0x088: map_key_clear(KEY_PC);		break;  		case 0x089: map_key_clear(KEY_TV);		break;  		case 0x08a: map_key_clear(KEY_WWW);		break; @@ -506,14 +769,19 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x0b7: map_key_clear(KEY_STOPCD);		break;  		case 0x0b8: map_key_clear(KEY_EJECTCD);		break;  		case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT);	break; +		case 0x0b9: map_key_clear(KEY_SHUFFLE);		break; +		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;  		case 0x0e9: map_key_clear(KEY_VOLUMEUP);	break;  		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; @@ -527,18 +795,29 @@ 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;  		case 0x194: map_key_clear(KEY_FILE);		break;  		case 0x196: map_key_clear(KEY_WWW);		break;  		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 0x1b6: map_key_clear(KEY_MEDIA);		break; -		case 0x1b7: map_key_clear(KEY_SOUND);		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;  		case 0x1bc: map_key_clear(KEY_MESSENGER);	break;  		case 0x1bd: map_key_clear(KEY_INFO);		break;  		case 0x201: map_key_clear(KEY_NEW);		break; @@ -567,7 +846,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case 0x233: map_key_clear(KEY_SCROLLUP);	break;  		case 0x234: map_key_clear(KEY_SCROLLDOWN);	break;  		case 0x238: map_rel(REL_HWHEEL);		break; +		case 0x23d: map_key_clear(KEY_EDIT);		break;  		case 0x25f: map_key_clear(KEY_CANCEL);		break; +		case 0x269: map_key_clear(KEY_INSERT);		break; +		case 0x26a: map_key_clear(KEY_DELETE);		break;  		case 0x279: map_key_clear(KEY_REDO);		break;  		case 0x289: map_key_clear(KEY_REPLY);		break; @@ -578,6 +860,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		}  		break; +	case HID_UP_GENDEVCTRLS: +		if (hidinput_setup_battery(device, HID_INPUT_REPORT, field)) +			goto ignore; +		else +			goto unknown; +		break; +  	case HID_UP_HPVENDOR:	/* Reported on a Dutch layout HP5308 */  		set_bit(EV_REP, input->evbit);  		switch (usage->hid & HID_USAGE) { @@ -597,6 +886,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		}  		break; +	case HID_UP_HPVENDOR2: +		set_bit(EV_REP, input->evbit); +		switch (usage->hid & HID_USAGE) { +		case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN);	break; +		case 0x004: map_key_clear(KEY_BRIGHTNESSUP);	break; +		default:    goto ignore; +		} +		break; +  	case HID_UP_MSVENDOR:  		goto ignore; @@ -771,6 +1069,22 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct  		return;  	} +	/* +	 * Ignore out-of-range values as per HID specification, +	 * section 5.10 and 6.2.25. +	 * +	 * The logical_minimum < logical_maximum check is done so that we +	 * don't unintentionally discard values sent by devices which +	 * don't specify logical min and max. +	 */ +	if ((field->flags & HID_MAIN_ITEM_VARIABLE) && +	    (field->logical_minimum < field->logical_maximum) && +	    (value < field->logical_minimum || +	     value > field->logical_maximum)) { +		dbg_hid("Ignoring out-of-range value %x\n", value); +		return; +	} +  	/* report the usage code as scancode if the key status has changed */  	if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)  		input_event(input, EV_MSC, MSC_SCAN, usage->hid); @@ -810,18 +1124,249 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int  }  EXPORT_SYMBOL_GPL(hidinput_find_field); +struct hid_field *hidinput_get_led_field(struct hid_device *hid) +{ +	struct hid_report *report; +	struct hid_field *field; +	int i, j; + +	list_for_each_entry(report, +			    &hid->report_enum[HID_OUTPUT_REPORT].report_list, +			    list) { +		for (i = 0; i < report->maxfield; i++) { +			field = report->field[i]; +			for (j = 0; j < field->maxusage; j++) +				if (field->usage[j].type == EV_LED) +					return field; +		} +	} +	return NULL; +} +EXPORT_SYMBOL_GPL(hidinput_get_led_field); + +unsigned int hidinput_count_leds(struct hid_device *hid) +{ +	struct hid_report *report; +	struct hid_field *field; +	int i, j; +	unsigned int count = 0; + +	list_for_each_entry(report, +			    &hid->report_enum[HID_OUTPUT_REPORT].report_list, +			    list) { +		for (i = 0; i < report->maxfield; i++) { +			field = report->field[i]; +			for (j = 0; j < field->maxusage; j++) +				if (field->usage[j].type == EV_LED && +				    field->value[j]) +					count += 1; +		} +	} +	return count; +} +EXPORT_SYMBOL_GPL(hidinput_count_leds); + +static void hidinput_led_worker(struct work_struct *work) +{ +	struct hid_device *hid = container_of(work, struct hid_device, +					      led_work); +	struct hid_field *field; +	struct hid_report *report; +	int len, ret; +	__u8 *buf; + +	field = hidinput_get_led_field(hid); +	if (!field) +		return; + +	/* +	 * field->report is accessed unlocked regarding HID core. So there might +	 * be another incoming SET-LED request from user-space, which changes +	 * the LED state while we assemble our outgoing buffer. However, this +	 * doesn't matter as hid_output_report() correctly converts it into a +	 * boolean value no matter what information is currently set on the LED +	 * field (even garbage). So the remote device will always get a valid +	 * request. +	 * And in case we send a wrong value, a next led worker is spawned +	 * for every SET-LED request so the following worker will send the +	 * correct value, guaranteed! +	 */ + +	report = field->report; + +	/* use custom SET_REPORT request if possible (asynchronous) */ +	if (hid->ll_driver->request) +		return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); + +	/* fall back to generic raw-output-report */ +	len = ((report->size - 1) >> 3) + 1 + (report->id > 0); +	buf = hid_alloc_report_buf(report, GFP_KERNEL); +	if (!buf) +		return; + +	hid_output_report(report, buf); +	/* synchronous 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); +} + +static int hidinput_input_event(struct input_dev *dev, unsigned int type, +				unsigned int code, int value) +{ +	struct hid_device *hid = input_get_drvdata(dev); +	struct hid_field *field; +	int offset; + +	if (type == EV_FF) +		return input_ff_event(dev, type, code, value); + +	if (type != EV_LED) +		return -1; + +	if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { +		hid_warn(dev, "event field not found\n"); +		return -1; +	} + +	hid_set_field(field, offset, value); + +	schedule_work(&hid->led_work); +	return 0; +} +  static int hidinput_open(struct input_dev *dev)  {  	struct hid_device *hid = input_get_drvdata(dev); -	return hid->ll_driver->open(hid); +	return hid_hw_open(hid);  }  static void hidinput_close(struct input_dev *dev)  {  	struct hid_device *hid = input_get_drvdata(dev); -	hid->ll_driver->close(hid); +	hid_hw_close(hid); +} + +static void report_features(struct hid_device *hid) +{ +	struct hid_driver *drv = hid->driver; +	struct hid_report_enum *rep_enum; +	struct hid_report *rep; +	int i, j; + +	rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; +	list_for_each_entry(rep, &rep_enum->report_list, list) +		for (i = 0; i < rep->maxfield; i++) { +			/* Ignore if report count is out of bounds. */ +			if (rep->field[i]->report_count < 1) +				continue; + +			for (j = 0; j < rep->field[i]->maxusage; j++) { +				/* Verify if Battery Strength feature is available */ +				hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); + +				if (drv->feature_mapping) +					drv->feature_mapping(hid, rep->field[i], +							     rep->field[i]->usage + j); +			} +		} +} + +static struct hid_input *hidinput_allocate(struct hid_device *hid) +{ +	struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); +	struct input_dev *input_dev = input_allocate_device(); +	if (!hidinput || !input_dev) { +		kfree(hidinput); +		input_free_device(input_dev); +		hid_err(hid, "Out of memory during hid input probe\n"); +		return NULL; +	} + +	input_set_drvdata(input_dev, hid); +	input_dev->event = hidinput_input_event; +	input_dev->open = hidinput_open; +	input_dev->close = hidinput_close; +	input_dev->setkeycode = hidinput_setkeycode; +	input_dev->getkeycode = hidinput_getkeycode; + +	input_dev->name = hid->name; +	input_dev->phys = hid->phys; +	input_dev->uniq = hid->uniq; +	input_dev->id.bustype = hid->bus; +	input_dev->id.vendor  = hid->vendor; +	input_dev->id.product = hid->product; +	input_dev->id.version = hid->version; +	input_dev->dev.parent = &hid->dev; +	hidinput->input = input_dev; +	list_add_tail(&hidinput->list, &hid->inputs); + +	return hidinput; +} + +static bool hidinput_has_been_populated(struct hid_input *hidinput) +{ +	int i; +	unsigned long r = 0; + +	for (i = 0; i < BITS_TO_LONGS(EV_CNT); i++) +		r |= hidinput->input->evbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) +		r |= hidinput->input->keybit[i]; + +	for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++) +		r |= hidinput->input->relbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++) +		r |= hidinput->input->absbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++) +		r |= hidinput->input->mscbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(LED_CNT); i++) +		r |= hidinput->input->ledbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(SND_CNT); i++) +		r |= hidinput->input->sndbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(FF_CNT); i++) +		r |= hidinput->input->ffbit[i]; + +	for (i = 0; i < BITS_TO_LONGS(SW_CNT); i++) +		r |= hidinput->input->swbit[i]; + +	return !!r; +} + +static void hidinput_cleanup_hidinput(struct hid_device *hid, +		struct hid_input *hidinput) +{ +	struct hid_report *report; +	int i, k; + +	list_del(&hidinput->list); +	input_free_device(hidinput->input); + +	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { +		if (k == HID_OUTPUT_REPORT && +			hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) +			continue; + +		list_for_each_entry(report, &hid->report_enum[k].report_list, +				    list) { + +			for (i = 0; i < report->maxfield; i++) +				if (report->field[i]->hidinput == hidinput) +					report->field[i]->hidinput = NULL; +		} +	} + +	kfree(hidinput);  }  /* @@ -832,13 +1377,13 @@ static void hidinput_close(struct input_dev *dev)  int hidinput_connect(struct hid_device *hid, unsigned int force)  { +	struct hid_driver *drv = hid->driver;  	struct hid_report *report;  	struct hid_input *hidinput = NULL; -	struct input_dev *input_dev;  	int i, j, k; -	int max_report_type = HID_OUTPUT_REPORT;  	INIT_LIST_HEAD(&hid->inputs); +	INIT_WORK(&hid->led_work, hidinput_led_worker);  	if (!force) {  		for (i = 0; i < hid->maxcollection; i++) { @@ -853,43 +1398,22 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  			return -1;  	} -	if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) -		max_report_type = HID_INPUT_REPORT; +	report_features(hid); + +	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { +		if (k == HID_OUTPUT_REPORT && +			hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) +			continue; -	for (k = HID_INPUT_REPORT; k <= max_report_type; k++)  		list_for_each_entry(report, &hid->report_enum[k].report_list, list) {  			if (!report->maxfield)  				continue;  			if (!hidinput) { -				hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); -				input_dev = input_allocate_device(); -				if (!hidinput || !input_dev) { -					kfree(hidinput); -					input_free_device(input_dev); -					err_hid("Out of memory during hid input probe"); +				hidinput = hidinput_allocate(hid); +				if (!hidinput)  					goto out_unwind; -				} - -				input_set_drvdata(input_dev, hid); -				input_dev->event = -					hid->ll_driver->hidinput_input_event; -				input_dev->open = hidinput_open; -				input_dev->close = hidinput_close; -				input_dev->setkeycode_new = hidinput_setkeycode; -				input_dev->getkeycode_new = hidinput_getkeycode; - -				input_dev->name = hid->name; -				input_dev->phys = hid->phys; -				input_dev->uniq = hid->uniq; -				input_dev->id.bustype = hid->bus; -				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; -				hidinput->input = input_dev; -				list_add_tail(&hidinput->list, &hid->inputs);  			}  			for (i = 0; i < report->maxfield; i++) @@ -897,6 +1421,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  					hidinput_configure_usage(hidinput, report->field[i],  								 report->field[i]->usage + j); +			if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && +			    !hidinput_has_been_populated(hidinput)) +				continue; +  			if (hid->quirks & HID_QUIRK_MULTI_INPUT) {  				/* This will leave hidinput NULL, so that it  				 * allocates another one if we have more inputs on @@ -904,18 +1432,38 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  				 * UGCI) cram a lot of unrelated inputs into the  				 * same interface. */  				hidinput->report = report; +				if (drv->input_configured) +					drv->input_configured(hid, hidinput);  				if (input_register_device(hidinput->input))  					goto out_cleanup;  				hidinput = NULL;  			}  		} +	} + +	if (hidinput && (hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) && +	    !hidinput_has_been_populated(hidinput)) { +		/* no need to register an input device not populated */ +		hidinput_cleanup_hidinput(hid, hidinput); +		hidinput = NULL; +	} + +	if (list_empty(&hid->inputs)) { +		hid_err(hid, "No inputs registered, leaving\n"); +		goto out_unwind; +	} -	if (hidinput && input_register_device(hidinput->input)) -		goto out_cleanup; +	if (hidinput) { +		if (drv->input_configured) +			drv->input_configured(hid, hidinput); +		if (input_register_device(hidinput->input)) +			goto out_cleanup; +	}  	return 0;  out_cleanup: +	list_del(&hidinput->list);  	input_free_device(hidinput->input);  	kfree(hidinput);  out_unwind: @@ -930,11 +1478,19 @@ void hidinput_disconnect(struct hid_device *hid)  {  	struct hid_input *hidinput, *next; +	hidinput_cleanup_battery(hid); +  	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {  		list_del(&hidinput->list);  		input_unregister_device(hidinput->input);  		kfree(hidinput);  	} + +	/* led_work is spawned by input_dev callbacks, but doesn't access the +	 * parent input_dev at all. Once all input devices are removed, we +	 * know that led_work will never get restarted, so we can cancel it +	 * synchronously and are safe. */ +	cancel_work_sync(&hid->led_work);  }  EXPORT_SYMBOL_GPL(hidinput_disconnect);  | 
