HID: input: accommodate priorities for slotted devices
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>
Thu, 3 Feb 2022 14:32:25 +0000 (15:32 +0100)
committerJiri Kosina <jkosina@suse.cz>
Tue, 1 Mar 2022 14:46:03 +0000 (15:46 +0100)
Multitouch devices in hybrid mode are reporting multiple times the
same collection. We should accommodate for this in our handling
of priorities by defining the slots they belong to.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Reviewed-by: Ping Cheng <ping.cheng@wacom.com>
Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-input.c
include/linux/hid.h

index 9f88536406486504e39b3f94dce3296034bf5e69..56d4e91c47503a5dfec987fd4f925724393d0e5b 100644 (file)
@@ -48,6 +48,16 @@ static const struct {
        __s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct usage_priority {
+       __u32 usage;                    /* the HID usage associated */
+       bool global;                    /* we assume all usages to be slotted,
+                                        * unless global
+                                        */
+       unsigned int slot_overwrite;    /* for globals: allows to set the usage
+                                        * before or after the slots
+                                        */
+};
+
 /*
  * hid-input will convert this list into priorities:
  * the first element will have the highest priority
@@ -57,17 +67,30 @@ static const struct {
  * hid-input will then shift the priority by 8 bits to leave some space
  * in case drivers want to interleave other fields.
  *
+ * To accommodate slotted devices, the slot priority is
+ * defined in the next 8 bits (defined by 0xff - slot).
+ *
  * If drivers want to add fields before those, hid-input will
  * leave out the first 8 bits of the priority value.
  *
  * This still leaves us 65535 individual priority values.
  */
-static const __u32 hidinput_usages_priorities[] = {
-       HID_DG_ERASER,          /* Eraser (eraser touching) must always come before tipswitch */
-       HID_DG_INVERT,          /* Invert must always come before In Range */
-       HID_DG_TIPSWITCH,       /* Is the tip of the tool touching? */
-       HID_DG_TIPPRESSURE,     /* Tip Pressure might emulate tip switch */
-       HID_DG_INRANGE,         /* In Range needs to come after the other tool states */
+static const struct usage_priority hidinput_usages_priorities[] = {
+       { /* Eraser (eraser touching) must always come before tipswitch */
+         .usage = HID_DG_ERASER,
+       },
+       { /* Invert must always come before In Range */
+         .usage = HID_DG_INVERT,
+       },
+       { /* Is the tip of the tool touching? */
+         .usage = HID_DG_TIPSWITCH,
+       },
+       { /* Tip Pressure might emulate tip switch */
+         .usage = HID_DG_TIPPRESSURE,
+       },
+       { /* In Range needs to come after the other tool states */
+         .usage = HID_DG_INRANGE,
+       },
 };
 
 #define map_abs(c)     hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c))
@@ -612,6 +635,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 {
        struct input_dev *input = hidinput->input;
        struct hid_device *device = input_get_drvdata(input);
+       const struct usage_priority *usage_priority = NULL;
        int max = 0, code;
        unsigned int i = 0;
        unsigned long *bit = NULL;
@@ -633,13 +657,26 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
 
        /* assign a priority based on the static list declared here */
        for (i = 0; i < ARRAY_SIZE(hidinput_usages_priorities); i++) {
-               if (usage->hid == hidinput_usages_priorities[i]) {
+               if (usage->hid == hidinput_usages_priorities[i].usage) {
+                       usage_priority = &hidinput_usages_priorities[i];
+
                        field->usages_priorities[usage_index] =
                                (ARRAY_SIZE(hidinput_usages_priorities) - i) << 8;
                        break;
                }
        }
 
+       /*
+        * For slotted devices, we need to also add the slot index
+        * in the priority.
+        */
+       if (usage_priority && usage_priority->global)
+               field->usages_priorities[usage_index] |=
+                       usage_priority->slot_overwrite;
+       else
+               field->usages_priorities[usage_index] |=
+                       (0xff - field->slot_idx) << 16;
+
        if (device->driver->input_mapping) {
                int ret = device->driver->input_mapping(device, hidinput, field,
                                usage, &bit, &max);
@@ -2068,7 +2105,57 @@ static struct hid_input *hidinput_match_application(struct hid_report *report)
 static inline void hidinput_configure_usages(struct hid_input *hidinput,
                                             struct hid_report *report)
 {
-       int i, j;
+       int i, j, k;
+       int first_field_index = 0;
+       int slot_collection_index = -1;
+       int prev_collection_index = -1;
+       unsigned int slot_idx = 0;
+       struct hid_field *field;
+
+       /*
+        * First tag all the fields that are part of a slot,
+        * a slot needs to have one Contact ID in the collection
+        */
+       for (i = 0; i < report->maxfield; i++) {
+               field = report->field[i];
+
+               /* ignore fields without usage */
+               if (field->maxusage < 1)
+                       continue;
+
+               /*
+                * janitoring when collection_index changes
+                */
+               if (prev_collection_index != field->usage->collection_index) {
+                       prev_collection_index = field->usage->collection_index;
+                       first_field_index = i;
+               }
+
+               /*
+                * if we already found a Contact ID in the collection,
+                * tag and continue to the next.
+                */
+               if (slot_collection_index == field->usage->collection_index) {
+                       field->slot_idx = slot_idx;
+                       continue;
+               }
+
+               /* check if the current field has Contact ID */
+               for (j = 0; j < field->maxusage; j++) {
+                       if (field->usage[j].hid == HID_DG_CONTACTID) {
+                               slot_collection_index = field->usage->collection_index;
+                               slot_idx++;
+
+                               /*
+                                * mark all previous fields and this one in the
+                                * current collection to be slotted.
+                                */
+                               for (k = first_field_index; k <= i; k++)
+                                       report->field[k]->slot_idx = slot_idx;
+                               break;
+                       }
+               }
+       }
 
        for (i = 0; i < report->maxfield; i++)
                for (j = 0; j < report->field[i]->maxusage; j++)
index feb8df61168f99cea2db7001f58cfb61cd3fb2cf..4363a63b9775ef27e4193cd507b77394fd854b9e 100644 (file)
@@ -492,6 +492,7 @@ struct hid_field {
        /* hidinput data */
        struct hid_input *hidinput;     /* associated input structure */
        __u16 dpad;                     /* dpad input code */
+       unsigned int slot_idx;          /* slot index in a report */
 };
 
 #define HID_MAX_FIELDS 256