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