[PATCH] hid/i2c-hid: override HID descriptors for certain devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



A particular touchpad (SIPODEV SP1064) refuses to supply the HID
descriptors. This patch provides the framework for overriding these
descriptors based on DMI data. It also includes the descriptors for
said touchpad, as well as the DMI data for the 4 laptops known to
use this device.

Relevant Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1526312

Cc: Hans de Goede <hdegoede@xxxxxxxxxx>
Reported-and-tested-by: ahormann@xxxxxxx
Reported-and-tested-by: Bruno Jesus <bruno.fl.jesus@xxxxxxxxx>
Reported-and-tested-by: Dietrich <enaut.w@xxxxxxxxxxxxxx>
Reported-and-tested-by: kloxdami@xxxxxxxxx
Signed-off-by: Julian Sax <jsbc@xxxxxx>
---
 drivers/hid/i2c-hid/Makefile                  |   3 +
 .../hid/i2c-hid/{i2c-hid.c => i2c-hid-core.c} |  54 ++++---
 drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c      | 149 ++++++++++++++++++
 drivers/hid/i2c-hid/i2c-hid.h                 |  17 ++
 4 files changed, 205 insertions(+), 18 deletions(-)
 rename drivers/hid/i2c-hid/{i2c-hid.c => i2c-hid-core.c} (96%)
 create mode 100644 drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
 create mode 100644 drivers/hid/i2c-hid/i2c-hid.h

diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile
index 832d8f9aaba2..099e1ce2f234 100644
--- a/drivers/hid/i2c-hid/Makefile
+++ b/drivers/hid/i2c-hid/Makefile
@@ -3,3 +3,6 @@
 #
 
 obj-$(CONFIG_I2C_HID)				+= i2c-hid.o
+
+i2c-hid-objs					=  i2c-hid-core.o
+i2c-hid-$(CONFIG_DMI)				+= i2c-hid-dmi-quirks.o
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid-core.c
similarity index 96%
rename from drivers/hid/i2c-hid/i2c-hid.c
rename to drivers/hid/i2c-hid/i2c-hid-core.c
index cc33622253aa..3cdd1e1aeb95 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -43,6 +43,7 @@
 #include <linux/platform_data/i2c-hid.h>
 
 #include "../hid-ids.h"
+#include "i2c-hid.h"
 
 /* quirks to control the device */
 #define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV	BIT(0)
@@ -673,6 +674,7 @@ static int i2c_hid_parse(struct hid_device *hid)
 	char *rdesc;
 	int ret;
 	int tries = 3;
+	char *use_override;
 
 	i2c_hid_dbg(ihid, "entering %s\n", __func__);
 
@@ -691,26 +693,36 @@ static int i2c_hid_parse(struct hid_device *hid)
 	if (ret)
 		return ret;
 
-	rdesc = kzalloc(rsize, GFP_KERNEL);
+	use_override = i2c_hid_get_dmi_hid_report_desc_override(&rsize);
 
-	if (!rdesc) {
-		dbg_hid("couldn't allocate rdesc memory\n");
-		return -ENOMEM;
-	}
+	if (use_override) {
+		rdesc = use_override;
+		i2c_hid_dbg(ihid, "Using a HID report descriptor override\n");
+	} else {
+		rdesc = kzalloc(rsize, GFP_KERNEL);
 
-	i2c_hid_dbg(ihid, "asking HID report descriptor\n");
+		if (!rdesc) {
+			dbg_hid("couldn't allocate rdesc memory\n");
+			return -ENOMEM;
+		}
 
-	ret = i2c_hid_command(client, &hid_report_descr_cmd, rdesc, rsize);
-	if (ret) {
-		hid_err(hid, "reading report descriptor failed\n");
-		kfree(rdesc);
-		return -EIO;
+		i2c_hid_dbg(ihid, "asking HID report descriptor\n");
+
+		ret = i2c_hid_command(client, &hid_report_descr_cmd,
+				      rdesc, rsize);
+		if (ret) {
+			hid_err(hid, "reading report descriptor failed\n");
+			kfree(rdesc);
+			return -EIO;
+		}
 	}
 
 	i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
 
 	ret = hid_parse_report(hid, rdesc, rsize);
-	kfree(rdesc);
+	if (!use_override)
+		kfree(rdesc);
+
 	if (ret) {
 		dbg_hid("parsing report descriptor failed\n");
 		return ret;
@@ -837,12 +849,18 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
 	int ret;
 
 	/* i2c hid fetch using a fixed descriptor size (30 bytes) */
-	i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
-	ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer,
-				sizeof(struct i2c_hid_desc));
-	if (ret) {
-		dev_err(&client->dev, "hid_descr_cmd failed\n");
-		return -ENODEV;
+	if (i2c_hid_get_dmi_i2c_hid_desc_override()) {
+		i2c_hid_dbg(ihid, "Using a HID descriptor override\n");
+		ihid->hdesc = *i2c_hid_get_dmi_i2c_hid_desc_override();
+	} else {
+		i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
+		ret = i2c_hid_command(client, &hid_descr_cmd,
+				      ihid->hdesc_buffer,
+				      sizeof(struct i2c_hid_desc));
+		if (ret) {
+			dev_err(&client->dev, "hid_descr_cmd failed\n");
+			return -ENODEV;
+		}
 	}
 
 	/* Validate the length of HID descriptor, the 4 first bytes:
diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
new file mode 100644
index 000000000000..400f076c6be3
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Quirks for I2C-HID devices that do not supply proper descriptors
+ *
+ * Copyright (c) 2018 Julian Sax <jsbc@xxxxxx>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/dmi.h>
+#include <linux/mod_devicetable.h>
+
+#include "i2c-hid.h"
+
+
+struct i2c_hid_desc_override {
+	union {
+		struct i2c_hid_desc *i2c_hid_desc;
+		uint8_t             *i2c_hid_desc_buffer;
+	};
+	uint8_t              *hid_report_desc;
+	unsigned int          hid_report_desc_size;
+};
+
+
+/* descriptors for the SIPODEV SP1064 touchpad */
+static const struct i2c_hid_desc_override sipodev_desc = {
+	.i2c_hid_desc_buffer = (uint8_t [])
+	{0x1e, 0x00, 0x00, 0x01, 0xdb, 0x01, 0x21, 0x00, 0x24, 0x00,
+	 0x1b, 0x00, 0x25, 0x00, 0x11, 0x00, 0x22, 0x00, 0x23, 0x00,
+	 0x11, 0x09, 0x88, 0x52, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+	.hid_report_desc = (uint8_t [])
+	{0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x01, 0x09, 0x01,
+	 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x02, 0x25, 0x01,
+	 0x75, 0x01, 0x95, 0x02, 0x81, 0x02, 0x95, 0x06, 0x81, 0x01,
+	 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, 0x25, 0x7f,
+	 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, 0xc0, 0xc0, 0x05, 0x0d,
+	 0x09, 0x05, 0xa1, 0x01, 0x85, 0x04, 0x05, 0x0d, 0x09, 0x22,
+	 0xa1, 0x02, 0x15, 0x00, 0x25, 0x01, 0x09, 0x47, 0x09, 0x42,
+	 0x95, 0x02, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x03,
+	 0x25, 0x05, 0x09, 0x51, 0x81, 0x02, 0x75, 0x01, 0x95, 0x03,
+	 0x81, 0x03, 0x05, 0x01, 0x26, 0x44, 0x0a, 0x75, 0x10, 0x55,
+	 0x0e, 0x65, 0x11, 0x09, 0x30, 0x46, 0x1a, 0x04, 0x95, 0x01,
+	 0x81, 0x02, 0x46, 0xbc, 0x02, 0x26, 0x34, 0x05, 0x09, 0x31,
+	 0x81, 0x02, 0xc0, 0x05, 0x0d, 0x09, 0x22, 0xa1, 0x02, 0x25,
+	 0x01, 0x09, 0x47, 0x09, 0x42, 0x95, 0x02, 0x75, 0x01, 0x81,
+	 0x02, 0x95, 0x01, 0x75, 0x03, 0x25, 0x05, 0x09, 0x51, 0x81,
+	 0x02, 0x75, 0x01, 0x95, 0x03, 0x81, 0x03, 0x05, 0x01, 0x26,
+	 0x44, 0x0a, 0x75, 0x10, 0x09, 0x30, 0x46, 0x1a, 0x04, 0x95,
+	 0x01, 0x81, 0x02, 0x46, 0xbc, 0x02, 0x26, 0x34, 0x05, 0x09,
+	 0x31, 0x81, 0x02, 0xc0, 0x05, 0x0d, 0x09, 0x22, 0xa1, 0x02,
+	 0x25, 0x01, 0x09, 0x47, 0x09, 0x42, 0x95, 0x02, 0x75, 0x01,
+	 0x81, 0x02, 0x95, 0x01, 0x75, 0x03, 0x25, 0x05, 0x09, 0x51,
+	 0x81, 0x02, 0x75, 0x01, 0x95, 0x03, 0x81, 0x03, 0x05, 0x01,
+	 0x26, 0x44, 0x0a, 0x75, 0x10, 0x09, 0x30, 0x46, 0x1a, 0x04,
+	 0x95, 0x01, 0x81, 0x02, 0x46, 0xbc, 0x02, 0x26, 0x34, 0x05,
+	 0x09, 0x31, 0x81, 0x02, 0xc0, 0x05, 0x0d, 0x09, 0x22, 0xa1,
+	 0x02, 0x25, 0x01, 0x09, 0x47, 0x09, 0x42, 0x95, 0x02, 0x75,
+	 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x03, 0x25, 0x05, 0x09,
+	 0x51, 0x81, 0x02, 0x75, 0x01, 0x95, 0x03, 0x81, 0x03, 0x05,
+	 0x01, 0x26, 0x44, 0x0a, 0x75, 0x10, 0x09, 0x30, 0x46, 0x1a,
+	 0x04, 0x95, 0x01, 0x81, 0x02, 0x46, 0xbc, 0x02, 0x26, 0x34,
+	 0x05, 0x09, 0x31, 0x81, 0x02, 0xc0, 0x05, 0x0d, 0x55, 0x0c,
+	 0x66, 0x01, 0x10, 0x47, 0xff, 0xff, 0x00, 0x00, 0x27, 0xff,
+	 0xff, 0x00, 0x00, 0x75, 0x10, 0x95, 0x01, 0x09, 0x56, 0x81,
+	 0x02, 0x09, 0x54, 0x25, 0x7f, 0x75, 0x08, 0x81, 0x02, 0x05,
+	 0x09, 0x09, 0x01, 0x25, 0x01, 0x75, 0x01, 0x95, 0x01, 0x81,
+	 0x02, 0x95, 0x07, 0x81, 0x03, 0x05, 0x0d, 0x85, 0x02, 0x09,
+	 0x55, 0x09, 0x59, 0x75, 0x04, 0x95, 0x02, 0x25, 0x0f, 0xb1,
+	 0x02, 0x05, 0x0d, 0x85, 0x07, 0x09, 0x60, 0x75, 0x01, 0x95,
+	 0x01, 0x25, 0x01, 0xb1, 0x02, 0x95, 0x07, 0xb1, 0x03, 0x85,
+	 0x06, 0x06, 0x00, 0xff, 0x09, 0xc5, 0x26, 0xff, 0x00, 0x75,
+	 0x08, 0x96, 0x00, 0x01, 0xb1, 0x02, 0xc0, 0x06, 0x00, 0xff,
+	 0x09, 0x01, 0xa1, 0x01, 0x85, 0x0d, 0x26, 0xff, 0x00, 0x19,
+	 0x01, 0x29, 0x02, 0x75, 0x08, 0x95, 0x02, 0xb1, 0x02, 0xc0,
+	 0x05, 0x0d, 0x09, 0x0e, 0xa1, 0x01, 0x85, 0x03, 0x09, 0x22,
+	 0xa1, 0x02, 0x09, 0x52, 0x25, 0x0a, 0x95, 0x01, 0xb1, 0x02,
+	 0xc0, 0x09, 0x22, 0xa1, 0x00, 0x85, 0x05, 0x09, 0x57, 0x09,
+	 0x58, 0x75, 0x01, 0x95, 0x02, 0x25, 0x01, 0xb1, 0x02, 0x95,
+	 0x06, 0xb1, 0x03, 0xc0, 0xc0 },
+	.hid_report_desc_size = 475
+};
+
+
+static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
+	{
+		.ident = "Teclast F6 Pro",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F6 Pro"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	},
+	{
+		.ident = "Teclast F7",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F7"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	},
+	{
+		.ident = "Trekstor Primebook C13",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C13"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	},
+	{
+		.ident = "Trekstor Primebook C11",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	}
+};
+
+
+struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(void)
+{
+	struct i2c_hid_desc_override *override;
+	const struct dmi_system_id *system_id;
+
+	system_id = dmi_first_match(i2c_hid_dmi_desc_override_table);
+	if (!system_id)
+		return NULL;
+
+	override = system_id->driver_data;
+	return override->i2c_hid_desc;
+}
+
+char *i2c_hid_get_dmi_hid_report_desc_override(unsigned int *size)
+{
+	struct i2c_hid_desc_override *override;
+	const struct dmi_system_id *system_id;
+
+	system_id = dmi_first_match(i2c_hid_dmi_desc_override_table);
+	if (!system_id)
+		return NULL;
+
+	override = system_id->driver_data;
+	*size = override->hid_report_desc_size;
+	return override->hid_report_desc;
+}
+
diff --git a/drivers/hid/i2c-hid/i2c-hid.h b/drivers/hid/i2c-hid/i2c-hid.h
new file mode 100644
index 000000000000..c543bb5ef1c1
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef I2C_HID_H
+#define I2C_HID_H
+
+
+#ifdef CONFIG_DMI
+struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(void);
+char *i2c_hid_get_dmi_hid_report_desc_override(unsigned int *size);
+#else
+static inline struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(void)
+{ return NULL; }
+static inline char *i2c_hid_get_dmi_hid_report_desc_override(unsigned int *size)
+{ return NULL; }
+#endif
+
+#endif
-- 
2.17.0

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux