|
@@ -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);
|
|
|
}
|
|
|
|