1bd9528
From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
1bd9528
Date: Wed, 10 Sep 2014 18:02:37 -0700
1bd9528
Subject: [PATCH] HID: rmi: check sanity of the incoming report
1bd9528
1bd9528
In the Dell XPS 13 9333, it appears that sometimes the bus get confused
1bd9528
and corrupts the incoming data. It fills the input report with the
1bd9528
sentinel value "ff". Synaptics told us that such behavior does not comes
1bd9528
from the touchpad itself, so we filter out such reports here.
1bd9528
1bd9528
Unfortunately, we can not simply discard the incoming data because they
1bd9528
may contain useful information. Most of the time, the misbehavior is
1bd9528
quite near the end of the report, so we can still use the valid part of
1bd9528
it.
1bd9528
1bd9528
Fixes:
1bd9528
https://bugzilla.redhat.com/show_bug.cgi?id=1123584
1bd9528
1bd9528
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
1bd9528
Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
1bd9528
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1bd9528
---
1bd9528
 drivers/hid/hid-rmi.c | 44 ++++++++++++++++++++++++++++++++++++++------
1bd9528
 1 file changed, 38 insertions(+), 6 deletions(-)
1bd9528
1bd9528
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
1bd9528
index 8389e8109218..3cccff73b9b9 100644
1bd9528
--- a/drivers/hid/hid-rmi.c
1bd9528
+++ b/drivers/hid/hid-rmi.c
1bd9528
@@ -320,10 +320,7 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
1bd9528
 	int offset;
1bd9528
 	int i;
1bd9528
 
1bd9528
-	if (size < hdata->f11.report_size)
1bd9528
-		return 0;
1bd9528
-
1bd9528
-	if (!(irq & hdata->f11.irq_mask))
1bd9528
+	if (!(irq & hdata->f11.irq_mask) || size <= 0)
1bd9528
 		return 0;
1bd9528
 
1bd9528
 	offset = (hdata->max_fingers >> 2) + 1;
1bd9528
@@ -332,9 +329,19 @@ static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
1bd9528
 		int fs_bit_position = (i & 0x3) << 1;
1bd9528
 		int finger_state = (data[fs_byte_position] >> fs_bit_position) &
1bd9528
 					0x03;
1bd9528
+		int position = offset + 5 * i;
1bd9528
+
1bd9528
+		if (position + 5 > size) {
1bd9528
+			/* partial report, go on with what we received */
1bd9528
+			printk_once(KERN_WARNING
1bd9528
+				"%s %s: Detected incomplete finger report. Finger reports may occasionally get dropped on this platform.\n",
1bd9528
+				 dev_driver_string(&hdev->dev),
1bd9528
+				 dev_name(&hdev->dev));
1bd9528
+			hid_dbg(hdev, "Incomplete finger report\n");
1bd9528
+			break;
1bd9528
+		}
1bd9528
 
1bd9528
-		rmi_f11_process_touch(hdata, i, finger_state,
1bd9528
-				&data[offset + 5 * i]);
1bd9528
+		rmi_f11_process_touch(hdata, i, finger_state, &data[position]);
1bd9528
 	}
1bd9528
 	input_mt_sync_frame(hdata->input);
1bd9528
 	input_sync(hdata->input);
1bd9528
@@ -352,6 +359,11 @@ static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
1bd9528
 	if (!(irq & hdata->f30.irq_mask))
1bd9528
 		return 0;
1bd9528
 
1bd9528
+	if (size < (int)hdata->f30.report_size) {
1bd9528
+		hid_warn(hdev, "Click Button pressed, but the click data is missing\n");
1bd9528
+		return 0;
1bd9528
+	}
1bd9528
+
1bd9528
 	for (i = 0; i < hdata->gpio_led_count; i++) {
1bd9528
 		if (test_bit(i, &hdata->button_mask)) {
1bd9528
 			value = (data[i / 8] >> (i & 0x07)) & BIT(0);
1bd9528
@@ -412,9 +424,29 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
1bd9528
 	return 1;
1bd9528
 }
1bd9528
 
1bd9528
+static int rmi_check_sanity(struct hid_device *hdev, u8 *data, int size)
1bd9528
+{
1bd9528
+	int valid_size = size;
1bd9528
+	/*
1bd9528
+	 * On the Dell XPS 13 9333, the bus sometimes get confused and fills
1bd9528
+	 * the report with a sentinel value "ff". Synaptics told us that such
1bd9528
+	 * behavior does not comes from the touchpad itself, so we filter out
1bd9528
+	 * such reports here.
1bd9528
+	 */
1bd9528
+
1bd9528
+	while ((data[valid_size - 1] == 0xff) && valid_size > 0)
1bd9528
+		valid_size--;
1bd9528
+
1bd9528
+	return valid_size;
1bd9528
+}
1bd9528
+
1bd9528
 static int rmi_raw_event(struct hid_device *hdev,
1bd9528
 		struct hid_report *report, u8 *data, int size)
1bd9528
 {
1bd9528
+	size = rmi_check_sanity(hdev, data, size);
1bd9528
+	if (size < 2)
1bd9528
+		return 0;
1bd9528
+
1bd9528
 	switch (data[0]) {
1bd9528
 	case RMI_READ_DATA_REPORT_ID:
1bd9528
 		return rmi_read_data_event(hdev, data, size);
1bd9528
-- 
1bd9528
1.9.3
1bd9528