From c0bf57411b6b03dd72d4d20f362ba1ca72244834 Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Wed, 19 Oct 2016 18:03:54 -0700 Subject: HID: input: Recognize ABS_WHEEL in hidinput_calc_abs_res The "Steering" usage (HID_UP_SIMULATION | 0xc8) is defined in HUT 1.12 as follows: "A steering wheel is a single degree-of-freedom device that rotates about an axis. The zero position is always the neutral or 'straight ahead' position, with positive values turning clockwise and negative values turning counterclockwise. If the Coordinate Values Wrap attribute is set, the steering wheel can be turned past 360 degrees." The hidinput_configure_usage function canonically maps this usage to the ABS_WHEEL axis, but hidinput_calc_abs_res does not recognize this axis as one for which it can calculate a resolution. This effectively prevents wheels from being assigned a proper resolution that userspace can use to determine the precise angle of input. This commit adds ABS_WHEEL as a rotational axis to hidinput_calc_abs_res. Signed-off-by: Jason Gerecke Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index fb9ace1cef8b..c4e935b5fe74 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 */ -- cgit v1.2.3-70-g09d2 From 72d19459d7919f966594576bb042d15a451f27ea Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 25 Nov 2016 14:27:21 +0100 Subject: HID: input: rework HID_QUIRK_MULTI_INPUT The purpose of HID_QUIRK_MULTI_INPUT is to have an input device per report id. This is useful when the HID device presents several HID collections of different device types. The current implementation of hid-input creates one input node per id per type (input or output). This is problematic for the LEDs of a keyboard as they are often set through an output report. The current code creates one input node with all the keyboard keys, and one other with only the LEDs. To solve this, we use a two-passes way: - first, we initialize all input nodes and associate one per report id - then, we register all the input nodes Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 95 ++++++++++++++++++++++++++++--------------------- include/linux/hid.h | 1 + 2 files changed, 55 insertions(+), 41 deletions(-) (limited to 'drivers/hid/hid-input.c') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index fb9ace1cef8b..55db58459531 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1468,6 +1468,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 +1503,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 +1534,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 +1575,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 +1593,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); } diff --git a/include/linux/hid.h b/include/linux/hid.h index b2ec82712baa..596b9232c19e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -479,6 +479,7 @@ struct hid_input { struct list_head list; struct hid_report *report; struct input_dev *input; + bool registered; }; enum hid_type { -- cgit v1.2.3-70-g09d2