Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ac36d15 authored by Benjamin Tissoires's avatar Benjamin Tissoires Committed by Jiri Kosina
Browse files

HID: hid-multitouch: fix wrong protocol detection



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: default avatarBenjamin Tissoires <benjamin.tissoires@enac.fr>
Acked-by: default avatarHenrik Rydberg <rydberg@euromail.se>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 16b79bb8
Loading
Loading
Loading
Loading
+46 −12
Original line number Diff line number Diff line
@@ -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;
}