From owner-svn-src-all@freebsd.org Sat Aug 4 12:24:38 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 AFED71069558; Sat, 4 Aug 2018 12:24:38 +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 5F4F072FBC; Sat, 4 Aug 2018 12:24:38 +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 3DF15773A; Sat, 4 Aug 2018 12:24:38 +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 w74COcTO056139; Sat, 4 Aug 2018 12:24:38 GMT (envelope-from wulf@FreeBSD.org) Received: (from wulf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w74COcOm056138; Sat, 4 Aug 2018 12:24:38 GMT (envelope-from wulf@FreeBSD.org) Message-Id: <201808041224.w74COcOm056138@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wulf set sender to wulf@FreeBSD.org using -f From: Vladimir Kondratyev Date: Sat, 4 Aug 2018 12:24:38 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r337287 - head/sys/dev/usb/input X-SVN-Group: head X-SVN-Commit-Author: wulf X-SVN-Commit-Paths: head/sys/dev/usb/input X-SVN-Commit-Revision: 337287 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.27 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: Sat, 04 Aug 2018 12:24:38 -0000 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;