HID: hid-multitouch: fix wrong protocol detection
authorBenjamin Tissoires <benjamin.tissoires@enac.fr>
Fri, 4 May 2012 12:53:46 +0000 (14:53 +0200)
committerJiri Kosina <jkosina@suse.cz>
Thu, 10 May 2012 09:35:43 +0000 (11:35 +0200)
The previous implementation introduced a randomness in the splitting
of the different touches reported by the device. This version is more
robust as we don't rely on hi->input->absbit, but on our own structure.

This also prepares hid-multitouch to better support Win8 devices.

[Jiri Kosina <jkosina@suse.cz>: fix build]

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@enac.fr>
Acked-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-multitouch.c

index 25b94253e68f6add47ec50203ee30653cb0ff35f..6e3332a99976d669e008131e0851ae8788113749 100644 (file)
@@ -70,9 +70,16 @@ struct mt_class {
        bool is_indirect;       /* true for touchpads */
 };
 
+struct mt_fields {
+       unsigned usages[HID_MAX_FIELDS];
+       unsigned int length;
+};
+
 struct mt_device {
        struct mt_slot curdata; /* placeholder of incoming data */
        struct mt_class mtclass;        /* our mt device class */
+       struct mt_fields *fields;       /* temporary placeholder for storing the
+                                          multitouch fields */
        unsigned last_field_index;      /* last field index of the report */
        unsigned last_slot_field;       /* the last field of a slot */
        __s8 inputmode;         /* InputMode HID feature, -1 if non-existent */
@@ -278,11 +285,15 @@ static void set_abs(struct input_dev *input, unsigned int code,
        input_set_abs_params(input, code, fmin, fmax, fuzz, 0);
 }
 
-static void set_last_slot_field(struct hid_usage *usage, struct mt_device *td,
+static void mt_store_field(struct hid_usage *usage, struct mt_device *td,
                struct hid_input *hi)
 {
-       if (!test_bit(usage->hid, hi->input->absbit))
-               td->last_slot_field = usage->hid;
+       struct mt_fields *f = td->fields;
+
+       if (f->length >= HID_MAX_FIELDS)
+               return;
+
+       f->usages[f->length++] = usage->hid;
 }
 
 static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
@@ -333,7 +344,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                cls->sn_move);
                        /* touchscreen emulation */
                        set_abs(hi->input, ABS_X, field, cls->sn_move);
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_GD_Y:
@@ -343,7 +354,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                cls->sn_move);
                        /* touchscreen emulation */
                        set_abs(hi->input, ABS_Y, field, cls->sn_move);
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                }
@@ -352,24 +363,24 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
        case HID_UP_DIGITIZER:
                switch (usage->hid) {
                case HID_DG_INRANGE:
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONFIDENCE:
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_TIPSWITCH:
                        hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
                        input_set_capability(hi->input, EV_KEY, BTN_TOUCH);
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONTACTID:
                        if (!td->maxcontacts)
                                td->maxcontacts = MT_DEFAULT_MAXCONTACT;
                        input_mt_init_slots(hi->input, td->maxcontacts);
-                       td->last_slot_field = usage->hid;
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        td->touches_by_report++;
                        return 1;
@@ -378,7 +389,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                        EV_ABS, ABS_MT_TOUCH_MAJOR);
                        set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
                                cls->sn_width);
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_HEIGHT:
@@ -388,7 +399,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                cls->sn_height);
                        input_set_abs_params(hi->input,
                                        ABS_MT_ORIENTATION, 0, 1, 0, 0);
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_TIPPRESSURE:
@@ -399,7 +410,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        /* touchscreen emulation */
                        set_abs(hi->input, ABS_PRESSURE, field,
                                cls->sn_pressure);
-                       set_last_slot_field(usage, td, hi);
+                       mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONTACTCOUNT:
@@ -653,6 +664,16 @@ static void mt_post_parse_default_settings(struct mt_device *td)
        td->mtclass.quirks = quirks;
 }
 
+static void mt_post_parse(struct mt_device *td)
+{
+       struct mt_fields *f = td->fields;
+
+       if (td->touches_by_report > 0) {
+               int field_count_per_touch = f->length / td->touches_by_report;
+               td->last_slot_field = f->usages[field_count_per_touch - 1];
+       }
+}
+
 static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret, i;
@@ -683,6 +704,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        td->maxcontact_report_id = -1;
        hid_set_drvdata(hdev, td);
 
+       td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL);
+       if (!td->fields) {
+               dev_err(&hdev->dev, "cannot allocate multitouch fields data\n");
+               ret = -ENOMEM;
+               goto fail;
+       }
+
        ret = hid_parse(hdev);
        if (ret != 0)
                goto fail;
@@ -691,6 +719,8 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (ret)
                goto fail;
 
+       mt_post_parse(td);
+
        if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
                mt_post_parse_default_settings(td);
 
@@ -708,9 +738,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        mt_set_maxcontacts(hdev);
        mt_set_input_mode(hdev);
 
+       kfree(td->fields);
+       td->fields = NULL;
+
        return 0;
 
 fail:
+       kfree(td->fields);
        kfree(td);
        return ret;
 }