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