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