Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 4 Aug 2018 12:24:38 +0000 (UTC)
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r337287 - head/sys/dev/usb/input
Message-ID:  <201808041224.w74COcOm056138@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: wulf
Date: Sat Aug  4 12:24:37 2018
New Revision: 337287
URL: https://svnweb.freebsd.org/changeset/base/337287

Log:
  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 after:	2 weeks

Modified:
  head/sys/dev/usb/input/wmt.c

Modified: head/sys/dev/usb/input/wmt.c
==============================================================================
--- head/sys/dev/usb/input/wmt.c	Sat Aug  4 11:53:41 2018	(r337286)
+++ head/sys/dev/usb/input/wmt.c	Sat Aug  4 12:24:37 2018	(r337287)
@@ -203,6 +203,10 @@ 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;
+
 	uint8_t			buf[WMT_BSIZE] __aligned(4);
 };
 
@@ -212,6 +216,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;
 
@@ -291,15 +296,36 @@ wmt_attach(device_t dev)
 		return (ENXIO);
 	}
 
-	mtx_init(&sc->mtx, "wmt lock", NULL, MTX_DEF);
+	if (!wmt_hid_parse(sc, d_ptr, d_len)) {
+		DPRINTF("multi-touch HID descriptor not found\n");
+		free(d_ptr, M_TEMP);
+		return (ENXIO);
+	}
 
 	/* Get HID report length */
 	sc->isize = hid_report_size(d_ptr, d_len, hid_input, NULL);
+	free(d_ptr, M_TEMP);
 	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);
+
+	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) {
@@ -307,9 +333,6 @@ wmt_attach(device_t dev)
 		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 +357,6 @@ wmt_attach(device_t dev)
 	return (0);
 
 detach:
-	free(d_ptr, M_TEMP);
 	wmt_detach(dev);
 	return (ENXIO);
 }
@@ -513,6 +535,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 +585,7 @@ 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;
 	bool touch_coll = false;
 	bool finger_coll = false;
 	bool cont_count_found = false;
@@ -547,8 +610,12 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 		case hid_feature:
 			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 +624,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 +735,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 +761,13 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr,
 		sc->ai[WMT_ORIENTATION].max = 1;
 	}
 
+	sc->cont_max_rlen = wmt_hid_report_size(d_ptr, d_len, hid_feature,
+	    cont_max_rid);
+
 	sc->report_id = report_id;
 	sc->caps = caps;
 	sc->nconts_max = cont;
+	sc->cont_max_rid = cont_max_rid;
 
 	/* Announce information about the touch device */
 	device_printf(sc->dev,
@@ -705,6 +781,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?201808041224.w74COcOm056138>