Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Nov 2018 21:26:27 +0000 (UTC)
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org
Subject:   svn commit: r340305 - stable/11/sys/dev/usb/input
Message-ID:  <201811092126.wA9LQR6j024800@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: wulf
Date: Fri Nov  9 21:26:26 2018
New Revision: 340305
URL: https://svnweb.freebsd.org/changeset/base/340305

Log:
  MFC r337287:
  
  wmt(4): Read 'Contact count maximum' usage value from feature report
  
  rather than from HID descriptor to match Microsoft documentation.
  Fall back to HID descriptor provided value if 'Get Report' request failed.
  
  MFC r337288:
  
  wmt(4): Read Microsoft's "Touch Hardware Quality Assurance" certificate blob
  
  if present to enable some devices like WaveShare touchscreens. Unlike
  Windows we discard content of the blob. We try mimic Windows driver
  behaviour from the USB device point of view.
  
  Submitted by:	glebius (initial version)
  
  MFC r337289:
  
  wmt(4): Use internal function to calculate input report size
  
  Usbhid's hid_report_size() calculates integral size of all reports of given
  kind found in the HID descriptor rather then exact size of report with given
  ID as its userland counterpart does. As all input data processed by the
  driver is located within the same report, calculate required driver's buffer
  size with userland version, imported in one of the previous commits.
  This allows us to skip zeroing of buffer on processing of each report.
  
  While here do some minor refactoring.
  
  MFC r338458:
  
  wmt(4): Fix regression introduced in r337289
  
  r337289 has a side effect of reducing usb frame 0 buffer size down to
  touch report size. That broke some devices e.g. "Raydium Touch System"
  which are capable of generating non-touch frames of bigger length.
  Fix it with enlarging frame 0 buffer up to internal wmt(4) buffer size.
  
  Reported by:	Roberto Fernandez Cueto <roberfern@gmail.com>
  Tested by:	Roberto Fernandez Cueto <roberfern@gmail.com>
  Differential Revision:	https://reviews.freebsd.org/D16772

Modified:
  stable/11/sys/dev/usb/input/wmt.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/dev/usb/input/wmt.c
==============================================================================
--- stable/11/sys/dev/usb/input/wmt.c	Fri Nov  9 21:16:45 2018	(r340304)
+++ stable/11/sys/dev/usb/input/wmt.c	Fri Nov  9 21:26:26 2018	(r340305)
@@ -203,6 +203,12 @@ struct wmt_softc
 	uint32_t		nconts_max;
 	uint8_t			report_id;
 
+	struct hid_location	cont_max_loc;
+	uint32_t		cont_max_rlen;
+	uint8_t			cont_max_rid;
+	uint32_t		thqa_cert_rlen;
+	uint8_t			thqa_cert_rid;
+
 	uint8_t			buf[WMT_BSIZE] __aligned(4);
 };
 
@@ -212,6 +218,7 @@ struct wmt_softc
 		if (USAGE_SUPPORTED((caps), (usage)))
 
 static bool wmt_hid_parse(struct wmt_softc *, const void *, uint16_t);
+static void wmt_cont_max_parse(struct wmt_softc *, const void *, uint16_t);
 
 static usb_callback_t	wmt_intr_callback;
 
@@ -279,6 +286,7 @@ wmt_attach(device_t dev)
 	uint16_t d_len;
 	size_t i;
 	int err;
+	bool hid_ok;
 
 	device_set_usb_desc(dev);
 	sc->dev = dev;
@@ -291,25 +299,49 @@ wmt_attach(device_t dev)
 		return (ENXIO);
 	}
 
-	mtx_init(&sc->mtx, "wmt lock", NULL, MTX_DEF);
+	hid_ok = wmt_hid_parse(sc, d_ptr, d_len);
+	free(d_ptr, M_TEMP);
+	if (!hid_ok) {
+		DPRINTF("multi-touch HID descriptor not found\n");
+		return (ENXIO);
+	}
 
-	/* Get HID report length */
-	sc->isize = hid_report_size(d_ptr, d_len, hid_input, NULL);
+	/* Check HID report length */
 	if (sc->isize <= 0 || sc->isize > WMT_BSIZE) {
 		DPRINTF("Input size invalid or too large: %d\n", sc->isize);
-		goto detach;
+		return (ENXIO);
 	}
 
+	/* Fetch and parse "Contact count maximum" feature report */
+	if (sc->cont_max_rlen > 0 && sc->cont_max_rlen <= WMT_BSIZE) {
+		err = usbd_req_get_report(uaa->device, NULL, sc->buf,
+		    sc->cont_max_rlen, uaa->info.bIfaceIndex,
+		    UHID_FEATURE_REPORT, sc->cont_max_rid);
+		if (err == USB_ERR_NORMAL_COMPLETION)
+			wmt_cont_max_parse(sc, sc->buf, sc->cont_max_rlen);
+		else
+			DPRINTF("usbd_req_get_report error=(%s)\n",
+			    usbd_errstr(err));
+	} else
+		DPRINTF("Feature report %hhu size invalid or too large: %u\n",
+		    sc->cont_max_rid, sc->cont_max_rlen);
+
+	/* Fetch THQA certificate to enable some devices like WaveShare */
+	if (sc->thqa_cert_rlen > 0 && sc->thqa_cert_rlen <= WMT_BSIZE &&
+	    sc->thqa_cert_rid != sc->cont_max_rid)
+		(void)usbd_req_get_report(uaa->device, NULL, sc->buf,
+		    sc->thqa_cert_rlen, uaa->info.bIfaceIndex,
+		    UHID_FEATURE_REPORT, sc->thqa_cert_rid);
+
+	mtx_init(&sc->mtx, "wmt lock", NULL, MTX_DEF);
+
 	err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
 	    sc->xfer, wmt_config, WMT_N_TRANSFER, sc, &sc->mtx);
-	if (err) {
+	if (err != USB_ERR_NORMAL_COMPLETION) {
 		DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(err));
 		goto detach;
 	}
 
-	if (!wmt_hid_parse(sc, d_ptr, d_len))
-		goto detach;
-
 	sc->evdev = evdev_alloc();
 	evdev_set_name(sc->evdev, device_get_desc(dev));
 	evdev_set_phys(sc->evdev, device_get_nameunit(dev));
@@ -334,7 +366,6 @@ wmt_attach(device_t dev)
 	return (0);
 
 detach:
-	free(d_ptr, M_TEMP);
 	wmt_detach(dev);
 	return (ENXIO);
 }
@@ -480,7 +511,7 @@ tr_ignore:
 
 	case USB_ST_SETUP:
 tr_setup:
-		usbd_xfer_set_frame_len(xfer, 0, sc->isize);
+		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
 		usbd_transfer_submit(xfer);
 		break;
 	default:
@@ -513,6 +544,46 @@ wmt_ev_open(struct evdev_dev *evdev, void *ev_softc)
 	return (0);
 }
 
+/* port of userland hid_report_size() from usbhid(3) to kernel */
+static int
+wmt_hid_report_size(const void *buf, uint16_t len, enum hid_kind k, uint8_t id)
+{
+	struct hid_data *d;
+	struct hid_item h;
+	uint32_t temp;
+	uint32_t hpos;
+	uint32_t lpos;
+	int report_id = 0;
+
+	hpos = 0;
+	lpos = 0xFFFFFFFF;
+
+	for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);) {
+		if (h.kind == k && h.report_ID == id) {
+			/* compute minimum */
+			if (lpos > h.loc.pos)
+				lpos = h.loc.pos;
+			/* compute end position */
+			temp = h.loc.pos + (h.loc.size * h.loc.count);
+			/* compute maximum */
+			if (hpos < temp)
+				hpos = temp;
+			if (h.report_ID != 0)
+				report_id = 1;
+		}
+	}
+	hid_end_parse(d);
+
+	/* safety check - can happen in case of currupt descriptors */
+	if (lpos > hpos)
+		temp = 0;
+	else
+		temp = hpos - lpos;
+
+	/* return length in bytes rounded up */
+	return ((temp + 7) / 8 + report_id);
+}
+
 static bool
 wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len)
 {
@@ -523,6 +594,8 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 	uint32_t caps = 0;
 	int32_t cont_count_max = 0;
 	uint8_t report_id = 0;
+	uint8_t cont_max_rid = 0;
+	uint8_t thqa_cert_rid = 0;
 	bool touch_coll = false;
 	bool finger_coll = false;
 	bool cont_count_found = false;
@@ -530,6 +603,7 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 
 #define WMT_HI_ABSOLUTE(hi)	\
 	(((hi).flags & (HIO_CONST|HIO_VARIABLE|HIO_RELATIVE)) == HIO_VARIABLE)
+#define	HUMS_THQA_CERT	0xC5
 
 	/* Parse features for maximum contact count */
 	hd = hid_start_parse(d_ptr, d_len, 1 << hid_feature);
@@ -545,10 +619,19 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 				touch_coll = false;
 			break;
 		case hid_feature:
+			if (hi.collevel == 1 && touch_coll && hi.usage ==
+			      HID_USAGE2(HUP_MICROSOFT, HUMS_THQA_CERT)) {
+				thqa_cert_rid = hi.report_ID;
+				break;
+			}
 			if (hi.collevel == 1 && touch_coll &&
 			    WMT_HI_ABSOLUTE(hi) && hi.usage ==
-			      HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX))
+			      HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX)) {
 				cont_count_max = hi.logical_maximum;
+				cont_max_rid = hi.report_ID;
+				if (sc != NULL)
+					sc->cont_max_loc = hi.loc;
+			}
 			break;
 		default:
 			break;
@@ -557,7 +640,7 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 	hid_end_parse(hd);
 
 	/* Maximum contact count is required usage */
-	if (cont_count_max < 1)
+	if (cont_max_rid == 0)
 		return (false);
 
 	touch_coll = false;
@@ -668,12 +751,17 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 	if (sc == NULL)
 		return (true);
 
+	/*
+	 * According to specifications 'Contact Count Maximum' should be read
+	 * from Feature Report rather than from HID descriptor. Set sane
+	 * default value now to handle the case of 'Get Report' request failure
+	 */
+	if (cont_count_max < 1)
+		cont_count_max = cont;
+
 	/* Cap contact count maximum to MAX_MT_SLOTS */
-	if (cont_count_max > MAX_MT_SLOTS) {
-		DPRINTF("Hardware reported %d contacts while only %d is "
-		    "supported\n", (int)cont_count_max, MAX_MT_SLOTS);
+	if (cont_count_max > MAX_MT_SLOTS)
 		cont_count_max = MAX_MT_SLOTS;
-	}
 
 	/* Set number of MT protocol type B slots */
 	sc->ai[WMT_SLOT] = (struct wmt_absinfo) {
@@ -689,9 +777,18 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 		sc->ai[WMT_ORIENTATION].max = 1;
 	}
 
+	sc->isize = wmt_hid_report_size(d_ptr, d_len, hid_input, report_id);
+	sc->cont_max_rlen = wmt_hid_report_size(d_ptr, d_len, hid_feature,
+	    cont_max_rid);
+	if (thqa_cert_rid > 0)
+		sc->thqa_cert_rlen = wmt_hid_report_size(d_ptr, d_len,
+		    hid_feature, thqa_cert_rid);
+
 	sc->report_id = report_id;
 	sc->caps = caps;
 	sc->nconts_max = cont;
+	sc->cont_max_rid = cont_max_rid;
+	sc->thqa_cert_rid = thqa_cert_rid;
 
 	/* Announce information about the touch device */
 	device_printf(sc->dev,
@@ -705,6 +802,27 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 	    (int)sc->ai[WMT_X].min, (int)sc->ai[WMT_Y].min,
 	    (int)sc->ai[WMT_X].max, (int)sc->ai[WMT_Y].max);
 	return (true);
+}
+
+static void
+wmt_cont_max_parse(struct wmt_softc *sc, const void *r_ptr, uint16_t r_len)
+{
+	uint32_t cont_count_max;
+
+	cont_count_max = hid_get_data_unsigned((const uint8_t *)r_ptr + 1,
+	    r_len - 1, &sc->cont_max_loc);
+	if (cont_count_max > MAX_MT_SLOTS) {
+		DPRINTF("Hardware reported %d contacts while only %d is "
+		    "supported\n", (int)cont_count_max, MAX_MT_SLOTS);
+		cont_count_max = MAX_MT_SLOTS;
+	}
+	/* Feature report is a primary source of 'Contact Count Maximum' */
+	if (cont_count_max > 0 &&
+	    cont_count_max != sc->ai[WMT_SLOT].max + 1) {
+		sc->ai[WMT_SLOT].max = cont_count_max - 1;
+		device_printf(sc->dev, "%d feature report contacts",
+		    cont_count_max);
+	}
 }
 
 static devclass_t wmt_devclass;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811092126.wA9LQR6j024800>