diff options
Diffstat (limited to 'drivers/hid/hid-input.c')
| -rw-r--r-- | drivers/hid/hid-input.c | 123 | 
1 files changed, 113 insertions, 10 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 930652c25120..ab93dd5927c3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1110,8 +1110,31 @@ mapped:  	set_bit(usage->type, input->evbit); -	while (usage->code <= max && test_and_set_bit(usage->code, bit)) -		usage->code = find_next_zero_bit(bit, max + 1, usage->code); +	/* +	 * This part is *really* controversial: +	 * - HID aims at being generic so we should do our best to export +	 *   all incoming events +	 * - HID describes what events are, so there is no reason for ABS_X +	 *   to be mapped to ABS_Y +	 * - HID is using *_MISC+N as a default value, but nothing prevents +	 *   *_MISC+N to overwrite a legitimate even, which confuses userspace +	 *   (for instance ABS_MISC + 7 is ABS_MT_SLOT, which has a different +	 *   processing) +	 * +	 * If devices still want to use this (at their own risk), they will +	 * have to use the quirk HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE, but +	 * the default should be a reliable mapping. +	 */ +	while (usage->code <= max && test_and_set_bit(usage->code, bit)) { +		if (device->quirks & HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE) { +			usage->code = find_next_zero_bit(bit, +							 max + 1, +							 usage->code); +		} else { +			device->status |= HID_STAT_DUP_DETECTED; +			goto ignore; +		} +	}  	if (usage->code > max)  		goto ignore; @@ -1487,15 +1510,56 @@ static void report_features(struct hid_device *hid)  		}  } -static struct hid_input *hidinput_allocate(struct hid_device *hid) +static struct hid_input *hidinput_allocate(struct hid_device *hid, +					   unsigned int application)  {  	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; +	const char *suffix = NULL; + +	if (!hidinput || !input_dev) +		goto fail; + +	if ((hid->quirks & HID_QUIRK_INPUT_PER_APP) && +	    hid->maxapplication > 1) { +		switch (application) { +		case HID_GD_KEYBOARD: +			suffix = "Keyboard"; +			break; +		case HID_GD_KEYPAD: +			suffix = "Keypad"; +			break; +		case HID_GD_MOUSE: +			suffix = "Mouse"; +			break; +		case HID_DG_STYLUS: +			suffix = "Pen"; +			break; +		case HID_DG_TOUCHSCREEN: +			suffix = "Touchscreen"; +			break; +		case HID_DG_TOUCHPAD: +			suffix = "Touchpad"; +			break; +		case HID_GD_SYSTEM_CONTROL: +			suffix = "System Control"; +			break; +		case HID_CP_CONSUMER_CONTROL: +			suffix = "Consumer Control"; +			break; +		case HID_GD_WIRELESS_RADIO_CTLS: +			suffix = "Wireless Radio Control"; +			break; +		default: +			break; +		} +	} + +	if (suffix) { +		hidinput->name = kasprintf(GFP_KERNEL, "%s %s", +					   hid->name, suffix); +		if (!hidinput->name) +			goto fail;  	}  	input_set_drvdata(input_dev, hid); @@ -1505,7 +1569,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)  	input_dev->setkeycode = hidinput_setkeycode;  	input_dev->getkeycode = hidinput_getkeycode; -	input_dev->name = hid->name; +	input_dev->name = hidinput->name ? hidinput->name : hid->name;  	input_dev->phys = hid->phys;  	input_dev->uniq = hid->uniq;  	input_dev->id.bustype = hid->bus; @@ -1513,10 +1577,19 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)  	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); +	INIT_LIST_HEAD(&hidinput->reports); +  	return hidinput; + +fail: +	kfree(hidinput); +	input_free_device(input_dev); +	hid_err(hid, "Out of memory during hid input probe\n"); +	return NULL;  }  static bool hidinput_has_been_populated(struct hid_input *hidinput) @@ -1562,6 +1635,7 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid,  	list_del(&hidinput->list);  	input_free_device(hidinput->input); +	kfree(hidinput->name);  	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {  		if (k == HID_OUTPUT_REPORT && @@ -1594,6 +1668,20 @@ static struct hid_input *hidinput_match(struct hid_report *report)  	return NULL;  } +static struct hid_input *hidinput_match_application(struct hid_report *report) +{ +	struct hid_device *hid = report->device; +	struct hid_input *hidinput; + +	list_for_each_entry(hidinput, &hid->inputs, list) { +		if (hidinput->report && +		    hidinput->report->application == report->application) +			return hidinput; +	} + +	return NULL; +} +  static inline void hidinput_configure_usages(struct hid_input *hidinput,  					     struct hid_report *report)  { @@ -1616,11 +1704,14 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  	struct hid_driver *drv = hid->driver;  	struct hid_report *report;  	struct hid_input *next, *hidinput = NULL; +	unsigned int application;  	int i, k;  	INIT_LIST_HEAD(&hid->inputs);  	INIT_WORK(&hid->led_work, hidinput_led_worker); +	hid->status &= ~HID_STAT_DUP_DETECTED; +  	if (!force) {  		for (i = 0; i < hid->maxcollection; i++) {  			struct hid_collection *col = &hid->collection[i]; @@ -1646,15 +1737,20 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  			if (!report->maxfield)  				continue; +			application = report->application; +  			/*  			 * Find the previous hidinput report attached  			 * to this report id.  			 */  			if (hid->quirks & HID_QUIRK_MULTI_INPUT)  				hidinput = hidinput_match(report); +			else if (hid->maxapplication > 1 && +				 (hid->quirks & HID_QUIRK_INPUT_PER_APP)) +				hidinput = hidinput_match_application(report);  			if (!hidinput) { -				hidinput = hidinput_allocate(hid); +				hidinput = hidinput_allocate(hid, application);  				if (!hidinput)  					goto out_unwind;  			} @@ -1663,6 +1759,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  			if (hid->quirks & HID_QUIRK_MULTI_INPUT)  				hidinput->report = report; + +			list_add_tail(&report->hidinput_list, +				      &hidinput->reports);  		}  	} @@ -1687,6 +1786,10 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  		goto out_unwind;  	} +	if (hid->status & HID_STAT_DUP_DETECTED) +		hid_dbg(hid, +			"Some usages could not be mapped, please use HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE if this is legitimate.\n"); +  	return 0;  out_unwind:  | 
