diff options
Diffstat (limited to 'drivers/hid/hid-input.c')
| -rw-r--r-- | drivers/hid/hid-input.c | 96 | 
1 files changed, 55 insertions, 41 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index fb9ace1cef8b..d05f903c7614 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -253,6 +253,7 @@ __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)  	case ABS_RX:  	case ABS_RY:  	case ABS_RZ: +	case ABS_WHEEL:  	case ABS_TILT_X:  	case ABS_TILT_Y:  		if (field->unit == 0x14) {		/* If degrees */ @@ -1468,6 +1469,31 @@ static void hidinput_cleanup_hidinput(struct hid_device *hid,  	kfree(hidinput);  } +static struct hid_input *hidinput_match(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->id == report->id) +			return hidinput; +	} + +	return NULL; +} + +static inline void hidinput_configure_usages(struct hid_input *hidinput, +					     struct hid_report *report) +{ +	int i, j; + +	for (i = 0; i < report->maxfield; i++) +		for (j = 0; j < report->field[i]->maxusage; j++) +			hidinput_configure_usage(hidinput, report->field[i], +						 report->field[i]->usage + j); +} +  /*   * Register the input device; print a message.   * Configure the input layer interface @@ -1478,8 +1504,8 @@ 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; -	int i, j, k; +	struct hid_input *next, *hidinput = NULL; +	int i, k;  	INIT_LIST_HEAD(&hid->inputs);  	INIT_WORK(&hid->led_work, hidinput_led_worker); @@ -1509,43 +1535,40 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  			if (!report->maxfield)  				continue; +			/* +			 * Find the previous hidinput report attached +			 * to this report id. +			 */ +			if (hid->quirks & HID_QUIRK_MULTI_INPUT) +				hidinput = hidinput_match(report); +  			if (!hidinput) {  				hidinput = hidinput_allocate(hid);  				if (!hidinput)  					goto out_unwind;  			} -			for (i = 0; i < report->maxfield; i++) -				for (j = 0; j < report->field[i]->maxusage; j++) -					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; +			hidinput_configure_usages(hidinput, report); -			if (hid->quirks & HID_QUIRK_MULTI_INPUT) { -				/* This will leave hidinput NULL, so that it -				 * allocates another one if we have more inputs on -				 * the same interface. Some devices (e.g. Happ's -				 * UGCI) cram a lot of unrelated inputs into the -				 * same interface. */ +			if (hid->quirks & HID_QUIRK_MULTI_INPUT)  				hidinput->report = report; -				if (drv->input_configured && -				    drv->input_configured(hid, hidinput)) -					goto out_cleanup; -				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; +	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { +		if ((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); +			continue; +		} + +		if (drv->input_configured && +		    drv->input_configured(hid, hidinput)) +			goto out_unwind; +		if (input_register_device(hidinput->input)) +			goto out_unwind; +		hidinput->registered = true;  	}  	if (list_empty(&hid->inputs)) { @@ -1553,20 +1576,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  		goto out_unwind;  	} -	if (hidinput) { -		if (drv->input_configured && -		    drv->input_configured(hid, hidinput)) -			goto out_cleanup; -		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:  	/* unwind the ones we already registered */  	hidinput_disconnect(hid); @@ -1583,7 +1594,10 @@ void hidinput_disconnect(struct hid_device *hid)  	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {  		list_del(&hidinput->list); -		input_unregister_device(hidinput->input); +		if (hidinput->registered) +			input_unregister_device(hidinput->input); +		else +			input_free_device(hidinput->input);  		kfree(hidinput);  	}  |