diff options
Diffstat (limited to 'drivers/hid/hid-input.c')
| -rw-r--r-- | drivers/hid/hid-input.c | 77 | 
1 files changed, 77 insertions, 0 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 21b196c394b..945b8158ec4 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1198,6 +1198,67 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)  	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); +} +  /*   * Register the input device; print a message.   * Configure the input layer interface @@ -1249,6 +1310,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 @@ -1265,6 +1330,18 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  		}  	} +	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) {  		if (drv->input_configured)  			drv->input_configured(hid, hidinput);  |