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