Commit 8e271100 authored by Julian Sax's avatar Julian Sax Committed by Greg Kroah-Hartman

HID: i2c-hid: override HID descriptors for certain devices

[ Upstream commit 9ee3e066 ]

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, which were extracted by listening to the traffic of the
windows filter driver, as well as the DMI data for the laptops known
to use this device.

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

Cc: Hans de Goede <hdegoede@redhat.com>
Reported-and-tested-by: ahormann@gmx.net
Reported-and-tested-by: default avatarBruno Jesus <bruno.fl.jesus@gmail.com>
Reported-and-tested-by: default avatarDietrich <enaut.w@googlemail.com>
Reported-and-tested-by: kloxdami@yahoo.com
Signed-off-by: default avatarJulian Sax <jsbc@gmx.de>
Reviewed-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 09b6c080
...@@ -3,3 +3,6 @@ ...@@ -3,3 +3,6 @@
# #
obj-$(CONFIG_I2C_HID) += i2c-hid.o obj-$(CONFIG_I2C_HID) += i2c-hid.o
i2c-hid-objs = i2c-hid-core.o
i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <linux/platform_data/i2c-hid.h> #include <linux/platform_data/i2c-hid.h>
#include "../hid-ids.h" #include "../hid-ids.h"
#include "i2c-hid.h"
/* quirks to control the device */ /* quirks to control the device */
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0) #define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
...@@ -687,6 +688,7 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -687,6 +688,7 @@ static int i2c_hid_parse(struct hid_device *hid)
char *rdesc; char *rdesc;
int ret; int ret;
int tries = 3; int tries = 3;
char *use_override;
i2c_hid_dbg(ihid, "entering %s\n", __func__); i2c_hid_dbg(ihid, "entering %s\n", __func__);
...@@ -705,6 +707,13 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -705,6 +707,13 @@ static int i2c_hid_parse(struct hid_device *hid)
if (ret) if (ret)
return ret; return ret;
use_override = i2c_hid_get_dmi_hid_report_desc_override(client->name,
&rsize);
if (use_override) {
rdesc = use_override;
i2c_hid_dbg(ihid, "Using a HID report descriptor override\n");
} else {
rdesc = kzalloc(rsize, GFP_KERNEL); rdesc = kzalloc(rsize, GFP_KERNEL);
if (!rdesc) { if (!rdesc) {
...@@ -714,17 +723,21 @@ static int i2c_hid_parse(struct hid_device *hid) ...@@ -714,17 +723,21 @@ static int i2c_hid_parse(struct hid_device *hid)
i2c_hid_dbg(ihid, "asking HID report descriptor\n"); i2c_hid_dbg(ihid, "asking HID report descriptor\n");
ret = i2c_hid_command(client, &hid_report_descr_cmd, rdesc, rsize); ret = i2c_hid_command(client, &hid_report_descr_cmd,
rdesc, rsize);
if (ret) { if (ret) {
hid_err(hid, "reading report descriptor failed\n"); hid_err(hid, "reading report descriptor failed\n");
kfree(rdesc); kfree(rdesc);
return -EIO; return -EIO;
} }
}
i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc); i2c_hid_dbg(ihid, "Report Descriptor: %*ph\n", rsize, rdesc);
ret = hid_parse_report(hid, rdesc, rsize); ret = hid_parse_report(hid, rdesc, rsize);
if (!use_override)
kfree(rdesc); kfree(rdesc);
if (ret) { if (ret) {
dbg_hid("parsing report descriptor failed\n"); dbg_hid("parsing report descriptor failed\n");
return ret; return ret;
...@@ -851,13 +864,20 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) ...@@ -851,13 +864,20 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
int ret; int ret;
/* i2c hid fetch using a fixed descriptor size (30 bytes) */ /* i2c hid fetch using a fixed descriptor size (30 bytes) */
if (i2c_hid_get_dmi_i2c_hid_desc_override(client->name)) {
i2c_hid_dbg(ihid, "Using a HID descriptor override\n");
ihid->hdesc =
*i2c_hid_get_dmi_i2c_hid_desc_override(client->name);
} else {
i2c_hid_dbg(ihid, "Fetching the HID descriptor\n"); i2c_hid_dbg(ihid, "Fetching the HID descriptor\n");
ret = i2c_hid_command(client, &hid_descr_cmd, ihid->hdesc_buffer, ret = i2c_hid_command(client, &hid_descr_cmd,
ihid->hdesc_buffer,
sizeof(struct i2c_hid_desc)); sizeof(struct i2c_hid_desc));
if (ret) { if (ret) {
dev_err(&client->dev, "hid_descr_cmd failed\n"); dev_err(&client->dev, "hid_descr_cmd failed\n");
return -ENODEV; return -ENODEV;
} }
}
/* Validate the length of HID descriptor, the 4 first bytes: /* Validate the length of HID descriptor, the 4 first bytes:
* bytes 0-1 -> length * bytes 0-1 -> length
......
This diff is collapsed.
/* 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(uint8_t *i2c_name);
char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
unsigned int *size);
#else
static inline struct i2c_hid_desc
*i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
{ return NULL; }
static inline char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
unsigned int *size)
{ return NULL; }
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment