Date: Wed, 23 Dec 2020 22:29:37 GMT From: Vladimir Kondratyev <wulf@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 8de78df54d90 - wmt(4): Add support for touchpads Message-ID: <202012232229.0BNMTbk3083091@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=8de78df54d907d4b5aa4b962c2c89259938aeda5 commit 8de78df54d907d4b5aa4b962c2c89259938aeda5 Author: Vladimir Kondratyev <wulf@FreeBSD.org> AuthorDate: 2020-12-23 22:18:18 +0000 Commit: Vladimir Kondratyev <wulf@FreeBSD.org> CommitDate: 2020-12-23 22:22:56 +0000 wmt(4): Add support for touchpads Obtained from: sysutils/iichid --- share/man/man4/wmt.4 | 4 - sys/dev/usb/input/wmt.c | 199 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 184 insertions(+), 19 deletions(-) diff --git a/share/man/man4/wmt.4 b/share/man/man4/wmt.4 index f778a07a5983..ddea0a6b45d3 100644 --- a/share/man/man4/wmt.4 +++ b/share/man/man4/wmt.4 @@ -73,10 +73,6 @@ driver was written by .An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org . .Sh BUGS .Nm -works only with touchscreens now. -Neither pens nor touchpads are supported. -.Pp -.Nm cannot act like .Xr sysmouse 4 , as diff --git a/sys/dev/usb/input/wmt.c b/sys/dev/usb/input/wmt.c index b3fbd8c82a13..745d0af9b20e 100644 --- a/sys/dev/usb/input/wmt.c +++ b/sys/dev/usb/input/wmt.c @@ -69,12 +69,26 @@ SYSCTL_INT(_hw_usb_wmt, OID_AUTO, debug, CTLFLAG_RWTUN, #endif #define WMT_BSIZE 1024 /* bytes, buffer size */ +#define WMT_BTN_MAX 8 /* Number of buttons supported */ enum { WMT_INTR_DT, WMT_N_TRANSFER, }; +enum wmt_type { + WMT_TYPE_UNKNOWN = 0, /* HID report descriptor is not probed */ + WMT_TYPE_UNSUPPORTED, /* Repdescr does not belong to MT device */ + WMT_TYPE_TOUCHPAD, + WMT_TYPE_TOUCHSCREEN, +}; + +enum wmt_input_mode { + WMT_INPUT_MODE_MOUSE = 0x0, + WMT_INPUT_MODE_MT_TOUCHSCREEN = 0x2, + WMT_INPUT_MODE_MT_TOUCHPAD = 0x3, +}; + enum { WMT_TIP_SWITCH, #define WMT_SLOT WMT_TIP_SWITCH @@ -187,29 +201,41 @@ struct wmt_absinfo { struct wmt_softc { device_t dev; - bool supported; + enum wmt_type type; struct mtx mtx; struct wmt_absinfo ai[WMT_N_USAGES]; struct hid_location locs[MAX_MT_SLOTS][WMT_N_USAGES]; struct hid_location cont_count_loc; + struct hid_location btn_loc[WMT_BTN_MAX]; + struct hid_location int_btn_loc; struct usb_xfer *xfer[WMT_N_TRANSFER]; struct evdev_dev *evdev; uint32_t slot_data[WMT_N_USAGES]; uint32_t caps; + uint32_t buttons; uint32_t isize; uint32_t nconts_per_report; uint32_t nconts_todo; uint32_t report_len; uint8_t report_id; + uint32_t max_button; + bool has_int_button; + bool is_clickpad; struct hid_location cont_max_loc; uint32_t cont_max_rlen; uint8_t cont_max_rid; + struct hid_location btn_type_loc; + uint32_t btn_type_rlen; + uint8_t btn_type_rid; uint32_t thqa_cert_rlen; uint8_t thqa_cert_rid; + struct hid_location input_mode_loc; + uint32_t input_mode_rlen; + uint8_t input_mode_rid; uint8_t buf[WMT_BSIZE] __aligned(4); }; @@ -219,8 +245,9 @@ struct wmt_softc { for ((usage) = 0; (usage) < WMT_N_USAGES; ++(usage)) \ if (USAGE_SUPPORTED((caps), (usage))) -static bool wmt_hid_parse(struct wmt_softc *, const void *, uint16_t); +static enum wmt_type wmt_hid_parse(struct wmt_softc *, const void *, uint16_t); static void wmt_cont_max_parse(struct wmt_softc *, const void *, uint16_t); +static int wmt_set_input_mode(struct wmt_softc *, enum wmt_input_mode); static usb_callback_t wmt_intr_callback; @@ -281,15 +308,16 @@ wmt_probe(device_t dev) return (ENXIO); /* Check if report descriptor belongs to a HID multitouch device */ - if (!sc->supported) - sc->supported = wmt_hid_parse(sc, d_ptr, d_len); - if (sc->supported) + if (sc->type == WMT_TYPE_UNKNOWN) + sc->type = wmt_hid_parse(sc, d_ptr, d_len); + if (sc->type != WMT_TYPE_UNSUPPORTED) err = BUS_PROBE_DEFAULT; else err = ENXIO; /* Check HID report length */ - if (sc->supported && (sc->isize <= 0 || sc->isize > WMT_BSIZE)) { + if (sc->type != WMT_TYPE_UNSUPPORTED && + (sc->isize <= 0 || sc->isize > WMT_BSIZE)) { DPRINTF("Input size invalid or too large: %d\n", sc->isize); err = ENXIO; } @@ -303,6 +331,7 @@ wmt_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct wmt_softc *sc = device_get_softc(dev); + int nbuttons, btn; size_t i; int err; @@ -323,6 +352,22 @@ wmt_attach(device_t dev) DPRINTF("Feature report %hhu size invalid or too large: %u\n", sc->cont_max_rid, sc->cont_max_rlen); + /* Fetch and parse "Button type" feature report */ + if (sc->btn_type_rlen > 1 && sc->btn_type_rlen <= WMT_BSIZE && + sc->btn_type_rid != sc->cont_max_rid) { + bzero(sc->buf, sc->btn_type_rlen); + err = usbd_req_get_report(uaa->device, NULL, sc->buf, + sc->btn_type_rlen, uaa->info.bIfaceIndex, + UHID_FEATURE_REPORT, sc->btn_type_rid); + } + if (sc->btn_type_rlen > 1) { + if (err == 0) + sc->is_clickpad = hid_get_data_unsigned(sc->buf + 1, + sc->btn_type_rlen - 1, &sc->btn_type_loc) == 0; + else + DPRINTF("usbd_req_get_report error=%d\n", err); + } + /* 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) @@ -330,6 +375,13 @@ wmt_attach(device_t dev) sc->thqa_cert_rlen, uaa->info.bIfaceIndex, UHID_FEATURE_REPORT, sc->thqa_cert_rid); + /* Switch touchpad in to absolute multitouch mode */ + if (sc->type == WMT_TYPE_TOUCHPAD) { + err = wmt_set_input_mode(sc, WMT_INPUT_MODE_MT_TOUCHPAD); + if (err != 0) + DPRINTF("Failed to set input mode: %d\n", err); + } + mtx_init(&sc->mtx, "wmt lock", NULL, MTX_DEF); err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, @@ -347,9 +399,28 @@ wmt_attach(device_t dev) evdev_set_serial(sc->evdev, usb_get_serial(uaa->device)); evdev_set_methods(sc->evdev, sc, &wmt_evdev_methods); evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT); - evdev_support_prop(sc->evdev, INPUT_PROP_DIRECT); + switch (sc->type) { + case WMT_TYPE_TOUCHSCREEN: + evdev_support_prop(sc->evdev, INPUT_PROP_DIRECT); + break; + case WMT_TYPE_TOUCHPAD: + evdev_support_prop(sc->evdev, INPUT_PROP_POINTER); + if (sc->is_clickpad) + evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD); + break; + default: + KASSERT(0, ("wmt_attach: unsupported touch device type")); + } evdev_support_event(sc->evdev, EV_SYN); evdev_support_event(sc->evdev, EV_ABS); + if (sc->max_button != 0 || sc->has_int_button) { + evdev_support_event(sc->evdev, EV_KEY); + if (sc->has_int_button) + evdev_support_key(sc->evdev, BTN_LEFT); + for (btn = 0; btn < sc->max_button; ++btn) + if (USAGE_SUPPORTED(sc->buttons, btn)) + evdev_support_key(sc->evdev, BTN_MOUSE + btn); + } WMT_FOREACH_USAGE(sc->caps, i) { if (wmt_hid_map[i].code != WMT_NO_CODE) evdev_support_abs(sc->evdev, wmt_hid_map[i].code, 0, @@ -361,6 +432,11 @@ wmt_attach(device_t dev) goto detach; /* Announce information about the touch device */ + nbuttons = bitcount32(sc->buttons); + device_printf(sc->dev, "Multitouch %s with %d external button%s%s\n", + sc->type == WMT_TYPE_TOUCHSCREEN ? "touchscreen" : "touchpad", + nbuttons, nbuttons != 1 ? "s" : "", + sc->is_clickpad ? ", click-pad" : ""); device_printf(sc->dev, "%d contacts and [%s%s%s%s%s]. Report range [%d:%d] - [%d:%d]\n", (int)sc->ai[WMT_SLOT].max + 1, @@ -395,10 +471,12 @@ wmt_process_report(struct wmt_softc *sc, uint8_t *buf, int len) { size_t usage; uint32_t *slot_data = sc->slot_data; - uint32_t cont; + uint32_t cont, btn; uint32_t cont_count; uint32_t width; uint32_t height; + uint32_t int_btn = 0; + uint32_t left_btn = 0; int32_t slot; /* @@ -496,8 +574,25 @@ wmt_process_report(struct wmt_softc *sc, uint8_t *buf, int len) } sc->nconts_todo -= cont_count; - if (sc->nconts_todo == 0) + if (sc->nconts_todo == 0) { + /* Report both the click and external left btns as BTN_LEFT */ + if (sc->has_int_button) + int_btn = hid_get_data(buf, len, &sc->int_btn_loc); + if (sc->max_button != 0 && (sc->buttons & 1 << 0) != 0) + left_btn = hid_get_data(buf, len, &sc->btn_loc[0]); + if (sc->has_int_button || + (sc->max_button != 0 && (sc->buttons & 1 << 0) != 0)) + evdev_push_key(sc->evdev, BTN_LEFT, + int_btn != 0 | left_btn != 0); + for (btn = 1; btn < sc->max_button; ++btn) { + if ((sc->buttons & 1 << btn) != 0) + evdev_push_key(sc->evdev, BTN_MOUSE + btn, + hid_get_data(buf, + len, + &sc->btn_loc[btn]) != 0); + } evdev_sync(sc->evdev); + } } static void @@ -640,19 +735,22 @@ wmt_hid_report_size(const void *buf, uint16_t len, enum hid_kind k, uint8_t id) return ((temp + 7) / 8 + report_id); } -static bool +static enum wmt_type wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) { struct hid_item hi; struct hid_data *hd; size_t i; size_t cont = 0; + enum wmt_type type = WMT_TYPE_UNSUPPORTED; + uint32_t left_btn, btn; int32_t cont_count_max = 0; uint8_t report_id = 0; bool touch_coll = false; bool finger_coll = false; bool cont_count_found = false; bool scan_time_found = false; + bool has_int_button = false; #define WMT_HI_ABSOLUTE(hi) \ (((hi).flags & (HIO_CONST|HIO_VARIABLE|HIO_RELATIVE)) == HIO_VARIABLE) @@ -664,8 +762,18 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) switch (hi.kind) { case hid_collection: if (hi.collevel == 1 && hi.usage == - HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN)) + HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN)) { + touch_coll = true; + type = WMT_TYPE_TOUCHSCREEN; + left_btn = 1; + break; + } + if (hi.collevel == 1 && hi.usage == + HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD)) { touch_coll = true; + type = WMT_TYPE_TOUCHPAD; + left_btn = 2; + } break; case hid_endcollection: if (hi.collevel == 0 && touch_coll) @@ -682,6 +790,12 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) cont_count_max = hi.logical_maximum; sc->cont_max_rid = hi.report_ID; sc->cont_max_loc = hi.loc; + break; + } + if (hi.collevel == 1 && touch_coll && hi.usage == + HID_USAGE2(HUP_DIGITIZERS, HUD_BUTTON_TYPE)) { + sc->btn_type_rid = hi.report_ID; + sc->btn_type_loc = hi.loc; } break; default: @@ -690,9 +804,11 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) } hid_end_parse(hd); + if (type == WMT_TYPE_UNSUPPORTED) + return (WMT_TYPE_UNSUPPORTED); /* Maximum contact count is required usage */ if (sc->cont_max_rid == 0) - return (false); + return (WMT_TYPE_UNSUPPORTED); touch_coll = false; @@ -727,6 +843,22 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) else break; + if (hi.collevel == 1 && left_btn == 2 && + hi.usage == HID_USAGE2(HUP_BUTTON, 1)) { + has_int_button = true; + sc->int_btn_loc = hi.loc; + break; + } + if (hi.collevel == 1 && + hi.usage >= HID_USAGE2(HUP_BUTTON, left_btn) && + hi.usage <= HID_USAGE2(HUP_BUTTON, WMT_BTN_MAX)) { + btn = (hi.usage & 0xFFFF) - left_btn; + sc->buttons |= 1 << btn; + sc->btn_loc[btn] = hi.loc; + if (btn >= sc->max_button) + sc->max_button = btn + 1; + break; + } if (hi.collevel == 1 && hi.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT)) { cont_count_found = true; @@ -783,12 +915,16 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) /* Check for required HID Usages */ if (!cont_count_found || !scan_time_found || cont == 0) - return (false); + return (WMT_TYPE_UNSUPPORTED); for (i = 0; i < WMT_N_USAGES; i++) { if (wmt_hid_map[i].required && !USAGE_SUPPORTED(sc->caps, i)) - return (false); + return (WMT_TYPE_UNSUPPORTED); } + /* Touchpads must have at least one button */ + if (type == WMT_TYPE_TOUCHPAD && !sc->max_button && !has_int_button) + return (WMT_TYPE_UNSUPPORTED); + /* * According to specifications 'Contact Count Maximum' should be read * from Feature Report rather than from HID descriptor. Set sane @@ -820,14 +956,18 @@ wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len) report_id); sc->cont_max_rlen = wmt_hid_report_size(d_ptr, d_len, hid_feature, sc->cont_max_rid); + if (sc->btn_type_rid > 0) + sc->btn_type_rlen = wmt_hid_report_size(d_ptr, d_len, + hid_feature, sc->btn_type_rid); if (sc->thqa_cert_rid > 0) sc->thqa_cert_rlen = wmt_hid_report_size(d_ptr, d_len, hid_feature, sc->thqa_cert_rid); sc->report_id = report_id; sc->nconts_per_report = cont; + sc->has_int_button = has_int_button; - return (true); + return (type); } static void @@ -851,6 +991,35 @@ wmt_cont_max_parse(struct wmt_softc *sc, const void *r_ptr, uint16_t r_len) } } +static int +wmt_set_input_mode(struct wmt_softc *sc, enum wmt_input_mode mode) +{ + struct usb_attach_arg *uaa = device_get_ivars(sc->dev); + int err; + + if (sc->input_mode_rlen < 3 || sc->input_mode_rlen > WMT_BSIZE) { + DPRINTF("Feature report %hhu size invalid or too large: %u\n", + sc->input_mode_rid, sc->input_mode_rlen); + return (USB_ERR_BAD_BUFSIZE); + } + + /* Input Mode report is not strictly required to be readable */ + err = usbd_req_get_report(uaa->device, NULL, sc->buf, + sc->input_mode_rlen, uaa->info.bIfaceIndex, + UHID_FEATURE_REPORT, sc->input_mode_rid); + if (err != USB_ERR_NORMAL_COMPLETION) + bzero(sc->buf + 1, sc->input_mode_rlen - 1); + + sc->buf[0] = sc->input_mode_rid; + hid_put_data_unsigned(sc->buf + 1, sc->input_mode_rlen - 1, + &sc->input_mode_loc, mode); + err = usbd_req_set_report(uaa->device, NULL, sc->buf, + sc->input_mode_rlen, uaa->info.bIfaceIndex, + UHID_FEATURE_REPORT, sc->input_mode_rid); + + return (err); +} + static const STRUCT_USB_HOST_ID wmt_devs[] = { /* generic HID class w/o boot interface */ {USB_IFACE_CLASS(UICLASS_HID),
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202012232229.0BNMTbk3083091>