176136d20SVladimir Kondratyev /*-
276136d20SVladimir Kondratyev * Copyright (c) 2014-2017 Vladimir Kondratyev <wulf@FreeBSD.org>
376136d20SVladimir Kondratyev * All rights reserved.
476136d20SVladimir Kondratyev *
576136d20SVladimir Kondratyev * Redistribution and use in source and binary forms, with or without
676136d20SVladimir Kondratyev * modification, are permitted provided that the following conditions
776136d20SVladimir Kondratyev * are met:
876136d20SVladimir Kondratyev * 1. Redistributions of source code must retain the above copyright
976136d20SVladimir Kondratyev * notice, this list of conditions and the following disclaimer.
1076136d20SVladimir Kondratyev * 2. Redistributions in binary form must reproduce the above copyright
1176136d20SVladimir Kondratyev * notice, this list of conditions and the following disclaimer in the
1276136d20SVladimir Kondratyev * documentation and/or other materials provided with the distribution.
1376136d20SVladimir Kondratyev *
1476136d20SVladimir Kondratyev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1576136d20SVladimir Kondratyev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1676136d20SVladimir Kondratyev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1776136d20SVladimir Kondratyev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1876136d20SVladimir Kondratyev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1976136d20SVladimir Kondratyev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2076136d20SVladimir Kondratyev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2176136d20SVladimir Kondratyev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2276136d20SVladimir Kondratyev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2376136d20SVladimir Kondratyev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2476136d20SVladimir Kondratyev * SUCH DAMAGE.
2576136d20SVladimir Kondratyev */
2676136d20SVladimir Kondratyev
2776136d20SVladimir Kondratyev /*
2876136d20SVladimir Kondratyev * MS Windows 7/8/10 compatible USB HID Multi-touch Device driver.
2976136d20SVladimir Kondratyev * https://msdn.microsoft.com/en-us/library/windows/hardware/jj151569(v=vs.85).aspx
3076136d20SVladimir Kondratyev * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
3176136d20SVladimir Kondratyev */
3276136d20SVladimir Kondratyev
3376136d20SVladimir Kondratyev #include <sys/param.h>
3476136d20SVladimir Kondratyev #include <sys/bus.h>
3576136d20SVladimir Kondratyev #include <sys/conf.h>
3676136d20SVladimir Kondratyev #include <sys/kernel.h>
3776136d20SVladimir Kondratyev #include <sys/lock.h>
3876136d20SVladimir Kondratyev #include <sys/malloc.h>
3976136d20SVladimir Kondratyev #include <sys/module.h>
4076136d20SVladimir Kondratyev #include <sys/mutex.h>
4176136d20SVladimir Kondratyev #include <sys/stddef.h>
4276136d20SVladimir Kondratyev #include <sys/sysctl.h>
4376136d20SVladimir Kondratyev #include <sys/systm.h>
4476136d20SVladimir Kondratyev
45eead9017SVladimir Kondratyev #include <dev/hid/hid.h>
46eead9017SVladimir Kondratyev
4776136d20SVladimir Kondratyev #include "usbdevs.h"
4876136d20SVladimir Kondratyev #include <dev/usb/usb.h>
4976136d20SVladimir Kondratyev #include <dev/usb/usbdi.h>
5076136d20SVladimir Kondratyev #include <dev/usb/usbdi_util.h>
5176136d20SVladimir Kondratyev #include <dev/usb/usbhid.h>
5276136d20SVladimir Kondratyev
5376136d20SVladimir Kondratyev #include <dev/usb/quirk/usb_quirk.h>
5476136d20SVladimir Kondratyev
5576136d20SVladimir Kondratyev #include <dev/evdev/evdev.h>
5676136d20SVladimir Kondratyev #include <dev/evdev/input.h>
5776136d20SVladimir Kondratyev
5876136d20SVladimir Kondratyev #define USB_DEBUG_VAR wmt_debug
5976136d20SVladimir Kondratyev #include <dev/usb/usb_debug.h>
6076136d20SVladimir Kondratyev
61f8d2b1f3SPawel Biernacki static SYSCTL_NODE(_hw_usb, OID_AUTO, wmt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
6276136d20SVladimir Kondratyev "USB MSWindows 7/8/10 compatible Multi-touch Device");
63501022d3SVladimir Kondratyev #ifdef USB_DEBUG
64501022d3SVladimir Kondratyev static int wmt_debug = 0;
6576136d20SVladimir Kondratyev SYSCTL_INT(_hw_usb_wmt, OID_AUTO, debug, CTLFLAG_RWTUN,
6676136d20SVladimir Kondratyev &wmt_debug, 1, "Debug level");
6776136d20SVladimir Kondratyev #endif
68501022d3SVladimir Kondratyev static bool wmt_timestamps = 0;
69501022d3SVladimir Kondratyev SYSCTL_BOOL(_hw_usb_wmt, OID_AUTO, timestamps, CTLFLAG_RDTUN,
70501022d3SVladimir Kondratyev &wmt_timestamps, 1, "Enable hardware timestamp reporting");
7176136d20SVladimir Kondratyev
7276136d20SVladimir Kondratyev #define WMT_BSIZE 1024 /* bytes, buffer size */
738de78df5SVladimir Kondratyev #define WMT_BTN_MAX 8 /* Number of buttons supported */
7476136d20SVladimir Kondratyev
7576136d20SVladimir Kondratyev enum {
7676136d20SVladimir Kondratyev WMT_INTR_DT,
7776136d20SVladimir Kondratyev WMT_N_TRANSFER,
7876136d20SVladimir Kondratyev };
7976136d20SVladimir Kondratyev
808de78df5SVladimir Kondratyev enum wmt_type {
818de78df5SVladimir Kondratyev WMT_TYPE_UNKNOWN = 0, /* HID report descriptor is not probed */
828de78df5SVladimir Kondratyev WMT_TYPE_UNSUPPORTED, /* Repdescr does not belong to MT device */
838de78df5SVladimir Kondratyev WMT_TYPE_TOUCHPAD,
848de78df5SVladimir Kondratyev WMT_TYPE_TOUCHSCREEN,
858de78df5SVladimir Kondratyev };
868de78df5SVladimir Kondratyev
878de78df5SVladimir Kondratyev enum wmt_input_mode {
888de78df5SVladimir Kondratyev WMT_INPUT_MODE_MOUSE = 0x0,
898de78df5SVladimir Kondratyev WMT_INPUT_MODE_MT_TOUCHSCREEN = 0x2,
908de78df5SVladimir Kondratyev WMT_INPUT_MODE_MT_TOUCHPAD = 0x3,
918de78df5SVladimir Kondratyev };
928de78df5SVladimir Kondratyev
9376136d20SVladimir Kondratyev enum {
94527b6d60SVladimir Kondratyev WMT_TIP_SWITCH = ABS_MT_INDEX(ABS_MT_TOOL_TYPE),
95527b6d60SVladimir Kondratyev WMT_WIDTH = ABS_MT_INDEX(ABS_MT_TOUCH_MAJOR),
96527b6d60SVladimir Kondratyev WMT_HEIGHT = ABS_MT_INDEX(ABS_MT_TOUCH_MINOR),
97527b6d60SVladimir Kondratyev WMT_ORIENTATION = ABS_MT_INDEX(ABS_MT_ORIENTATION),
98527b6d60SVladimir Kondratyev WMT_X = ABS_MT_INDEX(ABS_MT_POSITION_X),
99527b6d60SVladimir Kondratyev WMT_Y = ABS_MT_INDEX(ABS_MT_POSITION_Y),
100527b6d60SVladimir Kondratyev WMT_CONTACTID = ABS_MT_INDEX(ABS_MT_TRACKING_ID),
101527b6d60SVladimir Kondratyev WMT_PRESSURE = ABS_MT_INDEX(ABS_MT_PRESSURE),
102527b6d60SVladimir Kondratyev WMT_IN_RANGE = ABS_MT_INDEX(ABS_MT_DISTANCE),
103527b6d60SVladimir Kondratyev WMT_CONFIDENCE = ABS_MT_INDEX(ABS_MT_BLOB_ID),
104527b6d60SVladimir Kondratyev WMT_TOOL_X = ABS_MT_INDEX(ABS_MT_TOOL_X),
105527b6d60SVladimir Kondratyev WMT_TOOL_Y = ABS_MT_INDEX(ABS_MT_TOOL_Y),
10676136d20SVladimir Kondratyev };
10776136d20SVladimir Kondratyev
108527b6d60SVladimir Kondratyev #define WMT_N_USAGES MT_CNT
10976136d20SVladimir Kondratyev #define WMT_NO_USAGE -1
11076136d20SVladimir Kondratyev
11176136d20SVladimir Kondratyev struct wmt_hid_map_item {
11276136d20SVladimir Kondratyev char name[5];
11376136d20SVladimir Kondratyev int32_t usage; /* HID usage */
114527b6d60SVladimir Kondratyev bool reported; /* Item value is passed to evdev */
11576136d20SVladimir Kondratyev bool required; /* Required for MT Digitizers */
11676136d20SVladimir Kondratyev };
11776136d20SVladimir Kondratyev
11876136d20SVladimir Kondratyev static const struct wmt_hid_map_item wmt_hid_map[WMT_N_USAGES] = {
119527b6d60SVladimir Kondratyev [WMT_TIP_SWITCH] = {
12076136d20SVladimir Kondratyev .name = "TIP",
12176136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_SWITCH),
122527b6d60SVladimir Kondratyev .reported = false,
12376136d20SVladimir Kondratyev .required = true,
12476136d20SVladimir Kondratyev },
125527b6d60SVladimir Kondratyev [WMT_WIDTH] = {
12676136d20SVladimir Kondratyev .name = "WDTH",
12776136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_WIDTH),
128527b6d60SVladimir Kondratyev .reported = true,
12976136d20SVladimir Kondratyev .required = false,
13076136d20SVladimir Kondratyev },
131527b6d60SVladimir Kondratyev [WMT_HEIGHT] = {
13276136d20SVladimir Kondratyev .name = "HGHT",
13376136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_HEIGHT),
134527b6d60SVladimir Kondratyev .reported = true,
13576136d20SVladimir Kondratyev .required = false,
13676136d20SVladimir Kondratyev },
13776136d20SVladimir Kondratyev [WMT_ORIENTATION] = {
13876136d20SVladimir Kondratyev .name = "ORIE",
13976136d20SVladimir Kondratyev .usage = WMT_NO_USAGE,
140527b6d60SVladimir Kondratyev .reported = true,
14176136d20SVladimir Kondratyev .required = false,
14276136d20SVladimir Kondratyev },
14376136d20SVladimir Kondratyev [WMT_X] = {
14476136d20SVladimir Kondratyev .name = "X",
14576136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
146527b6d60SVladimir Kondratyev .reported = true,
14776136d20SVladimir Kondratyev .required = true,
14876136d20SVladimir Kondratyev },
14976136d20SVladimir Kondratyev [WMT_Y] = {
15076136d20SVladimir Kondratyev .name = "Y",
15176136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
152527b6d60SVladimir Kondratyev .reported = true,
15376136d20SVladimir Kondratyev .required = true,
15476136d20SVladimir Kondratyev },
15576136d20SVladimir Kondratyev [WMT_CONTACTID] = {
15676136d20SVladimir Kondratyev .name = "C_ID",
15776136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTID),
158527b6d60SVladimir Kondratyev .reported = true,
15976136d20SVladimir Kondratyev .required = true,
16076136d20SVladimir Kondratyev },
16176136d20SVladimir Kondratyev [WMT_PRESSURE] = {
16276136d20SVladimir Kondratyev .name = "PRES",
16376136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_TIP_PRESSURE),
164527b6d60SVladimir Kondratyev .reported = true,
16576136d20SVladimir Kondratyev .required = false,
16676136d20SVladimir Kondratyev },
16776136d20SVladimir Kondratyev [WMT_IN_RANGE] = {
16876136d20SVladimir Kondratyev .name = "RANG",
16976136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_IN_RANGE),
170527b6d60SVladimir Kondratyev .reported = true,
17176136d20SVladimir Kondratyev .required = false,
17276136d20SVladimir Kondratyev },
17376136d20SVladimir Kondratyev [WMT_CONFIDENCE] = {
17476136d20SVladimir Kondratyev .name = "CONF",
17576136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_DIGITIZERS, HUD_CONFIDENCE),
176527b6d60SVladimir Kondratyev .reported = false,
17776136d20SVladimir Kondratyev .required = false,
17876136d20SVladimir Kondratyev },
17976136d20SVladimir Kondratyev [WMT_TOOL_X] = { /* Shares HID usage with WMT_X */
18076136d20SVladimir Kondratyev .name = "TL_X",
18176136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_X),
182527b6d60SVladimir Kondratyev .reported = true,
18376136d20SVladimir Kondratyev .required = false,
18476136d20SVladimir Kondratyev },
18576136d20SVladimir Kondratyev [WMT_TOOL_Y] = { /* Shares HID usage with WMT_Y */
18676136d20SVladimir Kondratyev .name = "TL_Y",
18776136d20SVladimir Kondratyev .usage = HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_Y),
188527b6d60SVladimir Kondratyev .reported = true,
18976136d20SVladimir Kondratyev .required = false,
19076136d20SVladimir Kondratyev },
19176136d20SVladimir Kondratyev };
19276136d20SVladimir Kondratyev
19376136d20SVladimir Kondratyev struct wmt_absinfo {
19476136d20SVladimir Kondratyev int32_t min;
19576136d20SVladimir Kondratyev int32_t max;
19676136d20SVladimir Kondratyev int32_t res;
19776136d20SVladimir Kondratyev };
19876136d20SVladimir Kondratyev
1997eae6aabSVladimir Kondratyev struct wmt_softc {
20076136d20SVladimir Kondratyev device_t dev;
2018de78df5SVladimir Kondratyev enum wmt_type type;
2027eae6aabSVladimir Kondratyev
203527b6d60SVladimir Kondratyev int32_t cont_count_max;
20476136d20SVladimir Kondratyev struct mtx mtx;
20576136d20SVladimir Kondratyev struct wmt_absinfo ai[WMT_N_USAGES];
20676136d20SVladimir Kondratyev struct hid_location locs[MAX_MT_SLOTS][WMT_N_USAGES];
2070ba4b5ffSVladimir Kondratyev struct hid_location cont_count_loc;
2088de78df5SVladimir Kondratyev struct hid_location btn_loc[WMT_BTN_MAX];
2098de78df5SVladimir Kondratyev struct hid_location int_btn_loc;
210501022d3SVladimir Kondratyev struct hid_location scan_time_loc;
211501022d3SVladimir Kondratyev int32_t scan_time_max;
212501022d3SVladimir Kondratyev int32_t scan_time;
213501022d3SVladimir Kondratyev int32_t timestamp;
214501022d3SVladimir Kondratyev bool touch;
215501022d3SVladimir Kondratyev bool prev_touch;
21676136d20SVladimir Kondratyev
21776136d20SVladimir Kondratyev struct usb_xfer *xfer[WMT_N_TRANSFER];
21876136d20SVladimir Kondratyev struct evdev_dev *evdev;
21976136d20SVladimir Kondratyev
220527b6d60SVladimir Kondratyev union evdev_mt_slot slot_data;
221c26a3484SVladimir Kondratyev uint8_t caps[howmany(WMT_N_USAGES, 8)];
222c26a3484SVladimir Kondratyev uint8_t buttons[howmany(WMT_BTN_MAX, 8)];
22376136d20SVladimir Kondratyev uint32_t isize;
2240ba4b5ffSVladimir Kondratyev uint32_t nconts_per_report;
2250ba4b5ffSVladimir Kondratyev uint32_t nconts_todo;
22654c05047SVladimir Kondratyev uint32_t report_len;
22776136d20SVladimir Kondratyev uint8_t report_id;
2288de78df5SVladimir Kondratyev uint32_t max_button;
2298de78df5SVladimir Kondratyev bool has_int_button;
2308de78df5SVladimir Kondratyev bool is_clickpad;
231501022d3SVladimir Kondratyev bool do_timestamps;
23276136d20SVladimir Kondratyev
23336584a62SVladimir Kondratyev struct hid_location cont_max_loc;
23436584a62SVladimir Kondratyev uint32_t cont_max_rlen;
23536584a62SVladimir Kondratyev uint8_t cont_max_rid;
2368de78df5SVladimir Kondratyev struct hid_location btn_type_loc;
2378de78df5SVladimir Kondratyev uint32_t btn_type_rlen;
2388de78df5SVladimir Kondratyev uint8_t btn_type_rid;
2398107f311SVladimir Kondratyev uint32_t thqa_cert_rlen;
2408107f311SVladimir Kondratyev uint8_t thqa_cert_rid;
2418de78df5SVladimir Kondratyev struct hid_location input_mode_loc;
2428de78df5SVladimir Kondratyev uint32_t input_mode_rlen;
2438de78df5SVladimir Kondratyev uint8_t input_mode_rid;
24436584a62SVladimir Kondratyev
24576136d20SVladimir Kondratyev uint8_t buf[WMT_BSIZE] __aligned(4);
24676136d20SVladimir Kondratyev };
24776136d20SVladimir Kondratyev
24876136d20SVladimir Kondratyev #define WMT_FOREACH_USAGE(caps, usage) \
24976136d20SVladimir Kondratyev for ((usage) = 0; (usage) < WMT_N_USAGES; ++(usage)) \
250c26a3484SVladimir Kondratyev if (isset((caps), (usage)))
25176136d20SVladimir Kondratyev
2528de78df5SVladimir Kondratyev static enum wmt_type wmt_hid_parse(struct wmt_softc *, const void *, uint16_t);
2538de78df5SVladimir Kondratyev static int wmt_set_input_mode(struct wmt_softc *, enum wmt_input_mode);
25476136d20SVladimir Kondratyev
25576136d20SVladimir Kondratyev static usb_callback_t wmt_intr_callback;
25676136d20SVladimir Kondratyev
25776136d20SVladimir Kondratyev static device_probe_t wmt_probe;
25876136d20SVladimir Kondratyev static device_attach_t wmt_attach;
25976136d20SVladimir Kondratyev static device_detach_t wmt_detach;
26076136d20SVladimir Kondratyev
26176136d20SVladimir Kondratyev static evdev_open_t wmt_ev_open;
26276136d20SVladimir Kondratyev static evdev_close_t wmt_ev_close;
26376136d20SVladimir Kondratyev
26476136d20SVladimir Kondratyev static const struct evdev_methods wmt_evdev_methods = {
26576136d20SVladimir Kondratyev .ev_open = &wmt_ev_open,
26676136d20SVladimir Kondratyev .ev_close = &wmt_ev_close,
26776136d20SVladimir Kondratyev };
26876136d20SVladimir Kondratyev
26976136d20SVladimir Kondratyev static const struct usb_config wmt_config[WMT_N_TRANSFER] = {
27076136d20SVladimir Kondratyev [WMT_INTR_DT] = {
27176136d20SVladimir Kondratyev .type = UE_INTERRUPT,
27276136d20SVladimir Kondratyev .endpoint = UE_ADDR_ANY,
27376136d20SVladimir Kondratyev .direction = UE_DIR_IN,
27476136d20SVladimir Kondratyev .flags = { .pipe_bof = 1, .short_xfer_ok = 1 },
275b8e3d9b1SVladimir Kondratyev .bufsize = WMT_BSIZE,
27676136d20SVladimir Kondratyev .callback = &wmt_intr_callback,
27776136d20SVladimir Kondratyev },
27876136d20SVladimir Kondratyev };
27976136d20SVladimir Kondratyev
28076136d20SVladimir Kondratyev static int
wmt_probe(device_t dev)28176136d20SVladimir Kondratyev wmt_probe(device_t dev)
28276136d20SVladimir Kondratyev {
28376136d20SVladimir Kondratyev struct usb_attach_arg *uaa = device_get_ivars(dev);
2847eae6aabSVladimir Kondratyev struct wmt_softc *sc = device_get_softc(dev);
28576136d20SVladimir Kondratyev void *d_ptr;
28676136d20SVladimir Kondratyev uint16_t d_len;
28776136d20SVladimir Kondratyev int err;
28876136d20SVladimir Kondratyev
28976136d20SVladimir Kondratyev if (uaa->usb_mode != USB_MODE_HOST)
29076136d20SVladimir Kondratyev return (ENXIO);
29176136d20SVladimir Kondratyev
29276136d20SVladimir Kondratyev if (uaa->info.bInterfaceClass != UICLASS_HID)
29376136d20SVladimir Kondratyev return (ENXIO);
29476136d20SVladimir Kondratyev
29576136d20SVladimir Kondratyev if (usb_test_quirk(uaa, UQ_WMT_IGNORE))
29676136d20SVladimir Kondratyev return (ENXIO);
29776136d20SVladimir Kondratyev
29876136d20SVladimir Kondratyev err = usbd_req_get_hid_desc(uaa->device, NULL,
29976136d20SVladimir Kondratyev &d_ptr, &d_len, M_TEMP, uaa->info.bIfaceIndex);
30076136d20SVladimir Kondratyev if (err)
30176136d20SVladimir Kondratyev return (ENXIO);
30276136d20SVladimir Kondratyev
3037eae6aabSVladimir Kondratyev /* Check if report descriptor belongs to a HID multitouch device */
3048de78df5SVladimir Kondratyev if (sc->type == WMT_TYPE_UNKNOWN)
3058de78df5SVladimir Kondratyev sc->type = wmt_hid_parse(sc, d_ptr, d_len);
3068de78df5SVladimir Kondratyev if (sc->type != WMT_TYPE_UNSUPPORTED)
30776136d20SVladimir Kondratyev err = BUS_PROBE_DEFAULT;
30876136d20SVladimir Kondratyev else
30976136d20SVladimir Kondratyev err = ENXIO;
31076136d20SVladimir Kondratyev
3117eae6aabSVladimir Kondratyev /* Check HID report length */
3128de78df5SVladimir Kondratyev if (sc->type != WMT_TYPE_UNSUPPORTED &&
3138de78df5SVladimir Kondratyev (sc->isize <= 0 || sc->isize > WMT_BSIZE)) {
3147eae6aabSVladimir Kondratyev DPRINTF("Input size invalid or too large: %d\n", sc->isize);
3157eae6aabSVladimir Kondratyev err = ENXIO;
3167eae6aabSVladimir Kondratyev }
3177eae6aabSVladimir Kondratyev
31876136d20SVladimir Kondratyev free(d_ptr, M_TEMP);
31976136d20SVladimir Kondratyev return (err);
32076136d20SVladimir Kondratyev }
32176136d20SVladimir Kondratyev
32276136d20SVladimir Kondratyev static int
wmt_attach(device_t dev)32376136d20SVladimir Kondratyev wmt_attach(device_t dev)
32476136d20SVladimir Kondratyev {
32576136d20SVladimir Kondratyev struct usb_attach_arg *uaa = device_get_ivars(dev);
32676136d20SVladimir Kondratyev struct wmt_softc *sc = device_get_softc(dev);
32718a3b77eSVladimir Kondratyev uint32_t cont_count_max;
3288de78df5SVladimir Kondratyev int nbuttons, btn;
32976136d20SVladimir Kondratyev size_t i;
33076136d20SVladimir Kondratyev int err;
33176136d20SVladimir Kondratyev
33276136d20SVladimir Kondratyev device_set_usb_desc(dev);
33376136d20SVladimir Kondratyev sc->dev = dev;
33476136d20SVladimir Kondratyev
33536584a62SVladimir Kondratyev /* Fetch and parse "Contact count maximum" feature report */
33636584a62SVladimir Kondratyev if (sc->cont_max_rlen > 0 && sc->cont_max_rlen <= WMT_BSIZE) {
33736584a62SVladimir Kondratyev err = usbd_req_get_report(uaa->device, NULL, sc->buf,
33836584a62SVladimir Kondratyev sc->cont_max_rlen, uaa->info.bIfaceIndex,
33936584a62SVladimir Kondratyev UHID_FEATURE_REPORT, sc->cont_max_rid);
34018a3b77eSVladimir Kondratyev if (err == USB_ERR_NORMAL_COMPLETION) {
341eead9017SVladimir Kondratyev cont_count_max = hid_get_udata(sc->buf + 1,
34218a3b77eSVladimir Kondratyev sc->cont_max_rlen - 1, &sc->cont_max_loc);
34318a3b77eSVladimir Kondratyev /*
34418a3b77eSVladimir Kondratyev * Feature report is a primary source of
34518a3b77eSVladimir Kondratyev * 'Contact Count Maximum'
34618a3b77eSVladimir Kondratyev */
34718a3b77eSVladimir Kondratyev if (cont_count_max > 0)
348527b6d60SVladimir Kondratyev sc->cont_count_max = cont_count_max;
34918a3b77eSVladimir Kondratyev } else
35036584a62SVladimir Kondratyev DPRINTF("usbd_req_get_report error=(%s)\n",
35136584a62SVladimir Kondratyev usbd_errstr(err));
35236584a62SVladimir Kondratyev } else
35336584a62SVladimir Kondratyev DPRINTF("Feature report %hhu size invalid or too large: %u\n",
35436584a62SVladimir Kondratyev sc->cont_max_rid, sc->cont_max_rlen);
35536584a62SVladimir Kondratyev
3568de78df5SVladimir Kondratyev /* Fetch and parse "Button type" feature report */
3578de78df5SVladimir Kondratyev if (sc->btn_type_rlen > 1 && sc->btn_type_rlen <= WMT_BSIZE &&
3588de78df5SVladimir Kondratyev sc->btn_type_rid != sc->cont_max_rid) {
3598de78df5SVladimir Kondratyev bzero(sc->buf, sc->btn_type_rlen);
3608de78df5SVladimir Kondratyev err = usbd_req_get_report(uaa->device, NULL, sc->buf,
3618de78df5SVladimir Kondratyev sc->btn_type_rlen, uaa->info.bIfaceIndex,
3628de78df5SVladimir Kondratyev UHID_FEATURE_REPORT, sc->btn_type_rid);
3638de78df5SVladimir Kondratyev }
3648de78df5SVladimir Kondratyev if (sc->btn_type_rlen > 1) {
3658de78df5SVladimir Kondratyev if (err == 0)
366eead9017SVladimir Kondratyev sc->is_clickpad = hid_get_udata(sc->buf + 1,
3678de78df5SVladimir Kondratyev sc->btn_type_rlen - 1, &sc->btn_type_loc) == 0;
3688de78df5SVladimir Kondratyev else
3698de78df5SVladimir Kondratyev DPRINTF("usbd_req_get_report error=%d\n", err);
3708de78df5SVladimir Kondratyev }
3718de78df5SVladimir Kondratyev
3728107f311SVladimir Kondratyev /* Fetch THQA certificate to enable some devices like WaveShare */
3738107f311SVladimir Kondratyev if (sc->thqa_cert_rlen > 0 && sc->thqa_cert_rlen <= WMT_BSIZE &&
3748107f311SVladimir Kondratyev sc->thqa_cert_rid != sc->cont_max_rid)
3758107f311SVladimir Kondratyev (void)usbd_req_get_report(uaa->device, NULL, sc->buf,
3768107f311SVladimir Kondratyev sc->thqa_cert_rlen, uaa->info.bIfaceIndex,
3778107f311SVladimir Kondratyev UHID_FEATURE_REPORT, sc->thqa_cert_rid);
3788107f311SVladimir Kondratyev
3798de78df5SVladimir Kondratyev /* Switch touchpad in to absolute multitouch mode */
3808de78df5SVladimir Kondratyev if (sc->type == WMT_TYPE_TOUCHPAD) {
3818de78df5SVladimir Kondratyev err = wmt_set_input_mode(sc, WMT_INPUT_MODE_MT_TOUCHPAD);
3828de78df5SVladimir Kondratyev if (err != 0)
3838de78df5SVladimir Kondratyev DPRINTF("Failed to set input mode: %d\n", err);
3848de78df5SVladimir Kondratyev }
3858de78df5SVladimir Kondratyev
38618a3b77eSVladimir Kondratyev /* Cap contact count maximum to MAX_MT_SLOTS */
387527b6d60SVladimir Kondratyev if (sc->cont_count_max > MAX_MT_SLOTS) {
38818a3b77eSVladimir Kondratyev DPRINTF("Hardware reported %d contacts while only %d is "
389527b6d60SVladimir Kondratyev "supported\n", (int)sc->cont_count_max, MAX_MT_SLOTS);
390527b6d60SVladimir Kondratyev sc->cont_count_max = MAX_MT_SLOTS;
39118a3b77eSVladimir Kondratyev }
39218a3b77eSVladimir Kondratyev
393501022d3SVladimir Kondratyev if (/*usb_test_quirk(hw, UQ_MT_TIMESTAMP) ||*/ wmt_timestamps)
394501022d3SVladimir Kondratyev sc->do_timestamps = true;
395501022d3SVladimir Kondratyev
39636584a62SVladimir Kondratyev mtx_init(&sc->mtx, "wmt lock", NULL, MTX_DEF);
39736584a62SVladimir Kondratyev
39876136d20SVladimir Kondratyev err = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex,
39976136d20SVladimir Kondratyev sc->xfer, wmt_config, WMT_N_TRANSFER, sc, &sc->mtx);
4003a3dc5b5SVladimir Kondratyev if (err != USB_ERR_NORMAL_COMPLETION) {
40176136d20SVladimir Kondratyev DPRINTF("usbd_transfer_setup error=%s\n", usbd_errstr(err));
40276136d20SVladimir Kondratyev goto detach;
40376136d20SVladimir Kondratyev }
40476136d20SVladimir Kondratyev
40576136d20SVladimir Kondratyev sc->evdev = evdev_alloc();
40676136d20SVladimir Kondratyev evdev_set_name(sc->evdev, device_get_desc(dev));
40776136d20SVladimir Kondratyev evdev_set_phys(sc->evdev, device_get_nameunit(dev));
40876136d20SVladimir Kondratyev evdev_set_id(sc->evdev, BUS_USB, uaa->info.idVendor,
40976136d20SVladimir Kondratyev uaa->info.idProduct, 0);
41076136d20SVladimir Kondratyev evdev_set_serial(sc->evdev, usb_get_serial(uaa->device));
41176136d20SVladimir Kondratyev evdev_set_methods(sc->evdev, sc, &wmt_evdev_methods);
41276136d20SVladimir Kondratyev evdev_set_flag(sc->evdev, EVDEV_FLAG_MT_STCOMPAT);
4138de78df5SVladimir Kondratyev switch (sc->type) {
4148de78df5SVladimir Kondratyev case WMT_TYPE_TOUCHSCREEN:
41576136d20SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_DIRECT);
4168de78df5SVladimir Kondratyev break;
4178de78df5SVladimir Kondratyev case WMT_TYPE_TOUCHPAD:
4188de78df5SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_POINTER);
4198de78df5SVladimir Kondratyev if (sc->is_clickpad)
4208de78df5SVladimir Kondratyev evdev_support_prop(sc->evdev, INPUT_PROP_BUTTONPAD);
4218de78df5SVladimir Kondratyev break;
4228de78df5SVladimir Kondratyev default:
4238de78df5SVladimir Kondratyev KASSERT(0, ("wmt_attach: unsupported touch device type"));
4248de78df5SVladimir Kondratyev }
42576136d20SVladimir Kondratyev evdev_support_event(sc->evdev, EV_SYN);
42676136d20SVladimir Kondratyev evdev_support_event(sc->evdev, EV_ABS);
427501022d3SVladimir Kondratyev if (sc->do_timestamps) {
428501022d3SVladimir Kondratyev evdev_support_event(sc->evdev, EV_MSC);
429501022d3SVladimir Kondratyev evdev_support_msc(sc->evdev, MSC_TIMESTAMP);
430501022d3SVladimir Kondratyev }
431c26a3484SVladimir Kondratyev nbuttons = 0;
4328de78df5SVladimir Kondratyev if (sc->max_button != 0 || sc->has_int_button) {
4338de78df5SVladimir Kondratyev evdev_support_event(sc->evdev, EV_KEY);
4348de78df5SVladimir Kondratyev if (sc->has_int_button)
4358de78df5SVladimir Kondratyev evdev_support_key(sc->evdev, BTN_LEFT);
436c26a3484SVladimir Kondratyev for (btn = 0; btn < sc->max_button; ++btn) {
437c26a3484SVladimir Kondratyev if (isset(sc->buttons, btn)) {
4388de78df5SVladimir Kondratyev evdev_support_key(sc->evdev, BTN_MOUSE + btn);
439c26a3484SVladimir Kondratyev nbuttons++;
440c26a3484SVladimir Kondratyev }
441c26a3484SVladimir Kondratyev }
4428de78df5SVladimir Kondratyev }
443527b6d60SVladimir Kondratyev evdev_support_abs(sc->evdev,
444527b6d60SVladimir Kondratyev ABS_MT_SLOT, 0, sc->cont_count_max - 1, 0, 0, 0);
44576136d20SVladimir Kondratyev WMT_FOREACH_USAGE(sc->caps, i) {
446527b6d60SVladimir Kondratyev if (wmt_hid_map[i].reported)
447527b6d60SVladimir Kondratyev evdev_support_abs(sc->evdev, ABS_MT_FIRST + i,
44876136d20SVladimir Kondratyev sc->ai[i].min, sc->ai[i].max, 0, 0, sc->ai[i].res);
44976136d20SVladimir Kondratyev }
45076136d20SVladimir Kondratyev
45176136d20SVladimir Kondratyev err = evdev_register_mtx(sc->evdev, &sc->mtx);
45276136d20SVladimir Kondratyev if (err)
45376136d20SVladimir Kondratyev goto detach;
45476136d20SVladimir Kondratyev
4557eae6aabSVladimir Kondratyev /* Announce information about the touch device */
4568de78df5SVladimir Kondratyev device_printf(sc->dev, "Multitouch %s with %d external button%s%s\n",
4578de78df5SVladimir Kondratyev sc->type == WMT_TYPE_TOUCHSCREEN ? "touchscreen" : "touchpad",
4588de78df5SVladimir Kondratyev nbuttons, nbuttons != 1 ? "s" : "",
4598de78df5SVladimir Kondratyev sc->is_clickpad ? ", click-pad" : "");
4607eae6aabSVladimir Kondratyev device_printf(sc->dev,
4617eae6aabSVladimir Kondratyev "%d contacts and [%s%s%s%s%s]. Report range [%d:%d] - [%d:%d]\n",
462527b6d60SVladimir Kondratyev (int)sc->cont_count_max,
463c26a3484SVladimir Kondratyev isset(sc->caps, WMT_IN_RANGE) ? "R" : "",
464c26a3484SVladimir Kondratyev isset(sc->caps, WMT_CONFIDENCE) ? "C" : "",
465c26a3484SVladimir Kondratyev isset(sc->caps, WMT_WIDTH) ? "W" : "",
466c26a3484SVladimir Kondratyev isset(sc->caps, WMT_HEIGHT) ? "H" : "",
467c26a3484SVladimir Kondratyev isset(sc->caps, WMT_PRESSURE) ? "P" : "",
4687eae6aabSVladimir Kondratyev (int)sc->ai[WMT_X].min, (int)sc->ai[WMT_Y].min,
4697eae6aabSVladimir Kondratyev (int)sc->ai[WMT_X].max, (int)sc->ai[WMT_Y].max);
4707eae6aabSVladimir Kondratyev
47176136d20SVladimir Kondratyev return (0);
47276136d20SVladimir Kondratyev
47376136d20SVladimir Kondratyev detach:
47476136d20SVladimir Kondratyev wmt_detach(dev);
47576136d20SVladimir Kondratyev return (ENXIO);
47676136d20SVladimir Kondratyev }
47776136d20SVladimir Kondratyev
47876136d20SVladimir Kondratyev static int
wmt_detach(device_t dev)47976136d20SVladimir Kondratyev wmt_detach(device_t dev)
48076136d20SVladimir Kondratyev {
48176136d20SVladimir Kondratyev struct wmt_softc *sc = device_get_softc(dev);
48276136d20SVladimir Kondratyev
48376136d20SVladimir Kondratyev evdev_free(sc->evdev);
48476136d20SVladimir Kondratyev usbd_transfer_unsetup(sc->xfer, WMT_N_TRANSFER);
48576136d20SVladimir Kondratyev mtx_destroy(&sc->mtx);
48676136d20SVladimir Kondratyev return (0);
48776136d20SVladimir Kondratyev }
48876136d20SVladimir Kondratyev
48976136d20SVladimir Kondratyev static void
wmt_process_report(struct wmt_softc * sc,uint8_t * buf,int len)49076136d20SVladimir Kondratyev wmt_process_report(struct wmt_softc *sc, uint8_t *buf, int len)
49176136d20SVladimir Kondratyev {
49276136d20SVladimir Kondratyev size_t usage;
493527b6d60SVladimir Kondratyev union evdev_mt_slot *slot_data;
4948de78df5SVladimir Kondratyev uint32_t cont, btn;
4950ba4b5ffSVladimir Kondratyev uint32_t cont_count;
49676136d20SVladimir Kondratyev uint32_t width;
49776136d20SVladimir Kondratyev uint32_t height;
4988de78df5SVladimir Kondratyev uint32_t int_btn = 0;
4998de78df5SVladimir Kondratyev uint32_t left_btn = 0;
500527b6d60SVladimir Kondratyev int slot;
501501022d3SVladimir Kondratyev uint32_t scan_time;
502501022d3SVladimir Kondratyev int32_t delta;
50376136d20SVladimir Kondratyev
5040ba4b5ffSVladimir Kondratyev /*
5050ba4b5ffSVladimir Kondratyev * "In Parallel mode, devices report all contact information in a
5060ba4b5ffSVladimir Kondratyev * single packet. Each physical contact is represented by a logical
5070ba4b5ffSVladimir Kondratyev * collection that is embedded in the top-level collection."
5080ba4b5ffSVladimir Kondratyev *
5090ba4b5ffSVladimir Kondratyev * Since additional contacts that were not present will still be in the
5100ba4b5ffSVladimir Kondratyev * report with contactid=0 but contactids are zero-based, find
5110ba4b5ffSVladimir Kondratyev * contactcount first.
5120ba4b5ffSVladimir Kondratyev */
513eead9017SVladimir Kondratyev cont_count = hid_get_udata(buf, len, &sc->cont_count_loc);
5140ba4b5ffSVladimir Kondratyev /*
5150ba4b5ffSVladimir Kondratyev * "In Hybrid mode, the number of contacts that can be reported in one
5160ba4b5ffSVladimir Kondratyev * report is less than the maximum number of contacts that the device
5170ba4b5ffSVladimir Kondratyev * supports. For example, a device that supports a maximum of
5180ba4b5ffSVladimir Kondratyev * 4 concurrent physical contacts, can set up its top-level collection
5190ba4b5ffSVladimir Kondratyev * to deliver a maximum of two contacts in one report. If four contact
5200ba4b5ffSVladimir Kondratyev * points are present, the device can break these up into two serial
5210ba4b5ffSVladimir Kondratyev * reports that deliver two contacts each.
5220ba4b5ffSVladimir Kondratyev *
5230ba4b5ffSVladimir Kondratyev * "When a device delivers data in this manner, the Contact Count usage
5240ba4b5ffSVladimir Kondratyev * value in the first report should reflect the total number of
5250ba4b5ffSVladimir Kondratyev * contacts that are being delivered in the hybrid reports. The other
5260ba4b5ffSVladimir Kondratyev * serial reports should have a contact count of zero (0)."
5270ba4b5ffSVladimir Kondratyev */
5280ba4b5ffSVladimir Kondratyev if (cont_count != 0)
5290ba4b5ffSVladimir Kondratyev sc->nconts_todo = cont_count;
53076136d20SVladimir Kondratyev
53176136d20SVladimir Kondratyev #ifdef USB_DEBUG
5320ba4b5ffSVladimir Kondratyev DPRINTFN(6, "cont_count:%2u", (unsigned)cont_count);
53376136d20SVladimir Kondratyev if (wmt_debug >= 6) {
53476136d20SVladimir Kondratyev WMT_FOREACH_USAGE(sc->caps, usage) {
53576136d20SVladimir Kondratyev if (wmt_hid_map[usage].usage != WMT_NO_USAGE)
53676136d20SVladimir Kondratyev printf(" %-4s", wmt_hid_map[usage].name);
53776136d20SVladimir Kondratyev }
53876136d20SVladimir Kondratyev printf("\n");
53976136d20SVladimir Kondratyev }
54076136d20SVladimir Kondratyev #endif
54176136d20SVladimir Kondratyev
5420ba4b5ffSVladimir Kondratyev /* Find the number of contacts reported in current report */
5430ba4b5ffSVladimir Kondratyev cont_count = MIN(sc->nconts_todo, sc->nconts_per_report);
54476136d20SVladimir Kondratyev
54576136d20SVladimir Kondratyev /* Use protocol Type B for reporting events */
5460ba4b5ffSVladimir Kondratyev for (cont = 0; cont < cont_count; cont++) {
547527b6d60SVladimir Kondratyev slot_data = &sc->slot_data;
54876136d20SVladimir Kondratyev bzero(slot_data, sizeof(sc->slot_data));
54976136d20SVladimir Kondratyev WMT_FOREACH_USAGE(sc->caps, usage) {
55076136d20SVladimir Kondratyev if (sc->locs[cont][usage].size > 0)
551527b6d60SVladimir Kondratyev slot_data->val[usage] = hid_get_udata(
55276136d20SVladimir Kondratyev buf, len, &sc->locs[cont][usage]);
55376136d20SVladimir Kondratyev }
55476136d20SVladimir Kondratyev
555527b6d60SVladimir Kondratyev slot = evdev_mt_id_to_slot(sc->evdev, slot_data->id);
55676136d20SVladimir Kondratyev
55776136d20SVladimir Kondratyev #ifdef USB_DEBUG
55876136d20SVladimir Kondratyev DPRINTFN(6, "cont%01x: data = ", cont);
55976136d20SVladimir Kondratyev if (wmt_debug >= 6) {
56076136d20SVladimir Kondratyev WMT_FOREACH_USAGE(sc->caps, usage) {
56176136d20SVladimir Kondratyev if (wmt_hid_map[usage].usage != WMT_NO_USAGE)
562527b6d60SVladimir Kondratyev printf("%04x ", slot_data->val[usage]);
56376136d20SVladimir Kondratyev }
564527b6d60SVladimir Kondratyev printf("slot = %d\n", slot);
56576136d20SVladimir Kondratyev }
56676136d20SVladimir Kondratyev #endif
56776136d20SVladimir Kondratyev
56876136d20SVladimir Kondratyev if (slot == -1) {
56976136d20SVladimir Kondratyev DPRINTF("Slot overflow for contact_id %u\n",
570527b6d60SVladimir Kondratyev (unsigned)slot_data->id);
57176136d20SVladimir Kondratyev continue;
57276136d20SVladimir Kondratyev }
57376136d20SVladimir Kondratyev
574527b6d60SVladimir Kondratyev if (slot_data->val[WMT_TIP_SWITCH] != 0 &&
575c26a3484SVladimir Kondratyev !(isset(sc->caps, WMT_CONFIDENCE) &&
576527b6d60SVladimir Kondratyev slot_data->val[WMT_CONFIDENCE] == 0)) {
57776136d20SVladimir Kondratyev /* This finger is in proximity of the sensor */
578501022d3SVladimir Kondratyev sc->touch = true;
579527b6d60SVladimir Kondratyev slot_data->dist = !slot_data->val[WMT_IN_RANGE];
58076136d20SVladimir Kondratyev /* Divided by two to match visual scale of touch */
581527b6d60SVladimir Kondratyev width = slot_data->val[WMT_WIDTH] >> 1;
582527b6d60SVladimir Kondratyev height = slot_data->val[WMT_HEIGHT] >> 1;
583527b6d60SVladimir Kondratyev slot_data->ori = width > height;
584527b6d60SVladimir Kondratyev slot_data->maj = MAX(width, height);
585527b6d60SVladimir Kondratyev slot_data->min = MIN(width, height);
586527b6d60SVladimir Kondratyev } else
587527b6d60SVladimir Kondratyev slot_data = NULL;
58876136d20SVladimir Kondratyev
589527b6d60SVladimir Kondratyev evdev_mt_push_slot(sc->evdev, slot, slot_data);
59076136d20SVladimir Kondratyev }
5910ba4b5ffSVladimir Kondratyev
5920ba4b5ffSVladimir Kondratyev sc->nconts_todo -= cont_count;
593501022d3SVladimir Kondratyev if (sc->do_timestamps && sc->nconts_todo == 0) {
594501022d3SVladimir Kondratyev /* HUD_SCAN_TIME is measured in 100us, convert to us. */
595eead9017SVladimir Kondratyev scan_time = hid_get_udata(buf, len, &sc->scan_time_loc);
596501022d3SVladimir Kondratyev if (sc->prev_touch) {
597501022d3SVladimir Kondratyev delta = scan_time - sc->scan_time;
598501022d3SVladimir Kondratyev if (delta < 0)
599501022d3SVladimir Kondratyev delta += sc->scan_time_max;
600501022d3SVladimir Kondratyev } else
601501022d3SVladimir Kondratyev delta = 0;
602501022d3SVladimir Kondratyev sc->scan_time = scan_time;
603501022d3SVladimir Kondratyev sc->timestamp += delta * 100;
604501022d3SVladimir Kondratyev evdev_push_msc(sc->evdev, MSC_TIMESTAMP, sc->timestamp);
605501022d3SVladimir Kondratyev sc->prev_touch = sc->touch;
606501022d3SVladimir Kondratyev sc->touch = false;
607501022d3SVladimir Kondratyev if (!sc->prev_touch)
608501022d3SVladimir Kondratyev sc->timestamp = 0;
609501022d3SVladimir Kondratyev }
6108de78df5SVladimir Kondratyev if (sc->nconts_todo == 0) {
6118de78df5SVladimir Kondratyev /* Report both the click and external left btns as BTN_LEFT */
6128de78df5SVladimir Kondratyev if (sc->has_int_button)
6138de78df5SVladimir Kondratyev int_btn = hid_get_data(buf, len, &sc->int_btn_loc);
614c26a3484SVladimir Kondratyev if (isset(sc->buttons, 0))
6158de78df5SVladimir Kondratyev left_btn = hid_get_data(buf, len, &sc->btn_loc[0]);
616c26a3484SVladimir Kondratyev if (sc->has_int_button || isset(sc->buttons, 0))
6178de78df5SVladimir Kondratyev evdev_push_key(sc->evdev, BTN_LEFT,
618322a188dSRyan Libby (int_btn != 0) | (left_btn != 0));
6198de78df5SVladimir Kondratyev for (btn = 1; btn < sc->max_button; ++btn) {
620c26a3484SVladimir Kondratyev if (isset(sc->buttons, btn))
6218de78df5SVladimir Kondratyev evdev_push_key(sc->evdev, BTN_MOUSE + btn,
6228de78df5SVladimir Kondratyev hid_get_data(buf,
6238de78df5SVladimir Kondratyev len,
6248de78df5SVladimir Kondratyev &sc->btn_loc[btn]) != 0);
6258de78df5SVladimir Kondratyev }
62676136d20SVladimir Kondratyev evdev_sync(sc->evdev);
62776136d20SVladimir Kondratyev }
6288de78df5SVladimir Kondratyev }
62976136d20SVladimir Kondratyev
63076136d20SVladimir Kondratyev static void
wmt_intr_callback(struct usb_xfer * xfer,usb_error_t error)63176136d20SVladimir Kondratyev wmt_intr_callback(struct usb_xfer *xfer, usb_error_t error)
63276136d20SVladimir Kondratyev {
63376136d20SVladimir Kondratyev struct wmt_softc *sc = usbd_xfer_softc(xfer);
63476136d20SVladimir Kondratyev struct usb_page_cache *pc;
63576136d20SVladimir Kondratyev uint8_t *buf = sc->buf;
63676136d20SVladimir Kondratyev int len;
63776136d20SVladimir Kondratyev
63876136d20SVladimir Kondratyev usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
63976136d20SVladimir Kondratyev
64076136d20SVladimir Kondratyev switch (USB_GET_STATE(xfer)) {
64176136d20SVladimir Kondratyev case USB_ST_TRANSFERRED:
64276136d20SVladimir Kondratyev pc = usbd_xfer_get_frame(xfer, 0);
64376136d20SVladimir Kondratyev
64476136d20SVladimir Kondratyev DPRINTFN(6, "sc=%p actlen=%d\n", sc, len);
64576136d20SVladimir Kondratyev
64654c05047SVladimir Kondratyev if (len >= (int)sc->report_len ||
64754c05047SVladimir Kondratyev (len > 0 && sc->report_id != 0)) {
64876136d20SVladimir Kondratyev /* Limit report length to the maximum */
64954c05047SVladimir Kondratyev if (len > (int)sc->report_len)
65054c05047SVladimir Kondratyev len = sc->report_len;
65176136d20SVladimir Kondratyev
65276136d20SVladimir Kondratyev usbd_copy_out(pc, 0, buf, len);
65376136d20SVladimir Kondratyev
65476136d20SVladimir Kondratyev /* Ignore irrelevant reports */
65576136d20SVladimir Kondratyev if (sc->report_id && *buf != sc->report_id)
65676136d20SVladimir Kondratyev goto tr_ignore;
65776136d20SVladimir Kondratyev
65876136d20SVladimir Kondratyev /* Make sure we don't process old data */
65954c05047SVladimir Kondratyev if (len < sc->report_len)
66054c05047SVladimir Kondratyev bzero(buf + len, sc->report_len - len);
66176136d20SVladimir Kondratyev
66276136d20SVladimir Kondratyev /* Strip leading "report ID" byte */
66376136d20SVladimir Kondratyev if (sc->report_id) {
66476136d20SVladimir Kondratyev len--;
66576136d20SVladimir Kondratyev buf++;
66676136d20SVladimir Kondratyev }
66776136d20SVladimir Kondratyev
66876136d20SVladimir Kondratyev wmt_process_report(sc, buf, len);
66976136d20SVladimir Kondratyev } else {
67076136d20SVladimir Kondratyev tr_ignore:
67176136d20SVladimir Kondratyev DPRINTF("Ignored transfer, %d bytes\n", len);
67276136d20SVladimir Kondratyev }
67376136d20SVladimir Kondratyev
67476136d20SVladimir Kondratyev case USB_ST_SETUP:
67576136d20SVladimir Kondratyev tr_setup:
67654c05047SVladimir Kondratyev usbd_xfer_set_frame_len(xfer, 0, sc->isize);
67776136d20SVladimir Kondratyev usbd_transfer_submit(xfer);
67876136d20SVladimir Kondratyev break;
67976136d20SVladimir Kondratyev default:
68076136d20SVladimir Kondratyev if (error != USB_ERR_CANCELLED) {
68176136d20SVladimir Kondratyev /* Try clear stall first */
68276136d20SVladimir Kondratyev usbd_xfer_set_stall(xfer);
68376136d20SVladimir Kondratyev goto tr_setup;
68476136d20SVladimir Kondratyev }
68576136d20SVladimir Kondratyev break;
68676136d20SVladimir Kondratyev }
68776136d20SVladimir Kondratyev }
68876136d20SVladimir Kondratyev
68976136d20SVladimir Kondratyev static void
wmt_ev_close_11(struct evdev_dev * evdev,void * ev_softc)690911aed94SVladimir Kondratyev wmt_ev_close_11(struct evdev_dev *evdev, void *ev_softc)
69176136d20SVladimir Kondratyev {
692911aed94SVladimir Kondratyev struct wmt_softc *sc = ev_softc;
69376136d20SVladimir Kondratyev
69476136d20SVladimir Kondratyev mtx_assert(&sc->mtx, MA_OWNED);
69576136d20SVladimir Kondratyev usbd_transfer_stop(sc->xfer[WMT_INTR_DT]);
69676136d20SVladimir Kondratyev }
69776136d20SVladimir Kondratyev
69876136d20SVladimir Kondratyev static int
wmt_ev_open_11(struct evdev_dev * evdev,void * ev_softc)699911aed94SVladimir Kondratyev wmt_ev_open_11(struct evdev_dev *evdev, void *ev_softc)
70076136d20SVladimir Kondratyev {
701911aed94SVladimir Kondratyev struct wmt_softc *sc = ev_softc;
70276136d20SVladimir Kondratyev
70376136d20SVladimir Kondratyev mtx_assert(&sc->mtx, MA_OWNED);
70476136d20SVladimir Kondratyev usbd_transfer_start(sc->xfer[WMT_INTR_DT]);
70576136d20SVladimir Kondratyev
70676136d20SVladimir Kondratyev return (0);
70776136d20SVladimir Kondratyev }
70876136d20SVladimir Kondratyev
709911aed94SVladimir Kondratyev static int
wmt_ev_close(struct evdev_dev * evdev)710911aed94SVladimir Kondratyev wmt_ev_close(struct evdev_dev *evdev)
711911aed94SVladimir Kondratyev {
712911aed94SVladimir Kondratyev struct wmt_softc *sc = evdev_get_softc(evdev);
713911aed94SVladimir Kondratyev
714911aed94SVladimir Kondratyev wmt_ev_close_11(evdev, sc);
715911aed94SVladimir Kondratyev
716911aed94SVladimir Kondratyev return (0);
717911aed94SVladimir Kondratyev }
718911aed94SVladimir Kondratyev
719911aed94SVladimir Kondratyev static int
wmt_ev_open(struct evdev_dev * evdev)720911aed94SVladimir Kondratyev wmt_ev_open(struct evdev_dev *evdev)
721911aed94SVladimir Kondratyev {
722911aed94SVladimir Kondratyev struct wmt_softc *sc = evdev_get_softc(evdev);
723911aed94SVladimir Kondratyev
724911aed94SVladimir Kondratyev return (wmt_ev_open_11(evdev, sc));
725911aed94SVladimir Kondratyev
726911aed94SVladimir Kondratyev }
727911aed94SVladimir Kondratyev
7288de78df5SVladimir Kondratyev static enum wmt_type
wmt_hid_parse(struct wmt_softc * sc,const void * d_ptr,uint16_t d_len)72976136d20SVladimir Kondratyev wmt_hid_parse(struct wmt_softc *sc, const void *d_ptr, uint16_t d_len)
73076136d20SVladimir Kondratyev {
73176136d20SVladimir Kondratyev struct hid_item hi;
73276136d20SVladimir Kondratyev struct hid_data *hd;
73376136d20SVladimir Kondratyev size_t i;
73476136d20SVladimir Kondratyev size_t cont = 0;
7358de78df5SVladimir Kondratyev enum wmt_type type = WMT_TYPE_UNSUPPORTED;
7368de78df5SVladimir Kondratyev uint32_t left_btn, btn;
73776136d20SVladimir Kondratyev int32_t cont_count_max = 0;
73876136d20SVladimir Kondratyev uint8_t report_id = 0;
73976136d20SVladimir Kondratyev bool touch_coll = false;
74076136d20SVladimir Kondratyev bool finger_coll = false;
74176136d20SVladimir Kondratyev bool cont_count_found = false;
74276136d20SVladimir Kondratyev bool scan_time_found = false;
7438de78df5SVladimir Kondratyev bool has_int_button = false;
74476136d20SVladimir Kondratyev
74576136d20SVladimir Kondratyev #define WMT_HI_ABSOLUTE(hi) \
74676136d20SVladimir Kondratyev (((hi).flags & (HIO_CONST|HIO_VARIABLE|HIO_RELATIVE)) == HIO_VARIABLE)
7478107f311SVladimir Kondratyev #define HUMS_THQA_CERT 0xC5
74876136d20SVladimir Kondratyev
74976136d20SVladimir Kondratyev /* Parse features for maximum contact count */
75076136d20SVladimir Kondratyev hd = hid_start_parse(d_ptr, d_len, 1 << hid_feature);
75176136d20SVladimir Kondratyev while (hid_get_item(hd, &hi)) {
75276136d20SVladimir Kondratyev switch (hi.kind) {
75376136d20SVladimir Kondratyev case hid_collection:
75476136d20SVladimir Kondratyev if (hi.collevel == 1 && hi.usage ==
7558de78df5SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN)) {
75676136d20SVladimir Kondratyev touch_coll = true;
7578de78df5SVladimir Kondratyev type = WMT_TYPE_TOUCHSCREEN;
7588de78df5SVladimir Kondratyev left_btn = 1;
7598de78df5SVladimir Kondratyev break;
7608de78df5SVladimir Kondratyev }
7618de78df5SVladimir Kondratyev if (hi.collevel == 1 && hi.usage ==
7628de78df5SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHPAD)) {
7638de78df5SVladimir Kondratyev touch_coll = true;
7648de78df5SVladimir Kondratyev type = WMT_TYPE_TOUCHPAD;
7658de78df5SVladimir Kondratyev left_btn = 2;
7668de78df5SVladimir Kondratyev }
76776136d20SVladimir Kondratyev break;
76876136d20SVladimir Kondratyev case hid_endcollection:
76976136d20SVladimir Kondratyev if (hi.collevel == 0 && touch_coll)
77076136d20SVladimir Kondratyev touch_coll = false;
77176136d20SVladimir Kondratyev break;
77276136d20SVladimir Kondratyev case hid_feature:
7738107f311SVladimir Kondratyev if (hi.collevel == 1 && touch_coll && hi.usage ==
7748107f311SVladimir Kondratyev HID_USAGE2(HUP_MICROSOFT, HUMS_THQA_CERT)) {
7757eae6aabSVladimir Kondratyev sc->thqa_cert_rid = hi.report_ID;
7768107f311SVladimir Kondratyev break;
7778107f311SVladimir Kondratyev }
778c50cdb63SVladimir Kondratyev if (hi.collevel == 1 && touch_coll && hi.usage ==
77936584a62SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACT_MAX)) {
78076136d20SVladimir Kondratyev cont_count_max = hi.logical_maximum;
7817eae6aabSVladimir Kondratyev sc->cont_max_rid = hi.report_ID;
78236584a62SVladimir Kondratyev sc->cont_max_loc = hi.loc;
7838de78df5SVladimir Kondratyev break;
7848de78df5SVladimir Kondratyev }
7858de78df5SVladimir Kondratyev if (hi.collevel == 1 && touch_coll && hi.usage ==
7868de78df5SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_BUTTON_TYPE)) {
7878de78df5SVladimir Kondratyev sc->btn_type_rid = hi.report_ID;
7888de78df5SVladimir Kondratyev sc->btn_type_loc = hi.loc;
78936584a62SVladimir Kondratyev }
79076136d20SVladimir Kondratyev break;
79176136d20SVladimir Kondratyev default:
79276136d20SVladimir Kondratyev break;
79376136d20SVladimir Kondratyev }
79476136d20SVladimir Kondratyev }
79576136d20SVladimir Kondratyev hid_end_parse(hd);
79676136d20SVladimir Kondratyev
7978de78df5SVladimir Kondratyev if (type == WMT_TYPE_UNSUPPORTED)
7988de78df5SVladimir Kondratyev return (WMT_TYPE_UNSUPPORTED);
79976136d20SVladimir Kondratyev /* Maximum contact count is required usage */
8007eae6aabSVladimir Kondratyev if (sc->cont_max_rid == 0)
8018de78df5SVladimir Kondratyev return (WMT_TYPE_UNSUPPORTED);
80276136d20SVladimir Kondratyev
80376136d20SVladimir Kondratyev touch_coll = false;
80476136d20SVladimir Kondratyev
80576136d20SVladimir Kondratyev /* Parse input for other parameters */
80676136d20SVladimir Kondratyev hd = hid_start_parse(d_ptr, d_len, 1 << hid_input);
80776136d20SVladimir Kondratyev while (hid_get_item(hd, &hi)) {
80876136d20SVladimir Kondratyev switch (hi.kind) {
80976136d20SVladimir Kondratyev case hid_collection:
81076136d20SVladimir Kondratyev if (hi.collevel == 1 && hi.usage ==
81176136d20SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCHSCREEN))
81276136d20SVladimir Kondratyev touch_coll = true;
81376136d20SVladimir Kondratyev else if (touch_coll && hi.collevel == 2 &&
81476136d20SVladimir Kondratyev (report_id == 0 || report_id == hi.report_ID) &&
81576136d20SVladimir Kondratyev hi.usage == HID_USAGE2(HUP_DIGITIZERS, HUD_FINGER))
81676136d20SVladimir Kondratyev finger_coll = true;
81776136d20SVladimir Kondratyev break;
81876136d20SVladimir Kondratyev case hid_endcollection:
81976136d20SVladimir Kondratyev if (hi.collevel == 1 && finger_coll) {
82076136d20SVladimir Kondratyev finger_coll = false;
82176136d20SVladimir Kondratyev cont++;
82276136d20SVladimir Kondratyev } else if (hi.collevel == 0 && touch_coll)
82376136d20SVladimir Kondratyev touch_coll = false;
82476136d20SVladimir Kondratyev break;
82576136d20SVladimir Kondratyev case hid_input:
82676136d20SVladimir Kondratyev /*
82776136d20SVladimir Kondratyev * Ensure that all usages are located within the same
82876136d20SVladimir Kondratyev * report and proper collection.
82976136d20SVladimir Kondratyev */
83076136d20SVladimir Kondratyev if (WMT_HI_ABSOLUTE(hi) && touch_coll &&
83176136d20SVladimir Kondratyev (report_id == 0 || report_id == hi.report_ID))
83276136d20SVladimir Kondratyev report_id = hi.report_ID;
83376136d20SVladimir Kondratyev else
83476136d20SVladimir Kondratyev break;
83576136d20SVladimir Kondratyev
8368de78df5SVladimir Kondratyev if (hi.collevel == 1 && left_btn == 2 &&
8378de78df5SVladimir Kondratyev hi.usage == HID_USAGE2(HUP_BUTTON, 1)) {
8388de78df5SVladimir Kondratyev has_int_button = true;
8398de78df5SVladimir Kondratyev sc->int_btn_loc = hi.loc;
8408de78df5SVladimir Kondratyev break;
8418de78df5SVladimir Kondratyev }
8428de78df5SVladimir Kondratyev if (hi.collevel == 1 &&
8438de78df5SVladimir Kondratyev hi.usage >= HID_USAGE2(HUP_BUTTON, left_btn) &&
8448de78df5SVladimir Kondratyev hi.usage <= HID_USAGE2(HUP_BUTTON, WMT_BTN_MAX)) {
8458de78df5SVladimir Kondratyev btn = (hi.usage & 0xFFFF) - left_btn;
846c26a3484SVladimir Kondratyev setbit(sc->buttons, btn);
8478de78df5SVladimir Kondratyev sc->btn_loc[btn] = hi.loc;
8488de78df5SVladimir Kondratyev if (btn >= sc->max_button)
8498de78df5SVladimir Kondratyev sc->max_button = btn + 1;
8508de78df5SVladimir Kondratyev break;
8518de78df5SVladimir Kondratyev }
85276136d20SVladimir Kondratyev if (hi.collevel == 1 && hi.usage ==
85376136d20SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_CONTACTCOUNT)) {
85476136d20SVladimir Kondratyev cont_count_found = true;
8550ba4b5ffSVladimir Kondratyev sc->cont_count_loc = hi.loc;
85676136d20SVladimir Kondratyev break;
85776136d20SVladimir Kondratyev }
85876136d20SVladimir Kondratyev /* Scan time is required but clobbered by evdev */
85976136d20SVladimir Kondratyev if (hi.collevel == 1 && hi.usage ==
86076136d20SVladimir Kondratyev HID_USAGE2(HUP_DIGITIZERS, HUD_SCAN_TIME)) {
86176136d20SVladimir Kondratyev scan_time_found = true;
862501022d3SVladimir Kondratyev sc->scan_time_loc = hi.loc;
863501022d3SVladimir Kondratyev sc->scan_time_max = hi.logical_maximum;
86476136d20SVladimir Kondratyev break;
86576136d20SVladimir Kondratyev }
86676136d20SVladimir Kondratyev
86776136d20SVladimir Kondratyev if (!finger_coll || hi.collevel != 2)
86876136d20SVladimir Kondratyev break;
86976136d20SVladimir Kondratyev if (cont >= MAX_MT_SLOTS) {
87076136d20SVladimir Kondratyev DPRINTF("Finger %zu ignored\n", cont);
87176136d20SVladimir Kondratyev break;
87276136d20SVladimir Kondratyev }
87376136d20SVladimir Kondratyev
87476136d20SVladimir Kondratyev for (i = 0; i < WMT_N_USAGES; i++) {
87576136d20SVladimir Kondratyev if (hi.usage == wmt_hid_map[i].usage) {
87676136d20SVladimir Kondratyev /*
87776136d20SVladimir Kondratyev * HUG_X usage is an array mapped to
87876136d20SVladimir Kondratyev * both ABS_MT_POSITION and ABS_MT_TOOL
87976136d20SVladimir Kondratyev * events. So don`t stop search if we
88076136d20SVladimir Kondratyev * already have HUG_X mapping done.
88176136d20SVladimir Kondratyev */
88276136d20SVladimir Kondratyev if (sc->locs[cont][i].size)
88376136d20SVladimir Kondratyev continue;
88476136d20SVladimir Kondratyev sc->locs[cont][i] = hi.loc;
88576136d20SVladimir Kondratyev /*
88676136d20SVladimir Kondratyev * Hid parser returns valid logical and
88776136d20SVladimir Kondratyev * physical sizes for first finger only
88876136d20SVladimir Kondratyev * at least on ElanTS 0x04f3:0x0012.
88976136d20SVladimir Kondratyev */
89076136d20SVladimir Kondratyev if (cont > 0)
89176136d20SVladimir Kondratyev break;
892c26a3484SVladimir Kondratyev setbit(sc->caps, i);
89376136d20SVladimir Kondratyev sc->ai[i] = (struct wmt_absinfo) {
89476136d20SVladimir Kondratyev .max = hi.logical_maximum,
89576136d20SVladimir Kondratyev .min = hi.logical_minimum,
89676136d20SVladimir Kondratyev .res = hid_item_resolution(&hi),
89776136d20SVladimir Kondratyev };
89876136d20SVladimir Kondratyev break;
89976136d20SVladimir Kondratyev }
90076136d20SVladimir Kondratyev }
90176136d20SVladimir Kondratyev break;
90276136d20SVladimir Kondratyev default:
90376136d20SVladimir Kondratyev break;
90476136d20SVladimir Kondratyev }
90576136d20SVladimir Kondratyev }
90676136d20SVladimir Kondratyev hid_end_parse(hd);
90776136d20SVladimir Kondratyev
90876136d20SVladimir Kondratyev /* Check for required HID Usages */
90976136d20SVladimir Kondratyev if (!cont_count_found || !scan_time_found || cont == 0)
9108de78df5SVladimir Kondratyev return (WMT_TYPE_UNSUPPORTED);
91176136d20SVladimir Kondratyev for (i = 0; i < WMT_N_USAGES; i++) {
912c26a3484SVladimir Kondratyev if (wmt_hid_map[i].required && isclr(sc->caps, i))
9138de78df5SVladimir Kondratyev return (WMT_TYPE_UNSUPPORTED);
91476136d20SVladimir Kondratyev }
91576136d20SVladimir Kondratyev
9168de78df5SVladimir Kondratyev /* Touchpads must have at least one button */
9178de78df5SVladimir Kondratyev if (type == WMT_TYPE_TOUCHPAD && !sc->max_button && !has_int_button)
9188de78df5SVladimir Kondratyev return (WMT_TYPE_UNSUPPORTED);
9198de78df5SVladimir Kondratyev
92036584a62SVladimir Kondratyev /*
92136584a62SVladimir Kondratyev * According to specifications 'Contact Count Maximum' should be read
92236584a62SVladimir Kondratyev * from Feature Report rather than from HID descriptor. Set sane
92336584a62SVladimir Kondratyev * default value now to handle the case of 'Get Report' request failure
92436584a62SVladimir Kondratyev */
92536584a62SVladimir Kondratyev if (cont_count_max < 1)
92636584a62SVladimir Kondratyev cont_count_max = cont;
92736584a62SVladimir Kondratyev
92876136d20SVladimir Kondratyev /* Report touch orientation if both width and height are supported */
929c26a3484SVladimir Kondratyev if (isset(sc->caps, WMT_WIDTH) && isset(sc->caps, WMT_HEIGHT)) {
930c26a3484SVladimir Kondratyev setbit(sc->caps, WMT_ORIENTATION);
93176136d20SVladimir Kondratyev sc->ai[WMT_ORIENTATION].max = 1;
93276136d20SVladimir Kondratyev }
93376136d20SVladimir Kondratyev
934eead9017SVladimir Kondratyev sc->isize = hid_report_size_max(d_ptr, d_len, hid_input, NULL);
935eead9017SVladimir Kondratyev sc->report_len = hid_report_size(d_ptr, d_len, hid_input,
93654c05047SVladimir Kondratyev report_id);
937eead9017SVladimir Kondratyev sc->cont_max_rlen = hid_report_size(d_ptr, d_len, hid_feature,
9387eae6aabSVladimir Kondratyev sc->cont_max_rid);
9398de78df5SVladimir Kondratyev if (sc->btn_type_rid > 0)
940eead9017SVladimir Kondratyev sc->btn_type_rlen = hid_report_size(d_ptr, d_len,
9418de78df5SVladimir Kondratyev hid_feature, sc->btn_type_rid);
9427eae6aabSVladimir Kondratyev if (sc->thqa_cert_rid > 0)
943eead9017SVladimir Kondratyev sc->thqa_cert_rlen = hid_report_size(d_ptr, d_len,
9447eae6aabSVladimir Kondratyev hid_feature, sc->thqa_cert_rid);
94536584a62SVladimir Kondratyev
94676136d20SVladimir Kondratyev sc->report_id = report_id;
9470ba4b5ffSVladimir Kondratyev sc->nconts_per_report = cont;
9488de78df5SVladimir Kondratyev sc->has_int_button = has_int_button;
949527b6d60SVladimir Kondratyev sc->cont_count_max = cont_count_max;
95076136d20SVladimir Kondratyev
9518de78df5SVladimir Kondratyev return (type);
95276136d20SVladimir Kondratyev }
95376136d20SVladimir Kondratyev
9548de78df5SVladimir Kondratyev static int
wmt_set_input_mode(struct wmt_softc * sc,enum wmt_input_mode mode)9558de78df5SVladimir Kondratyev wmt_set_input_mode(struct wmt_softc *sc, enum wmt_input_mode mode)
9568de78df5SVladimir Kondratyev {
9578de78df5SVladimir Kondratyev struct usb_attach_arg *uaa = device_get_ivars(sc->dev);
9588de78df5SVladimir Kondratyev int err;
9598de78df5SVladimir Kondratyev
9608de78df5SVladimir Kondratyev if (sc->input_mode_rlen < 3 || sc->input_mode_rlen > WMT_BSIZE) {
9618de78df5SVladimir Kondratyev DPRINTF("Feature report %hhu size invalid or too large: %u\n",
9628de78df5SVladimir Kondratyev sc->input_mode_rid, sc->input_mode_rlen);
9638de78df5SVladimir Kondratyev return (USB_ERR_BAD_BUFSIZE);
9648de78df5SVladimir Kondratyev }
9658de78df5SVladimir Kondratyev
9668de78df5SVladimir Kondratyev /* Input Mode report is not strictly required to be readable */
9678de78df5SVladimir Kondratyev err = usbd_req_get_report(uaa->device, NULL, sc->buf,
9688de78df5SVladimir Kondratyev sc->input_mode_rlen, uaa->info.bIfaceIndex,
9698de78df5SVladimir Kondratyev UHID_FEATURE_REPORT, sc->input_mode_rid);
9708de78df5SVladimir Kondratyev if (err != USB_ERR_NORMAL_COMPLETION)
9718de78df5SVladimir Kondratyev bzero(sc->buf + 1, sc->input_mode_rlen - 1);
9728de78df5SVladimir Kondratyev
9738de78df5SVladimir Kondratyev sc->buf[0] = sc->input_mode_rid;
974eead9017SVladimir Kondratyev hid_put_udata(sc->buf + 1, sc->input_mode_rlen - 1,
9758de78df5SVladimir Kondratyev &sc->input_mode_loc, mode);
9768de78df5SVladimir Kondratyev err = usbd_req_set_report(uaa->device, NULL, sc->buf,
9778de78df5SVladimir Kondratyev sc->input_mode_rlen, uaa->info.bIfaceIndex,
9788de78df5SVladimir Kondratyev UHID_FEATURE_REPORT, sc->input_mode_rid);
9798de78df5SVladimir Kondratyev
9808de78df5SVladimir Kondratyev return (err);
9818de78df5SVladimir Kondratyev }
9828de78df5SVladimir Kondratyev
983236e308aSVladimir Kondratyev static const STRUCT_USB_HOST_ID wmt_devs[] = {
984236e308aSVladimir Kondratyev /* generic HID class w/o boot interface */
985236e308aSVladimir Kondratyev {USB_IFACE_CLASS(UICLASS_HID),
986236e308aSVladimir Kondratyev USB_IFACE_SUBCLASS(0),},
987236e308aSVladimir Kondratyev };
988236e308aSVladimir Kondratyev
98976136d20SVladimir Kondratyev static device_method_t wmt_methods[] = {
99076136d20SVladimir Kondratyev DEVMETHOD(device_probe, wmt_probe),
99176136d20SVladimir Kondratyev DEVMETHOD(device_attach, wmt_attach),
99276136d20SVladimir Kondratyev DEVMETHOD(device_detach, wmt_detach),
99376136d20SVladimir Kondratyev
99476136d20SVladimir Kondratyev DEVMETHOD_END
99576136d20SVladimir Kondratyev };
99676136d20SVladimir Kondratyev
99776136d20SVladimir Kondratyev static driver_t wmt_driver = {
99876136d20SVladimir Kondratyev .name = "wmt",
99976136d20SVladimir Kondratyev .methods = wmt_methods,
100076136d20SVladimir Kondratyev .size = sizeof(struct wmt_softc),
100176136d20SVladimir Kondratyev };
100276136d20SVladimir Kondratyev
1003bc9372d7SJohn Baldwin DRIVER_MODULE(wmt, uhub, wmt_driver, NULL, NULL);
100476136d20SVladimir Kondratyev MODULE_DEPEND(wmt, usb, 1, 1, 1);
100567de2db2SVladimir Kondratyev MODULE_DEPEND(wmt, hid, 1, 1, 1);
100676136d20SVladimir Kondratyev MODULE_DEPEND(wmt, evdev, 1, 1, 1);
100776136d20SVladimir Kondratyev MODULE_VERSION(wmt, 1);
1008236e308aSVladimir Kondratyev USB_PNP_HOST_INFO(wmt_devs);
1009