xref: /src/sys/dev/usb/input/wmt.c (revision ca48e43ba9ee73a07cdbad8365117793b01273bb)
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