From owner-svn-src-all@freebsd.org Fri Nov 9 21:26:28 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 143181108A41; Fri, 9 Nov 2018 21:26:28 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 924878EAF8; Fri, 9 Nov 2018 21:26:27 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 6EEE6196B4; Fri, 9 Nov 2018 21:26:27 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id wA9LQRMP024801; Fri, 9 Nov 2018 21:26:27 GMT (envelope-from wulf@FreeBSD.org) Received: (from wulf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id wA9LQR6j024800; Fri, 9 Nov 2018 21:26:27 GMT (envelope-from wulf@FreeBSD.org) Message-Id: <201811092126.wA9LQR6j024800@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wulf set sender to wulf@FreeBSD.org using -f From: Vladimir Kondratyev Date: Fri, 9 Nov 2018 21:26:27 +0000 (UTC) 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 X-SVN-Group: stable-11 X-SVN-Commit-Author: wulf X-SVN-Commit-Paths: stable/11/sys/dev/usb/input X-SVN-Commit-Revision: 340305 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Rspamd-Queue-Id: 924878EAF8 X-Spamd-Result: default: False [-106.87 / 200.00]; ARC_NA(0.00)[]; NEURAL_HAM_MEDIUM(-1.00)[-1.000,0]; ALLOW_DOMAIN_WHITELIST(-100.00)[FreeBSD.org]; FROM_HAS_DN(0.00)[]; RCPT_COUNT_THREE(0.00)[4]; TO_MATCH_ENVRCPT_ALL(0.00)[]; NEURAL_HAM_LONG(-1.00)[-1.000,0]; MIME_GOOD(-0.10)[text/plain]; TO_DN_NONE(0.00)[]; HAS_XAW(0.00)[]; R_SPF_SOFTFAIL(0.00)[~all]; DMARC_NA(0.00)[FreeBSD.org]; RCVD_COUNT_THREE(0.00)[4]; MX_GOOD(-0.01)[cached: mx1.FreeBSD.org]; NEURAL_HAM_SHORT(-1.00)[-1.000,0]; FROM_EQ_ENVFROM(0.00)[]; R_DKIM_NA(0.00)[]; RCVD_TLS_LAST(0.00)[]; ASN(0.00)[asn:11403, ipnet:2610:1c1:1::/48, country:US]; IP_SCORE(-3.76)[ip: (-9.91), ipnet: 2610:1c1:1::/48(-4.93), asn: 11403(-3.87), country: US(-0.09)] X-Rspamd-Server: mx1.freebsd.org X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 09 Nov 2018 21:26:28 -0000 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 Tested by: Roberto Fernandez Cueto 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;