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