xref: /openbsd-src/sys/dev/usb/ubcmtp.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: ubcmtp.c,v 1.12 2016/03/30 23:34:12 bru Exp $ */
2 
3 /*
4  * Copyright (c) 2013-2014, joshua stein <jcs@openbsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the copyright holder may not be used to endorse or
16  *    promote products derived from this software without specific
17  *    prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * Apple USB multitouch trackpad (Broadcom BCM5974) driver
34  *
35  * Protocol info/magic from bcm5974 Linux driver by Henrik Rydberg, et al.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/device.h>
40 #include <sys/errno.h>
41 #include <sys/malloc.h>
42 
43 #include <sys/ioctl.h>
44 #include <sys/systm.h>
45 #include <sys/tty.h>
46 
47 #include <dev/usb/usb.h>
48 #include <dev/usb/usbdi.h>
49 #include <dev/usb/usbdevs.h>
50 #include <dev/usb/uhidev.h>
51 #include <dev/usb/usbhid.h>
52 
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wscons/wsmousevar.h>
55 
56 /* #define UBCMTP_DEBUG */
57 
58 #ifdef UBCMTP_DEBUG
59 #define DPRINTF(x...)	do { printf(x); } while (0);
60 #else
61 #define DPRINTF(x...)
62 #endif
63 
64 /* magic to switch device from HID (default) mode into raw */
65 #define UBCMTP_WELLSPRING_MODE_RAW	0x01
66 #define UBCMTP_WELLSPRING_MODE_HID	0x08
67 #define UBCMTP_WELLSPRING_MODE_LEN	8
68 #define UBCMTP_WELLSPRING9_MODE_RAW	0x01
69 #define UBCMTP_WELLSPRING9_MODE_HID	0x00
70 #define UBCMTP_WELLSPRING9_MODE_LEN	2
71 
72 struct ubcmtp_button {
73 	uint8_t		unused;
74 	uint8_t		button;
75 	uint8_t		rel_x;
76 	uint8_t		rel_y;
77 };
78 
79 struct ubcmtp_finger {
80 	uint16_t	origin;
81 	uint16_t	abs_x;
82 	uint16_t	abs_y;
83 	uint16_t	rel_x;
84 	uint16_t	rel_y;
85 	uint16_t	tool_major;
86 	uint16_t	tool_minor;
87 	uint16_t	orientation;
88 	uint16_t	touch_major;
89 	uint16_t	touch_minor;
90 	uint16_t	unused[2];
91 	uint16_t	pressure;
92 	uint16_t	multi;
93 } __packed __attribute((aligned(2)));
94 
95 #define UBCMTP_MAX_FINGERS	16
96 #define UBCMTP_ALL_FINGER_SIZE	(UBCMTP_MAX_FINGERS * sizeof(struct ubcmtp_finger))
97 
98 #define UBCMTP_TYPE1		1
99 #define UBCMTP_TYPE1_TPOFF	(13 * sizeof(uint16_t))
100 #define UBCMTP_TYPE1_TPLEN	UBCMTP_TYPE1_TPOFF + UBCMTP_ALL_FINGER_SIZE
101 #define UBCMTP_TYPE1_TPIFACE	1
102 #define UBCMTP_TYPE1_BTIFACE	2
103 
104 #define UBCMTP_TYPE2		2
105 #define UBCMTP_TYPE2_TPOFF	(15 * sizeof(uint16_t))
106 #define UBCMTP_TYPE2_TPLEN	UBCMTP_TYPE2_TPOFF + UBCMTP_ALL_FINGER_SIZE
107 #define UBCMTP_TYPE2_TPIFACE	1
108 #define UBCMTP_TYPE2_BTOFF	15
109 
110 #define UBCMTP_TYPE3		3
111 #define UBCMTP_TYPE3_TPOFF	(19 * sizeof(uint16_t))
112 #define UBCMTP_TYPE3_TPLEN	UBCMTP_TYPE3_TPOFF + UBCMTP_ALL_FINGER_SIZE
113 #define UBCMTP_TYPE3_TPIFACE	2
114 #define UBCMTP_TYPE3_BTOFF	23
115 
116 #define UBCMTP_TYPE4		4
117 #define UBCMTP_TYPE4_TPOFF	(24 * sizeof(uint16_t))
118 #define UBCMTP_TYPE4_TPLEN	UBCMTP_TYPE4_TPOFF + UBCMTP_ALL_FINGER_SIZE
119 #define UBCMTP_TYPE4_TPIFACE	2
120 #define UBCMTP_TYPE4_BTOFF	31
121 
122 #define UBCMTP_FINGER_ORIENT	16384
123 #define UBCMTP_SN_PRESSURE	45
124 #define UBCMTP_SN_WIDTH		25
125 #define UBCMTP_SN_COORD		250
126 #define UBCMTP_SN_ORIENT	10
127 
128 struct ubcmtp_limit {
129 	int limit;
130 	int min;
131 	int max;
132 };
133 
134 struct ubcmtp_dev {
135 	int vendor;			/* vendor */
136 	int ansi, iso, jis;		/* 3 types of product */
137 	int type;			/* 1 (normal) or 2 (integrated btn) */
138 	struct ubcmtp_limit l_pressure;	/* finger pressure */
139 	struct ubcmtp_limit l_width;	/* finger width */
140 	struct ubcmtp_limit l_x;
141 	struct ubcmtp_limit l_y;
142 	struct ubcmtp_limit l_orientation;
143 };
144 
145 static struct ubcmtp_dev ubcmtp_devices[] = {
146 	/* type 1 devices with separate buttons */
147 	{
148 		USB_VENDOR_APPLE,
149 		/* MacbookAir */
150 		USB_PRODUCT_APPLE_WELLSPRING_ANSI,
151 		USB_PRODUCT_APPLE_WELLSPRING_ISO,
152 		USB_PRODUCT_APPLE_WELLSPRING_JIS,
153 		UBCMTP_TYPE1,
154 		{ UBCMTP_SN_PRESSURE, 0, 256 },
155 		{ UBCMTP_SN_WIDTH, 0, 2048 },
156 		{ UBCMTP_SN_COORD, -4824, 5342 },
157 		{ UBCMTP_SN_COORD, -172, 5820 },
158 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
159 	},
160 	{
161 		USB_VENDOR_APPLE,
162 		/* MacbookProPenryn */
163 		USB_PRODUCT_APPLE_WELLSPRING2_ANSI,
164 		USB_PRODUCT_APPLE_WELLSPRING2_ISO,
165 		USB_PRODUCT_APPLE_WELLSPRING2_JIS,
166 		UBCMTP_TYPE1,
167 		{ UBCMTP_SN_PRESSURE, 0, 256 },
168 		{ UBCMTP_SN_WIDTH, 0, 2048 },
169 		{ UBCMTP_SN_COORD, -4824, 4824 },
170 		{ UBCMTP_SN_COORD, -172, 4290 },
171 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
172 	},
173 	/* type 2 devices with integrated buttons */
174 	{
175 		USB_VENDOR_APPLE,
176 		/* Macbook5,1 */
177 		USB_PRODUCT_APPLE_WELLSPRING3_ANSI,
178 		USB_PRODUCT_APPLE_WELLSPRING3_ISO,
179 		USB_PRODUCT_APPLE_WELLSPRING3_JIS,
180 		UBCMTP_TYPE2,
181 		{ UBCMTP_SN_PRESSURE, 0, 300 },
182 		{ UBCMTP_SN_WIDTH, 0, 2048 },
183 		{ UBCMTP_SN_COORD, -4460, 5166 },
184 		{ UBCMTP_SN_COORD, -75, 6700 },
185 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
186 	},
187 	{
188 		USB_VENDOR_APPLE,
189 		/* MacbookAir3,1 */
190 		USB_PRODUCT_APPLE_WELLSPRING4A_ANSI,
191 		USB_PRODUCT_APPLE_WELLSPRING4A_ISO,
192 		USB_PRODUCT_APPLE_WELLSPRING4A_JIS,
193 		UBCMTP_TYPE2,
194 		{ UBCMTP_SN_PRESSURE, 0, 300 },
195 		{ UBCMTP_SN_WIDTH, 0, 2048 },
196 		{ UBCMTP_SN_COORD, -4616, 5112 },
197 		{ UBCMTP_SN_COORD, -142, 5234 },
198 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
199 	},
200 	{
201 		USB_VENDOR_APPLE,
202 		/* MacbookAir3,2 */
203 		USB_PRODUCT_APPLE_WELLSPRING4_ANSI,
204 		USB_PRODUCT_APPLE_WELLSPRING4_ISO,
205 		USB_PRODUCT_APPLE_WELLSPRING4_JIS,
206 		UBCMTP_TYPE2,
207 		{ UBCMTP_SN_PRESSURE, 0, 300 },
208 		{ UBCMTP_SN_WIDTH, 0, 2048 },
209 		{ UBCMTP_SN_COORD, -4620, 5140 },
210 		{ UBCMTP_SN_COORD, -150, 6600 },
211 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
212 	},
213 	{
214 		USB_VENDOR_APPLE,
215 		/* Macbook8 */
216 		USB_PRODUCT_APPLE_WELLSPRING5_ANSI,
217 		USB_PRODUCT_APPLE_WELLSPRING5_ISO,
218 		USB_PRODUCT_APPLE_WELLSPRING5_JIS,
219 		UBCMTP_TYPE2,
220 		{ UBCMTP_SN_PRESSURE, 0, 300 },
221 		{ UBCMTP_SN_WIDTH, 0, 2048 },
222 		{ UBCMTP_SN_COORD, -4415, 5050 },
223 		{ UBCMTP_SN_COORD, -55, 6680 },
224 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
225 	},
226 	{
227 		USB_VENDOR_APPLE,
228 		/* Macbook8,2 */
229 		USB_PRODUCT_APPLE_WELLSPRING5A_ANSI,
230 		USB_PRODUCT_APPLE_WELLSPRING5A_ISO,
231 		USB_PRODUCT_APPLE_WELLSPRING5A_JIS,
232 		UBCMTP_TYPE2,
233 		{ UBCMTP_SN_PRESSURE, 0, 300 },
234 		{ UBCMTP_SN_WIDTH, 0, 2048 },
235 		{ UBCMTP_SN_COORD, -4750, 5280 },
236 		{ UBCMTP_SN_COORD, -150, 6730 },
237 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
238 	},
239 	{
240 		USB_VENDOR_APPLE,
241 		/* MacbookAir4,2 */
242 		USB_PRODUCT_APPLE_WELLSPRING6_ANSI,
243 		USB_PRODUCT_APPLE_WELLSPRING6_ISO,
244 		USB_PRODUCT_APPLE_WELLSPRING6_JIS,
245 		UBCMTP_TYPE2,
246 		{ UBCMTP_SN_PRESSURE, 0, 300 },
247 		{ UBCMTP_SN_WIDTH, 0, 2048 },
248 		{ UBCMTP_SN_COORD, -4620, 5140 },
249 		{ UBCMTP_SN_COORD, -150, 6600 },
250 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
251 	},
252 	{
253 		USB_VENDOR_APPLE,
254 		/* MacbookAir4,1 */
255 		USB_PRODUCT_APPLE_WELLSPRING6A_ANSI,
256 		USB_PRODUCT_APPLE_WELLSPRING6A_ISO,
257 		USB_PRODUCT_APPLE_WELLSPRING6A_JIS,
258 		UBCMTP_TYPE2,
259 		{ UBCMTP_SN_PRESSURE, 0, 300 },
260 		{ UBCMTP_SN_WIDTH, 0, 2048 },
261 		{ UBCMTP_SN_COORD, -4620, 5140 },
262 		{ UBCMTP_SN_COORD, -150, 6600 },
263 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
264 	},
265 	{
266 		USB_VENDOR_APPLE,
267 		/* MacbookPro10,1 */
268 		USB_PRODUCT_APPLE_WELLSPRING7_ANSI,
269 		USB_PRODUCT_APPLE_WELLSPRING7_ISO,
270 		USB_PRODUCT_APPLE_WELLSPRING7_JIS,
271 		UBCMTP_TYPE2,
272 		{ UBCMTP_SN_PRESSURE, 0, 300 },
273 		{ UBCMTP_SN_WIDTH, 0, 2048 },
274 		{ UBCMTP_SN_COORD, -4750, 5280 },
275 		{ UBCMTP_SN_COORD, -150, 6730 },
276 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
277 	},
278 	{
279 		USB_VENDOR_APPLE,
280 		/* MacbookPro10,2 */
281 		USB_PRODUCT_APPLE_WELLSPRING7A_ANSI,
282 		USB_PRODUCT_APPLE_WELLSPRING7A_ISO,
283 		USB_PRODUCT_APPLE_WELLSPRING7A_JIS,
284 		UBCMTP_TYPE2,
285 		{ UBCMTP_SN_PRESSURE, 0, 300 },
286 		{ UBCMTP_SN_WIDTH, 0, 2048 },
287 		{ UBCMTP_SN_COORD, -4750, 5280 },
288 		{ UBCMTP_SN_COORD, -150, 6730 },
289 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
290 	},
291 	{
292 		USB_VENDOR_APPLE,
293 		/* MacbookAir6,1 */
294 		USB_PRODUCT_APPLE_WELLSPRING8_ANSI,
295 		USB_PRODUCT_APPLE_WELLSPRING8_ISO,
296 		USB_PRODUCT_APPLE_WELLSPRING8_JIS,
297 		UBCMTP_TYPE3,
298 		{ UBCMTP_SN_PRESSURE, 0, 300 },
299 		{ UBCMTP_SN_WIDTH, 0, 2048 },
300 		{ UBCMTP_SN_COORD, -4620, 5140 },
301 		{ UBCMTP_SN_COORD, -150, 6600 },
302 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
303 	},
304 	{
305 		USB_VENDOR_APPLE,
306 		/* MacbookPro12,1 */
307 		USB_PRODUCT_APPLE_WELLSPRING9_ANSI,
308 		USB_PRODUCT_APPLE_WELLSPRING9_ISO,
309 		USB_PRODUCT_APPLE_WELLSPRING9_JIS,
310 		UBCMTP_TYPE4,
311 		{ UBCMTP_SN_PRESSURE, 0, 300 },
312 		{ UBCMTP_SN_WIDTH, 0, 2048 },
313 		{ UBCMTP_SN_COORD, -4828, 5345 },
314 		{ UBCMTP_SN_COORD, -203, 6803 },
315 		{ UBCMTP_SN_ORIENT, -UBCMTP_FINGER_ORIENT, UBCMTP_FINGER_ORIENT },
316 	},
317 };
318 
319 /* coordinates for each tracked finger */
320 struct ubcmtp_pos {
321 	int	down;
322 	int	x;
323 	int	y;
324 	int	z;
325 	int	w;
326 	int	dx;
327 	int	dy;
328 };
329 
330 struct ubcmtp_softc {
331 	struct device		sc_dev;		/* base device */
332 
333 	struct ubcmtp_dev	*dev_type;
334 
335 	struct uhidev		sc_hdev;
336 	struct usbd_device	*sc_udev;
337 	struct device		*sc_wsmousedev;
338 	int			wsmode;
339 
340 	int			tp_ifacenum;	/* trackpad interface number */
341 	struct usbd_interface	*sc_tp_iface;	/* trackpad interface */
342 	struct usbd_pipe	*sc_tp_pipe;	/* trackpad pipe */
343 	int			sc_tp_epaddr;	/* endpoint addr */
344 	int			tp_maxlen;	/* max size of tp data */
345 	int			tp_offset;	/* finger offset into data */
346 	uint8_t			*tp_pkt;
347 
348 	int			bt_ifacenum;	/* button interface number */
349 	struct usbd_interface	*sc_bt_iface;	/* button interface */
350 	struct usbd_pipe	*sc_bt_pipe;	/* button pipe */
351 	int			sc_bt_epaddr;	/* endpoint addr */
352 	int			bt_maxlen;	/* max size of button data */
353 	uint8_t			*bt_pkt;
354 
355 	uint32_t		sc_status;
356 #define UBCMTP_ENABLED		1
357 
358 	struct ubcmtp_pos	pos[UBCMTP_MAX_FINGERS];
359 	int			btn;
360 };
361 
362 int	ubcmtp_enable(void *);
363 void	ubcmtp_disable(void *);
364 int	ubcmtp_ioctl(void *, unsigned long, caddr_t, int, struct proc *);
365 int	ubcmtp_raw_mode(struct ubcmtp_softc *, int);
366 int	ubcmtp_setup_pipes(struct ubcmtp_softc *);
367 void	ubcmtp_bt_intr(struct usbd_xfer *, void *, usbd_status);
368 void	ubcmtp_tp_intr(struct usbd_xfer *, void *, usbd_status);
369 
370 int	ubcmtp_match(struct device *, void *, void *);
371 void	ubcmtp_attach(struct device *, struct device *, void *);
372 int	ubcmtp_detach(struct device *, int);
373 int	ubcmtp_activate(struct device *, int);
374 
375 const struct wsmouse_accessops ubcmtp_accessops = {
376 	ubcmtp_enable,
377 	ubcmtp_ioctl,
378 	ubcmtp_disable,
379 };
380 
381 struct cfdriver ubcmtp_cd = {
382 	NULL, "ubcmtp", DV_DULL
383 };
384 
385 const struct cfattach ubcmtp_ca = {
386 	sizeof(struct ubcmtp_softc), ubcmtp_match, ubcmtp_attach, ubcmtp_detach,
387 	ubcmtp_activate,
388 };
389 
390 int
391 ubcmtp_match(struct device *parent, void *match, void *aux)
392 {
393 	struct usb_attach_arg *uaa = aux;
394 	usb_interface_descriptor_t *id;
395 	int i;
396 
397 	if (uaa->iface == NULL)
398 		return (UMATCH_NONE);
399 
400 	for (i = 0; i < nitems(ubcmtp_devices); i++) {
401 		if (uaa->vendor == ubcmtp_devices[i].vendor && (
402 		    uaa->product == ubcmtp_devices[i].ansi ||
403 		    uaa->product == ubcmtp_devices[i].iso ||
404 		    uaa->product == ubcmtp_devices[i].jis)) {
405 			/*
406 			 * The USB keyboard/mouse device will have one keyboard
407 			 * HID and two mouse HIDs, though only one will have a
408 			 * protocol of mouse -- we only want to take control of
409 			 * that one.
410 			 */
411 			id = usbd_get_interface_descriptor(uaa->iface);
412 			if (id->bInterfaceProtocol == UIPROTO_BOOT_MOUSE)
413 				return (UMATCH_VENDOR_PRODUCT_CONF_IFACE);
414 		}
415 	}
416 
417 	return (UMATCH_NONE);
418 }
419 
420 void
421 ubcmtp_attach(struct device *parent, struct device *self, void *aux)
422 {
423 	struct ubcmtp_softc *sc = (struct ubcmtp_softc *)self;
424 	struct usb_attach_arg *uaa = aux;
425 	struct usbd_device *dev = uaa->device;
426 	struct wsmousedev_attach_args a;
427 	usb_device_descriptor_t *udd;
428 	int i;
429 
430 	sc->sc_udev = uaa->device;
431 	sc->sc_status = 0;
432 
433 	if ((udd = usbd_get_device_descriptor(dev)) == NULL) {
434 		printf("ubcmtp: failed getting device descriptor\n");
435 		return;
436 	}
437 
438 	for (i = 0; i < nitems(ubcmtp_devices); i++) {
439 		if (uaa->vendor == ubcmtp_devices[i].vendor && (
440 		    uaa->product == ubcmtp_devices[i].ansi ||
441 		    uaa->product == ubcmtp_devices[i].iso ||
442 		    uaa->product == ubcmtp_devices[i].jis)) {
443 			sc->dev_type = &ubcmtp_devices[i];
444 			DPRINTF("%s: attached to 0x%x/0x%x type %d\n",
445 			    sc->sc_dev.dv_xname, uaa->vendor, uaa->product,
446 			    sc->dev_type->type);
447 			break;
448 		}
449 	}
450 
451 	if (sc->dev_type == NULL) {
452 		/* how did we match then? */
453 		printf("%s: failed looking up device in table\n",
454 		    sc->sc_dev.dv_xname);
455 		return;
456 	}
457 
458 	switch (sc->dev_type->type) {
459 	case UBCMTP_TYPE1:
460 		sc->tp_maxlen = UBCMTP_TYPE1_TPLEN;
461 		sc->tp_offset = UBCMTP_TYPE1_TPOFF;
462 		sc->tp_ifacenum = UBCMTP_TYPE1_TPIFACE;
463 
464 		/* button offsets */
465 		sc->bt_maxlen = sizeof(struct ubcmtp_button);
466 		sc->bt_ifacenum = UBCMTP_TYPE1_BTIFACE;
467 		break;
468 
469 	case UBCMTP_TYPE2:
470 		sc->tp_maxlen = UBCMTP_TYPE2_TPLEN;
471 		sc->tp_offset = UBCMTP_TYPE2_TPOFF;
472 		sc->tp_ifacenum = UBCMTP_TYPE2_TPIFACE;
473 		break;
474 
475 	case UBCMTP_TYPE3:
476 		sc->tp_maxlen = UBCMTP_TYPE3_TPLEN;
477 		sc->tp_offset = UBCMTP_TYPE3_TPOFF;
478 		sc->tp_ifacenum = UBCMTP_TYPE3_TPIFACE;
479 		break;
480 
481 	case UBCMTP_TYPE4:
482 		sc->tp_maxlen = UBCMTP_TYPE4_TPLEN;
483 		sc->tp_offset = UBCMTP_TYPE4_TPOFF;
484 		sc->tp_ifacenum = UBCMTP_TYPE4_TPIFACE;
485 		break;
486 	}
487 
488 	a.accessops = &ubcmtp_accessops;
489 	a.accesscookie = sc;
490 
491 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
492 }
493 
494 int
495 ubcmtp_detach(struct device *self, int flags)
496 {
497 	struct ubcmtp_softc *sc = (struct ubcmtp_softc *)self;
498 	int ret = 0;
499 
500 	if (sc->sc_wsmousedev != NULL)
501 		ret = config_detach(sc->sc_wsmousedev, flags);
502 
503 	return (ret);
504 }
505 
506 int
507 ubcmtp_activate(struct device *self, int act)
508 {
509 	struct ubcmtp_softc *sc = (struct ubcmtp_softc *)self;
510 	int rv = 0;
511 
512 	if (act == DVACT_DEACTIVATE) {
513 		if (sc->sc_wsmousedev != NULL)
514 			rv = config_deactivate(sc->sc_wsmousedev);
515 		usbd_deactivate(sc->sc_udev);
516 	}
517 
518 	return (rv);
519 }
520 
521 int
522 ubcmtp_enable(void *v)
523 {
524 	struct ubcmtp_softc *sc = v;
525 
526 	if (sc->sc_status & UBCMTP_ENABLED)
527 		return (EBUSY);
528 
529 	if (usbd_is_dying(sc->sc_udev))
530 		return (EIO);
531 
532 	if (ubcmtp_raw_mode(sc, 1) != 0) {
533 		printf("%s: failed to enter raw mode\n", sc->sc_dev.dv_xname);
534 		return (1);
535 	}
536 
537 	if (ubcmtp_setup_pipes(sc) == 0) {
538 		sc->sc_status |= UBCMTP_ENABLED;
539 		return (0);
540 	} else
541 		return (1);
542 }
543 
544 void
545 ubcmtp_disable(void *v)
546 {
547 	struct ubcmtp_softc *sc = v;
548 
549 	if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED))
550 		return;
551 
552 	sc->sc_status &= ~UBCMTP_ENABLED;
553 
554 	ubcmtp_raw_mode(sc, 0);
555 
556 	if (sc->sc_tp_pipe != NULL) {
557 		usbd_abort_pipe(sc->sc_tp_pipe);
558 		usbd_close_pipe(sc->sc_tp_pipe);
559 		sc->sc_tp_pipe = NULL;
560 	}
561 	if (sc->sc_bt_pipe != NULL) {
562 		usbd_abort_pipe(sc->sc_bt_pipe);
563 		usbd_close_pipe(sc->sc_bt_pipe);
564 		sc->sc_bt_pipe = NULL;
565 	}
566 
567 	if (sc->tp_pkt != NULL) {
568 		free(sc->tp_pkt, M_USBDEV, 0);
569 		sc->tp_pkt = NULL;
570 	}
571 	if (sc->bt_pkt != NULL) {
572 		free(sc->bt_pkt, M_USBDEV, 0);
573 		sc->bt_pkt = NULL;
574 	}
575 }
576 
577 int
578 ubcmtp_ioctl(void *v, unsigned long cmd, caddr_t data, int flag, struct proc *p)
579 {
580 	struct ubcmtp_softc *sc = v;
581 	struct wsmouse_calibcoords *wsmc = (struct wsmouse_calibcoords *)data;
582 	int wsmode;
583 
584 	DPRINTF("%s: in %s with cmd 0x%lx\n", sc->sc_dev.dv_xname, __func__,
585 	    cmd);
586 
587 	switch (cmd) {
588 	case WSMOUSEIO_GTYPE:
589 		/* so we can specify our own finger/w values to the
590 		 * xf86-input-synaptics driver like pms(4) */
591 		*(u_int *)data = WSMOUSE_TYPE_ELANTECH;
592 		break;
593 
594 	case WSMOUSEIO_GCALIBCOORDS:
595 		wsmc->minx = sc->dev_type->l_x.min;
596 		wsmc->maxx = sc->dev_type->l_x.max;
597 		wsmc->miny = sc->dev_type->l_y.min;
598 		wsmc->maxy = sc->dev_type->l_y.max;
599 		wsmc->swapxy = 0;
600 		wsmc->resx = 0;
601 		wsmc->resy = 0;
602 		break;
603 
604 	case WSMOUSEIO_SETMODE:
605 		wsmode = *(u_int *)data;
606 		if (wsmode != WSMOUSE_COMPAT && wsmode != WSMOUSE_NATIVE) {
607 			printf("%s: invalid mode %d\n", sc->sc_dev.dv_xname,
608 			    wsmode);
609 			return (EINVAL);
610 		}
611 
612 		DPRINTF("%s: changing mode to %s\n",
613 		    sc->sc_dev.dv_xname, (wsmode == WSMOUSE_COMPAT ? "compat" :
614 		    "native"));
615 
616 		sc->wsmode = wsmode;
617 		break;
618 
619 	default:
620 		return (-1);
621 	}
622 
623 	return (0);
624 }
625 
626 int
627 ubcmtp_raw_mode(struct ubcmtp_softc *sc, int enable)
628 {
629 	usb_device_request_t r;
630 	usbd_status err;
631 	uint8_t buf[8];
632 
633 	/* type 3 has no raw mode */
634 	if (sc->dev_type->type == UBCMTP_TYPE3)
635 		return (0);
636 
637 	r.bRequest = UR_GET_REPORT;
638 	r.bmRequestType = UT_READ_CLASS_INTERFACE;
639 	if (sc->dev_type->type < UBCMTP_TYPE4) {
640 		USETW2(r.wValue, UHID_FEATURE_REPORT, 0);
641 		USETW(r.wIndex, 0);
642 		USETW(r.wLength, UBCMTP_WELLSPRING_MODE_LEN);
643 	} else {
644 		USETW2(r.wValue, UHID_FEATURE_REPORT, 2);
645 		USETW(r.wIndex, 2);
646 		USETW(r.wLength, UBCMTP_WELLSPRING9_MODE_LEN);
647 	}
648 
649 	err = usbd_do_request(sc->sc_udev, &r, buf);
650 	if (err != USBD_NORMAL_COMPLETION) {
651 		printf("%s: %s: failed to get feature report\n",
652 		    sc->sc_dev.dv_xname, __func__);
653 		return (err);
654 	}
655 
656 	/* toggle magic byte and write everything back */
657 	if (sc->dev_type->type < UBCMTP_TYPE4)
658 		buf[0] = (enable ? UBCMTP_WELLSPRING_MODE_RAW :
659 		    UBCMTP_WELLSPRING_MODE_HID);
660 	else
661 		buf[1] = (enable ? UBCMTP_WELLSPRING9_MODE_RAW :
662 		    UBCMTP_WELLSPRING9_MODE_HID);
663 
664 	r.bRequest = UR_SET_REPORT;
665 	r.bmRequestType = UT_WRITE_CLASS_INTERFACE;
666 	if (sc->dev_type->type < UBCMTP_TYPE4) {
667 		USETW2(r.wValue, UHID_FEATURE_REPORT, 0);
668 		USETW(r.wIndex, 0);
669 		USETW(r.wLength, UBCMTP_WELLSPRING_MODE_LEN);
670 	} else {
671 		USETW2(r.wValue, UHID_FEATURE_REPORT, 2);
672 		USETW(r.wIndex, 2);
673 		USETW(r.wLength, UBCMTP_WELLSPRING9_MODE_LEN);
674 	}
675 
676 	err = usbd_do_request(sc->sc_udev, &r, buf);
677 	if (err != USBD_NORMAL_COMPLETION) {
678 		printf("%s: %s: failed to toggle raw mode\n",
679 		    sc->sc_dev.dv_xname, __func__);
680 		return (err);
681 	}
682 
683 	return (0);
684 }
685 
686 int
687 ubcmtp_setup_pipes(struct ubcmtp_softc *sc)
688 {
689 	usbd_status err;
690 	usb_endpoint_descriptor_t *ed;
691 
692 	if (sc->dev_type->type == UBCMTP_TYPE1) {
693 		/* setup physical button pipe */
694 
695 		if ((err = usbd_device2interface_handle(sc->sc_udev,
696 		    sc->bt_ifacenum, &sc->sc_bt_iface)) != 0) {
697 			printf("%s: failed getting button interface\n",
698 			    sc->sc_dev.dv_xname);
699 			goto fail1;
700 		}
701 		ed = usbd_interface2endpoint_descriptor(sc->sc_bt_iface, 0);
702 		if (ed == NULL) {
703 			printf("%s: failed getting button endpoint descriptor\n",
704 			    sc->sc_dev.dv_xname);
705 			goto fail1;
706 		}
707 		sc->sc_bt_epaddr = ed->bEndpointAddress;
708 		sc->bt_pkt = malloc(sc->bt_maxlen, M_USBDEV, M_WAITOK);
709 		if (sc->bt_pkt == NULL)
710 			goto fail1;
711 
712 		DPRINTF("%s: button iface at 0x%x, max size %d\n",
713 		    sc->sc_dev.dv_xname, sc->sc_bt_epaddr, sc->bt_maxlen);
714 
715 		err = usbd_open_pipe_intr(sc->sc_bt_iface, sc->sc_bt_epaddr,
716 		    USBD_SHORT_XFER_OK, &sc->sc_bt_pipe, sc, sc->bt_pkt,
717 		    sc->bt_maxlen, ubcmtp_bt_intr, USBD_DEFAULT_INTERVAL);
718 		if (err != USBD_NORMAL_COMPLETION) {
719 			printf("%s: failed opening button pipe\n",
720 			    sc->sc_dev.dv_xname);
721 			goto fail1;
722 		}
723 	}
724 
725 	/* setup trackpad data pipe */
726 
727 	if ((err = usbd_device2interface_handle(sc->sc_udev, sc->tp_ifacenum,
728 	    &sc->sc_tp_iface)) != 0) {
729 		printf("%s: failed getting trackpad data interface\n",
730 		    sc->sc_dev.dv_xname);
731 		goto fail2;
732 	}
733 	ed = usbd_interface2endpoint_descriptor(sc->sc_tp_iface, 0);
734 	if (ed == NULL) {
735 		printf("%s: failed getting trackpad data endpoint descriptor\n",
736 		    sc->sc_dev.dv_xname);
737 		goto fail2;
738 	}
739 	sc->sc_tp_epaddr = ed->bEndpointAddress;
740 	sc->tp_pkt = malloc(sc->tp_maxlen, M_USBDEV, M_WAITOK);
741 	if (sc->tp_pkt == NULL)
742 		goto fail2;
743 
744 	DPRINTF("%s: trackpad data iface at 0x%x, max size %d\n",
745 	    sc->sc_dev.dv_xname, sc->sc_tp_epaddr, sc->tp_maxlen);
746 
747 	err = usbd_open_pipe_intr(sc->sc_tp_iface, sc->sc_tp_epaddr,
748 	    USBD_SHORT_XFER_OK, &sc->sc_tp_pipe, sc, sc->tp_pkt, sc->tp_maxlen,
749 	    ubcmtp_tp_intr, USBD_DEFAULT_INTERVAL);
750 	if (err != USBD_NORMAL_COMPLETION) {
751 		printf("%s: error opening trackpad data pipe\n",
752 		    sc->sc_dev.dv_xname);
753 		goto fail2;
754 	}
755 
756 	return (0);
757 
758 fail2:
759 	if (sc->sc_tp_pipe != NULL) {
760 		usbd_abort_pipe(sc->sc_tp_pipe);
761 		usbd_close_pipe(sc->sc_tp_pipe);
762 	}
763 	if (sc->tp_pkt != NULL)
764 		free(sc->tp_pkt, M_USBDEV, 0);
765 fail1:
766 	if (sc->sc_bt_pipe != NULL) {
767 		usbd_abort_pipe(sc->sc_bt_pipe);
768 		usbd_close_pipe(sc->sc_bt_pipe);
769 	}
770 	if (sc->bt_pkt != NULL)
771 		free(sc->bt_pkt, M_USBDEV, 0);
772 
773 	return (1);
774 }
775 
776 void
777 ubcmtp_tp_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
778 {
779 	struct ubcmtp_softc *sc = priv;
780 	struct ubcmtp_finger *pkt;
781 	u_int32_t pktlen;
782 	int i, diff = 0, finger = 0, fingers = 0, s, t;
783 
784 	if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED))
785 		return;
786 
787 	if (status != USBD_NORMAL_COMPLETION) {
788 		DPRINTF("%s: %s with status 0x%x\n", sc->sc_dev.dv_xname,
789 		    __func__, status);
790 
791 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
792 			return;
793 		if (status == USBD_STALLED)
794 			usbd_clear_endpoint_stall_async(sc->sc_tp_pipe);
795 		return;
796 	}
797 
798 	usbd_get_xfer_status(xfer, NULL, NULL, &pktlen, NULL);
799 
800 	if (sc->tp_pkt == NULL || pktlen < sc->tp_offset)
801 		return;
802 
803 	pkt = (struct ubcmtp_finger *)(sc->tp_pkt + sc->tp_offset);
804 	fingers = (pktlen - sc->tp_offset) / sizeof(struct ubcmtp_finger);
805 
806 	for (i = 0; i < fingers; i++) {
807 		if ((int16_t)letoh16(pkt[i].touch_major) == 0) {
808 			/* finger lifted */
809 			sc->pos[i].down = 0;
810 			diff = 1;
811 			continue;
812 		}
813 
814 		if (!sc->pos[finger].down) {
815 			sc->pos[finger].down = 1;
816 			diff = 1;
817 		}
818 
819 		if ((t = (int16_t)letoh16(pkt[i].abs_x)) != sc->pos[finger].x) {
820 			sc->pos[finger].x = t;
821 			diff = 1;
822 		}
823 
824 		if ((t = (int16_t)letoh16(pkt[i].abs_y)) != sc->pos[finger].y) {
825 			sc->pos[finger].y = t;
826 			diff = 1;
827 		}
828 
829 		if ((t = (int16_t)letoh16(pkt[i].rel_x)) != sc->pos[finger].dx) {
830 			sc->pos[finger].dx = t;
831 			diff = 1;
832 		}
833 
834 		if ((t = (int16_t)letoh16(pkt[i].rel_y)) != sc->pos[finger].dy) {
835 			sc->pos[finger].dy = t;
836 			diff = 1;
837 		}
838 
839 		finger++;
840 	}
841 
842 	if (sc->dev_type->type == UBCMTP_TYPE2 ||
843 	    sc->dev_type->type == UBCMTP_TYPE3 ||
844 	    sc->dev_type->type == UBCMTP_TYPE4) {
845 		if (sc->dev_type->type == UBCMTP_TYPE2)
846 			t = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE2_BTOFF]));
847 		else if (sc->dev_type->type == UBCMTP_TYPE3)
848 			t = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE3_BTOFF]));
849 		else if (sc->dev_type->type == UBCMTP_TYPE4)
850 			t = !!((int16_t)letoh16(sc->tp_pkt[UBCMTP_TYPE4_BTOFF]));
851 
852 		if (sc->btn != t) {
853 			sc->btn = t;
854 			diff = 1;
855 
856 			DPRINTF("%s: [button]\n", sc->sc_dev.dv_xname);
857 		}
858 	}
859 
860 	if (diff) {
861 		s = spltty();
862 
863 		DPRINTF("%s: ", sc->sc_dev.dv_xname);
864 
865 		if (sc->wsmode == WSMOUSE_NATIVE) {
866 			DPRINTF("absolute input %d, %d (finger %d, button %d)\n",
867 			    sc->pos[0].x, sc->pos[0].y, finger, sc->btn);
868 			WSMOUSE_TOUCH(sc->sc_wsmousedev, sc->btn, sc->pos[0].x,
869 			    sc->pos[0].y, (finger ? 50 : 0), finger);
870 		} else {
871 			DPRINTF("relative input %d, %d (button %d)\n",
872 			    sc->pos[0].dx, sc->pos[0].dy, sc->btn);
873 			WSMOUSE_INPUT(sc->sc_wsmousedev, sc->btn,
874 			    sc->pos[0].dx, sc->pos[0].dy, 0, 0);
875 		}
876 		splx(s);
877 	}
878 }
879 
880 /* hardware button interrupt */
881 void
882 ubcmtp_bt_intr(struct usbd_xfer *xfer, void *priv, usbd_status status)
883 {
884 	struct ubcmtp_softc *sc = priv;
885 	struct ubcmtp_button *pkt;
886 	u_int32_t len;
887 
888 	if (usbd_is_dying(sc->sc_udev) || !(sc->sc_status & UBCMTP_ENABLED))
889 		return;
890 
891 	if (status != USBD_NORMAL_COMPLETION) {
892 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
893 			return;
894 		if (status == USBD_STALLED)
895 			usbd_clear_endpoint_stall_async(sc->sc_tp_pipe);
896 		return;
897 	}
898 
899 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
900 
901 	if (sc->bt_pkt == NULL || len < sizeof(struct ubcmtp_button))
902 		return;
903 
904 	pkt = (struct ubcmtp_button *)(sc->bt_pkt);
905 
906 	DPRINTF("%s: button interrupt (%d, %d, %d, %d)", sc->sc_dev.dv_xname,
907 	    pkt->unused, pkt->button, pkt->rel_x, pkt->rel_y);
908 
909 	if (pkt->button != sc->btn) {
910 		sc->btn = pkt->button;
911 		wsmouse_buttons(sc->sc_wsmousedev, sc->btn);
912 		wsmouse_input_sync(sc->sc_wsmousedev);
913 	}
914 }
915