Merge branches 'for-4.10/asus', 'for-4.10/cp2112', 'for-4.10/i2c-hid-nopower', 'for...
[sfrench/cifs-2.6.git] / drivers / hid / hid-input.c
index fb9ace1cef8b50cbcb52955eb41f4490cb7aff7f..d05f903c7614530625d9adc696cdc7f3661abe15 100644 (file)
@@ -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);
        }