FreeBSD/Linux Kernel Cross Reference
sys/dev/hid/bcm5974.c
1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2012 Huang Wen Hui
5 * Copyright (c) 2021 Vladimir Kondratyev <wulf@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/endian.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/module.h>
39 #include <sys/sysctl.h>
40 #include <sys/systm.h>
41
42 #include <dev/evdev/input.h>
43 #include <dev/evdev/evdev.h>
44
45 #define HID_DEBUG_VAR bcm5974_debug
46 #include <dev/hid/hid.h>
47 #include <dev/hid/hidbus.h>
48 #include <dev/hid/hidquirk.h>
49
50 #include <dev/usb/usb.h>
51 #include <dev/usb/usbdi.h>
52 #include <dev/usb/usbhid.h>
53 #include <dev/usb/usb_ioctl.h>
54
55 #include "usbdevs.h"
56
57 #define BCM5974_BUFFER_MAX (248 * 4) /* 4 Type4 SPI frames */
58 #define BCM5974_TLC_PAGE HUP_GENERIC_DESKTOP
59 #define BCM5974_TLC_USAGE HUG_MOUSE
60
61 /* magic to switch device from HID (default) mode into raw */
62 /* Type1 & Type2 trackpads */
63 #define BCM5974_USB_IFACE_INDEX 0
64 #define BCM5974_USB_REPORT_LEN 8
65 #define BCM5974_USB_REPORT_ID 0
66 #define BCM5974_USB_MODE_RAW 0x01
67 #define BCM5974_USB_MODE_HID 0x08
68 /* Type4 trackpads */
69 #define BCM5974_HID_REPORT_LEN 2
70 #define BCM5974_HID_REPORT_ID 2
71 #define BCM5974_HID_MODE_RAW 0x01
72 #define BCM5974_HID_MODE_HID 0x00
73
74 /* Tunables */
75 static SYSCTL_NODE(_hw_hid, OID_AUTO, bcm5974, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
76 "HID wellspring touchpad");
77
78 #ifdef HID_DEBUG
79 enum wsp_log_level {
80 BCM5974_LLEVEL_DISABLED = 0,
81 BCM5974_LLEVEL_ERROR,
82 BCM5974_LLEVEL_DEBUG, /* for troubleshooting */
83 BCM5974_LLEVEL_INFO, /* for diagnostics */
84 };
85 /* the default is to only log errors */
86 static int bcm5974_debug = BCM5974_LLEVEL_ERROR;
87
88 SYSCTL_INT(_hw_hid_bcm5974, OID_AUTO, debug, CTLFLAG_RWTUN,
89 &bcm5974_debug, BCM5974_LLEVEL_ERROR, "BCM5974 debug level");
90 #endif /* HID_DEBUG */
91
92 /*
93 * Some tables, structures, definitions and constant values for the
94 * touchpad protocol has been copied from Linux's
95 * "drivers/input/mouse/bcm5974.c" which has the following copyright
96 * holders under GPLv2. All device specific code in this driver has
97 * been written from scratch. The decoding algorithm is based on
98 * output from FreeBSD's usbdump.
99 *
100 * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
101 * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
102 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
103 * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
104 * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
105 * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
106 * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
107 * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
108 * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
109 */
110
111 /* trackpad header types */
112 enum tp_type {
113 TYPE1, /* plain trackpad */
114 TYPE2, /* button integrated in trackpad */
115 TYPE3, /* additional header fields since June 2013 */
116 TYPE4, /* additional header field for pressure data */
117 TYPE_CNT
118 };
119
120 /* list of device capability bits */
121 #define HAS_INTEGRATED_BUTTON 1
122
123 struct tp_type_params {
124 uint8_t caps; /* device capability bitmask */
125 uint8_t button; /* offset to button data */
126 uint8_t offset; /* offset to trackpad finger data */
127 uint8_t delta; /* offset from header to finger struct */
128 } const static tp[TYPE_CNT] = {
129 [TYPE1] = {
130 .caps = 0,
131 .button = 0,
132 .offset = 13 * 2,
133 .delta = 0,
134 },
135 [TYPE2] = {
136 .caps = HAS_INTEGRATED_BUTTON,
137 .button = 15,
138 .offset = 15 * 2,
139 .delta = 0,
140 },
141 [TYPE3] = {
142 .caps = HAS_INTEGRATED_BUTTON,
143 .button = 23,
144 .offset = 19 * 2,
145 .delta = 0,
146 },
147 [TYPE4] = {
148 .caps = HAS_INTEGRATED_BUTTON,
149 .button = 31,
150 .offset = 23 * 2,
151 .delta = 2,
152 },
153 };
154
155 /* trackpad finger structure - little endian */
156 struct tp_finger {
157 uint16_t origin; /* zero when switching track finger */
158 uint16_t abs_x; /* absolute x coodinate */
159 uint16_t abs_y; /* absolute y coodinate */
160 uint16_t rel_x; /* relative x coodinate */
161 uint16_t rel_y; /* relative y coodinate */
162 uint16_t tool_major; /* tool area, major axis */
163 uint16_t tool_minor; /* tool area, minor axis */
164 uint16_t orientation; /* 16384 when point, else 15 bit angle */
165 uint16_t touch_major; /* touch area, major axis */
166 uint16_t touch_minor; /* touch area, minor axis */
167 uint16_t unused[2]; /* zeros */
168 uint16_t pressure; /* pressure on forcetouch touchpad */
169 uint16_t multi; /* one finger: varies, more fingers:
170 * constant */
171 } __packed;
172
173 #define BCM5974_LE2H(x) ((int32_t)(int16_t)le16toh(x))
174
175 /* trackpad finger data size, empirically at least ten fingers */
176 #define MAX_FINGERS MAX_MT_SLOTS
177
178 #define MAX_FINGER_ORIENTATION 16384
179
180 enum {
181 BCM5974_FLAG_WELLSPRING1,
182 BCM5974_FLAG_WELLSPRING2,
183 BCM5974_FLAG_WELLSPRING3,
184 BCM5974_FLAG_WELLSPRING4,
185 BCM5974_FLAG_WELLSPRING4A,
186 BCM5974_FLAG_WELLSPRING5,
187 BCM5974_FLAG_WELLSPRING6A,
188 BCM5974_FLAG_WELLSPRING6,
189 BCM5974_FLAG_WELLSPRING5A,
190 BCM5974_FLAG_WELLSPRING7,
191 BCM5974_FLAG_WELLSPRING7A,
192 BCM5974_FLAG_WELLSPRING8,
193 BCM5974_FLAG_WELLSPRING9,
194 BCM5974_FLAG_MAX,
195 };
196
197 /* device-specific parameters */
198 struct bcm5974_axis {
199 int snratio; /* signal-to-noise ratio */
200 int min; /* device minimum reading */
201 int max; /* device maximum reading */
202 int size; /* physical size, mm */
203 };
204
205 /* device-specific configuration */
206 struct bcm5974_dev_params {
207 const struct tp_type_params* tp;
208 struct bcm5974_axis p; /* finger pressure limits */
209 struct bcm5974_axis w; /* finger width limits */
210 struct bcm5974_axis x; /* horizontal limits */
211 struct bcm5974_axis y; /* vertical limits */
212 struct bcm5974_axis o; /* orientation limits */
213 };
214
215 /* logical signal quality */
216 #define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
217 #define SN_WIDTH 25 /* width signal-to-noise ratio */
218 #define SN_COORD 250 /* coordinate signal-to-noise ratio */
219 #define SN_ORIENT 10 /* orientation signal-to-noise ratio */
220
221 static const struct bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = {
222 [BCM5974_FLAG_WELLSPRING1] = {
223 .tp = tp + TYPE1,
224 .p = { SN_PRESSURE, 0, 256, 0 },
225 .w = { SN_WIDTH, 0, 2048, 0 },
226 .x = { SN_COORD, -4824, 5342, 105 },
227 .y = { SN_COORD, -172, 5820, 75 },
228 .o = { SN_ORIENT,
229 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
230 },
231 [BCM5974_FLAG_WELLSPRING2] = {
232 .tp = tp + TYPE1,
233 .p = { SN_PRESSURE, 0, 256, 0 },
234 .w = { SN_WIDTH, 0, 2048, 0 },
235 .x = { SN_COORD, -4824, 4824, 105 },
236 .y = { SN_COORD, -172, 4290, 75 },
237 .o = { SN_ORIENT,
238 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
239 },
240 [BCM5974_FLAG_WELLSPRING3] = {
241 .tp = tp + TYPE2,
242 .p = { SN_PRESSURE, 0, 300, 0 },
243 .w = { SN_WIDTH, 0, 2048, 0 },
244 .x = { SN_COORD, -4460, 5166, 105 },
245 .y = { SN_COORD, -75, 6700, 75 },
246 .o = { SN_ORIENT,
247 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
248 },
249 [BCM5974_FLAG_WELLSPRING4] = {
250 .tp = tp + TYPE2,
251 .p = { SN_PRESSURE, 0, 300, 0 },
252 .w = { SN_WIDTH, 0, 2048, 0 },
253 .x = { SN_COORD, -4620, 5140, 105 },
254 .y = { SN_COORD, -150, 6600, 75 },
255 .o = { SN_ORIENT,
256 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
257 },
258 [BCM5974_FLAG_WELLSPRING4A] = {
259 .tp = tp + TYPE2,
260 .p = { SN_PRESSURE, 0, 300, 0 },
261 .w = { SN_WIDTH, 0, 2048, 0 },
262 .x = { SN_COORD, -4616, 5112, 105 },
263 .y = { SN_COORD, -142, 5234, 75 },
264 .o = { SN_ORIENT,
265 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
266 },
267 [BCM5974_FLAG_WELLSPRING5] = {
268 .tp = tp + TYPE2,
269 .p = { SN_PRESSURE, 0, 300, 0 },
270 .w = { SN_WIDTH, 0, 2048, 0 },
271 .x = { SN_COORD, -4415, 5050, 105 },
272 .y = { SN_COORD, -55, 6680, 75 },
273 .o = { SN_ORIENT,
274 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
275 },
276 [BCM5974_FLAG_WELLSPRING6] = {
277 .tp = tp + TYPE2,
278 .p = { SN_PRESSURE, 0, 300, 0 },
279 .w = { SN_WIDTH, 0, 2048, 0 },
280 .x = { SN_COORD, -4620, 5140, 105 },
281 .y = { SN_COORD, -150, 6600, 75 },
282 .o = { SN_ORIENT,
283 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
284 },
285 [BCM5974_FLAG_WELLSPRING5A] = {
286 .tp = tp + TYPE2,
287 .p = { SN_PRESSURE, 0, 300, 0 },
288 .w = { SN_WIDTH, 0, 2048, 0 },
289 .x = { SN_COORD, -4750, 5280, 105 },
290 .y = { SN_COORD, -150, 6730, 75 },
291 .o = { SN_ORIENT,
292 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
293 },
294 [BCM5974_FLAG_WELLSPRING6A] = {
295 .tp = tp + TYPE2,
296 .p = { SN_PRESSURE, 0, 300, 0 },
297 .w = { SN_WIDTH, 0, 2048, 0 },
298 .x = { SN_COORD, -4620, 5140, 105 },
299 .y = { SN_COORD, -150, 6600, 75 },
300 .o = { SN_ORIENT,
301 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
302 },
303 [BCM5974_FLAG_WELLSPRING7] = {
304 .tp = tp + TYPE2,
305 .p = { SN_PRESSURE, 0, 300, 0 },
306 .w = { SN_WIDTH, 0, 2048, 0 },
307 .x = { SN_COORD, -4750, 5280, 105 },
308 .y = { SN_COORD, -150, 6730, 75 },
309 .o = { SN_ORIENT,
310 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
311 },
312 [BCM5974_FLAG_WELLSPRING7A] = {
313 .tp = tp + TYPE2,
314 .p = { SN_PRESSURE, 0, 300, 0 },
315 .w = { SN_WIDTH, 0, 2048, 0 },
316 .x = { SN_COORD, -4750, 5280, 105 },
317 .y = { SN_COORD, -150, 6730, 75 },
318 .o = { SN_ORIENT,
319 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
320 },
321 [BCM5974_FLAG_WELLSPRING8] = {
322 .tp = tp + TYPE3,
323 .p = { SN_PRESSURE, 0, 300, 0 },
324 .w = { SN_WIDTH, 0, 2048, 0 },
325 .x = { SN_COORD, -4620, 5140, 105 },
326 .y = { SN_COORD, -150, 6600, 75 },
327 .o = { SN_ORIENT,
328 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
329 },
330 [BCM5974_FLAG_WELLSPRING9] = {
331 .tp = tp + TYPE4,
332 .p = { SN_PRESSURE, 0, 300, 0 },
333 .w = { SN_WIDTH, 0, 2048, 0 },
334 .x = { SN_COORD, -4828, 5345, 105 },
335 .y = { SN_COORD, -203, 6803, 75 },
336 .o = { SN_ORIENT,
337 -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
338 },
339 };
340
341 #define BCM5974_DEV(v,p,i) { \
342 HID_BVPI(BUS_USB, USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i), \
343 HID_TLC(BCM5974_TLC_PAGE, BCM5974_TLC_USAGE), \
344 }
345
346 static const struct hid_device_id bcm5974_devs[] = {
347 /* MacbookAir1.1 */
348 BCM5974_DEV(APPLE, WELLSPRING_ANSI, BCM5974_FLAG_WELLSPRING1),
349 BCM5974_DEV(APPLE, WELLSPRING_ISO, BCM5974_FLAG_WELLSPRING1),
350 BCM5974_DEV(APPLE, WELLSPRING_JIS, BCM5974_FLAG_WELLSPRING1),
351
352 /* MacbookProPenryn, aka wellspring2 */
353 BCM5974_DEV(APPLE, WELLSPRING2_ANSI, BCM5974_FLAG_WELLSPRING2),
354 BCM5974_DEV(APPLE, WELLSPRING2_ISO, BCM5974_FLAG_WELLSPRING2),
355 BCM5974_DEV(APPLE, WELLSPRING2_JIS, BCM5974_FLAG_WELLSPRING2),
356
357 /* Macbook5,1 (unibody), aka wellspring3 */
358 BCM5974_DEV(APPLE, WELLSPRING3_ANSI, BCM5974_FLAG_WELLSPRING3),
359 BCM5974_DEV(APPLE, WELLSPRING3_ISO, BCM5974_FLAG_WELLSPRING3),
360 BCM5974_DEV(APPLE, WELLSPRING3_JIS, BCM5974_FLAG_WELLSPRING3),
361
362 /* MacbookAir3,2 (unibody), aka wellspring4 */
363 BCM5974_DEV(APPLE, WELLSPRING4_ANSI, BCM5974_FLAG_WELLSPRING4),
364 BCM5974_DEV(APPLE, WELLSPRING4_ISO, BCM5974_FLAG_WELLSPRING4),
365 BCM5974_DEV(APPLE, WELLSPRING4_JIS, BCM5974_FLAG_WELLSPRING4),
366
367 /* MacbookAir3,1 (unibody), aka wellspring4 */
368 BCM5974_DEV(APPLE, WELLSPRING4A_ANSI, BCM5974_FLAG_WELLSPRING4A),
369 BCM5974_DEV(APPLE, WELLSPRING4A_ISO, BCM5974_FLAG_WELLSPRING4A),
370 BCM5974_DEV(APPLE, WELLSPRING4A_JIS, BCM5974_FLAG_WELLSPRING4A),
371
372 /* Macbook8 (unibody, March 2011) */
373 BCM5974_DEV(APPLE, WELLSPRING5_ANSI, BCM5974_FLAG_WELLSPRING5),
374 BCM5974_DEV(APPLE, WELLSPRING5_ISO, BCM5974_FLAG_WELLSPRING5),
375 BCM5974_DEV(APPLE, WELLSPRING5_JIS, BCM5974_FLAG_WELLSPRING5),
376
377 /* MacbookAir4,1 (unibody, July 2011) */
378 BCM5974_DEV(APPLE, WELLSPRING6A_ANSI, BCM5974_FLAG_WELLSPRING6A),
379 BCM5974_DEV(APPLE, WELLSPRING6A_ISO, BCM5974_FLAG_WELLSPRING6A),
380 BCM5974_DEV(APPLE, WELLSPRING6A_JIS, BCM5974_FLAG_WELLSPRING6A),
381
382 /* MacbookAir4,2 (unibody, July 2011) */
383 BCM5974_DEV(APPLE, WELLSPRING6_ANSI, BCM5974_FLAG_WELLSPRING6),
384 BCM5974_DEV(APPLE, WELLSPRING6_ISO, BCM5974_FLAG_WELLSPRING6),
385 BCM5974_DEV(APPLE, WELLSPRING6_JIS, BCM5974_FLAG_WELLSPRING6),
386
387 /* Macbook8,2 (unibody) */
388 BCM5974_DEV(APPLE, WELLSPRING5A_ANSI, BCM5974_FLAG_WELLSPRING5A),
389 BCM5974_DEV(APPLE, WELLSPRING5A_ISO, BCM5974_FLAG_WELLSPRING5A),
390 BCM5974_DEV(APPLE, WELLSPRING5A_JIS, BCM5974_FLAG_WELLSPRING5A),
391
392 /* MacbookPro10,1 (unibody, June 2012) */
393 /* MacbookPro11,1-3 (unibody, June 2013) */
394 BCM5974_DEV(APPLE, WELLSPRING7_ANSI, BCM5974_FLAG_WELLSPRING7),
395 BCM5974_DEV(APPLE, WELLSPRING7_ISO, BCM5974_FLAG_WELLSPRING7),
396 BCM5974_DEV(APPLE, WELLSPRING7_JIS, BCM5974_FLAG_WELLSPRING7),
397
398 /* MacbookPro10,2 (unibody, October 2012) */
399 BCM5974_DEV(APPLE, WELLSPRING7A_ANSI, BCM5974_FLAG_WELLSPRING7A),
400 BCM5974_DEV(APPLE, WELLSPRING7A_ISO, BCM5974_FLAG_WELLSPRING7A),
401 BCM5974_DEV(APPLE, WELLSPRING7A_JIS, BCM5974_FLAG_WELLSPRING7A),
402
403 /* MacbookAir6,2 (unibody, June 2013) */
404 BCM5974_DEV(APPLE, WELLSPRING8_ANSI, BCM5974_FLAG_WELLSPRING8),
405 BCM5974_DEV(APPLE, WELLSPRING8_ISO, BCM5974_FLAG_WELLSPRING8),
406 BCM5974_DEV(APPLE, WELLSPRING8_JIS, BCM5974_FLAG_WELLSPRING8),
407
408 /* MacbookPro12,1 MacbookPro11,4 */
409 BCM5974_DEV(APPLE, WELLSPRING9_ANSI, BCM5974_FLAG_WELLSPRING9),
410 BCM5974_DEV(APPLE, WELLSPRING9_ISO, BCM5974_FLAG_WELLSPRING9),
411 BCM5974_DEV(APPLE, WELLSPRING9_JIS, BCM5974_FLAG_WELLSPRING9),
412 };
413
414 struct bcm5974_softc {
415 device_t sc_dev;
416 struct evdev_dev *sc_evdev;
417 /* device configuration */
418 const struct bcm5974_dev_params *sc_params;
419 bool sc_saved_mode;
420 };
421
422 static const uint8_t bcm5974_rdesc[] = {
423 0x05, BCM5974_TLC_PAGE, /* Usage Page (BCM5974_TLC_PAGE) */
424 0x09, BCM5974_TLC_USAGE,/* Usage (BCM5974_TLC_USAGE) */
425 0xA1, 0x01, /* Collection (Application) */
426 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
427 0x09, 0x01, /* Usage (0x01) */
428 0x15, 0x00, /* Logical Minimum (0) */
429 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
430 0x75, 0x08, /* Report Size (8) */
431 0x96, /* Report Count (BCM5974_BUFFER_MAX) */
432 BCM5974_BUFFER_MAX & 0xFF,
433 BCM5974_BUFFER_MAX >> 8 & 0xFF,
434 0x81, 0x02, /* Input (Data,Var,Abs) */
435 0xC0, /* End Collection */
436 };
437
438 /*
439 * function prototypes
440 */
441 static evdev_open_t bcm5974_ev_open;
442 static evdev_close_t bcm5974_ev_close;
443 static const struct evdev_methods bcm5974_evdev_methods = {
444 .ev_open = &bcm5974_ev_open,
445 .ev_close = &bcm5974_ev_close,
446 };
447 static hid_intr_t bcm5974_intr;
448
449 /* Device methods. */
450 static device_identify_t bcm5974_identify;
451 static device_probe_t bcm5974_probe;
452 static device_attach_t bcm5974_attach;
453 static device_detach_t bcm5974_detach;
454
455 /*
456 * Type1 and Type2 touchpads use keyboard USB interface to switch from HID to
457 * RAW mode. Although it is possible to extend hkbd driver to support such a
458 * mode change requests, it's not wanted due to cross device tree dependencies.
459 * So, find lowest common denominator (struct usb_device of grandparent usbhid
460 * driver) of touchpad and keyboard drivers and issue direct USB requests.
461 */
462 static int
463 bcm5974_set_device_mode_usb(struct bcm5974_softc *sc, bool on)
464 {
465 uint8_t mode_bytes[BCM5974_USB_REPORT_LEN];
466 struct usb_ctl_request ucr;
467 int err;
468
469 ucr.ucr_request.bmRequestType = UT_READ_CLASS_INTERFACE;
470 ucr.ucr_request.bRequest = UR_GET_REPORT;
471 USETW2(ucr.ucr_request.wValue,
472 UHID_FEATURE_REPORT, BCM5974_USB_REPORT_ID);
473 ucr.ucr_request.wIndex[0] = BCM5974_USB_IFACE_INDEX;
474 ucr.ucr_request.wIndex[1] = 0;
475 USETW(ucr.ucr_request.wLength, BCM5974_USB_REPORT_LEN);
476 ucr.ucr_data = mode_bytes;
477
478 err = hid_ioctl(sc->sc_dev, USB_REQUEST, (uintptr_t)&ucr);
479 if (err != 0) {
480 DPRINTF("Failed to read device mode (%d)\n", err);
481 return (EIO);
482 }
483 #if 0
484 /*
485 * XXX Need to wait at least 250ms for hardware to get
486 * ready. The device mode handling appears to be handled
487 * asynchronously and we should not issue these commands too
488 * quickly.
489 */
490 pause("WHW", hz / 4);
491 #endif
492 mode_bytes[0] = on ? BCM5974_USB_MODE_RAW : BCM5974_USB_MODE_HID;
493 ucr.ucr_request.bmRequestType = UT_WRITE_CLASS_INTERFACE;
494 ucr.ucr_request.bRequest = UR_SET_REPORT;
495
496 err = hid_ioctl(sc->sc_dev, USB_REQUEST, (uintptr_t)&ucr);
497 if (err != 0) {
498 DPRINTF("Failed to write device mode (%d)\n", err);
499 return (EIO);
500 }
501
502 return (0);
503 }
504
505 static int
506 bcm5974_set_device_mode_hid(struct bcm5974_softc *sc, bool on)
507 {
508 uint8_t mode_bytes[BCM5974_HID_REPORT_LEN] = {
509 BCM5974_HID_REPORT_ID,
510 on ? BCM5974_HID_MODE_RAW : BCM5974_HID_MODE_HID,
511 };
512 #if 0
513 int err;
514
515 err = hid_get_report(sc->sc_dev, mode_bytes, BCM5974_HID_REPORT_LEN,
516 NULL, HID_FEATURE_REPORT, BCM5974_HID_REPORT_ID);
517 if (err != 0) {
518 DPRINTF("Failed to read device mode (%d)\n", err);
519 return (err);
520 }
521 /*
522 * XXX Need to wait at least 250ms for hardware to get
523 * ready. The device mode handling appears to be handled
524 * asynchronously and we should not issue these commands too
525 * quickly.
526 */
527 pause("WHW", hz / 4);
528 mode_bytes[1] = on ? BCM5974_HID_MODE_RAW : BCM5974_HID_MODE_HID;
529 #endif
530 return (hid_set_report(sc->sc_dev, mode_bytes, BCM5974_HID_REPORT_LEN,
531 HID_FEATURE_REPORT, BCM5974_HID_REPORT_ID));
532 }
533
534 static int
535 bcm5974_set_device_mode(struct bcm5974_softc *sc, bool on)
536 {
537 int err = 0;
538
539 switch (sc->sc_params->tp - tp) {
540 case TYPE1:
541 case TYPE2:
542 err = bcm5974_set_device_mode_usb(sc, on);
543 break;
544 case TYPE3: /* Type 3 does not require a mode switch */
545 break;
546 case TYPE4:
547 err = bcm5974_set_device_mode_hid(sc, on);
548 break;
549 default:
550 KASSERT(0 == 1, ("Unknown trackpad type"));
551 }
552
553 if (!err)
554 sc->sc_saved_mode = on;
555
556 return (err);
557 }
558
559 static void
560 bcm5974_identify(driver_t *driver, device_t parent)
561 {
562 void *d_ptr;
563 hid_size_t d_len;
564
565 /*
566 * The bcm5974 touchpad has no stable RAW mode TLC in its report
567 * descriptor. So replace existing HID mode mouse TLC with dummy one
568 * to set proper transport layer buffer sizes, make driver probe
569 * simpler and prevent unwanted hms driver attachment.
570 */
571 if (HIDBUS_LOOKUP_ID(parent, bcm5974_devs) != NULL &&
572 hid_get_report_descr(parent, &d_ptr, &d_len) == 0 &&
573 hid_is_mouse(d_ptr, d_len))
574 hid_set_report_descr(parent, bcm5974_rdesc,
575 sizeof(bcm5974_rdesc));
576 }
577
578 static int
579 bcm5974_probe(device_t dev)
580 {
581 int err;
582
583 err = HIDBUS_LOOKUP_DRIVER_INFO(dev, bcm5974_devs);
584 if (err != 0)
585 return (err);
586
587 hidbus_set_desc(dev, "Touchpad");
588
589 return (BUS_PROBE_DEFAULT);
590 }
591
592 static int
593 bcm5974_attach(device_t dev)
594 {
595 struct bcm5974_softc *sc = device_get_softc(dev);
596 const struct hid_device_info *hw = hid_get_device_info(dev);
597 int err;
598
599 DPRINTFN(BCM5974_LLEVEL_INFO, "sc=%p\n", sc);
600
601 sc->sc_dev = dev;
602
603 /* get device specific configuration */
604 sc->sc_params = bcm5974_dev_params + hidbus_get_driver_info(dev);
605
606 sc->sc_evdev = evdev_alloc();
607 evdev_set_name(sc->sc_evdev, device_get_desc(dev));
608 evdev_set_phys(sc->sc_evdev, device_get_nameunit(dev));
609 evdev_set_id(sc->sc_evdev, hw->idBus, hw->idVendor, hw->idProduct,
610 hw->idVersion);
611 evdev_set_serial(sc->sc_evdev, hw->serial);
612 evdev_set_methods(sc->sc_evdev, sc, &bcm5974_evdev_methods);
613 evdev_support_prop(sc->sc_evdev, INPUT_PROP_POINTER);
614 evdev_support_event(sc->sc_evdev, EV_SYN);
615 evdev_support_event(sc->sc_evdev, EV_ABS);
616 evdev_support_event(sc->sc_evdev, EV_KEY);
617 evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_EXT_EPOCH); /* hidbus child */
618
619 #define BCM5974_ABS(evdev, code, param) \
620 evdev_support_abs((evdev), (code), (param).min, (param).max, \
621 ((param).max - (param).min) / (param).snratio, 0, \
622 (param).size != 0 ? ((param).max - (param).min) / (param).size : 0);
623
624 /* finger position */
625 BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_X, sc->sc_params->x);
626 BCM5974_ABS(sc->sc_evdev, ABS_MT_POSITION_Y, sc->sc_params->y);
627 /* finger pressure */
628 BCM5974_ABS(sc->sc_evdev, ABS_MT_PRESSURE, sc->sc_params->p);
629 /* finger touch area */
630 BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
631 BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
632 /* finger approach area */
633 BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w);
634 BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w);
635 /* finger orientation */
636 BCM5974_ABS(sc->sc_evdev, ABS_MT_ORIENTATION, sc->sc_params->o);
637 /* button properties */
638 evdev_support_key(sc->sc_evdev, BTN_LEFT);
639 if ((sc->sc_params->tp->caps & HAS_INTEGRATED_BUTTON) != 0)
640 evdev_support_prop(sc->sc_evdev, INPUT_PROP_BUTTONPAD);
641 /* Enable automatic touch assignment for type B MT protocol */
642 evdev_support_abs(sc->sc_evdev, ABS_MT_SLOT,
643 0, MAX_FINGERS - 1, 0, 0, 0);
644 evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID,
645 -1, MAX_FINGERS - 1, 0, 0, 0);
646 evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK);
647 evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL);
648 /* Synaptics compatibility events */
649 evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT);
650
651 err = evdev_register(sc->sc_evdev);
652 if (err)
653 goto detach;
654
655 hidbus_set_intr(dev, bcm5974_intr, sc);
656
657 return (0);
658
659 detach:
660 bcm5974_detach(dev);
661 return (ENOMEM);
662 }
663
664 static int
665 bcm5974_detach(device_t dev)
666 {
667 struct bcm5974_softc *sc = device_get_softc(dev);
668
669 evdev_free(sc->sc_evdev);
670
671 return (0);
672 }
673
674 static int
675 bcm5974_resume(device_t dev)
676 {
677 struct bcm5974_softc *sc = device_get_softc(dev);
678
679 bcm5974_set_device_mode(sc, sc->sc_saved_mode);
680
681 return (0);
682 }
683
684 static void
685 bcm5974_intr(void *context, void *data, hid_size_t len)
686 {
687 struct bcm5974_softc *sc = context;
688 const struct bcm5974_dev_params *params = sc->sc_params;
689 union evdev_mt_slot slot_data;
690 struct tp_finger *f;
691 int ntouch; /* the finger number in touch */
692 int ibt; /* button status */
693 int i;
694 int slot;
695 uint8_t fsize = sizeof(struct tp_finger) + params->tp->delta;
696
697 if ((len < params->tp->offset + fsize) ||
698 ((len - params->tp->offset) % fsize) != 0) {
699 DPRINTFN(BCM5974_LLEVEL_INFO, "Invalid length: %d, %x, %x\n",
700 len, sc->tp_data[0], sc->tp_data[1]);
701 return;
702 }
703
704 ibt = ((uint8_t *)data)[params->tp->button];
705 ntouch = (len - params->tp->offset) / fsize;
706
707 for (i = 0, slot = 0; i != ntouch; i++) {
708 f = (struct tp_finger *)(((uint8_t *)data) +
709 params->tp->offset + params->tp->delta + i * fsize);
710 DPRINTFN(BCM5974_LLEVEL_INFO,
711 "[%d]ibt=%d, taps=%d, o=%4d, ax=%5d, ay=%5d, "
712 "rx=%5d, ry=%5d, tlmaj=%4d, tlmin=%4d, ot=%4x, "
713 "tchmaj=%4d, tchmin=%4d, pressure=%4d, m=%4x\n",
714 i, ibt, ntouch, BCM5974_LE2H(f->origin),
715 BCM5974_LE2H(f->abs_x), BCM5974_LE2H(f->abs_y),
716 BCM5974_LE2H(f->rel_x), BCM5974_LE2H(f->rel_y),
717 BCM5974_LE2H(f->tool_major), BCM5974_LE2H(f->tool_minor),
718 BCM5974_LE2H(f->orientation), BCM5974_LE2H(f->touch_major),
719 BCM5974_LE2H(f->touch_minor), BCM5974_LE2H(f->pressure),
720 BCM5974_LE2H(f->multi));
721
722 if (BCM5974_LE2H(f->touch_major) == 0)
723 continue;
724 slot_data = (union evdev_mt_slot) {
725 .id = slot,
726 .x = BCM5974_LE2H(f->abs_x),
727 .y = params->y.min + params->y.max -
728 BCM5974_LE2H(f->abs_y),
729 .p = BCM5974_LE2H(f->pressure),
730 .maj = BCM5974_LE2H(f->touch_major) << 1,
731 .min = BCM5974_LE2H(f->touch_minor) << 1,
732 .w_maj = BCM5974_LE2H(f->tool_major) << 1,
733 .w_min = BCM5974_LE2H(f->tool_minor) << 1,
734 .ori = params->o.max - BCM5974_LE2H(f->orientation),
735 };
736 evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data);
737 slot++;
738 }
739
740 evdev_push_key(sc->sc_evdev, BTN_LEFT, ibt);
741 evdev_sync(sc->sc_evdev);
742 }
743
744 static int
745 bcm5974_ev_open(struct evdev_dev *evdev)
746 {
747 struct bcm5974_softc *sc = evdev_get_softc(evdev);
748 int err;
749
750 /*
751 * By default the touchpad behaves like a HID device, sending
752 * packets with reportID = 8. Such reports contain only
753 * limited information. They encode movement deltas and button
754 * events, but do not include data from the pressure
755 * sensors. The device input mode can be switched from HID
756 * reports to raw sensor data using vendor-specific USB
757 * control commands:
758 */
759 err = bcm5974_set_device_mode(sc, true);
760 if (err != 0) {
761 DPRINTF("failed to set mode to RAW MODE (%d)\n", err);
762 return (err);
763 }
764
765 return (hidbus_intr_start(sc->sc_dev));
766 }
767
768 static int
769 bcm5974_ev_close(struct evdev_dev *evdev)
770 {
771 struct bcm5974_softc *sc = evdev_get_softc(evdev);
772 int err;
773
774 err = hidbus_intr_stop(sc->sc_dev);
775 if (err != 0)
776 return (err);
777
778 /*
779 * During re-enumeration of the device we need to force the
780 * device back into HID mode before switching it to RAW
781 * mode. Else the device does not work like expected.
782 */
783 err = bcm5974_set_device_mode(sc, false);
784 if (err != 0)
785 DPRINTF("Failed to set mode to HID MODE (%d)\n", err);
786
787 return (err);
788 }
789
790 static device_method_t bcm5974_methods[] = {
791 /* Device interface */
792 DEVMETHOD(device_identify, bcm5974_identify),
793 DEVMETHOD(device_probe, bcm5974_probe),
794 DEVMETHOD(device_attach, bcm5974_attach),
795 DEVMETHOD(device_detach, bcm5974_detach),
796 DEVMETHOD(device_resume, bcm5974_resume),
797 DEVMETHOD_END
798 };
799
800 static driver_t bcm5974_driver = {
801 .name = "bcm5974",
802 .methods = bcm5974_methods,
803 .size = sizeof(struct bcm5974_softc)
804 };
805
806 DRIVER_MODULE(bcm5974, hidbus, bcm5974_driver, NULL, NULL);
807 MODULE_DEPEND(bcm5974, hidbus, 1, 1, 1);
808 MODULE_DEPEND(bcm5974, hid, 1, 1, 1);
809 MODULE_DEPEND(bcm5974, evdev, 1, 1, 1);
810 MODULE_VERSION(bcm5974, 1);
811 HID_PNP_INFO(bcm5974_devs);
Cache object: 822df85601fd4dfe41168ffd32317df3
|