xref: /openbsd-src/sys/dev/usb/ubcmtp.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: ubcmtp.c,v 1.26 2024/05/23 03:21:09 jsg Exp $ */
2000800aeSjcs 
3000800aeSjcs /*
4000800aeSjcs  * Copyright (c) 2013-2014, joshua stein <jcs@openbsd.org>
5000800aeSjcs  *
66169a18dSjcs  * Permission to use, copy, modify, and distribute this software for any
76169a18dSjcs  * purpose with or without fee is hereby granted, provided that the above
86169a18dSjcs  * copyright notice and this permission notice appear in all copies.
9000800aeSjcs  *
106169a18dSjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
116169a18dSjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
126169a18dSjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
136169a18dSjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
146169a18dSjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
156169a18dSjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
166169a18dSjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17000800aeSjcs  */
18000800aeSjcs 
19000800aeSjcs /*
20000800aeSjcs  * Apple USB multitouch trackpad (Broadcom BCM5974) driver
21000800aeSjcs  *
22000800aeSjcs  * Protocol info/magic from bcm5974 Linux driver by Henrik Rydberg, et al.
23000800aeSjcs  */
24000800aeSjcs 
25000800aeSjcs #include <sys/param.h>
26000800aeSjcs #include <sys/device.h>
27000800aeSjcs #include <sys/errno.h>
28000800aeSjcs #include <sys/malloc.h>
29000800aeSjcs 
30000800aeSjcs #include <sys/systm.h>
31000800aeSjcs 
32000800aeSjcs #include <dev/usb/usb.h>
33000800aeSjcs #include <dev/usb/usbdi.h>
34000800aeSjcs #include <dev/usb/usbdevs.h>
35000800aeSjcs #include <dev/usb/usbhid.h>
36000800aeSjcs 
37000800aeSjcs #include <dev/wscons/wsconsio.h>
38000800aeSjcs #include <dev/wscons/wsmousevar.h>
39000800aeSjcs 
40000800aeSjcs /* #define UBCMTP_DEBUG */
41000800aeSjcs 
42000800aeSjcs #ifdef UBCMTP_DEBUG
43000800aeSjcs #define DPRINTF(x...)	do { printf(x); } while (0);
44000800aeSjcs #else
45000800aeSjcs #define DPRINTF(x...)
46000800aeSjcs #endif
47000800aeSjcs 
48000800aeSjcs /* magic to switch device from HID (default) mode into raw */
49000800aeSjcs #define UBCMTP_WELLSPRING_MODE_RAW	0x01
50000800aeSjcs #define UBCMTP_WELLSPRING_MODE_HID	0x08
51000800aeSjcs #define UBCMTP_WELLSPRING_MODE_LEN	8
5217875688Skettenis #define UBCMTP_WELLSPRING9_MODE_RAW	0x01
5317875688Skettenis #define UBCMTP_WELLSPRING9_MODE_HID	0x00
5417875688Skettenis #define UBCMTP_WELLSPRING9_MODE_LEN	2
55000800aeSjcs 
56000800aeSjcs struct ubcmtp_button {
57000800aeSjcs 	uint8_t		unused;
58000800aeSjcs 	uint8_t		button;
59000800aeSjcs 	uint8_t		rel_x;
60000800aeSjcs 	uint8_t		rel_y;
61000800aeSjcs };
62000800aeSjcs 
63000800aeSjcs struct ubcmtp_finger {
64000800aeSjcs 	uint16_t	origin;
65000800aeSjcs 	uint16_t	abs_x;
66000800aeSjcs 	uint16_t	abs_y;
67000800aeSjcs 	uint16_t	rel_x;
68000800aeSjcs 	uint16_t	rel_y;
69000800aeSjcs 	uint16_t	tool_major;
70000800aeSjcs 	uint16_t	tool_minor;
71000800aeSjcs 	uint16_t	orientation;
72000800aeSjcs 	uint16_t	touch_major;
73000800aeSjcs 	uint16_t	touch_minor;
7417875688Skettenis 	uint16_t	unused[2];
7517875688Skettenis 	uint16_t	pressure;
76000800aeSjcs 	uint16_t	multi;
77000800aeSjcs } __packed __attribute((aligned(2)));
78000800aeSjcs 
79000800aeSjcs #define UBCMTP_MAX_FINGERS	16
80000800aeSjcs #define UBCMTP_ALL_FINGER_SIZE	(UBCMTP_MAX_FINGERS * sizeof(struct ubcmtp_finger))
81000800aeSjcs 
82000800aeSjcs #define UBCMTP_TYPE1		1
83000800aeSjcs #define UBCMTP_TYPE1_TPOFF	(13 * sizeof(uint16_t))
84000800aeSjcs #define UBCMTP_TYPE1_TPLEN	UBCMTP_TYPE1_TPOFF + UBCMTP_ALL_FINGER_SIZE
85000800aeSjcs #define UBCMTP_TYPE1_TPIFACE	1
86000800aeSjcs #define UBCMTP_TYPE1_BTIFACE	2
87000800aeSjcs 
88000800aeSjcs #define UBCMTP_TYPE2		2
89000800aeSjcs #define UBCMTP_TYPE2_TPOFF	(15 * sizeof(uint16_t))
90000800aeSjcs #define UBCMTP_TYPE2_TPLEN	UBCMTP_TYPE2_TPOFF + UBCMTP_ALL_FINGER_SIZE
91000800aeSjcs #define UBCMTP_TYPE2_TPIFACE	1
92000800aeSjcs #define UBCMTP_TYPE2_BTOFF	15
93000800aeSjcs 
94000800aeSjcs #define UBCMTP_TYPE3		3
95000800aeSjcs #define UBCMTP_TYPE3_TPOFF	(19 * sizeof(uint16_t))
96000800aeSjcs #define UBCMTP_TYPE3_TPLEN	UBCMTP_TYPE3_TPOFF + UBCMTP_ALL_FINGER_SIZE
97000800aeSjcs #define UBCMTP_TYPE3_TPIFACE	2
98000800aeSjcs #define UBCMTP_TYPE3_BTOFF	23
99000800aeSjcs 
10017875688Skettenis #define UBCMTP_TYPE4		4
10117875688Skettenis #define UBCMTP_TYPE4_TPOFF	(24 * sizeof(uint16_t))
10217875688Skettenis #define UBCMTP_TYPE4_TPLEN	UBCMTP_TYPE4_TPOFF + UBCMTP_ALL_FINGER_SIZE
10317875688Skettenis #define UBCMTP_TYPE4_TPIFACE	2
10417875688Skettenis #define UBCMTP_TYPE4_BTOFF	31
105ab224d92Sjcs #define UBCMTP_TYPE4_FINGERPAD	(1 * sizeof(uint16_t))
10617875688Skettenis 
107000800aeSjcs #define UBCMTP_FINGER_ORIENT	16384
108000800aeSjcs #define UBCMTP_SN_PRESSURE	45
109000800aeSjcs #define UBCMTP_SN_WIDTH		25
110000800aeSjcs #define UBCMTP_SN_COORD		250
111000800aeSjcs #define UBCMTP_SN_ORIENT	10
112000800aeSjcs 
1130161d159Sbru /* Identify clickpads in ubcmtp_configure. */
1140161d159Sbru #define IS_CLICKPAD(ubcmtp_type) (ubcmtp_type != UBCMTP_TYPE1)
1150161d159Sbru 
1160161d159Sbru /* Use a constant, synaptics-compatible pressure value for now. */
1170161d159Sbru #define DEFAULT_PRESSURE	40
1180161d159Sbru 
119000800aeSjcs struct ubcmtp_limit {
120000800aeSjcs 	int limit;
121000800aeSjcs 	int min;
122000800aeSjcs 	int max;
123000800aeSjcs };
124000800aeSjcs 
125000800aeSjcs struct ubcmtp_dev {
126000800aeSjcs 	int vendor;			/* vendor */
127000800aeSjcs 	int ansi, iso, jis;		/* 3 types of product */
128000800aeSjcs 	int type;			/* 1 (normal) or 2 (integrated btn) */
129000800aeSjcs 	struct ubcmtp_limit l_pressure;	/* finger pressure */
130000800aeSjcs 	struct ubcmtp_limit l_width;	/* finger width */
131000800aeSjcs 	struct ubcmtp_limit l_x;
132000800aeSjcs 	struct ubcmtp_limit l_y;
133000800aeSjcs 	struct ubcmtp_limit l_orientation;
134000800aeSjcs };
135000800aeSjcs 
136680ee9d8Skn static const struct ubcmtp_dev ubcmtp_devices[] = {
137000800aeSjcs 	/* type 1 devices with separate buttons */
138000800aeSjcs 	{
139000800aeSjcs 		USB_VENDOR_APPLE,
140000800aeSjcs 		/* MacbookAir */
141000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING_ANSI,
142000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING_ISO,
143000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING_JIS,
144000800aeSjcs 		UBCMTP_TYPE1,
145000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 256 },
146000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
147000800aeSjcs 		{ UBCMTP_SN_COORD, -4824, 5342 },
148000800aeSjcs 		{ UBCMTP_SN_COORD, -172, 5820 },
149000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
150000800aeSjcs 	},
151000800aeSjcs 	{
152000800aeSjcs 		USB_VENDOR_APPLE,
153000800aeSjcs 		/* MacbookProPenryn */
154000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING2_ANSI,
155000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING2_ISO,
156000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING2_JIS,
157000800aeSjcs 		UBCMTP_TYPE1,
158000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 256 },
159000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
160000800aeSjcs 		{ UBCMTP_SN_COORD, -4824, 4824 },
161000800aeSjcs 		{ UBCMTP_SN_COORD, -172, 4290 },
162000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
163000800aeSjcs 	},
164000800aeSjcs 	/* type 2 devices with integrated buttons */
165000800aeSjcs 	{
166000800aeSjcs 		USB_VENDOR_APPLE,
167000800aeSjcs 		/* Macbook5,1 */
168000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING3_ANSI,
169000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING3_ISO,
170000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING3_JIS,
171000800aeSjcs 		UBCMTP_TYPE2,
172000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
173000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
174000800aeSjcs 		{ UBCMTP_SN_COORD, -4460, 5166 },
175000800aeSjcs 		{ UBCMTP_SN_COORD, -75, 6700 },
176000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
177000800aeSjcs 	},
178000800aeSjcs 	{
179000800aeSjcs 		USB_VENDOR_APPLE,
180000800aeSjcs 		/* MacbookAir3,1 */
181000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING4A_ANSI,
182000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING4A_ISO,
183000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING4A_JIS,
184000800aeSjcs 		UBCMTP_TYPE2,
185000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
186000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
187000800aeSjcs 		{ UBCMTP_SN_COORD, -4616, 5112 },
188000800aeSjcs 		{ UBCMTP_SN_COORD, -142, 5234 },
189000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
190000800aeSjcs 	},
191000800aeSjcs 	{
192000800aeSjcs 		USB_VENDOR_APPLE,
193000800aeSjcs 		/* MacbookAir3,2 */
194000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING4_ANSI,
195000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING4_ISO,
196000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING4_JIS,
197000800aeSjcs 		UBCMTP_TYPE2,
198000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
199000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
200000800aeSjcs 		{ UBCMTP_SN_COORD, -4620, 5140 },
201000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6600 },
202000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
203000800aeSjcs 	},
204000800aeSjcs 	{
205000800aeSjcs 		USB_VENDOR_APPLE,
206000800aeSjcs 		/* Macbook8 */
207000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING5_ANSI,
208000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING5_ISO,
209000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING5_JIS,
210000800aeSjcs 		UBCMTP_TYPE2,
211000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
212000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
213000800aeSjcs 		{ UBCMTP_SN_COORD, -4415, 5050 },
214000800aeSjcs 		{ UBCMTP_SN_COORD, -55, 6680 },
215000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
216000800aeSjcs 	},
217000800aeSjcs 	{
218000800aeSjcs 		USB_VENDOR_APPLE,
219000800aeSjcs 		/* Macbook8,2 */
220000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING5A_ANSI,
221000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING5A_ISO,
222000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING5A_JIS,
223000800aeSjcs 		UBCMTP_TYPE2,
224000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
225000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
226000800aeSjcs 		{ UBCMTP_SN_COORD, -4750, 5280 },
227000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6730 },
228000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
229000800aeSjcs 	},
230000800aeSjcs 	{
231000800aeSjcs 		USB_VENDOR_APPLE,
232000800aeSjcs 		/* MacbookAir4,2 */
233000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING6_ANSI,
234000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING6_ISO,
235000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING6_JIS,
236000800aeSjcs 		UBCMTP_TYPE2,
237000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
238000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
239000800aeSjcs 		{ UBCMTP_SN_COORD, -4620, 5140 },
240000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6600 },
241000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
242000800aeSjcs 	},
243000800aeSjcs 	{
244000800aeSjcs 		USB_VENDOR_APPLE,
245000800aeSjcs 		/* MacbookAir4,1 */
246000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING6A_ANSI,
247000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING6A_ISO,
248000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING6A_JIS,
249000800aeSjcs 		UBCMTP_TYPE2,
250000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
251000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
252000800aeSjcs 		{ UBCMTP_SN_COORD, -4620, 5140 },
253000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6600 },
254000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
255000800aeSjcs 	},
256000800aeSjcs 	{
257000800aeSjcs 		USB_VENDOR_APPLE,
258000800aeSjcs 		/* MacbookPro10,1 */
259000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING7_ANSI,
260000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING7_ISO,
261000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING7_JIS,
262000800aeSjcs 		UBCMTP_TYPE2,
263000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
264000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
265000800aeSjcs 		{ UBCMTP_SN_COORD, -4750, 5280 },
266000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6730 },
267000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
268000800aeSjcs 	},
269000800aeSjcs 	{
270000800aeSjcs 		USB_VENDOR_APPLE,
271000800aeSjcs 		/* MacbookPro10,2 */
272000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING7A_ANSI,
273000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING7A_ISO,
274000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING7A_JIS,
275000800aeSjcs 		UBCMTP_TYPE2,
276000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
277000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
278000800aeSjcs 		{ UBCMTP_SN_COORD, -4750, 5280 },
279000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6730 },
280000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
281000800aeSjcs 	},
282000800aeSjcs 	{
283000800aeSjcs 		USB_VENDOR_APPLE,
284000800aeSjcs 		/* MacbookAir6,1 */
285000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING8_ANSI,
286000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING8_ISO,
287000800aeSjcs 		USB_PRODUCT_APPLE_WELLSPRING8_JIS,
288000800aeSjcs 		UBCMTP_TYPE3,
289000800aeSjcs 		{ UBCMTP_SN_PRESSURE, 0, 300 },
290000800aeSjcs 		{ UBCMTP_SN_WIDTH, 0, 2048 },
291000800aeSjcs 		{ UBCMTP_SN_COORD, -4620, 5140 },
292000800aeSjcs 		{ UBCMTP_SN_COORD, -150, 6600 },
293000800aeSjcs 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
294000800aeSjcs 	},
29517875688Skettenis 	{
29617875688Skettenis 		USB_VENDOR_APPLE,
29717875688Skettenis 		/* MacbookPro12,1 */
29817875688Skettenis 		USB_PRODUCT_APPLE_WELLSPRING9_ANSI,
29917875688Skettenis 		USB_PRODUCT_APPLE_WELLSPRING9_ISO,
30017875688Skettenis 		USB_PRODUCT_APPLE_WELLSPRING9_JIS,
30117875688Skettenis 		UBCMTP_TYPE4,
30217875688Skettenis 		{ UBCMTP_SN_PRESSURE, 0, 300 },
30317875688Skettenis 		{ UBCMTP_SN_WIDTH, 0, 2048 },
30417875688Skettenis 		{ UBCMTP_SN_COORD, -4828, 5345 },
30517875688Skettenis 		{ UBCMTP_SN_COORD, -203, 6803 },
30617875688Skettenis 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
30717875688Skettenis 	},
308000800aeSjcs };
309000800aeSjcs 
3109310c18aSbru static struct wsmouse_param ubcmtp_wsmousecfg[] = {
3119310c18aSbru 	{ WSMOUSECFG_MTBTN_MAXDIST, 0 }, /* 0: Compute a default value. */
3129310c18aSbru };
3139310c18aSbru 
314000800aeSjcs struct ubcmtp_softc {
315000800aeSjcs 	struct device		sc_dev;		/* base device */
316000800aeSjcs 
317680ee9d8Skn 	const struct ubcmtp_dev	*dev_type;
318000800aeSjcs 
319000800aeSjcs 	struct usbd_device	*sc_udev;
320000800aeSjcs 	struct device		*sc_wsmousedev;
321000800aeSjcs 
322000800aeSjcs 	struct usbd_interface	*sc_tp_iface;	/* trackpad interface */
323000800aeSjcs 	struct usbd_pipe	*sc_tp_pipe;	/* trackpad pipe */
324000800aeSjcs 	int			sc_tp_epaddr;	/* endpoint addr */
325000800aeSjcs 	int			tp_maxlen;	/* max size of tp data */
326000800aeSjcs 	int			tp_offset;	/* finger offset into data */
327ab224d92Sjcs 	int			tp_fingerpad;	/* padding between finger data */
328000800aeSjcs 	uint8_t			*tp_pkt;
329000800aeSjcs 
330000800aeSjcs 	struct usbd_interface	*sc_bt_iface;	/* button interface */
331000800aeSjcs 	struct usbd_pipe	*sc_bt_pipe;	/* button pipe */
332000800aeSjcs 	int			sc_bt_epaddr;	/* endpoint addr */
333000800aeSjcs 	int			bt_maxlen;	/* max size of button data */
334000800aeSjcs 	uint8_t			*bt_pkt;
335000800aeSjcs 
336000800aeSjcs 	uint32_t		sc_status;
337000800aeSjcs #define UBCMTP_ENABLED		1
338000800aeSjcs 
3390161d159Sbru 	struct mtpoint		frame[UBCMTP_MAX_FINGERS];
3400161d159Sbru 	int			contacts;
341000800aeSjcs 	int			btn;
342000800aeSjcs };
343000800aeSjcs 
344000800aeSjcs int	ubcmtp_enable(void *);
345000800aeSjcs void	ubcmtp_disable(void *);
346000800aeSjcs int	ubcmtp_ioctl(void *, unsigned long, caddr_t, int, struct proc *);
347000800aeSjcs int	ubcmtp_raw_mode(struct ubcmtp_softc *, int);
348000800aeSjcs int	ubcmtp_setup_pipes(struct ubcmtp_softc *);
349000800aeSjcs void	ubcmtp_bt_intr(struct usbd_xfer *, void *, usbd_status);
350000800aeSjcs void	ubcmtp_tp_intr(struct usbd_xfer *, void *, usbd_status);
351000800aeSjcs 
352000800aeSjcs int	ubcmtp_match(struct device *, void *, void *);
353000800aeSjcs void	ubcmtp_attach(struct device *, struct device *, void *);
354000800aeSjcs int	ubcmtp_detach(struct device *, int);
355000800aeSjcs int	ubcmtp_activate(struct device *, int);
3560161d159Sbru int	ubcmtp_configure(struct ubcmtp_softc *);
357000800aeSjcs 
358000800aeSjcs const struct wsmouse_accessops ubcmtp_accessops = {
359000800aeSjcs 	ubcmtp_enable,
360000800aeSjcs 	ubcmtp_ioctl,
361000800aeSjcs 	ubcmtp_disable,
362000800aeSjcs };
363000800aeSjcs 
364000800aeSjcs struct cfdriver ubcmtp_cd = {
365000800aeSjcs 	NULL, "ubcmtp", DV_DULL
366000800aeSjcs };
367000800aeSjcs 
368000800aeSjcs const struct cfattach ubcmtp_ca = {
369000800aeSjcs 	sizeof(struct ubcmtp_softc), ubcmtp_match, ubcmtp_attach, ubcmtp_detach,
370000800aeSjcs 	ubcmtp_activate,
371000800aeSjcs };
372000800aeSjcs 
373000800aeSjcs int
ubcmtp_match(struct device * parent,void * match,void * aux)374000800aeSjcs ubcmtp_match(struct device *parent, void *match, void *aux)
375000800aeSjcs {
376000800aeSjcs 	struct usb_attach_arg *uaa = aux;
377000800aeSjcs 	usb_interface_descriptor_t *id;
378000800aeSjcs 	int i;
379000800aeSjcs 
3808b3948dcSjcs 	if (uaa->iface == NULL)
381000800aeSjcs 		return (UMATCH_NONE);
382000800aeSjcs 
383000800aeSjcs 	for (i = 0; i < nitems(ubcmtp_devices); i++) {
3848b3948dcSjcs 		if (uaa->vendor == ubcmtp_devices[i].vendor && (
3858b3948dcSjcs 		    uaa->product == ubcmtp_devices[i].ansi ||
3868b3948dcSjcs 		    uaa->product == ubcmtp_devices[i].iso ||
3878b3948dcSjcs 		    uaa->product == ubcmtp_devices[i].jis)) {
3882bdc593dSmpi 			if (uaa->nifaces < 2)
3892bdc593dSmpi 				return (UMATCH_NONE);
3902bdc593dSmpi 			if ((ubcmtp_devices[i].type != UBCMTP_TYPE2) &&
3912bdc593dSmpi 			    (uaa->nifaces < 3))
3922bdc593dSmpi 				return (UMATCH_NONE);
3932bdc593dSmpi 
394000800aeSjcs 			/*
395000800aeSjcs 			 * The USB keyboard/mouse device will have one keyboard
396000800aeSjcs 			 * HID and two mouse HIDs, though only one will have a
397000800aeSjcs 			 * protocol of mouse -- we only want to take control of
398000800aeSjcs 			 * that one.
399000800aeSjcs 			 */
400000800aeSjcs 			id = usbd_get_interface_descriptor(uaa->iface);
401000800aeSjcs 			if (id->bInterfaceProtocol == UIPROTO_BOOT_MOUSE)
402000800aeSjcs 				return (UMATCH_VENDOR_PRODUCT_CONF_IFACE);
403000800aeSjcs 		}
404000800aeSjcs 	}
405000800aeSjcs 
406000800aeSjcs 	return (UMATCH_NONE);
407000800aeSjcs }
408000800aeSjcs 
409000800aeSjcs void
ubcmtp_attach(struct device * parent,struct device * self,void * aux)410000800aeSjcs ubcmtp_attach(struct device *parent, struct device *self, void *aux)
411000800aeSjcs {
412000800aeSjcs 	struct ubcmtp_softc *sc = (struct ubcmtp_softc *)self;
413000800aeSjcs 	struct usb_attach_arg *uaa = aux;
414000800aeSjcs 	struct usbd_device *dev = uaa->device;
415000800aeSjcs 	struct wsmousedev_attach_args a;
416000800aeSjcs 	usb_device_descriptor_t *udd;
417000800aeSjcs 	int i;
418000800aeSjcs 
419000800aeSjcs 	sc->sc_udev = uaa->device;
420000800aeSjcs 	sc->sc_status = 0;
421ab224d92Sjcs 	sc->tp_fingerpad = 0;
422000800aeSjcs 
423000800aeSjcs 	if ((udd = usbd_get_device_descriptor(dev)) == NULL) {
424000800aeSjcs 		printf("ubcmtp: failed getting device descriptor\n");
425000800aeSjcs 		return;
426000800aeSjcs 	}
427000800aeSjcs 
428000800aeSjcs 	for (i = 0; i < nitems(ubcmtp_devices); i++) {
429000800aeSjcs 		if (uaa->vendor == ubcmtp_devices[i].vendor && (
430000800aeSjcs 		    uaa->product == ubcmtp_devices[i].ansi ||
431000800aeSjcs 		    uaa->product == ubcmtp_devices[i].iso ||
432000800aeSjcs 		    uaa->product == ubcmtp_devices[i].jis)) {
433000800aeSjcs 			sc->dev_type = &ubcmtp_devices[i];
434000800aeSjcs 			DPRINTF("%s: attached to 0x%x/0x%x type %d\n",
435000800aeSjcs 			    sc->sc_dev.dv_xname, uaa->vendor, uaa->product,
436000800aeSjcs 			    sc->dev_type->type);
437000800aeSjcs 			break;
438000800aeSjcs 		}
439000800aeSjcs 	}
440000800aeSjcs 
441000800aeSjcs 	if (sc->dev_type == NULL) {
442000800aeSjcs 		/* how did we match then? */
443000800aeSjcs 		printf("%s: failed looking up device in table\n",
444000800aeSjcs 		    sc->sc_dev.dv_xname);
445000800aeSjcs 		return;
446000800aeSjcs 	}
447000800aeSjcs 
448000800aeSjcs 	switch (sc->dev_type->type) {
449000800aeSjcs 	case UBCMTP_TYPE1:
450000800aeSjcs 		sc->tp_maxlen = UBCMTP_TYPE1_TPLEN;
451000800aeSjcs 		sc->tp_offset = UBCMTP_TYPE1_TPOFF;
4522bdc593dSmpi 		sc->sc_tp_iface = uaa->ifaces[UBCMTP_TYPE1_TPIFACE];
4532bdc593dSmpi 		usbd_claim_iface(sc->sc_udev, UBCMTP_TYPE1_TPIFACE);
454000800aeSjcs 
455000800aeSjcs 		/* button offsets */
456000800aeSjcs 		sc->bt_maxlen = sizeof(struct ubcmtp_button);
4572bdc593dSmpi 		sc->sc_bt_iface = uaa->ifaces[UBCMTP_TYPE1_BTIFACE];
4582bdc593dSmpi 		usbd_claim_iface(sc->sc_udev, UBCMTP_TYPE1_BTIFACE);
459000800aeSjcs 		break;
460000800aeSjcs 
461000800aeSjcs 	case UBCMTP_TYPE2:
462000800aeSjcs 		sc->tp_maxlen = UBCMTP_TYPE2_TPLEN;
463000800aeSjcs 		sc->tp_offset = UBCMTP_TYPE2_TPOFF;
4642bdc593dSmpi 		sc->sc_tp_iface = uaa->ifaces[UBCMTP_TYPE2_TPIFACE];
4652bdc593dSmpi 		usbd_claim_iface(sc->sc_udev, UBCMTP_TYPE2_TPIFACE);
466000800aeSjcs 		break;
467000800aeSjcs 
468000800aeSjcs 	case UBCMTP_TYPE3:
469000800aeSjcs 		sc->tp_maxlen = UBCMTP_TYPE3_TPLEN;
470000800aeSjcs 		sc->tp_offset = UBCMTP_TYPE3_TPOFF;
4712bdc593dSmpi 		sc->sc_tp_iface = uaa->ifaces[UBCMTP_TYPE3_TPIFACE];
4722bdc593dSmpi 		usbd_claim_iface(sc->sc_udev, UBCMTP_TYPE3_TPIFACE);
473000800aeSjcs 		break;
47417875688Skettenis 
47517875688Skettenis 	case UBCMTP_TYPE4:
47617875688Skettenis 		sc->tp_maxlen = UBCMTP_TYPE4_TPLEN;
47717875688Skettenis 		sc->tp_offset = UBCMTP_TYPE4_TPOFF;
4782bdc593dSmpi 		sc->sc_tp_iface = uaa->ifaces[UBCMTP_TYPE4_TPIFACE];
479ab224d92Sjcs 		sc->tp_fingerpad = UBCMTP_TYPE4_FINGERPAD;
4802bdc593dSmpi 		usbd_claim_iface(sc->sc_udev, UBCMTP_TYPE4_TPIFACE);
48117875688Skettenis 		break;
482000800aeSjcs 	}
483000800aeSjcs 
484000800aeSjcs 	a.accessops = &ubcmtp_accessops;
485000800aeSjcs 	a.accesscookie = sc;
486000800aeSjcs 
487000800aeSjcs 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
4880161d159Sbru 	if (sc->sc_wsmousedev != NULL && ubcmtp_configure(sc))
4890161d159Sbru 		ubcmtp_disable(sc);
490000800aeSjcs }
491000800aeSjcs 
492000800aeSjcs int
ubcmtp_detach(struct device * self,int flags)493000800aeSjcs ubcmtp_detach(struct device *self, int flags)
494000800aeSjcs {
495000800aeSjcs 	struct ubcmtp_softc *sc = (struct ubcmtp_softc *)self;
496000800aeSjcs 	int ret = 0;
497000800aeSjcs 
498000800aeSjcs 	if (sc->sc_wsmousedev != NULL)
499000800aeSjcs 		ret = config_detach(sc->sc_wsmousedev, flags);
500000800aeSjcs 
501000800aeSjcs 	return (ret);
502000800aeSjcs }
503000800aeSjcs 
504000800aeSjcs int
ubcmtp_activate(struct device * self,int act)505000800aeSjcs ubcmtp_activate(struct device *self, int act)
506000800aeSjcs {
507000800aeSjcs 	struct ubcmtp_softc *sc = (struct ubcmtp_softc *)self;
508bfaff836Skettenis 	int rv = 0;
509000800aeSjcs 
510000800aeSjcs 	if (act == DVACT_DEACTIVATE) {
511000800aeSjcs 		if (sc->sc_wsmousedev != NULL)
512bfaff836Skettenis 			rv = config_deactivate(sc->sc_wsmousedev);
513744b7621Sjcs 		usbd_deactivate(sc->sc_udev);
514000800aeSjcs 	}
515bfaff836Skettenis 
516bfaff836Skettenis 	return (rv);
517000800aeSjcs }
518000800aeSjcs 
519000800aeSjcs int
ubcmtp_configure(struct ubcmtp_softc * sc)5200161d159Sbru ubcmtp_configure(struct ubcmtp_softc *sc)
5210161d159Sbru {
5220161d159Sbru 	struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
5230161d159Sbru 
52457f285d3Sjcs 	hw->type = WSMOUSE_TYPE_TOUCHPAD;
5250161d159Sbru 	hw->hw_type = (IS_CLICKPAD(sc->dev_type->type)
5260161d159Sbru 	    ? WSMOUSEHW_CLICKPAD : WSMOUSEHW_TOUCHPAD);
5270161d159Sbru 	hw->x_min = sc->dev_type->l_x.min;
5280161d159Sbru 	hw->x_max = sc->dev_type->l_x.max;
5290161d159Sbru 	hw->y_min = sc->dev_type->l_y.min;
5300161d159Sbru 	hw->y_max = sc->dev_type->l_y.max;
5310161d159Sbru 	hw->mt_slots = UBCMTP_MAX_FINGERS;
5320161d159Sbru 	hw->flags = WSMOUSEHW_MT_TRACKING;
5330161d159Sbru 
5349310c18aSbru 	return wsmouse_configure(sc->sc_wsmousedev,
5359310c18aSbru 	    ubcmtp_wsmousecfg, nitems(ubcmtp_wsmousecfg));
5360161d159Sbru }
5370161d159Sbru 
5380161d159Sbru int
ubcmtp_enable(void * v)539000800aeSjcs ubcmtp_enable(void *v)
540000800aeSjcs {
541000800aeSjcs 	struct ubcmtp_softc *sc = v;
542000800aeSjcs 
543000800aeSjcs 	if (sc->sc_status & UBCMTP_ENABLED)
544000800aeSjcs 		return (EBUSY);
545000800aeSjcs 
546744b7621Sjcs 	if (usbd_is_dying(sc->sc_udev))
547744b7621Sjcs 		return (EIO);
548744b7621Sjcs 
549000800aeSjcs 	if (ubcmtp_raw_mode(sc, 1) != 0) {
550000800aeSjcs 		printf("%s: failed to enter raw mode\n", sc->sc_dev.dv_xname);
551000800aeSjcs 		return (1);
552000800aeSjcs 	}
553000800aeSjcs 
554000800aeSjcs 	if (ubcmtp_setup_pipes(sc) == 0) {
555000800aeSjcs 		sc->sc_status |= UBCMTP_ENABLED;
556000800aeSjcs 		return (0);
557000800aeSjcs 	} else
558000800aeSjcs 		return (1);
559000800aeSjcs }
560000800aeSjcs 
561000800aeSjcs void
ubcmtp_disable(void * v)562000800aeSjcs ubcmtp_disable(void *v)
563000800aeSjcs {
564000800aeSjcs 	struct ubcmtp_softc *sc = v;
565000800aeSjcs 
566744b7621Sjcs 	if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED))
567000800aeSjcs 		return;
568000800aeSjcs 
569000800aeSjcs 	sc->sc_status &= ~UBCMTP_ENABLED;
570000800aeSjcs 
571000800aeSjcs 	ubcmtp_raw_mode(sc, 0);
572000800aeSjcs 
573000800aeSjcs 	if (sc->sc_tp_pipe != NULL) {
574000800aeSjcs 		usbd_close_pipe(sc->sc_tp_pipe);
575000800aeSjcs 		sc->sc_tp_pipe = NULL;
576000800aeSjcs 	}
577000800aeSjcs 	if (sc->sc_bt_pipe != NULL) {
578000800aeSjcs 		usbd_close_pipe(sc->sc_bt_pipe);
579000800aeSjcs 		sc->sc_bt_pipe = NULL;
580000800aeSjcs 	}
581000800aeSjcs 
582000800aeSjcs 	if (sc->tp_pkt != NULL) {
583234dfda1Sderaadt 		free(sc->tp_pkt, M_USBDEV, sc->tp_maxlen);
584000800aeSjcs 		sc->tp_pkt = NULL;
585000800aeSjcs 	}
586000800aeSjcs 	if (sc->bt_pkt != NULL) {
587234dfda1Sderaadt 		free(sc->bt_pkt, M_USBDEV, sc->bt_maxlen);
588000800aeSjcs 		sc->bt_pkt = NULL;
589000800aeSjcs 	}
590000800aeSjcs }
591000800aeSjcs 
592000800aeSjcs int
ubcmtp_ioctl(void * v,unsigned long cmd,caddr_t data,int flag,struct proc * p)593000800aeSjcs ubcmtp_ioctl(void *v, unsigned long cmd, caddr_t data, int flag, struct proc *p)
594000800aeSjcs {
595000800aeSjcs 	struct ubcmtp_softc *sc = v;
596000800aeSjcs 	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
597000800aeSjcs 	int wsmode;
598000800aeSjcs 
5993c7c9148Sjcs 	DPRINTF("%s: in %s with cmd 0x%lx\n", sc->sc_dev.dv_xname, __func__,
600000800aeSjcs 	    cmd);
601000800aeSjcs 
602000800aeSjcs 	switch (cmd) {
60357f285d3Sjcs 	case WSMOUSEIO_GTYPE: {
60457f285d3Sjcs 		struct wsmousehw *hw = wsmouse_get_hw(sc->sc_wsmousedev);
60557f285d3Sjcs 		*(u_int *)data = hw->type;
606000800aeSjcs 		break;
60757f285d3Sjcs 	}
608000800aeSjcs 
609000800aeSjcs 	case WSMOUSEIO_GCALIBCOORDS:
610000800aeSjcs 		wsmc->minx = sc->dev_type->l_x.min;
611000800aeSjcs 		wsmc->maxx = sc->dev_type->l_x.max;
612000800aeSjcs 		wsmc->miny = sc->dev_type->l_y.min;
613000800aeSjcs 		wsmc->maxy = sc->dev_type->l_y.max;
614000800aeSjcs 		wsmc->swapxy = 0;
615000800aeSjcs 		wsmc->resx = 0;
616000800aeSjcs 		wsmc->resy = 0;
617000800aeSjcs 		break;
618000800aeSjcs 
619000800aeSjcs 	case WSMOUSEIO_SETMODE:
620000800aeSjcs 		wsmode = *(u_int *)data;
621000800aeSjcs 		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
622000800aeSjcs 			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
623000800aeSjcs 			    wsmode);
624000800aeSjcs 			return (EINVAL);
625000800aeSjcs 		}
6260161d159Sbru 		wsmouse_set_mode(sc->sc_wsmousedev, wsmode);
627000800aeSjcs 
628000800aeSjcs 		DPRINTF("%s: changing mode to %s\n",
629000800aeSjcs 		    sc->sc_dev.dv_xname, (wsmode == WSMOUSE_COMPAT ? "compat" :
630000800aeSjcs 		    "native"));
631000800aeSjcs 
632000800aeSjcs 		break;
633000800aeSjcs 
634000800aeSjcs 	default:
635000800aeSjcs 		return (-1);
636000800aeSjcs 	}
637000800aeSjcs 
638000800aeSjcs 	return (0);
639000800aeSjcs }
640000800aeSjcs 
641000800aeSjcs int
ubcmtp_raw_mode(struct ubcmtp_softc * sc,int enable)642000800aeSjcs ubcmtp_raw_mode(struct ubcmtp_softc *sc, int enable)
643000800aeSjcs {
6444496cc6bSjcs 	usb_device_request_t r;
645000800aeSjcs 	usbd_status err;
646000800aeSjcs 	uint8_t buf[8];
647000800aeSjcs 
648000800aeSjcs 	/* type 3 has no raw mode */
64917875688Skettenis 	if (sc->dev_type->type == UBCMTP_TYPE3)
650000800aeSjcs 		return (0);
651000800aeSjcs 
6524496cc6bSjcs 	r.bRequest = UR_GET_REPORT;
6534496cc6bSjcs 	r.bmRequestType = UT_READ_CLASS_INTERFACE;
65417875688Skettenis 	if (sc->dev_type->type < UBCMTP_TYPE4) {
6554496cc6bSjcs 		USETW2(r.wValue, UHID_FEATURE_REPORT, 0);
6564496cc6bSjcs 		USETW(r.wIndex, 0);
6574496cc6bSjcs 		USETW(r.wLength, UBCMTP_WELLSPRING_MODE_LEN);
65817875688Skettenis 	} else {
65917875688Skettenis 		USETW2(r.wValue, UHID_FEATURE_REPORT, 2);
66017875688Skettenis 		USETW(r.wIndex, 2);
66117875688Skettenis 		USETW(r.wLength, UBCMTP_WELLSPRING9_MODE_LEN);
66217875688Skettenis 	}
6634496cc6bSjcs 
6644496cc6bSjcs 	err = usbd_do_request(sc->sc_udev, &r, buf);
665000800aeSjcs 	if (err != USBD_NORMAL_COMPLETION) {
666000800aeSjcs 		printf("%s: %s: failed to get feature report\n",
667000800aeSjcs 		    sc->sc_dev.dv_xname, __func__);
668000800aeSjcs 		return (err);
669000800aeSjcs 	}
670000800aeSjcs 
67117875688Skettenis 	/* toggle magic byte and write everything back */
67217875688Skettenis 	if (sc->dev_type->type < UBCMTP_TYPE4)
673000800aeSjcs 		buf[0] = (enable ? UBCMTP_WELLSPRING_MODE_RAW :
674000800aeSjcs 		    UBCMTP_WELLSPRING_MODE_HID);
67517875688Skettenis 	else
67617875688Skettenis 		buf[1] = (enable ? UBCMTP_WELLSPRING9_MODE_RAW :
67717875688Skettenis 		    UBCMTP_WELLSPRING9_MODE_HID);
678000800aeSjcs 
6794496cc6bSjcs 	r.bRequest = UR_SET_REPORT;
6804496cc6bSjcs 	r.bmRequestType = UT_WRITE_CLASS_INTERFACE;
68117875688Skettenis 	if (sc->dev_type->type < UBCMTP_TYPE4) {
6824496cc6bSjcs 		USETW2(r.wValue, UHID_FEATURE_REPORT, 0);
6834496cc6bSjcs 		USETW(r.wIndex, 0);
6844496cc6bSjcs 		USETW(r.wLength, UBCMTP_WELLSPRING_MODE_LEN);
68517875688Skettenis 	} else {
68617875688Skettenis 		USETW2(r.wValue, UHID_FEATURE_REPORT, 2);
68717875688Skettenis 		USETW(r.wIndex, 2);
68817875688Skettenis 		USETW(r.wLength, UBCMTP_WELLSPRING9_MODE_LEN);
68917875688Skettenis 	}
6904496cc6bSjcs 
6914496cc6bSjcs 	err = usbd_do_request(sc->sc_udev, &r, buf);
692000800aeSjcs 	if (err != USBD_NORMAL_COMPLETION) {
693000800aeSjcs 		printf("%s: %s: failed to toggle raw mode\n",
694000800aeSjcs 		    sc->sc_dev.dv_xname, __func__);
695000800aeSjcs 		return (err);
696000800aeSjcs 	}
697000800aeSjcs 
698000800aeSjcs 	return (0);
699000800aeSjcs }
700000800aeSjcs 
701000800aeSjcs int
ubcmtp_setup_pipes(struct ubcmtp_softc * sc)702000800aeSjcs ubcmtp_setup_pipes(struct ubcmtp_softc *sc)
703000800aeSjcs {
704000800aeSjcs 	usbd_status err;
705000800aeSjcs 	usb_endpoint_descriptor_t *ed;
706000800aeSjcs 
707000800aeSjcs 	if (sc->dev_type->type == UBCMTP_TYPE1) {
708000800aeSjcs 		/* setup physical button pipe */
709000800aeSjcs 
710000800aeSjcs 		ed = usbd_interface2endpoint_descriptor(sc->sc_bt_iface, 0);
711000800aeSjcs 		if (ed == NULL) {
712000800aeSjcs 			printf("%s: failed getting button endpoint descriptor\n",
713000800aeSjcs 			    sc->sc_dev.dv_xname);
714000800aeSjcs 			goto fail1;
715000800aeSjcs 		}
716000800aeSjcs 		sc->sc_bt_epaddr = ed->bEndpointAddress;
717000800aeSjcs 		sc->bt_pkt = malloc(sc->bt_maxlen, M_USBDEV, M_WAITOK);
718000800aeSjcs 		if (sc->bt_pkt == NULL)
719000800aeSjcs 			goto fail1;
720000800aeSjcs 
721000800aeSjcs 		DPRINTF("%s: button iface at 0x%x, max size %d\n",
722000800aeSjcs 		    sc->sc_dev.dv_xname, sc->sc_bt_epaddr, sc->bt_maxlen);
723000800aeSjcs 
724000800aeSjcs 		err = usbd_open_pipe_intr(sc->sc_bt_iface, sc->sc_bt_epaddr,
725000800aeSjcs 		    USBD_SHORT_XFER_OK, &sc->sc_bt_pipe, sc, sc->bt_pkt,
726000800aeSjcs 		    sc->bt_maxlen, ubcmtp_bt_intr, USBD_DEFAULT_INTERVAL);
727000800aeSjcs 		if (err != USBD_NORMAL_COMPLETION) {
728000800aeSjcs 			printf("%s: failed opening button pipe\n",
729000800aeSjcs 			    sc->sc_dev.dv_xname);
730000800aeSjcs 			goto fail1;
731000800aeSjcs 		}
732000800aeSjcs 	}
733000800aeSjcs 
734000800aeSjcs 	/* setup trackpad data pipe */
735000800aeSjcs 
736000800aeSjcs 	ed = usbd_interface2endpoint_descriptor(sc->sc_tp_iface, 0);
737000800aeSjcs 	if (ed == NULL) {
738000800aeSjcs 		printf("%s: failed getting trackpad data endpoint descriptor\n",
739000800aeSjcs 		    sc->sc_dev.dv_xname);
740000800aeSjcs 		goto fail2;
741000800aeSjcs 	}
742000800aeSjcs 	sc->sc_tp_epaddr = ed->bEndpointAddress;
743000800aeSjcs 	sc->tp_pkt = malloc(sc->tp_maxlen, M_USBDEV, M_WAITOK);
744000800aeSjcs 	if (sc->tp_pkt == NULL)
745000800aeSjcs 		goto fail2;
746000800aeSjcs 
747000800aeSjcs 	DPRINTF("%s: trackpad data iface at 0x%x, max size %d\n",
748000800aeSjcs 	    sc->sc_dev.dv_xname, sc->sc_tp_epaddr, sc->tp_maxlen);
749000800aeSjcs 
750000800aeSjcs 	err = usbd_open_pipe_intr(sc->sc_tp_iface, sc->sc_tp_epaddr,
751000800aeSjcs 	    USBD_SHORT_XFER_OK, &sc->sc_tp_pipe, sc, sc->tp_pkt, sc->tp_maxlen,
752000800aeSjcs 	    ubcmtp_tp_intr, USBD_DEFAULT_INTERVAL);
753000800aeSjcs 	if (err != USBD_NORMAL_COMPLETION) {
754000800aeSjcs 		printf("%s: error opening trackpad data pipe\n",
755000800aeSjcs 		    sc->sc_dev.dv_xname);
756000800aeSjcs 		goto fail2;
757000800aeSjcs 	}
758000800aeSjcs 
759000800aeSjcs 	return (0);
760000800aeSjcs 
761000800aeSjcs fail2:
762f88cb03eSmglocker 	if (sc->sc_tp_pipe != NULL)
763000800aeSjcs 		usbd_close_pipe(sc->sc_tp_pipe);
764000800aeSjcs 	if (sc->tp_pkt != NULL)
765234dfda1Sderaadt 		free(sc->tp_pkt, M_USBDEV, sc->tp_maxlen);
766000800aeSjcs fail1:
767f88cb03eSmglocker 	if (sc->sc_bt_pipe != NULL)
768000800aeSjcs 		usbd_close_pipe(sc->sc_bt_pipe);
769000800aeSjcs 	if (sc->bt_pkt != NULL)
770234dfda1Sderaadt 		free(sc->bt_pkt, M_USBDEV, sc->bt_maxlen);
771000800aeSjcs 
772000800aeSjcs 	return (1);
773000800aeSjcs }
774000800aeSjcs 
775000800aeSjcs void
ubcmtp_tp_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)776000800aeSjcs ubcmtp_tp_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
777000800aeSjcs {
778000800aeSjcs 	struct ubcmtp_softc *sc = priv;
779ab224d92Sjcs 	struct ubcmtp_finger *finger;
780000800aeSjcs 	u_int32_t pktlen;
781ab224d92Sjcs 	int off, s, btn, contacts = 0;
782000800aeSjcs 
783744b7621Sjcs 	if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED))
784000800aeSjcs 		return;
785000800aeSjcs 
786000800aeSjcs 	if (status != USBD_NORMAL_COMPLETION) {
787000800aeSjcs 		DPRINTF("%s: %s with status 0x%x\n", sc->sc_dev.dv_xname,
78825041f19Sjsg 		    __func__, status);
789000800aeSjcs 
790000800aeSjcs 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
791000800aeSjcs 			return;
792000800aeSjcs 		if (status == USBD_STALLED)
793000800aeSjcs 			usbd_clear_endpoint_stall_async(sc->sc_tp_pipe);
794000800aeSjcs 		return;
795000800aeSjcs 	}
796000800aeSjcs 
797000800aeSjcs 	usbd_get_xfer_status(xfer, NULL, NULL, &pktlen, NULL);
798000800aeSjcs 
799000800aeSjcs 	if (sc->tp_pkt == NULL || pktlen < sc->tp_offset)
800000800aeSjcs 		return;
801000800aeSjcs 
8020161d159Sbru 	contacts = 0;
803ab224d92Sjcs 	for (off = sc->tp_offset; off < pktlen;
804ab224d92Sjcs 	    off += (sizeof(struct ubcmtp_finger) + sc->tp_fingerpad)) {
805ab224d92Sjcs 		finger = (struct ubcmtp_finger *)(sc->tp_pkt + off);
806ab224d92Sjcs 
807ab224d92Sjcs 		if ((int16_t)letoh16(finger->touch_major) == 0)
8080161d159Sbru 			continue; /* finger lifted */
809ab224d92Sjcs 
810ab224d92Sjcs 		sc->frame[contacts].x = (int16_t)letoh16(finger->abs_x);
811ab224d92Sjcs 		sc->frame[contacts].y = (int16_t)letoh16(finger->abs_y);
8120161d159Sbru 		sc->frame[contacts].pressure = DEFAULT_PRESSURE;
8130161d159Sbru 		contacts++;
814000800aeSjcs 	}
815000800aeSjcs 
8160161d159Sbru 	btn = sc->btn;
817000800aeSjcs 	if (sc->dev_type->type == UBCMTP_TYPE2)
8180161d159Sbru 		sc->btn = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE2_BTOFF]));
819000800aeSjcs 	else if (sc->dev_type->type == UBCMTP_TYPE3)
8200161d159Sbru 		sc->btn = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE3_BTOFF]));
82117875688Skettenis 	else if (sc->dev_type->type == UBCMTP_TYPE4)
8220161d159Sbru 		sc->btn = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE4_BTOFF]));
823000800aeSjcs 
8240161d159Sbru 	if (contacts || sc->contacts || sc->btn != btn) {
8250161d159Sbru 		sc->contacts = contacts;
826000800aeSjcs 		s = spltty();
8270161d159Sbru 		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
8280161d159Sbru 		wsmouse_mtframe(sc->sc_wsmousedev, sc->frame, contacts);
8290161d159Sbru 		wsmouse_input_sync(sc->sc_wsmousedev);
830000800aeSjcs 		splx(s);
831000800aeSjcs 	}
832000800aeSjcs }
833000800aeSjcs 
834000800aeSjcs /* hardware button interrupt */
835000800aeSjcs void
ubcmtp_bt_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)836000800aeSjcs ubcmtp_bt_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
837000800aeSjcs {
838000800aeSjcs 	struct ubcmtp_softc *sc = priv;
839000800aeSjcs 	struct ubcmtp_button *pkt;
840000800aeSjcs 	u_int32_t len;
841000800aeSjcs 
842744b7621Sjcs 	if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED))
843000800aeSjcs 		return;
844000800aeSjcs 
845000800aeSjcs 	if (status != USBD_NORMAL_COMPLETION) {
8465a9f566eSmpi 		DPRINTF("%s: %s with status 0x%x\n", sc->sc_dev.dv_xname,
8475a9f566eSmpi 		    __func__, status);
848000800aeSjcs 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
849000800aeSjcs 			return;
850000800aeSjcs 		if (status == USBD_STALLED)
851000800aeSjcs 			usbd_clear_endpoint_stall_async(sc->sc_tp_pipe);
852000800aeSjcs 		return;
853000800aeSjcs 	}
854000800aeSjcs 
855000800aeSjcs 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
856000800aeSjcs 
857000800aeSjcs 	if (sc->bt_pkt == NULL || len < sizeof(struct ubcmtp_button))
858000800aeSjcs 		return;
859000800aeSjcs 
860000800aeSjcs 	pkt = (struct ubcmtp_button *)(sc->bt_pkt);
861000800aeSjcs 
862000800aeSjcs 	DPRINTF("%s: button interrupt (%d, %d, %d, %d)", sc->sc_dev.dv_xname,
863000800aeSjcs 	    pkt->unused, pkt->button, pkt->rel_x, pkt->rel_y);
864000800aeSjcs 
865000800aeSjcs 	if (pkt->button != sc->btn) {
866000800aeSjcs 		sc->btn = pkt->button;
8675ad8c6a9Sbru 		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
8685ad8c6a9Sbru 		wsmouse_input_sync(sc->sc_wsmousedev);
869000800aeSjcs 	}
870000800aeSjcs }
871