git: 8d5fef85f007 - main - ietp(4): Load dummy HID report descriptor if mangled

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Sun, 05 Feb 2023 15:33:33 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=8d5fef85f007c81d15ea6de825a4462b3829ea16

commit 8d5fef85f007c81d15ea6de825a4462b3829ea16
Author:     Aymeric Wibo <obiwac@gmail.com>
AuthorDate: 2023-02-05 15:32:08 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2023-02-05 15:32:08 +0000

    ietp(4): Load dummy HID report descriptor if mangled
    
    Some Elantech trackpads have a mangled HID report descriptor, which
    reads as having an incorrect input size (i.e. < IETP_REPORT_LEN_LO).
    If the input size is incorrect, load a dummy report descriptor.
    
    Reviewed by:    wulf
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D38387
---
 sys/dev/hid/ietp.c | 91 +++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 22 deletions(-)

diff --git a/sys/dev/hid/ietp.c b/sys/dev/hid/ietp.c
index 6d0570c88e30..3381f76da630 100644
--- a/sys/dev/hid/ietp.c
+++ b/sys/dev/hid/ietp.c
@@ -118,7 +118,7 @@ struct ietp_softc {
 	uint16_t		trace_y;
 	uint16_t		res_x;		/* dots per mm */
 	uint16_t		res_y;
-	bool			hi_precission;
+	bool			hi_precision;
 	bool			is_clickpad;
 	bool			has_3buttons;
 };
@@ -132,9 +132,10 @@ static int		ietp_attach(struct ietp_softc *);
 static int		ietp_detach(struct ietp_softc *);
 static int32_t		ietp_res2dpmm(uint8_t, bool);
 
-static device_probe_t   ietp_iic_probe;
-static device_attach_t  ietp_iic_attach;
-static device_detach_t  ietp_iic_detach;
+static device_identify_t ietp_iic_identify;
+static device_probe_t	ietp_iic_probe;
+static device_attach_t	ietp_iic_attach;
+static device_detach_t	ietp_iic_detach;
 static device_resume_t	ietp_iic_resume;
 
 static int		ietp_iic_read_reg(device_t, uint16_t, size_t, void *);
@@ -200,6 +201,17 @@ static const struct hid_device_id ietp_iic_devs[] = {
 	IETP_IIC_DEV("ELAN1000"),
 };
 
+static uint8_t const ietp_dummy_rdesc[] = {
+	0x05, HUP_GENERIC_DESKTOP,	/* Usage Page (Generic Desktop Ctrls)	*/
+	0x09, HUG_MOUSE,		/* Usage (Mouse)			*/
+	0xA1, 0x01,			/* Collection (Application)		*/
+	0x09, 0x01,			/*   Usage (0x01)			*/
+	0x95, IETP_REPORT_LEN_LO,	/*   Report Count (IETP_REPORT_LEN_LO)	*/
+	0x75, 0x08,			/*   Report Size (8)			*/
+	0x81, 0x02,			/*   Input (Data,Var,Abs)		*/
+	0xC0,				/* End Collection			*/
+};
+
 static const struct evdev_methods ietp_evdev_methods = {
 	.ev_open = &ietp_ev_open,
 	.ev_close = &ietp_ev_close,
@@ -241,9 +253,9 @@ ietp_attach(struct ietp_softc *sc)
 	int32_t minor, major;
 	int error;
 
-	sc->report_id = sc->hi_precission ?
+	sc->report_id = sc->hi_precision ?
 	    IETP_REPORT_ID_HI : IETP_REPORT_ID_LO;
-	sc->report_len = sc->hi_precission ?
+	sc->report_len = sc->hi_precision ?
 	    IETP_REPORT_LEN_HI : IETP_REPORT_LEN_LO;
 
 	/* Try to detect 3-rd button by relative mouse TLC */
@@ -334,15 +346,31 @@ ietp_intr(void *context, void *buf, hid_size_t len)
 	int32_t x, y, w, h, wh;
 
 	/* we seem to get 0 length reports sometimes, ignore them */
+	if (len == 0)
+		return;
+	if (len != sc->report_len) {
+		DPRINTF("wrong report length (%d vs %d expected)", len, sc->report_len);
+		return;
+	}
+
 	report = buf;
-	if (*report != sc->report_id || len < sc->report_len)
+	if (*report != sc->report_id)
 		return;
 
+	evdev_push_key(sc->evdev, BTN_LEFT,
+	    report[IETP_TOUCH_INFO] & IETP_TOUCH_LMB);
+	evdev_push_key(sc->evdev, BTN_MIDDLE,
+	    report[IETP_TOUCH_INFO] & IETP_TOUCH_MMB);
+	evdev_push_key(sc->evdev, BTN_RIGHT,
+	    report[IETP_TOUCH_INFO] & IETP_TOUCH_RMB);
+	evdev_push_abs(sc->evdev, ABS_DISTANCE,
+	    (report[IETP_HOVER_INFO] & 0x40) >> 6);
+
 	for (finger = 0, fdata = report + IETP_FINGER_DATA;
 	     finger < IETP_MAX_FINGERS;
 	     finger++, fdata += IETP_FINGER_DATA_LEN) {
 		if ((report[IETP_TOUCH_INFO] & (1 << (finger + 3))) != 0) {
-			if (sc->hi_precission) {
+			if (sc->hi_precision) {
 				x = fdata[0] << 8 | fdata[1];
 				y = fdata[2] << 8 | fdata[3];
 				wh = report[IETP_WH_DATA + finger];
@@ -379,28 +407,46 @@ ietp_intr(void *context, void *buf, hid_size_t len)
 		}
 	}
 
-	evdev_push_key(sc->evdev, BTN_LEFT,
-	    report[IETP_TOUCH_INFO] & IETP_TOUCH_LMB);
-	evdev_push_key(sc->evdev, BTN_MIDDLE,
-	    report[IETP_TOUCH_INFO] & IETP_TOUCH_MMB);
-	evdev_push_key(sc->evdev, BTN_RIGHT,
-	    report[IETP_TOUCH_INFO] & IETP_TOUCH_RMB);
-	evdev_push_abs(sc->evdev, ABS_DISTANCE,
-	    (report[IETP_HOVER_INFO] & 0x40) >> 6);
-
 	evdev_sync(sc->evdev);
 }
 
 static int32_t
-ietp_res2dpmm(uint8_t res, bool hi_precission)
+ietp_res2dpmm(uint8_t res, bool hi_precision)
 {
 	int32_t dpi;
 
-	dpi = hi_precission ? 300 + res * 100 : 790 + res * 10;
+	dpi = hi_precision ? 300 + res * 100 : 790 + res * 10;
 
 	return (dpi * 10 /254);
 }
 
+static void
+ietp_iic_identify(driver_t *driver, device_t parent)
+{
+	void *d_ptr;
+	hid_size_t d_len;
+	int isize;
+	uint8_t iid;
+
+	if (HIDBUS_LOOKUP_ID(parent, ietp_iic_devs) == NULL)
+		return;
+	if (hid_get_report_descr(parent, &d_ptr, &d_len) != 0)
+		return;
+
+	/*
+	 * Some Elantech trackpads have a mangled HID report descriptor, which
+	 * reads as having an incorrect input size (i.e. < IETP_REPORT_LEN_LO).
+	 * If the input size is incorrect, load a dummy report descriptor.
+	 */
+
+	isize = hid_report_size_max(d_ptr, d_len, hid_input, &iid);
+	if (isize >= IETP_REPORT_LEN_LO)
+		return;
+
+	hid_set_report_descr(parent, ietp_dummy_rdesc,
+	    sizeof(ietp_dummy_rdesc));
+}
+
 static int
 ietp_iic_probe(device_t dev)
 {
@@ -442,7 +488,7 @@ ietp_iic_attach(device_t dev)
 		return (EIO);
 	}
 	pattern = buf == 0xFFFF ? 0 : buf8[1];
-	sc->hi_precission = pattern >= 0x02;
+	sc->hi_precision = pattern >= 0x02;
 
 	reg = pattern >= 0x01 ? IETP_IC_TYPE : IETP_OSM_VERSION;
 	if (ietp_iic_read_reg(dev, reg, sizeof(buf), &buf) != 0) {
@@ -492,8 +538,8 @@ ietp_iic_attach(device_t dev)
 		return (EIO);
 	}
 	/* Conversion from internal format to dot per mm */
-	sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precission);
-	sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precission);
+	sc->res_x = ietp_res2dpmm(buf8[0], sc->hi_precision);
+	sc->res_y = ietp_res2dpmm(buf8[1], sc->hi_precision);
 
 	return (ietp_attach(sc));
 }
@@ -610,6 +656,7 @@ ietp_iic_write_reg(device_t dev, uint16_t reg, uint16_t val)
 }
 
 static device_method_t ietp_methods[] = {
+	DEVMETHOD(device_identify,	ietp_iic_identify),
 	DEVMETHOD(device_probe,		ietp_iic_probe),
 	DEVMETHOD(device_attach,	ietp_iic_attach),
 	DEVMETHOD(device_detach,	ietp_iic_detach),