stable.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: linux-kernel@vger.kernel.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	stable@vger.kernel.org, Hans de Goede <hdegoede@redhat.com>,
	Julian Sax <jsbc@gmx.de>,
	Benjamin Tissoires <benjamin.tissoires@redhat.com>,
	Jiri Kosina <jkosina@suse.cz>, Sasha Levin <sashal@kernel.org>,
	ahormann@gmx.net, Bruno Jesus <bruno.fl.jesus@gmail.com>,
	Dietrich <enaut.w@googlemail.com>,
	kloxdami@yahoo.com
Subject: [PATCH 4.19 066/110] HID: i2c-hid: override HID descriptors for certain devices
Date: Thu, 18 Apr 2019 19:56:55 +0200	[thread overview]
Message-ID: <20190418160444.636150561@linuxfoundation.org> (raw)
In-Reply-To: <20190418160437.484158340@linuxfoundation.org>

[ Upstream commit 9ee3e06610fdb8a601cde59c92089fb6c1deb4aa ]

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: Bruno Jesus <bruno.fl.jesus@gmail.com>
Reported-and-tested-by: Dietrich <enaut.w@googlemail.com>
Reported-and-tested-by: kloxdami@yahoo.com
Signed-off-by: Julian Sax <jsbc@gmx.de>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 drivers/hid/i2c-hid/Makefile                  |   3 +
 .../hid/i2c-hid/{i2c-hid.c => i2c-hid-core.c} |  56 ++-
 drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c      | 376 ++++++++++++++++++
 drivers/hid/i2c-hid/i2c-hid.h                 |  20 +
 4 files changed, 437 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 88daa388e1f6..3cde7c1b9c33 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)
@@ -687,6 +688,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__);
 
@@ -705,26 +707,37 @@ 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(client->name,
+								&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);
+
+		if (!rdesc) {
+			dbg_hid("couldn't allocate rdesc memory\n");
+			return -ENOMEM;
+		}
 
-	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);
-	if (ret) {
-		hid_err(hid, "reading report descriptor failed\n");
-		kfree(rdesc);
-		return -EIO;
+		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;
@@ -851,12 +864,19 @@ 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(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");
+		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..1d645c9ab417
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Quirks for I2C-HID devices that do not supply proper descriptors
+ *
+ * Copyright (c) 2018 Julian Sax <jsbc@gmx.de>
+ *
+ */
+
+#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;
+	uint8_t              *i2c_name;
+};
+
+
+/*
+ * descriptors for the SIPODEV SP1064 touchpad
+ *
+ * This device does not supply any descriptors and on windows a filter
+ * driver operates between the i2c-hid layer and the device and injects
+ * these descriptors when the device is prompted. The descriptors were
+ * extracted by listening to the i2c-hid traffic that occurs between the
+ * windows filter driver and the windows i2c-hid driver.
+ */
+
+static const struct i2c_hid_desc_override sipodev_desc = {
+	.i2c_hid_desc_buffer = (uint8_t [])
+	{0x1e, 0x00,                  /* Length of descriptor                 */
+	 0x00, 0x01,                  /* Version of descriptor                */
+	 0xdb, 0x01,                  /* Length of report descriptor          */
+	 0x21, 0x00,                  /* Location of report descriptor        */
+	 0x24, 0x00,                  /* Location of input report             */
+	 0x1b, 0x00,                  /* Max input report length              */
+	 0x25, 0x00,                  /* Location of output report            */
+	 0x11, 0x00,                  /* Max output report length             */
+	 0x22, 0x00,                  /* Location of command register         */
+	 0x23, 0x00,                  /* Location of data register            */
+	 0x11, 0x09,                  /* Vendor ID                            */
+	 0x88, 0x52,                  /* Product ID                           */
+	 0x06, 0x00,                  /* Version ID                           */
+	 0x00, 0x00, 0x00, 0x00       /* Reserved                             */
+	},
+
+	.hid_report_desc = (uint8_t [])
+	{0x05, 0x01,                  /* Usage Page (Desktop),                */
+	 0x09, 0x02,                  /* Usage (Mouse),                       */
+	 0xA1, 0x01,                  /* Collection (Application),            */
+	 0x85, 0x01,                  /*     Report ID (1),                   */
+	 0x09, 0x01,                  /*     Usage (Pointer),                 */
+	 0xA1, 0x00,                  /*     Collection (Physical),           */
+	 0x05, 0x09,                  /*         Usage Page (Button),         */
+	 0x19, 0x01,                  /*         Usage Minimum (01h),         */
+	 0x29, 0x02,                  /*         Usage Maximum (02h),         */
+	 0x25, 0x01,                  /*         Logical Maximum (1),         */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x95, 0x06,                  /*         Report Count (6),            */
+	 0x81, 0x01,                  /*         Input (Constant),            */
+	 0x05, 0x01,                  /*         Usage Page (Desktop),        */
+	 0x09, 0x30,                  /*         Usage (X),                   */
+	 0x09, 0x31,                  /*         Usage (Y),                   */
+	 0x15, 0x81,                  /*         Logical Minimum (-127),      */
+	 0x25, 0x7F,                  /*         Logical Maximum (127),       */
+	 0x75, 0x08,                  /*         Report Size (8),             */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x81, 0x06,                  /*         Input (Variable, Relative),  */
+	 0xC0,                        /*     End Collection,                  */
+	 0xC0,                        /* End Collection,                      */
+	 0x05, 0x0D,                  /* Usage Page (Digitizer),              */
+	 0x09, 0x05,                  /* Usage (Touchpad),                    */
+	 0xA1, 0x01,                  /* Collection (Application),            */
+	 0x85, 0x04,                  /*     Report ID (4),                   */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x09, 0x22,                  /*     Usage (Finger),                  */
+	 0xA1, 0x02,                  /*     Collection (Logical),            */
+	 0x15, 0x00,                  /*         Logical Minimum (0),         */
+	 0x25, 0x01,                  /*         Logical Maximum (1),         */
+	 0x09, 0x47,                  /*         Usage (Touch Valid),         */
+	 0x09, 0x42,                  /*         Usage (Tip Switch),          */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x75, 0x03,                  /*         Report Size (3),             */
+	 0x25, 0x05,                  /*         Logical Maximum (5),         */
+	 0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x95, 0x03,                  /*         Report Count (3),            */
+	 0x81, 0x03,                  /*         Input (Constant, Variable),  */
+	 0x05, 0x01,                  /*         Usage Page (Desktop),        */
+	 0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+	 0x75, 0x10,                  /*         Report Size (16),            */
+	 0x55, 0x0E,                  /*         Unit Exponent (14),          */
+	 0x65, 0x11,                  /*         Unit (Centimeter),           */
+	 0x09, 0x30,                  /*         Usage (X),                   */
+	 0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+	 0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+	 0x09, 0x31,                  /*         Usage (Y),                   */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0xC0,                        /*     End Collection,                  */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x09, 0x22,                  /*     Usage (Finger),                  */
+	 0xA1, 0x02,                  /*     Collection (Logical),            */
+	 0x25, 0x01,                  /*         Logical Maximum (1),         */
+	 0x09, 0x47,                  /*         Usage (Touch Valid),         */
+	 0x09, 0x42,                  /*         Usage (Tip Switch),          */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x75, 0x03,                  /*         Report Size (3),             */
+	 0x25, 0x05,                  /*         Logical Maximum (5),         */
+	 0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x95, 0x03,                  /*         Report Count (3),            */
+	 0x81, 0x03,                  /*         Input (Constant, Variable),  */
+	 0x05, 0x01,                  /*         Usage Page (Desktop),        */
+	 0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+	 0x75, 0x10,                  /*         Report Size (16),            */
+	 0x09, 0x30,                  /*         Usage (X),                   */
+	 0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+	 0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+	 0x09, 0x31,                  /*         Usage (Y),                   */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0xC0,                        /*     End Collection,                  */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x09, 0x22,                  /*     Usage (Finger),                  */
+	 0xA1, 0x02,                  /*     Collection (Logical),            */
+	 0x25, 0x01,                  /*         Logical Maximum (1),         */
+	 0x09, 0x47,                  /*         Usage (Touch Valid),         */
+	 0x09, 0x42,                  /*         Usage (Tip Switch),          */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x75, 0x03,                  /*         Report Size (3),             */
+	 0x25, 0x05,                  /*         Logical Maximum (5),         */
+	 0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x95, 0x03,                  /*         Report Count (3),            */
+	 0x81, 0x03,                  /*         Input (Constant, Variable),  */
+	 0x05, 0x01,                  /*         Usage Page (Desktop),        */
+	 0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+	 0x75, 0x10,                  /*         Report Size (16),            */
+	 0x09, 0x30,                  /*         Usage (X),                   */
+	 0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+	 0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+	 0x09, 0x31,                  /*         Usage (Y),                   */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0xC0,                        /*     End Collection,                  */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x09, 0x22,                  /*     Usage (Finger),                  */
+	 0xA1, 0x02,                  /*     Collection (Logical),            */
+	 0x25, 0x01,                  /*         Logical Maximum (1),         */
+	 0x09, 0x47,                  /*         Usage (Touch Valid),         */
+	 0x09, 0x42,                  /*         Usage (Tip Switch),          */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x75, 0x03,                  /*         Report Size (3),             */
+	 0x25, 0x05,                  /*         Logical Maximum (5),         */
+	 0x09, 0x51,                  /*         Usage (Contact Identifier),  */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x95, 0x03,                  /*         Report Count (3),            */
+	 0x81, 0x03,                  /*         Input (Constant, Variable),  */
+	 0x05, 0x01,                  /*         Usage Page (Desktop),        */
+	 0x26, 0x44, 0x0A,            /*         Logical Maximum (2628),      */
+	 0x75, 0x10,                  /*         Report Size (16),            */
+	 0x09, 0x30,                  /*         Usage (X),                   */
+	 0x46, 0x1A, 0x04,            /*         Physical Maximum (1050),     */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0x46, 0xBC, 0x02,            /*         Physical Maximum (700),      */
+	 0x26, 0x34, 0x05,            /*         Logical Maximum (1332),      */
+	 0x09, 0x31,                  /*         Usage (Y),                   */
+	 0x81, 0x02,                  /*         Input (Variable),            */
+	 0xC0,                        /*     End Collection,                  */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x55, 0x0C,                  /*     Unit Exponent (12),              */
+	 0x66, 0x01, 0x10,            /*     Unit (Seconds),                  */
+	 0x47, 0xFF, 0xFF, 0x00, 0x00,/*     Physical Maximum (65535),        */
+	 0x27, 0xFF, 0xFF, 0x00, 0x00,/*     Logical Maximum (65535),         */
+	 0x75, 0x10,                  /*     Report Size (16),                */
+	 0x95, 0x01,                  /*     Report Count (1),                */
+	 0x09, 0x56,                  /*     Usage (Scan Time),               */
+	 0x81, 0x02,                  /*     Input (Variable),                */
+	 0x09, 0x54,                  /*     Usage (Contact Count),           */
+	 0x25, 0x7F,                  /*     Logical Maximum (127),           */
+	 0x75, 0x08,                  /*     Report Size (8),                 */
+	 0x81, 0x02,                  /*     Input (Variable),                */
+	 0x05, 0x09,                  /*     Usage Page (Button),             */
+	 0x09, 0x01,                  /*     Usage (01h),                     */
+	 0x25, 0x01,                  /*     Logical Maximum (1),             */
+	 0x75, 0x01,                  /*     Report Size (1),                 */
+	 0x95, 0x01,                  /*     Report Count (1),                */
+	 0x81, 0x02,                  /*     Input (Variable),                */
+	 0x95, 0x07,                  /*     Report Count (7),                */
+	 0x81, 0x03,                  /*     Input (Constant, Variable),      */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x85, 0x02,                  /*     Report ID (2),                   */
+	 0x09, 0x55,                  /*     Usage (Contact Count Maximum),   */
+	 0x09, 0x59,                  /*     Usage (59h),                     */
+	 0x75, 0x04,                  /*     Report Size (4),                 */
+	 0x95, 0x02,                  /*     Report Count (2),                */
+	 0x25, 0x0F,                  /*     Logical Maximum (15),            */
+	 0xB1, 0x02,                  /*     Feature (Variable),              */
+	 0x05, 0x0D,                  /*     Usage Page (Digitizer),          */
+	 0x85, 0x07,                  /*     Report ID (7),                   */
+	 0x09, 0x60,                  /*     Usage (60h),                     */
+	 0x75, 0x01,                  /*     Report Size (1),                 */
+	 0x95, 0x01,                  /*     Report Count (1),                */
+	 0x25, 0x01,                  /*     Logical Maximum (1),             */
+	 0xB1, 0x02,                  /*     Feature (Variable),              */
+	 0x95, 0x07,                  /*     Report Count (7),                */
+	 0xB1, 0x03,                  /*     Feature (Constant, Variable),    */
+	 0x85, 0x06,                  /*     Report ID (6),                   */
+	 0x06, 0x00, 0xFF,            /*     Usage Page (FF00h),              */
+	 0x09, 0xC5,                  /*     Usage (C5h),                     */
+	 0x26, 0xFF, 0x00,            /*     Logical Maximum (255),           */
+	 0x75, 0x08,                  /*     Report Size (8),                 */
+	 0x96, 0x00, 0x01,            /*     Report Count (256),              */
+	 0xB1, 0x02,                  /*     Feature (Variable),              */
+	 0xC0,                        /* End Collection,                      */
+	 0x06, 0x00, 0xFF,            /* Usage Page (FF00h),                  */
+	 0x09, 0x01,                  /* Usage (01h),                         */
+	 0xA1, 0x01,                  /* Collection (Application),            */
+	 0x85, 0x0D,                  /*     Report ID (13),                  */
+	 0x26, 0xFF, 0x00,            /*     Logical Maximum (255),           */
+	 0x19, 0x01,                  /*     Usage Minimum (01h),             */
+	 0x29, 0x02,                  /*     Usage Maximum (02h),             */
+	 0x75, 0x08,                  /*     Report Size (8),                 */
+	 0x95, 0x02,                  /*     Report Count (2),                */
+	 0xB1, 0x02,                  /*     Feature (Variable),              */
+	 0xC0,                        /* End Collection,                      */
+	 0x05, 0x0D,                  /* Usage Page (Digitizer),              */
+	 0x09, 0x0E,                  /* Usage (Configuration),               */
+	 0xA1, 0x01,                  /* Collection (Application),            */
+	 0x85, 0x03,                  /*     Report ID (3),                   */
+	 0x09, 0x22,                  /*     Usage (Finger),                  */
+	 0xA1, 0x02,                  /*     Collection (Logical),            */
+	 0x09, 0x52,                  /*         Usage (Device Mode),         */
+	 0x25, 0x0A,                  /*         Logical Maximum (10),        */
+	 0x95, 0x01,                  /*         Report Count (1),            */
+	 0xB1, 0x02,                  /*         Feature (Variable),          */
+	 0xC0,                        /*     End Collection,                  */
+	 0x09, 0x22,                  /*     Usage (Finger),                  */
+	 0xA1, 0x00,                  /*     Collection (Physical),           */
+	 0x85, 0x05,                  /*         Report ID (5),               */
+	 0x09, 0x57,                  /*         Usage (57h),                 */
+	 0x09, 0x58,                  /*         Usage (58h),                 */
+	 0x75, 0x01,                  /*         Report Size (1),             */
+	 0x95, 0x02,                  /*         Report Count (2),            */
+	 0x25, 0x01,                  /*         Logical Maximum (1),         */
+	 0xB1, 0x02,                  /*         Feature (Variable),          */
+	 0x95, 0x06,                  /*         Report Count (6),            */
+	 0xB1, 0x03,                  /*         Feature (Constant, Variable),*/
+	 0xC0,                        /*     End Collection,                  */
+	 0xC0                         /* End Collection                       */
+	},
+	.hid_report_desc_size = 475,
+	.i2c_name = "SYNA3602:00"
+};
+
+
+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
+	},
+	{
+		.ident = "Direkt-Tek DTLAPY116-2",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "DTLAPY116-2"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	},
+	{
+		.ident = "Mediacom Flexbook Edge 11",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDIACOM"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	}
+};
+
+
+struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
+{
+	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;
+	if (strcmp(override->i2c_name, i2c_name))
+		return NULL;
+
+	return override->i2c_hid_desc;
+}
+
+char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
+					       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;
+	if (strcmp(override->i2c_name, i2c_name))
+		return NULL;
+
+	*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..a8c19aef5824
--- /dev/null
+++ b/drivers/hid/i2c-hid/i2c-hid.h
@@ -0,0 +1,20 @@
+/* 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
-- 
2.19.1




  parent reply	other threads:[~2019-04-18 18:31 UTC|newest]

Thread overview: 119+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-04-18 17:55 [PATCH 4.19 000/110] 4.19.36-stable review Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 001/110] ARC: u-boot args: check that magic number is correct Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 002/110] arc: hsdk_defconfig: Enable CONFIG_BLK_DEV_RAM Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 003/110] inotify: Fix fsnotify_mark refcount leak in inotify_update_existing_watch() Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 004/110] perf/core: Restore mmap record type correctly Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 005/110] perf data: Dont store auxtrace index for directory data file Greg Kroah-Hartman
2019-04-18 18:15   ` Dan Rue
2019-04-18 17:55 ` [PATCH 4.19 006/110] ext4: avoid panic during forced reboot Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 007/110] ext4: add missing brelse() in add_new_gdb_meta_bg() Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 008/110] ext4: report real fs size after failed resize Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 009/110] ALSA: echoaudio: add a check for ioremap_nocache Greg Kroah-Hartman
2019-04-18 17:55 ` [PATCH 4.19 010/110] ALSA: sb8: add a check for request_region Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 011/110] auxdisplay: hd44780: Fix memory leak on ->remove() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 012/110] drm/udl: use drm_gem_object_put_unlocked Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 013/110] IB/mlx4: Fix race condition between catas error reset and aliasguid flows Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 014/110] i40iw: Avoid panic when handling the inetdev event Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 015/110] mmc: davinci: remove extraneous __init annotation Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 016/110] ALSA: opl3: fix mismatch between snd_opl3_drum_switch definition and declaration Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 017/110] thermal/intel_powerclamp: fix __percpu declaration of worker_data Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 018/110] thermal: samsung: Fix incorrect check after code merge Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 019/110] thermal: bcm2835: Fix crash in bcm2835_thermal_debugfs Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 020/110] thermal/int340x_thermal: Add additional UUIDs Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 021/110] thermal/int340x_thermal: fix mode setting Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 022/110] thermal/intel_powerclamp: fix truncated kthread name Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 023/110] scsi: iscsi: flush running unbind operations when removing a session Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 024/110] sched/cpufreq: Fix 32-bit math overflow Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 025/110] sched/core: Fix buffer overflow in cgroup2 property cpu.max Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 026/110] x86/mm: Dont leak kernel addresses Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 027/110] tools/power turbostat: return the exit status of a command Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 028/110] perf list: Dont forget to drop the reference to the allocated thread_map Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 029/110] perf config: Fix an error in the config template documentation Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 030/110] perf config: Fix a memory leak in collect_config() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 031/110] perf build-id: Fix memory leak in print_sdt_events() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 032/110] perf top: Fix error handling in cmd_top() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 033/110] perf hist: Add missing map__put() in error case Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 034/110] perf evsel: Free evsel->counts in perf_evsel__exit() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 035/110] perf tests: Fix a memory leak of cpu_map object in the openat_syscall_event_on_all_cpus test Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 036/110] perf tests: Fix memory leak by expr__find_other() in test__expr() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 037/110] perf tests: Fix a memory leak in test__perf_evsel__tp_sched_test() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 038/110] ACPI / utils: Drop reference in test for device presence Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 039/110] PM / Domains: Avoid a potential deadlock Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 040/110] blk-iolatency: #include "blk.h" Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 041/110] drm/exynos/mixer: fix MIXER shadow registry synchronisation code Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 042/110] irqchip/stm32: Dont clear rising/falling config registers at init Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 043/110] irqchip/mbigen: Dont clear eventid when freeing an MSI Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 044/110] x86/hpet: Prevent potential NULL pointer dereference Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 045/110] x86/hyperv: " Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 046/110] x86/cpu/cyrix: Use correct macros for Cyrix calls on Geode processors Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 047/110] drm/nouveau/debugfs: Fix check of pm_runtime_get_sync failure Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 048/110] iommu/vt-d: Check capability before disabling protected memory Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 049/110] x86/hw_breakpoints: Make default case in hw_breakpoint_arch_parse() return an error Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 050/110] fix incorrect error code mapping for OBJECTID_NOT_FOUND Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 051/110] x86/gart: Exclude GART aperture from kcore Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 052/110] ext4: prohibit fstrim in norecovery mode Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 053/110] drm/cirrus: Use drm_framebuffer_put to avoid kernel oops in clean-up Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 054/110] gpio: pxa: handle corner case of unprobed device Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 055/110] rsi: improve kernel thread handling to fix kernel panic Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 056/110] f2fs: fix to avoid NULL pointer dereference on se->discard_map Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 057/110] 9p: do not trust pdu content for stat item size Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 058/110] 9p locks: add mount option for lock retry interval Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 059/110] ASoC: Fix UBSAN warning at snd_soc_get/put_volsw_sx() Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 060/110] f2fs: fix to do sanity check with current segment number Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 061/110] netfilter: xt_cgroup: shrink size of v2 path Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 062/110] serial: uartps: console_setup() cant be placed to init section Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 063/110] powerpc/pseries: Remove prrn_work workqueue Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 064/110] media: au0828: cannot kfree dev before usb disconnect Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 065/110] Bluetooth: Fix debugfs NULL pointer dereference Greg Kroah-Hartman
2019-04-18 17:56 ` Greg Kroah-Hartman [this message]
2019-04-18 17:56 ` [PATCH 4.19 067/110] pinctrl: core: make sure strcmp() doesnt get a null parameter Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 068/110] ARM: samsung: Limit SAMSUNG_PM_CHECK config option to non-Exynos platforms Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 069/110] usbip: fix vhci_hcd controller counting Greg Kroah-Hartman
2019-04-18 17:56 ` [PATCH 4.19 070/110] ACPI / SBS: Fix GPE storm on recent MacBookPros Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 071/110] HID: usbhid: Add quirk for Redragon/Dragonrise Seymur 2 Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 072/110] KVM: nVMX: restore host state in nested_vmx_vmexit for VMFail Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 073/110] compiler.h: update definition of unreachable() Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 074/110] netfilter: nf_flow_table: remove flowtable hook flush routine in netns exit routine Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 075/110] f2fs: cleanup dirty pages if recover failed Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 076/110] net: stmmac: Set OWN bit for jumbo frames Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 077/110] cifs: fallback to older infolevels on findfirst queryinfo retry Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 078/110] kernel: hung_task.c: disable on suspend Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 079/110] platform/x86: Add Intel AtomISP2 dummy / power-management driver Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 080/110] drm/ttm: Fix bo_global and mem_global kfree error Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 081/110] ALSA: hda: fix front speakers on Huawei MBXP Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 082/110] ACPI: EC / PM: Disable non-wakeup GPEs for suspend-to-idle Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 083/110] net/rds: fix warn in rds_message_alloc_sgs Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 084/110] xfrm: destroy xfrm_state synchronously on net exit path Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 085/110] crypto: sha256/arm - fix crash bug in Thumb2 build Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 086/110] crypto: sha512/arm " Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 087/110] net: ip6_gre: fix possible NULL pointer dereference in ip6erspan_set_version Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 088/110] iommu/dmar: Fix buffer overflow during PCI bus notification Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 089/110] scsi: core: Avoid that system resume triggers a kernel warning Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 090/110] soc/tegra: pmc: Drop locking from tegra_powergate_is_powered() Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 091/110] lkdtm: Print real addresses Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 092/110] lkdtm: Add tests for NULL pointer dereference Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 093/110] drm/panel: panel-innolux: set display off in innolux_panel_unprepare Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 094/110] crypto: axis - fix for recursive locking from bottom half Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 095/110] Revert "ACPI / EC: Remove old CLEAR_ON_RESUME quirk" Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 096/110] coresight: cpu-debug: Support for CA73 CPUs Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 097/110] PCI: Blacklist power management of Gigabyte X299 DESIGNARE EX PCIe ports Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 098/110] drm/nouveau/volt/gf117: fix speedo readout register Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 099/110] ARM: 8839/1: kprobe: make patch_lock a raw_spinlock_t Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 100/110] drm/amdkfd: use init_mqd function to allocate object for hid_mqd (CI) Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 101/110] appletalk: Fix use-after-free in atalk_proc_exit Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 102/110] lib/div64.c: off by one in shift Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 103/110] rxrpc: Fix client call connect/disconnect race Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 104/110] f2fs: fix to dirty inode for i_mode recovery Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 105/110] include/linux/swap.h: use offsetof() instead of custom __swapoffset macro Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 106/110] bpf: fix use after free in bpf_evict_inode Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 107/110] tools include: Adopt linux/bits.h Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 108/110] IB/hfi1: Failed to drain send queue when QP is put into error state Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 109/110] mm: hide incomplete nr_indirectly_reclaimable in /proc/zoneinfo Greg Kroah-Hartman
2019-04-18 17:57 ` [PATCH 4.19 110/110] [PATCH 4.19.y 2/2] mm: hide incomplete nr_indirectly_reclaimable in sysfs Greg Kroah-Hartman
2019-04-19  5:19 ` [PATCH 4.19 000/110] 4.19.36-stable review nobuhiro1.iwamatsu
2019-04-19 10:08 ` Jon Hunter
2019-04-19 10:42 ` Guenter Roeck
2019-04-19 14:22 ` shuah
2019-04-19 19:40 ` Guenter Roeck
2019-04-19 20:47   ` Guenter Roeck
2019-04-20  5:33 ` Naresh Kamboju

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190418160444.636150561@linuxfoundation.org \
    --to=gregkh@linuxfoundation.org \
    --cc=ahormann@gmx.net \
    --cc=benjamin.tissoires@redhat.com \
    --cc=bruno.fl.jesus@gmail.com \
    --cc=enaut.w@googlemail.com \
    --cc=hdegoede@redhat.com \
    --cc=jkosina@suse.cz \
    --cc=jsbc@gmx.de \
    --cc=kloxdami@yahoo.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sashal@kernel.org \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).