7ca18ea
From 84ac7d370783d4819c5986da1c5d5c62d360dc8f Mon Sep 17 00:00:00 2001
7ca18ea
From: Mika Westerberg <mika.westerberg@linux.intel.com>
7ca18ea
Date: Wed, 7 Oct 2015 15:33:43 +0300
7ca18ea
Subject: [PATCH] HID: multitouch: Fetch feature reports on demand for Win8
7ca18ea
 devices
7ca18ea
7ca18ea
Some newer Intel Skylake based Dell laptops with Win8 precision touchpad
7ca18ea
fail when initial feature reports are fetched from it. Below is an example
7ca18ea
output with some additional debug included:
7ca18ea
7ca18ea
 i2c_hid i2c-DLL0704:01: Fetching the HID descriptor
7ca18ea
 i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=20 00
7ca18ea
 i2c_hid i2c-DLL0704:01: HID Descriptor: 1e 00 00 01 99 02 21 00 24 ...
7ca18ea
 ...
7ca18ea
 i2c_hid i2c-DLL0704:01: i2c_hid_get_report
7ca18ea
 i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 38 02 23 00
7ca18ea
 i2c_hid i2c-DLL0704:01: report (len=4): 04 00 08 05
7ca18ea
 i2c_hid i2c-DLL0704:01: report id 13
7ca18ea
 i2c_hid i2c-DLL0704:01: i2c_hid_get_report
7ca18ea
 i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 3d 02 23 00
7ca18ea
 i2c_hid i2c-DLL0704:01: failed to retrieve report from device.
7ca18ea
 i2c_hid i2c-DLL0704:01: report id 7
7ca18ea
 i2c_hid i2c-DLL0704:01: i2c_hid_get_report
7ca18ea
 i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 37 02 23 00
7ca18ea
 i2c_hid i2c-DLL0704:01: report (len=259): 03 01 07 fc 28 fe 84 40 ...
7ca18ea
 i2c_hid i2c-DLL0704:01: report id 4
7ca18ea
 i2c_hid i2c-DLL0704:01: i2c_hid_get_report
7ca18ea
 i2c_hid i2c-DLL0704:01: __i2c_hid_command: cmd=22 00 34 02 23 00
7ca18ea
7ca18ea
We manage to fetch few reports but then the touchpad dies:
7ca18ea
7ca18ea
 i2c_designware i2c_designware.1: i2c_dw_handle_tx_abort: lost arbitration
7ca18ea
 i2c_hid i2c-DLL0704:01: failed to retrieve report from device.
7ca18ea
7ca18ea
it eventually pulls the whole I2C bus low:
7ca18ea
7ca18ea
 i2c_designware i2c_designware.1: controller timed out
7ca18ea
 i2c_hid i2c-DLL0704:01: failed to set a report to device.
7ca18ea
7ca18ea
Fix this by preventing initial feature report retrieval for Win8 devices.
7ca18ea
Instead we fetch reports as needed in mt_feature_mapping(). This prevents
7ca18ea
fetching reports which might cause problems with the device in question.
7ca18ea
7ca18ea
Suggested-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
7ca18ea
Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
7ca18ea
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
7ca18ea
Tested-by: Seth Forshee <seth.forshee@canonical.com>
7ca18ea
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
7ca18ea
---
7ca18ea
 drivers/hid/hid-multitouch.c | 45 +++++++++++++++++++++++++++++++++++++++++++-
7ca18ea
 1 file changed, 44 insertions(+), 1 deletion(-)
7ca18ea
7ca18ea
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
7ca18ea
index 426b2f1a3450..4afe8d78b366 100644
7ca18ea
--- a/drivers/hid/hid-multitouch.c
7ca18ea
+++ b/drivers/hid/hid-multitouch.c
7ca18ea
@@ -309,6 +309,41 @@ static struct attribute_group mt_attribute_group = {
7ca18ea
 	.attrs = sysfs_attrs
7ca18ea
 };
7ca18ea
 
7ca18ea
+static void mt_get_feature(struct hid_device *hdev, struct hid_report *report)
7ca18ea
+{
7ca18ea
+	struct mt_device *td = hid_get_drvdata(hdev);
7ca18ea
+	int ret, size = hid_report_len(report);
7ca18ea
+	u8 *buf;
7ca18ea
+
7ca18ea
+	/*
7ca18ea
+	 * Only fetch the feature report if initial reports are not already
7ca18ea
+	 * been retrieved. Currently this is only done for Windows 8 touch
7ca18ea
+	 * devices.
7ca18ea
+	 */
7ca18ea
+	if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS))
7ca18ea
+		return;
7ca18ea
+	if (td->mtclass.name != MT_CLS_WIN_8)
7ca18ea
+		return;
7ca18ea
+
7ca18ea
+	buf = hid_alloc_report_buf(report, GFP_KERNEL);
7ca18ea
+	if (!buf)
7ca18ea
+		return;
7ca18ea
+
7ca18ea
+	ret = hid_hw_raw_request(hdev, report->id, buf, size,
7ca18ea
+				 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
7ca18ea
+	if (ret < 0) {
7ca18ea
+		dev_warn(&hdev->dev, "failed to fetch feature %d\n",
7ca18ea
+			 report->id);
7ca18ea
+	} else {
7ca18ea
+		ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf,
7ca18ea
+					   size, 0);
7ca18ea
+		if (ret)
7ca18ea
+			dev_warn(&hdev->dev, "failed to report feature\n");
7ca18ea
+	}
7ca18ea
+
7ca18ea
+	kfree(buf);
7ca18ea
+}
7ca18ea
+
7ca18ea
 static void mt_feature_mapping(struct hid_device *hdev,
7ca18ea
 		struct hid_field *field, struct hid_usage *usage)
7ca18ea
 {
7ca18ea
@@ -327,6 +362,8 @@ static void mt_feature_mapping(struct hid_device *hdev,
7ca18ea
 
7ca18ea
 		break;
7ca18ea
 	case HID_DG_CONTACTMAX:
7ca18ea
+		mt_get_feature(hdev, field->report);
7ca18ea
+
7ca18ea
 		td->maxcontact_report_id = field->report->id;
7ca18ea
 		td->maxcontacts = field->value[0];
7ca18ea
 		if (!td->maxcontacts &&
7ca18ea
@@ -343,6 +380,7 @@ static void mt_feature_mapping(struct hid_device *hdev,
7ca18ea
 			break;
7ca18ea
 		}
7ca18ea
 
7ca18ea
+		mt_get_feature(hdev, field->report);
7ca18ea
 		if (field->value[usage->usage_index] == MT_BUTTONTYPE_CLICKPAD)
7ca18ea
 			td->is_buttonpad = true;
7ca18ea
 
7ca18ea
@@ -1026,8 +1064,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
7ca18ea
 		 * reports. Fortunately, the Win8 spec says that all touches
7ca18ea
 		 * should be sent during each report, making the initialization
7ca18ea
 		 * of input reports unnecessary.
7ca18ea
+		 *
7ca18ea
+		 * In addition some touchpads do not behave well if we read
7ca18ea
+		 * all feature reports from them. Instead we prevent
7ca18ea
+		 * initial report fetching and then selectively fetch each
7ca18ea
+		 * report we are interested in.
7ca18ea
 		 */
7ca18ea
-		hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS;
7ca18ea
+		hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
7ca18ea
 
7ca18ea
 	td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
7ca18ea
 	if (!td) {
7ca18ea
-- 
7ca18ea
2.5.0
7ca18ea