HID: multitouch: Properly deal with Win8 PTP reports with 0 touches
authorHans de Goede <hdegoede@redhat.com>
Wed, 22 Nov 2017 11:57:08 +0000 (12:57 +0100)
committerJiri Kosina <jkosina@suse.cz>
Wed, 22 Nov 2017 15:01:06 +0000 (16:01 +0100)
The Windows Precision Touchpad spec "Figure 4 Button Only Down and Up"
and "Table 9 Report Sequence for Button Only Down and Up" indicate
that the first packet of a (possibly hybrid mode multi-packet) frame
may contain a contact-count of 0 if only a button is pressed and no
fingers are detected.

This means that a value of 0 for contact-count is a valid value and
should be used as expected contact count when it is the first packet
(num_received == 0), as extra check to make sure that this is the first
packet of a buttons only frame, we also check that the timestamp is
different.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-multitouch.c

index 9ef24b518f12ca90ba21ff6c341dfcde2ce0a4d9..d8b1cad74faf78736ac46125c15be38d8d538dab 100644 (file)
@@ -119,6 +119,9 @@ struct mt_device {
        unsigned long mt_io_flags;      /* mt flags (MT_IO_FLAGS_*) */
        int cc_index;   /* contact count field index in the report */
        int cc_value_index;     /* contact count value index in the field */
+       int scantime_index;     /* scantime field index in the report */
+       int scantime_val_index; /* scantime value index in the field */
+       int prev_scantime;      /* scantime reported in the previous packet */
        unsigned last_slot_field;       /* the last field of a slot */
        unsigned mt_report_id;  /* the report ID of the multitouch device */
        unsigned long initial_quirks;   /* initial quirks state */
@@ -599,6 +602,12 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                EV_MSC, MSC_TIMESTAMP);
                        input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP);
                        mt_store_field(usage, td, hi);
+                       /* Ignore if indexes are out of bounds. */
+                       if (field->index >= field->report->maxfield ||
+                           usage->usage_index >= field->report_count)
+                               return 1;
+                       td->scantime_index = field->index;
+                       td->scantime_val_index = usage->usage_index;
                        return 1;
                case HID_DG_CONTACTCOUNT:
                        /* Ignore if indexes are out of bounds. */
@@ -855,9 +864,10 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
 static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
 {
        struct mt_device *td = hid_get_drvdata(hid);
+       __s32 cls = td->mtclass.name;
        struct hid_field *field;
        unsigned count;
-       int r, n;
+       int r, n, scantime = 0;
 
        /* sticky fingers release in progress, abort */
        if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
@@ -867,12 +877,29 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
         * Includes multi-packet support where subsequent
         * packets are sent with zero contactcount.
         */
+       if (td->scantime_index >= 0) {
+               field = report->field[td->scantime_index];
+               scantime = field->value[td->scantime_val_index];
+       }
        if (td->cc_index >= 0) {
                struct hid_field *field = report->field[td->cc_index];
                int value = field->value[td->cc_value_index];
-               if (value)
+
+               /*
+                * For Win8 PTPs the first packet (td->num_received == 0) may
+                * have a contactcount of 0 if there only is a button event.
+                * We double check that this is not a continuation packet
+                * of a possible multi-packet frame be checking that the
+                * timestamp has changed.
+                */
+               if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
+                   td->num_received == 0 && td->prev_scantime != scantime)
+                       td->num_expected = value;
+               /* A non 0 contact count always indicates a first packet */
+               else if (value)
                        td->num_expected = value;
        }
+       td->prev_scantime = scantime;
 
        for (r = 0; r < report->maxfield; r++) {
                field = report->field[r];
@@ -1329,6 +1356,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        td->maxcontact_report_id = -1;
        td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
        td->cc_index = -1;
+       td->scantime_index = -1;
        td->mt_report_id = -1;
        hid_set_drvdata(hdev, td);