1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * Copyright (c) 2014 Rohit Grover |
---|
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 | * |
---|
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
17 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
20 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
21 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
22 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
24 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
25 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
26 | * SUCH DAMAGE. |
---|
27 | */ |
---|
28 | |
---|
29 | /* |
---|
30 | * Some tables, structures, definitions and constant values for the |
---|
31 | * touchpad protocol has been copied from Linux's |
---|
32 | * "drivers/input/mouse/bcm5974.c" which has the following copyright |
---|
33 | * holders under GPLv2. All device specific code in this driver has |
---|
34 | * been written from scratch. The decoding algorithm is based on |
---|
35 | * output from FreeBSD's usbdump. |
---|
36 | * |
---|
37 | * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) |
---|
38 | * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) |
---|
39 | * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) |
---|
40 | * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) |
---|
41 | * Copyright (C) 2005 Stelian Pop (stelian@popies.net) |
---|
42 | * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) |
---|
43 | * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) |
---|
44 | * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) |
---|
45 | * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) |
---|
46 | */ |
---|
47 | |
---|
48 | /* |
---|
49 | * Author's note: 'atp' supports two distinct families of Apple trackpad |
---|
50 | * products: the older Fountain/Geyser and the latest Wellspring trackpads. |
---|
51 | * The first version made its appearance with FreeBSD 8 and worked only with |
---|
52 | * the Fountain/Geyser hardware. A fork of this driver for Wellspring was |
---|
53 | * contributed by Huang Wen Hui. This driver unifies the Wellspring effort |
---|
54 | * and also improves upon the original work. |
---|
55 | * |
---|
56 | * I'm grateful to Stephan Scheunig, Angela Naegele, and Nokia IT-support |
---|
57 | * for helping me with access to hardware. Thanks also go to Nokia for |
---|
58 | * giving me an opportunity to do this work. |
---|
59 | */ |
---|
60 | |
---|
61 | #include <sys/cdefs.h> |
---|
62 | __FBSDID("$FreeBSD$"); |
---|
63 | |
---|
64 | #include <sys/stdint.h> |
---|
65 | #include <sys/stddef.h> |
---|
66 | #include <rtems/bsd/sys/param.h> |
---|
67 | #include <sys/types.h> |
---|
68 | #include <sys/systm.h> |
---|
69 | #include <sys/kernel.h> |
---|
70 | #include <sys/bus.h> |
---|
71 | #include <sys/module.h> |
---|
72 | #include <rtems/bsd/sys/lock.h> |
---|
73 | #include <sys/mutex.h> |
---|
74 | #include <sys/sysctl.h> |
---|
75 | #include <sys/malloc.h> |
---|
76 | #include <sys/conf.h> |
---|
77 | #include <sys/fcntl.h> |
---|
78 | #include <sys/file.h> |
---|
79 | #include <sys/selinfo.h> |
---|
80 | #include <sys/poll.h> |
---|
81 | |
---|
82 | #include <dev/usb/usb.h> |
---|
83 | #include <dev/usb/usbdi.h> |
---|
84 | #include <dev/usb/usbdi_util.h> |
---|
85 | #include <dev/usb/usbhid.h> |
---|
86 | |
---|
87 | #include <rtems/bsd/local/usbdevs.h> |
---|
88 | |
---|
89 | #define USB_DEBUG_VAR atp_debug |
---|
90 | #include <dev/usb/usb_debug.h> |
---|
91 | |
---|
92 | #include <sys/mouse.h> |
---|
93 | |
---|
94 | #define ATP_DRIVER_NAME "atp" |
---|
95 | |
---|
96 | /* |
---|
97 | * Driver specific options: the following options may be set by |
---|
98 | * `options' statements in the kernel configuration file. |
---|
99 | */ |
---|
100 | |
---|
101 | /* The divisor used to translate sensor reported positions to mickeys. */ |
---|
102 | #ifndef ATP_SCALE_FACTOR |
---|
103 | #define ATP_SCALE_FACTOR 16 |
---|
104 | #endif |
---|
105 | |
---|
106 | /* Threshold for small movement noise (in mickeys) */ |
---|
107 | #ifndef ATP_SMALL_MOVEMENT_THRESHOLD |
---|
108 | #define ATP_SMALL_MOVEMENT_THRESHOLD 30 |
---|
109 | #endif |
---|
110 | |
---|
111 | /* Threshold of instantaneous deltas beyond which movement is considered fast.*/ |
---|
112 | #ifndef ATP_FAST_MOVEMENT_TRESHOLD |
---|
113 | #define ATP_FAST_MOVEMENT_TRESHOLD 150 |
---|
114 | #endif |
---|
115 | |
---|
116 | /* |
---|
117 | * This is the age in microseconds beyond which a touch is considered |
---|
118 | * to be a slide; and therefore a tap event isn't registered. |
---|
119 | */ |
---|
120 | #ifndef ATP_TOUCH_TIMEOUT |
---|
121 | #define ATP_TOUCH_TIMEOUT 125000 |
---|
122 | #endif |
---|
123 | |
---|
124 | #ifndef ATP_IDLENESS_THRESHOLD |
---|
125 | #define ATP_IDLENESS_THRESHOLD 10 |
---|
126 | #endif |
---|
127 | |
---|
128 | #ifndef FG_SENSOR_NOISE_THRESHOLD |
---|
129 | #define FG_SENSOR_NOISE_THRESHOLD 2 |
---|
130 | #endif |
---|
131 | |
---|
132 | /* |
---|
133 | * A double-tap followed by a single-finger slide is treated as a |
---|
134 | * special gesture. The driver responds to this gesture by assuming a |
---|
135 | * virtual button-press for the lifetime of the slide. The following |
---|
136 | * threshold is the maximum time gap (in microseconds) between the two |
---|
137 | * tap events preceding the slide for such a gesture. |
---|
138 | */ |
---|
139 | #ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD |
---|
140 | #define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000 |
---|
141 | #endif |
---|
142 | |
---|
143 | /* |
---|
144 | * The wait duration in ticks after losing a touch contact before |
---|
145 | * zombied strokes are reaped and turned into button events. |
---|
146 | */ |
---|
147 | #define ATP_ZOMBIE_STROKE_REAP_INTERVAL (hz / 20) /* 50 ms */ |
---|
148 | |
---|
149 | /* The multiplier used to translate sensor reported positions to mickeys. */ |
---|
150 | #define FG_SCALE_FACTOR 380 |
---|
151 | |
---|
152 | /* |
---|
153 | * The movement threshold for a stroke; this is the maximum difference |
---|
154 | * in position which will be resolved as a continuation of a stroke |
---|
155 | * component. |
---|
156 | */ |
---|
157 | #define FG_MAX_DELTA_MICKEYS ((3 * (FG_SCALE_FACTOR)) >> 1) |
---|
158 | |
---|
159 | /* Distance-squared threshold for matching a finger with a known stroke */ |
---|
160 | #ifndef WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ |
---|
161 | #define WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ 1000000 |
---|
162 | #endif |
---|
163 | |
---|
164 | /* Ignore pressure spans with cumulative press. below this value. */ |
---|
165 | #define FG_PSPAN_MIN_CUM_PRESSURE 10 |
---|
166 | |
---|
167 | /* Maximum allowed width for pressure-spans.*/ |
---|
168 | #define FG_PSPAN_MAX_WIDTH 4 |
---|
169 | |
---|
170 | /* end of driver specific options */ |
---|
171 | |
---|
172 | /* Tunables */ |
---|
173 | static SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB ATP"); |
---|
174 | |
---|
175 | #ifdef USB_DEBUG |
---|
176 | enum atp_log_level { |
---|
177 | ATP_LLEVEL_DISABLED = 0, |
---|
178 | ATP_LLEVEL_ERROR, |
---|
179 | ATP_LLEVEL_DEBUG, /* for troubleshooting */ |
---|
180 | ATP_LLEVEL_INFO, /* for diagnostics */ |
---|
181 | }; |
---|
182 | static int atp_debug = ATP_LLEVEL_ERROR; /* the default is to only log errors */ |
---|
183 | SYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RWTUN, |
---|
184 | &atp_debug, ATP_LLEVEL_ERROR, "ATP debug level"); |
---|
185 | #endif /* USB_DEBUG */ |
---|
186 | |
---|
187 | static u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT; |
---|
188 | SYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RWTUN, |
---|
189 | &atp_touch_timeout, 125000, "age threshold in microseconds for a touch"); |
---|
190 | |
---|
191 | static u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD; |
---|
192 | SYSCTL_UINT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RWTUN, |
---|
193 | &atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD, |
---|
194 | "maximum time in microseconds to allow association between a double-tap and " |
---|
195 | "drag gesture"); |
---|
196 | |
---|
197 | static u_int atp_mickeys_scale_factor = ATP_SCALE_FACTOR; |
---|
198 | static int atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS); |
---|
199 | SYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RWTUN, |
---|
200 | &atp_mickeys_scale_factor, sizeof(atp_mickeys_scale_factor), |
---|
201 | atp_sysctl_scale_factor_handler, "IU", "movement scale factor"); |
---|
202 | |
---|
203 | static u_int atp_small_movement_threshold = ATP_SMALL_MOVEMENT_THRESHOLD; |
---|
204 | SYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RWTUN, |
---|
205 | &atp_small_movement_threshold, ATP_SMALL_MOVEMENT_THRESHOLD, |
---|
206 | "the small movement black-hole for filtering noise"); |
---|
207 | |
---|
208 | static u_int atp_tap_minimum = 1; |
---|
209 | SYSCTL_UINT(_hw_usb_atp, OID_AUTO, tap_minimum, CTLFLAG_RWTUN, |
---|
210 | &atp_tap_minimum, 1, "Minimum number of taps before detection"); |
---|
211 | |
---|
212 | /* |
---|
213 | * Strokes which accumulate at least this amount of absolute movement |
---|
214 | * from the aggregate of their components are considered as |
---|
215 | * slides. Unit: mickeys. |
---|
216 | */ |
---|
217 | static u_int atp_slide_min_movement = 2 * ATP_SMALL_MOVEMENT_THRESHOLD; |
---|
218 | SYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RWTUN, |
---|
219 | &atp_slide_min_movement, 2 * ATP_SMALL_MOVEMENT_THRESHOLD, |
---|
220 | "strokes with at least this amt. of movement are considered slides"); |
---|
221 | |
---|
222 | /* |
---|
223 | * The minimum age of a stroke for it to be considered mature; this |
---|
224 | * helps filter movements (noise) from immature strokes. Units: interrupts. |
---|
225 | */ |
---|
226 | static u_int atp_stroke_maturity_threshold = 4; |
---|
227 | SYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RWTUN, |
---|
228 | &atp_stroke_maturity_threshold, 4, |
---|
229 | "the minimum age of a stroke for it to be considered mature"); |
---|
230 | |
---|
231 | typedef enum atp_trackpad_family { |
---|
232 | TRACKPAD_FAMILY_FOUNTAIN_GEYSER, |
---|
233 | TRACKPAD_FAMILY_WELLSPRING, |
---|
234 | TRACKPAD_FAMILY_MAX /* keep this at the tail end of the enumeration */ |
---|
235 | } trackpad_family_t; |
---|
236 | |
---|
237 | enum fountain_geyser_product { |
---|
238 | FOUNTAIN, |
---|
239 | GEYSER1, |
---|
240 | GEYSER1_17inch, |
---|
241 | GEYSER2, |
---|
242 | GEYSER3, |
---|
243 | GEYSER4, |
---|
244 | FOUNTAIN_GEYSER_PRODUCT_MAX /* keep this at the end */ |
---|
245 | }; |
---|
246 | |
---|
247 | enum wellspring_product { |
---|
248 | WELLSPRING1, |
---|
249 | WELLSPRING2, |
---|
250 | WELLSPRING3, |
---|
251 | WELLSPRING4, |
---|
252 | WELLSPRING4A, |
---|
253 | WELLSPRING5, |
---|
254 | WELLSPRING6A, |
---|
255 | WELLSPRING6, |
---|
256 | WELLSPRING5A, |
---|
257 | WELLSPRING7, |
---|
258 | WELLSPRING7A, |
---|
259 | WELLSPRING8, |
---|
260 | WELLSPRING_PRODUCT_MAX /* keep this at the end of the enumeration */ |
---|
261 | }; |
---|
262 | |
---|
263 | /* trackpad header types */ |
---|
264 | enum fountain_geyser_trackpad_type { |
---|
265 | FG_TRACKPAD_TYPE_GEYSER1, |
---|
266 | FG_TRACKPAD_TYPE_GEYSER2, |
---|
267 | FG_TRACKPAD_TYPE_GEYSER3, |
---|
268 | FG_TRACKPAD_TYPE_GEYSER4, |
---|
269 | }; |
---|
270 | enum wellspring_trackpad_type { |
---|
271 | WSP_TRACKPAD_TYPE1, /* plain trackpad */ |
---|
272 | WSP_TRACKPAD_TYPE2, /* button integrated in trackpad */ |
---|
273 | WSP_TRACKPAD_TYPE3 /* additional header fields since June 2013 */ |
---|
274 | }; |
---|
275 | |
---|
276 | /* |
---|
277 | * Trackpad family and product and family are encoded together in the |
---|
278 | * driver_info value associated with a trackpad product. |
---|
279 | */ |
---|
280 | #define N_PROD_BITS 8 /* Number of bits used to encode product */ |
---|
281 | #define ENCODE_DRIVER_INFO(FAMILY, PROD) \ |
---|
282 | (((FAMILY) << N_PROD_BITS) | (PROD)) |
---|
283 | #define DECODE_FAMILY_FROM_DRIVER_INFO(INFO) ((INFO) >> N_PROD_BITS) |
---|
284 | #define DECODE_PRODUCT_FROM_DRIVER_INFO(INFO) \ |
---|
285 | ((INFO) & ((1 << N_PROD_BITS) - 1)) |
---|
286 | |
---|
287 | #define FG_DRIVER_INFO(PRODUCT) \ |
---|
288 | ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_FOUNTAIN_GEYSER, PRODUCT) |
---|
289 | #define WELLSPRING_DRIVER_INFO(PRODUCT) \ |
---|
290 | ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_WELLSPRING, PRODUCT) |
---|
291 | |
---|
292 | /* |
---|
293 | * The following structure captures the state of a pressure span along |
---|
294 | * an axis. Each contact with the touchpad results in separate |
---|
295 | * pressure spans along the two axes. |
---|
296 | */ |
---|
297 | typedef struct fg_pspan { |
---|
298 | u_int width; /* in units of sensors */ |
---|
299 | u_int cum; /* cumulative compression (from all sensors) */ |
---|
300 | u_int cog; /* center of gravity */ |
---|
301 | u_int loc; /* location (scaled using the mickeys factor) */ |
---|
302 | boolean_t matched; /* to track pspans as they match against strokes. */ |
---|
303 | } fg_pspan; |
---|
304 | |
---|
305 | #define FG_MAX_PSPANS_PER_AXIS 3 |
---|
306 | #define FG_MAX_STROKES (2 * FG_MAX_PSPANS_PER_AXIS) |
---|
307 | |
---|
308 | #define WELLSPRING_INTERFACE_INDEX 1 |
---|
309 | |
---|
310 | /* trackpad finger data offsets, le16-aligned */ |
---|
311 | #define WSP_TYPE1_FINGER_DATA_OFFSET (13 * 2) |
---|
312 | #define WSP_TYPE2_FINGER_DATA_OFFSET (15 * 2) |
---|
313 | #define WSP_TYPE3_FINGER_DATA_OFFSET (19 * 2) |
---|
314 | |
---|
315 | /* trackpad button data offsets */ |
---|
316 | #define WSP_TYPE2_BUTTON_DATA_OFFSET 15 |
---|
317 | #define WSP_TYPE3_BUTTON_DATA_OFFSET 23 |
---|
318 | |
---|
319 | /* list of device capability bits */ |
---|
320 | #define HAS_INTEGRATED_BUTTON 1 |
---|
321 | |
---|
322 | /* trackpad finger structure - little endian */ |
---|
323 | struct wsp_finger_sensor_data { |
---|
324 | int16_t origin; /* zero when switching track finger */ |
---|
325 | int16_t abs_x; /* absolute x coordinate */ |
---|
326 | int16_t abs_y; /* absolute y coordinate */ |
---|
327 | int16_t rel_x; /* relative x coordinate */ |
---|
328 | int16_t rel_y; /* relative y coordinate */ |
---|
329 | int16_t tool_major; /* tool area, major axis */ |
---|
330 | int16_t tool_minor; /* tool area, minor axis */ |
---|
331 | int16_t orientation; /* 16384 when point, else 15 bit angle */ |
---|
332 | int16_t touch_major; /* touch area, major axis */ |
---|
333 | int16_t touch_minor; /* touch area, minor axis */ |
---|
334 | int16_t unused[3]; /* zeros */ |
---|
335 | int16_t multi; /* one finger: varies, more fingers: constant */ |
---|
336 | } __packed; |
---|
337 | |
---|
338 | typedef struct wsp_finger { |
---|
339 | /* to track fingers as they match against strokes. */ |
---|
340 | boolean_t matched; |
---|
341 | |
---|
342 | /* location (scaled using the mickeys factor) */ |
---|
343 | int x; |
---|
344 | int y; |
---|
345 | } wsp_finger_t; |
---|
346 | |
---|
347 | #define WSP_MAX_FINGERS 16 |
---|
348 | #define WSP_SIZEOF_FINGER_SENSOR_DATA sizeof(struct wsp_finger_sensor_data) |
---|
349 | #define WSP_SIZEOF_ALL_FINGER_DATA (WSP_MAX_FINGERS * \ |
---|
350 | WSP_SIZEOF_FINGER_SENSOR_DATA) |
---|
351 | #define WSP_MAX_FINGER_ORIENTATION 16384 |
---|
352 | |
---|
353 | #define ATP_SENSOR_DATA_BUF_MAX 1024 |
---|
354 | #if (ATP_SENSOR_DATA_BUF_MAX < ((WSP_MAX_FINGERS * 14 * 2) + \ |
---|
355 | WSP_TYPE3_FINGER_DATA_OFFSET)) |
---|
356 | /* note: 14 * 2 in the above is based on sizeof(struct wsp_finger_sensor_data)*/ |
---|
357 | #error "ATP_SENSOR_DATA_BUF_MAX is too small" |
---|
358 | #endif |
---|
359 | |
---|
360 | #define ATP_MAX_STROKES MAX(WSP_MAX_FINGERS, FG_MAX_STROKES) |
---|
361 | |
---|
362 | #define FG_MAX_XSENSORS 26 |
---|
363 | #define FG_MAX_YSENSORS 16 |
---|
364 | |
---|
365 | /* device-specific configuration */ |
---|
366 | struct fg_dev_params { |
---|
367 | u_int data_len; /* for sensor data */ |
---|
368 | u_int n_xsensors; |
---|
369 | u_int n_ysensors; |
---|
370 | enum fountain_geyser_trackpad_type prot; |
---|
371 | }; |
---|
372 | struct wsp_dev_params { |
---|
373 | uint8_t caps; /* device capability bitmask */ |
---|
374 | uint8_t tp_type; /* type of trackpad interface */ |
---|
375 | uint8_t finger_data_offset; /* offset to trackpad finger data */ |
---|
376 | }; |
---|
377 | |
---|
378 | static const struct fg_dev_params fg_dev_params[FOUNTAIN_GEYSER_PRODUCT_MAX] = { |
---|
379 | [FOUNTAIN] = { |
---|
380 | .data_len = 81, |
---|
381 | .n_xsensors = 16, |
---|
382 | .n_ysensors = 16, |
---|
383 | .prot = FG_TRACKPAD_TYPE_GEYSER1 |
---|
384 | }, |
---|
385 | [GEYSER1] = { |
---|
386 | .data_len = 81, |
---|
387 | .n_xsensors = 16, |
---|
388 | .n_ysensors = 16, |
---|
389 | .prot = FG_TRACKPAD_TYPE_GEYSER1 |
---|
390 | }, |
---|
391 | [GEYSER1_17inch] = { |
---|
392 | .data_len = 81, |
---|
393 | .n_xsensors = 26, |
---|
394 | .n_ysensors = 16, |
---|
395 | .prot = FG_TRACKPAD_TYPE_GEYSER1 |
---|
396 | }, |
---|
397 | [GEYSER2] = { |
---|
398 | .data_len = 64, |
---|
399 | .n_xsensors = 15, |
---|
400 | .n_ysensors = 9, |
---|
401 | .prot = FG_TRACKPAD_TYPE_GEYSER2 |
---|
402 | }, |
---|
403 | [GEYSER3] = { |
---|
404 | .data_len = 64, |
---|
405 | .n_xsensors = 20, |
---|
406 | .n_ysensors = 10, |
---|
407 | .prot = FG_TRACKPAD_TYPE_GEYSER3 |
---|
408 | }, |
---|
409 | [GEYSER4] = { |
---|
410 | .data_len = 64, |
---|
411 | .n_xsensors = 20, |
---|
412 | .n_ysensors = 10, |
---|
413 | .prot = FG_TRACKPAD_TYPE_GEYSER4 |
---|
414 | } |
---|
415 | }; |
---|
416 | |
---|
417 | static const STRUCT_USB_HOST_ID fg_devs[] = { |
---|
418 | /* PowerBooks Feb 2005, iBooks G4 */ |
---|
419 | { USB_VPI(USB_VENDOR_APPLE, 0x020e, FG_DRIVER_INFO(FOUNTAIN)) }, |
---|
420 | { USB_VPI(USB_VENDOR_APPLE, 0x020f, FG_DRIVER_INFO(FOUNTAIN)) }, |
---|
421 | { USB_VPI(USB_VENDOR_APPLE, 0x0210, FG_DRIVER_INFO(FOUNTAIN)) }, |
---|
422 | { USB_VPI(USB_VENDOR_APPLE, 0x030a, FG_DRIVER_INFO(FOUNTAIN)) }, |
---|
423 | { USB_VPI(USB_VENDOR_APPLE, 0x030b, FG_DRIVER_INFO(GEYSER1)) }, |
---|
424 | |
---|
425 | /* PowerBooks Oct 2005 */ |
---|
426 | { USB_VPI(USB_VENDOR_APPLE, 0x0214, FG_DRIVER_INFO(GEYSER2)) }, |
---|
427 | { USB_VPI(USB_VENDOR_APPLE, 0x0215, FG_DRIVER_INFO(GEYSER2)) }, |
---|
428 | { USB_VPI(USB_VENDOR_APPLE, 0x0216, FG_DRIVER_INFO(GEYSER2)) }, |
---|
429 | |
---|
430 | /* Core Duo MacBook & MacBook Pro */ |
---|
431 | { USB_VPI(USB_VENDOR_APPLE, 0x0217, FG_DRIVER_INFO(GEYSER3)) }, |
---|
432 | { USB_VPI(USB_VENDOR_APPLE, 0x0218, FG_DRIVER_INFO(GEYSER3)) }, |
---|
433 | { USB_VPI(USB_VENDOR_APPLE, 0x0219, FG_DRIVER_INFO(GEYSER3)) }, |
---|
434 | |
---|
435 | /* Core2 Duo MacBook & MacBook Pro */ |
---|
436 | { USB_VPI(USB_VENDOR_APPLE, 0x021a, FG_DRIVER_INFO(GEYSER4)) }, |
---|
437 | { USB_VPI(USB_VENDOR_APPLE, 0x021b, FG_DRIVER_INFO(GEYSER4)) }, |
---|
438 | { USB_VPI(USB_VENDOR_APPLE, 0x021c, FG_DRIVER_INFO(GEYSER4)) }, |
---|
439 | |
---|
440 | /* Core2 Duo MacBook3,1 */ |
---|
441 | { USB_VPI(USB_VENDOR_APPLE, 0x0229, FG_DRIVER_INFO(GEYSER4)) }, |
---|
442 | { USB_VPI(USB_VENDOR_APPLE, 0x022a, FG_DRIVER_INFO(GEYSER4)) }, |
---|
443 | { USB_VPI(USB_VENDOR_APPLE, 0x022b, FG_DRIVER_INFO(GEYSER4)) }, |
---|
444 | |
---|
445 | /* 17 inch PowerBook */ |
---|
446 | { USB_VPI(USB_VENDOR_APPLE, 0x020d, FG_DRIVER_INFO(GEYSER1_17inch)) }, |
---|
447 | }; |
---|
448 | |
---|
449 | static const struct wsp_dev_params wsp_dev_params[WELLSPRING_PRODUCT_MAX] = { |
---|
450 | [WELLSPRING1] = { |
---|
451 | .caps = 0, |
---|
452 | .tp_type = WSP_TRACKPAD_TYPE1, |
---|
453 | .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, |
---|
454 | }, |
---|
455 | [WELLSPRING2] = { |
---|
456 | .caps = 0, |
---|
457 | .tp_type = WSP_TRACKPAD_TYPE1, |
---|
458 | .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, |
---|
459 | }, |
---|
460 | [WELLSPRING3] = { |
---|
461 | .caps = HAS_INTEGRATED_BUTTON, |
---|
462 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
463 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
464 | }, |
---|
465 | [WELLSPRING4] = { |
---|
466 | .caps = HAS_INTEGRATED_BUTTON, |
---|
467 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
468 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
469 | }, |
---|
470 | [WELLSPRING4A] = { |
---|
471 | .caps = HAS_INTEGRATED_BUTTON, |
---|
472 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
473 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
474 | }, |
---|
475 | [WELLSPRING5] = { |
---|
476 | .caps = HAS_INTEGRATED_BUTTON, |
---|
477 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
478 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
479 | }, |
---|
480 | [WELLSPRING6] = { |
---|
481 | .caps = HAS_INTEGRATED_BUTTON, |
---|
482 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
483 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
484 | }, |
---|
485 | [WELLSPRING5A] = { |
---|
486 | .caps = HAS_INTEGRATED_BUTTON, |
---|
487 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
488 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
489 | }, |
---|
490 | [WELLSPRING6A] = { |
---|
491 | .caps = HAS_INTEGRATED_BUTTON, |
---|
492 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
493 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
494 | }, |
---|
495 | [WELLSPRING7] = { |
---|
496 | .caps = HAS_INTEGRATED_BUTTON, |
---|
497 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
498 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
499 | }, |
---|
500 | [WELLSPRING7A] = { |
---|
501 | .caps = HAS_INTEGRATED_BUTTON, |
---|
502 | .tp_type = WSP_TRACKPAD_TYPE2, |
---|
503 | .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, |
---|
504 | }, |
---|
505 | [WELLSPRING8] = { |
---|
506 | .caps = HAS_INTEGRATED_BUTTON, |
---|
507 | .tp_type = WSP_TRACKPAD_TYPE3, |
---|
508 | .finger_data_offset = WSP_TYPE3_FINGER_DATA_OFFSET, |
---|
509 | }, |
---|
510 | }; |
---|
511 | |
---|
512 | #define ATP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } |
---|
513 | |
---|
514 | /* TODO: STRUCT_USB_HOST_ID */ |
---|
515 | static const struct usb_device_id wsp_devs[] = { |
---|
516 | /* MacbookAir1.1 */ |
---|
517 | ATP_DEV(APPLE, WELLSPRING_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING1)), |
---|
518 | ATP_DEV(APPLE, WELLSPRING_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING1)), |
---|
519 | ATP_DEV(APPLE, WELLSPRING_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING1)), |
---|
520 | |
---|
521 | /* MacbookProPenryn, aka wellspring2 */ |
---|
522 | ATP_DEV(APPLE, WELLSPRING2_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING2)), |
---|
523 | ATP_DEV(APPLE, WELLSPRING2_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING2)), |
---|
524 | ATP_DEV(APPLE, WELLSPRING2_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING2)), |
---|
525 | |
---|
526 | /* Macbook5,1 (unibody), aka wellspring3 */ |
---|
527 | ATP_DEV(APPLE, WELLSPRING3_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING3)), |
---|
528 | ATP_DEV(APPLE, WELLSPRING3_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING3)), |
---|
529 | ATP_DEV(APPLE, WELLSPRING3_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING3)), |
---|
530 | |
---|
531 | /* MacbookAir3,2 (unibody), aka wellspring4 */ |
---|
532 | ATP_DEV(APPLE, WELLSPRING4_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4)), |
---|
533 | ATP_DEV(APPLE, WELLSPRING4_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4)), |
---|
534 | ATP_DEV(APPLE, WELLSPRING4_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4)), |
---|
535 | |
---|
536 | /* MacbookAir3,1 (unibody), aka wellspring4 */ |
---|
537 | ATP_DEV(APPLE, WELLSPRING4A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), |
---|
538 | ATP_DEV(APPLE, WELLSPRING4A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), |
---|
539 | ATP_DEV(APPLE, WELLSPRING4A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), |
---|
540 | |
---|
541 | /* Macbook8 (unibody, March 2011) */ |
---|
542 | ATP_DEV(APPLE, WELLSPRING5_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5)), |
---|
543 | ATP_DEV(APPLE, WELLSPRING5_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5)), |
---|
544 | ATP_DEV(APPLE, WELLSPRING5_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5)), |
---|
545 | |
---|
546 | /* MacbookAir4,1 (unibody, July 2011) */ |
---|
547 | ATP_DEV(APPLE, WELLSPRING6A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), |
---|
548 | ATP_DEV(APPLE, WELLSPRING6A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), |
---|
549 | ATP_DEV(APPLE, WELLSPRING6A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), |
---|
550 | |
---|
551 | /* MacbookAir4,2 (unibody, July 2011) */ |
---|
552 | ATP_DEV(APPLE, WELLSPRING6_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6)), |
---|
553 | ATP_DEV(APPLE, WELLSPRING6_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6)), |
---|
554 | ATP_DEV(APPLE, WELLSPRING6_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6)), |
---|
555 | |
---|
556 | /* Macbook8,2 (unibody) */ |
---|
557 | ATP_DEV(APPLE, WELLSPRING5A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), |
---|
558 | ATP_DEV(APPLE, WELLSPRING5A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), |
---|
559 | ATP_DEV(APPLE, WELLSPRING5A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), |
---|
560 | |
---|
561 | /* MacbookPro10,1 (unibody, June 2012) */ |
---|
562 | /* MacbookPro11,? (unibody, June 2013) */ |
---|
563 | ATP_DEV(APPLE, WELLSPRING7_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7)), |
---|
564 | ATP_DEV(APPLE, WELLSPRING7_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7)), |
---|
565 | ATP_DEV(APPLE, WELLSPRING7_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7)), |
---|
566 | |
---|
567 | /* MacbookPro10,2 (unibody, October 2012) */ |
---|
568 | ATP_DEV(APPLE, WELLSPRING7A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), |
---|
569 | ATP_DEV(APPLE, WELLSPRING7A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), |
---|
570 | ATP_DEV(APPLE, WELLSPRING7A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), |
---|
571 | |
---|
572 | /* MacbookAir6,2 (unibody, June 2013) */ |
---|
573 | ATP_DEV(APPLE, WELLSPRING8_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING8)), |
---|
574 | ATP_DEV(APPLE, WELLSPRING8_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING8)), |
---|
575 | ATP_DEV(APPLE, WELLSPRING8_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING8)), |
---|
576 | }; |
---|
577 | |
---|
578 | typedef enum atp_stroke_type { |
---|
579 | ATP_STROKE_TOUCH, |
---|
580 | ATP_STROKE_SLIDE, |
---|
581 | } atp_stroke_type; |
---|
582 | |
---|
583 | typedef enum atp_axis { |
---|
584 | X = 0, |
---|
585 | Y = 1, |
---|
586 | NUM_AXES |
---|
587 | } atp_axis; |
---|
588 | |
---|
589 | #define ATP_FIFO_BUF_SIZE 8 /* bytes */ |
---|
590 | #define ATP_FIFO_QUEUE_MAXLEN 50 /* units */ |
---|
591 | |
---|
592 | enum { |
---|
593 | ATP_INTR_DT, |
---|
594 | ATP_RESET, |
---|
595 | ATP_N_TRANSFER, |
---|
596 | }; |
---|
597 | |
---|
598 | typedef struct fg_stroke_component { |
---|
599 | /* Fields encapsulating the pressure-span. */ |
---|
600 | u_int loc; /* location (scaled) */ |
---|
601 | u_int cum_pressure; /* cumulative compression */ |
---|
602 | u_int max_cum_pressure; /* max cumulative compression */ |
---|
603 | boolean_t matched; /*to track components as they match against pspans.*/ |
---|
604 | |
---|
605 | int delta_mickeys; /* change in location (un-smoothened movement)*/ |
---|
606 | } fg_stroke_component_t; |
---|
607 | |
---|
608 | /* |
---|
609 | * The following structure captures a finger contact with the |
---|
610 | * touchpad. A stroke comprises two p-span components and some state. |
---|
611 | */ |
---|
612 | typedef struct atp_stroke { |
---|
613 | TAILQ_ENTRY(atp_stroke) entry; |
---|
614 | |
---|
615 | atp_stroke_type type; |
---|
616 | uint32_t flags; /* the state of this stroke */ |
---|
617 | #define ATSF_ZOMBIE 0x1 |
---|
618 | boolean_t matched; /* to track match against fingers.*/ |
---|
619 | |
---|
620 | struct timeval ctime; /* create time; for coincident siblings. */ |
---|
621 | |
---|
622 | /* |
---|
623 | * Unit: interrupts; we maintain this value in |
---|
624 | * addition to 'ctime' in order to avoid the |
---|
625 | * expensive call to microtime() at every |
---|
626 | * interrupt. |
---|
627 | */ |
---|
628 | uint32_t age; |
---|
629 | |
---|
630 | /* Location */ |
---|
631 | int x; |
---|
632 | int y; |
---|
633 | |
---|
634 | /* Fields containing information about movement. */ |
---|
635 | int instantaneous_dx; /* curr. change in X location (un-smoothened) */ |
---|
636 | int instantaneous_dy; /* curr. change in Y location (un-smoothened) */ |
---|
637 | int pending_dx; /* cum. of pending short movements */ |
---|
638 | int pending_dy; /* cum. of pending short movements */ |
---|
639 | int movement_dx; /* interpreted smoothened movement */ |
---|
640 | int movement_dy; /* interpreted smoothened movement */ |
---|
641 | int cum_movement_x; /* cum. horizontal movement */ |
---|
642 | int cum_movement_y; /* cum. vertical movement */ |
---|
643 | |
---|
644 | /* |
---|
645 | * The following member is relevant only for fountain-geyser trackpads. |
---|
646 | * For these, there is the need to track pressure-spans and cumulative |
---|
647 | * pressures for stroke components. |
---|
648 | */ |
---|
649 | fg_stroke_component_t components[NUM_AXES]; |
---|
650 | } atp_stroke_t; |
---|
651 | |
---|
652 | struct atp_softc; /* forward declaration */ |
---|
653 | typedef void (*sensor_data_interpreter_t)(struct atp_softc *sc, u_int len); |
---|
654 | |
---|
655 | struct atp_softc { |
---|
656 | device_t sc_dev; |
---|
657 | struct usb_device *sc_usb_device; |
---|
658 | struct mtx sc_mutex; /* for synchronization */ |
---|
659 | struct usb_fifo_sc sc_fifo; |
---|
660 | |
---|
661 | #define MODE_LENGTH 8 |
---|
662 | char sc_mode_bytes[MODE_LENGTH]; /* device mode */ |
---|
663 | |
---|
664 | trackpad_family_t sc_family; |
---|
665 | const void *sc_params; /* device configuration */ |
---|
666 | sensor_data_interpreter_t sensor_data_interpreter; |
---|
667 | |
---|
668 | mousehw_t sc_hw; |
---|
669 | mousemode_t sc_mode; |
---|
670 | mousestatus_t sc_status; |
---|
671 | |
---|
672 | u_int sc_state; |
---|
673 | #define ATP_ENABLED 0x01 |
---|
674 | #define ATP_ZOMBIES_EXIST 0x02 |
---|
675 | #define ATP_DOUBLE_TAP_DRAG 0x04 |
---|
676 | #define ATP_VALID 0x08 |
---|
677 | |
---|
678 | struct usb_xfer *sc_xfer[ATP_N_TRANSFER]; |
---|
679 | |
---|
680 | u_int sc_pollrate; |
---|
681 | int sc_fflags; |
---|
682 | |
---|
683 | atp_stroke_t sc_strokes_data[ATP_MAX_STROKES]; |
---|
684 | TAILQ_HEAD(,atp_stroke) sc_stroke_free; |
---|
685 | TAILQ_HEAD(,atp_stroke) sc_stroke_used; |
---|
686 | u_int sc_n_strokes; |
---|
687 | |
---|
688 | struct callout sc_callout; |
---|
689 | |
---|
690 | /* |
---|
691 | * button status. Set to non-zero if the mouse-button is physically |
---|
692 | * pressed. This state variable is exposed through softc to allow |
---|
693 | * reap_sibling_zombies to avoid registering taps while the trackpad |
---|
694 | * button is pressed. |
---|
695 | */ |
---|
696 | uint8_t sc_ibtn; |
---|
697 | |
---|
698 | /* |
---|
699 | * Time when touch zombies were last reaped; useful for detecting |
---|
700 | * double-touch-n-drag. |
---|
701 | */ |
---|
702 | struct timeval sc_touch_reap_time; |
---|
703 | |
---|
704 | u_int sc_idlecount; |
---|
705 | |
---|
706 | /* Regarding the data transferred from t-pad in USB INTR packets. */ |
---|
707 | u_int sc_expected_sensor_data_len; |
---|
708 | uint8_t sc_sensor_data[ATP_SENSOR_DATA_BUF_MAX] __aligned(4); |
---|
709 | |
---|
710 | int sc_cur_x[FG_MAX_XSENSORS]; /* current sensor readings */ |
---|
711 | int sc_cur_y[FG_MAX_YSENSORS]; |
---|
712 | int sc_base_x[FG_MAX_XSENSORS]; /* base sensor readings */ |
---|
713 | int sc_base_y[FG_MAX_YSENSORS]; |
---|
714 | int sc_pressure_x[FG_MAX_XSENSORS]; /* computed pressures */ |
---|
715 | int sc_pressure_y[FG_MAX_YSENSORS]; |
---|
716 | fg_pspan sc_pspans_x[FG_MAX_PSPANS_PER_AXIS]; |
---|
717 | fg_pspan sc_pspans_y[FG_MAX_PSPANS_PER_AXIS]; |
---|
718 | }; |
---|
719 | |
---|
720 | /* |
---|
721 | * The last byte of the fountain-geyser sensor data contains status bits; the |
---|
722 | * following values define the meanings of these bits. |
---|
723 | * (only Geyser 3/4) |
---|
724 | */ |
---|
725 | enum geyser34_status_bits { |
---|
726 | FG_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */ |
---|
727 | FG_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/ |
---|
728 | }; |
---|
729 | |
---|
730 | typedef enum interface_mode { |
---|
731 | RAW_SENSOR_MODE = (uint8_t)0x01, |
---|
732 | HID_MODE = (uint8_t)0x08 |
---|
733 | } interface_mode; |
---|
734 | |
---|
735 | |
---|
736 | /* |
---|
737 | * function prototypes |
---|
738 | */ |
---|
739 | static usb_fifo_cmd_t atp_start_read; |
---|
740 | static usb_fifo_cmd_t atp_stop_read; |
---|
741 | static usb_fifo_open_t atp_open; |
---|
742 | static usb_fifo_close_t atp_close; |
---|
743 | static usb_fifo_ioctl_t atp_ioctl; |
---|
744 | |
---|
745 | static struct usb_fifo_methods atp_fifo_methods = { |
---|
746 | .f_open = &atp_open, |
---|
747 | .f_close = &atp_close, |
---|
748 | .f_ioctl = &atp_ioctl, |
---|
749 | .f_start_read = &atp_start_read, |
---|
750 | .f_stop_read = &atp_stop_read, |
---|
751 | .basename[0] = ATP_DRIVER_NAME, |
---|
752 | }; |
---|
753 | |
---|
754 | /* device initialization and shutdown */ |
---|
755 | static usb_error_t atp_set_device_mode(struct atp_softc *, interface_mode); |
---|
756 | static void atp_reset_callback(struct usb_xfer *, usb_error_t); |
---|
757 | static int atp_enable(struct atp_softc *); |
---|
758 | static void atp_disable(struct atp_softc *); |
---|
759 | |
---|
760 | /* sensor interpretation */ |
---|
761 | static void fg_interpret_sensor_data(struct atp_softc *, u_int); |
---|
762 | static void fg_extract_sensor_data(const int8_t *, u_int, atp_axis, |
---|
763 | int *, enum fountain_geyser_trackpad_type); |
---|
764 | static void fg_get_pressures(int *, const int *, const int *, int); |
---|
765 | static void fg_detect_pspans(int *, u_int, u_int, fg_pspan *, u_int *); |
---|
766 | static void wsp_interpret_sensor_data(struct atp_softc *, u_int); |
---|
767 | |
---|
768 | /* movement detection */ |
---|
769 | static boolean_t fg_match_stroke_component(fg_stroke_component_t *, |
---|
770 | const fg_pspan *, atp_stroke_type); |
---|
771 | static void fg_match_strokes_against_pspans(struct atp_softc *, |
---|
772 | atp_axis, fg_pspan *, u_int, u_int); |
---|
773 | static boolean_t wsp_match_strokes_against_fingers(struct atp_softc *, |
---|
774 | wsp_finger_t *, u_int); |
---|
775 | static boolean_t fg_update_strokes(struct atp_softc *, fg_pspan *, u_int, |
---|
776 | fg_pspan *, u_int); |
---|
777 | static boolean_t wsp_update_strokes(struct atp_softc *, |
---|
778 | wsp_finger_t [WSP_MAX_FINGERS], u_int); |
---|
779 | static void fg_add_stroke(struct atp_softc *, const fg_pspan *, const fg_pspan *); |
---|
780 | static void fg_add_new_strokes(struct atp_softc *, fg_pspan *, |
---|
781 | u_int, fg_pspan *, u_int); |
---|
782 | static void wsp_add_stroke(struct atp_softc *, const wsp_finger_t *); |
---|
783 | static void atp_advance_stroke_state(struct atp_softc *, |
---|
784 | atp_stroke_t *, boolean_t *); |
---|
785 | static boolean_t atp_stroke_has_small_movement(const atp_stroke_t *); |
---|
786 | static void atp_update_pending_mickeys(atp_stroke_t *); |
---|
787 | static boolean_t atp_compute_stroke_movement(atp_stroke_t *); |
---|
788 | static void atp_terminate_stroke(struct atp_softc *, atp_stroke_t *); |
---|
789 | |
---|
790 | /* tap detection */ |
---|
791 | static boolean_t atp_is_horizontal_scroll(const atp_stroke_t *); |
---|
792 | static boolean_t atp_is_vertical_scroll(const atp_stroke_t *); |
---|
793 | static void atp_reap_sibling_zombies(void *); |
---|
794 | static void atp_convert_to_slide(struct atp_softc *, atp_stroke_t *); |
---|
795 | |
---|
796 | /* updating fifo */ |
---|
797 | static void atp_reset_buf(struct atp_softc *); |
---|
798 | static void atp_add_to_queue(struct atp_softc *, int, int, int, uint32_t); |
---|
799 | |
---|
800 | /* Device methods. */ |
---|
801 | static device_probe_t atp_probe; |
---|
802 | static device_attach_t atp_attach; |
---|
803 | static device_detach_t atp_detach; |
---|
804 | static usb_callback_t atp_intr; |
---|
805 | |
---|
806 | static const struct usb_config atp_xfer_config[ATP_N_TRANSFER] = { |
---|
807 | [ATP_INTR_DT] = { |
---|
808 | .type = UE_INTERRUPT, |
---|
809 | .endpoint = UE_ADDR_ANY, |
---|
810 | .direction = UE_DIR_IN, |
---|
811 | .flags = { |
---|
812 | .pipe_bof = 1, /* block pipe on failure */ |
---|
813 | .short_xfer_ok = 1, |
---|
814 | }, |
---|
815 | .bufsize = ATP_SENSOR_DATA_BUF_MAX, |
---|
816 | .callback = &atp_intr, |
---|
817 | }, |
---|
818 | [ATP_RESET] = { |
---|
819 | .type = UE_CONTROL, |
---|
820 | .endpoint = 0, /* Control pipe */ |
---|
821 | .direction = UE_DIR_ANY, |
---|
822 | .bufsize = sizeof(struct usb_device_request) + MODE_LENGTH, |
---|
823 | .callback = &atp_reset_callback, |
---|
824 | .interval = 0, /* no pre-delay */ |
---|
825 | }, |
---|
826 | }; |
---|
827 | |
---|
828 | static atp_stroke_t * |
---|
829 | atp_alloc_stroke(struct atp_softc *sc) |
---|
830 | { |
---|
831 | atp_stroke_t *pstroke; |
---|
832 | |
---|
833 | pstroke = TAILQ_FIRST(&sc->sc_stroke_free); |
---|
834 | if (pstroke == NULL) |
---|
835 | goto done; |
---|
836 | |
---|
837 | TAILQ_REMOVE(&sc->sc_stroke_free, pstroke, entry); |
---|
838 | memset(pstroke, 0, sizeof(*pstroke)); |
---|
839 | TAILQ_INSERT_TAIL(&sc->sc_stroke_used, pstroke, entry); |
---|
840 | |
---|
841 | sc->sc_n_strokes++; |
---|
842 | done: |
---|
843 | return (pstroke); |
---|
844 | } |
---|
845 | |
---|
846 | static void |
---|
847 | atp_free_stroke(struct atp_softc *sc, atp_stroke_t *pstroke) |
---|
848 | { |
---|
849 | if (pstroke == NULL) |
---|
850 | return; |
---|
851 | |
---|
852 | sc->sc_n_strokes--; |
---|
853 | |
---|
854 | TAILQ_REMOVE(&sc->sc_stroke_used, pstroke, entry); |
---|
855 | TAILQ_INSERT_TAIL(&sc->sc_stroke_free, pstroke, entry); |
---|
856 | } |
---|
857 | |
---|
858 | static void |
---|
859 | atp_init_stroke_pool(struct atp_softc *sc) |
---|
860 | { |
---|
861 | u_int x; |
---|
862 | |
---|
863 | TAILQ_INIT(&sc->sc_stroke_free); |
---|
864 | TAILQ_INIT(&sc->sc_stroke_used); |
---|
865 | |
---|
866 | sc->sc_n_strokes = 0; |
---|
867 | |
---|
868 | memset(&sc->sc_strokes_data, 0, sizeof(sc->sc_strokes_data)); |
---|
869 | |
---|
870 | for (x = 0; x != ATP_MAX_STROKES; x++) { |
---|
871 | TAILQ_INSERT_TAIL(&sc->sc_stroke_free, &sc->sc_strokes_data[x], |
---|
872 | entry); |
---|
873 | } |
---|
874 | } |
---|
875 | |
---|
876 | static usb_error_t |
---|
877 | atp_set_device_mode(struct atp_softc *sc, interface_mode newMode) |
---|
878 | { |
---|
879 | uint8_t mode_value; |
---|
880 | usb_error_t err; |
---|
881 | |
---|
882 | if ((newMode != RAW_SENSOR_MODE) && (newMode != HID_MODE)) |
---|
883 | return (USB_ERR_INVAL); |
---|
884 | |
---|
885 | if ((newMode == RAW_SENSOR_MODE) && |
---|
886 | (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER)) |
---|
887 | mode_value = (uint8_t)0x04; |
---|
888 | else |
---|
889 | mode_value = newMode; |
---|
890 | |
---|
891 | err = usbd_req_get_report(sc->sc_usb_device, NULL /* mutex */, |
---|
892 | sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, |
---|
893 | 0x03 /* type */, 0x00 /* id */); |
---|
894 | if (err != USB_ERR_NORMAL_COMPLETION) { |
---|
895 | DPRINTF("Failed to read device mode (%d)\n", err); |
---|
896 | return (err); |
---|
897 | } |
---|
898 | |
---|
899 | if (sc->sc_mode_bytes[0] == mode_value) |
---|
900 | return (err); |
---|
901 | |
---|
902 | /* |
---|
903 | * XXX Need to wait at least 250ms for hardware to get |
---|
904 | * ready. The device mode handling appears to be handled |
---|
905 | * asynchronously and we should not issue these commands too |
---|
906 | * quickly. |
---|
907 | */ |
---|
908 | pause("WHW", hz / 4); |
---|
909 | |
---|
910 | sc->sc_mode_bytes[0] = mode_value; |
---|
911 | return (usbd_req_set_report(sc->sc_usb_device, NULL /* mutex */, |
---|
912 | sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, |
---|
913 | 0x03 /* type */, 0x00 /* id */)); |
---|
914 | } |
---|
915 | |
---|
916 | static void |
---|
917 | atp_reset_callback(struct usb_xfer *xfer, usb_error_t error) |
---|
918 | { |
---|
919 | usb_device_request_t req; |
---|
920 | struct usb_page_cache *pc; |
---|
921 | struct atp_softc *sc = usbd_xfer_softc(xfer); |
---|
922 | |
---|
923 | uint8_t mode_value; |
---|
924 | if (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER) |
---|
925 | mode_value = 0x04; |
---|
926 | else |
---|
927 | mode_value = RAW_SENSOR_MODE; |
---|
928 | |
---|
929 | switch (USB_GET_STATE(xfer)) { |
---|
930 | case USB_ST_SETUP: |
---|
931 | sc->sc_mode_bytes[0] = mode_value; |
---|
932 | req.bmRequestType = UT_WRITE_CLASS_INTERFACE; |
---|
933 | req.bRequest = UR_SET_REPORT; |
---|
934 | USETW2(req.wValue, |
---|
935 | (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */); |
---|
936 | USETW(req.wIndex, 0); |
---|
937 | USETW(req.wLength, MODE_LENGTH); |
---|
938 | |
---|
939 | pc = usbd_xfer_get_frame(xfer, 0); |
---|
940 | usbd_copy_in(pc, 0, &req, sizeof(req)); |
---|
941 | pc = usbd_xfer_get_frame(xfer, 1); |
---|
942 | usbd_copy_in(pc, 0, sc->sc_mode_bytes, MODE_LENGTH); |
---|
943 | |
---|
944 | usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); |
---|
945 | usbd_xfer_set_frame_len(xfer, 1, MODE_LENGTH); |
---|
946 | usbd_xfer_set_frames(xfer, 2); |
---|
947 | usbd_transfer_submit(xfer); |
---|
948 | break; |
---|
949 | |
---|
950 | case USB_ST_TRANSFERRED: |
---|
951 | default: |
---|
952 | break; |
---|
953 | } |
---|
954 | } |
---|
955 | |
---|
956 | static int |
---|
957 | atp_enable(struct atp_softc *sc) |
---|
958 | { |
---|
959 | if (sc->sc_state & ATP_ENABLED) |
---|
960 | return (0); |
---|
961 | |
---|
962 | /* reset status */ |
---|
963 | memset(&sc->sc_status, 0, sizeof(sc->sc_status)); |
---|
964 | |
---|
965 | atp_init_stroke_pool(sc); |
---|
966 | |
---|
967 | sc->sc_state |= ATP_ENABLED; |
---|
968 | |
---|
969 | DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n"); |
---|
970 | return (0); |
---|
971 | } |
---|
972 | |
---|
973 | static void |
---|
974 | atp_disable(struct atp_softc *sc) |
---|
975 | { |
---|
976 | sc->sc_state &= ~(ATP_ENABLED | ATP_VALID); |
---|
977 | DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n"); |
---|
978 | } |
---|
979 | |
---|
980 | static void |
---|
981 | fg_interpret_sensor_data(struct atp_softc *sc, u_int data_len) |
---|
982 | { |
---|
983 | u_int n_xpspans = 0; |
---|
984 | u_int n_ypspans = 0; |
---|
985 | uint8_t status_bits; |
---|
986 | |
---|
987 | const struct fg_dev_params *params = |
---|
988 | (const struct fg_dev_params *)sc->sc_params; |
---|
989 | |
---|
990 | fg_extract_sensor_data(sc->sc_sensor_data, params->n_xsensors, X, |
---|
991 | sc->sc_cur_x, params->prot); |
---|
992 | fg_extract_sensor_data(sc->sc_sensor_data, params->n_ysensors, Y, |
---|
993 | sc->sc_cur_y, params->prot); |
---|
994 | |
---|
995 | /* |
---|
996 | * If this is the initial update (from an untouched |
---|
997 | * pad), we should set the base values for the sensor |
---|
998 | * data; deltas with respect to these base values can |
---|
999 | * be used as pressure readings subsequently. |
---|
1000 | */ |
---|
1001 | status_bits = sc->sc_sensor_data[params->data_len - 1]; |
---|
1002 | if (((params->prot == FG_TRACKPAD_TYPE_GEYSER3) || |
---|
1003 | (params->prot == FG_TRACKPAD_TYPE_GEYSER4)) && |
---|
1004 | ((sc->sc_state & ATP_VALID) == 0)) { |
---|
1005 | if (status_bits & FG_STATUS_BASE_UPDATE) { |
---|
1006 | memcpy(sc->sc_base_x, sc->sc_cur_x, |
---|
1007 | params->n_xsensors * sizeof(*sc->sc_base_x)); |
---|
1008 | memcpy(sc->sc_base_y, sc->sc_cur_y, |
---|
1009 | params->n_ysensors * sizeof(*sc->sc_base_y)); |
---|
1010 | sc->sc_state |= ATP_VALID; |
---|
1011 | return; |
---|
1012 | } |
---|
1013 | } |
---|
1014 | |
---|
1015 | /* Get pressure readings and detect p-spans for both axes. */ |
---|
1016 | fg_get_pressures(sc->sc_pressure_x, sc->sc_cur_x, sc->sc_base_x, |
---|
1017 | params->n_xsensors); |
---|
1018 | fg_detect_pspans(sc->sc_pressure_x, params->n_xsensors, |
---|
1019 | FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_x, &n_xpspans); |
---|
1020 | fg_get_pressures(sc->sc_pressure_y, sc->sc_cur_y, sc->sc_base_y, |
---|
1021 | params->n_ysensors); |
---|
1022 | fg_detect_pspans(sc->sc_pressure_y, params->n_ysensors, |
---|
1023 | FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_y, &n_ypspans); |
---|
1024 | |
---|
1025 | /* Update strokes with new pspans to detect movements. */ |
---|
1026 | if (fg_update_strokes(sc, sc->sc_pspans_x, n_xpspans, sc->sc_pspans_y, n_ypspans)) |
---|
1027 | sc->sc_status.flags |= MOUSE_POSCHANGED; |
---|
1028 | |
---|
1029 | sc->sc_ibtn = (status_bits & FG_STATUS_BUTTON) ? MOUSE_BUTTON1DOWN : 0; |
---|
1030 | sc->sc_status.button = sc->sc_ibtn; |
---|
1031 | |
---|
1032 | /* |
---|
1033 | * The Fountain/Geyser device continues to trigger interrupts |
---|
1034 | * at a fast rate even after touchpad activity has |
---|
1035 | * stopped. Upon detecting that the device has remained idle |
---|
1036 | * beyond a threshold, we reinitialize it to silence the |
---|
1037 | * interrupts. |
---|
1038 | */ |
---|
1039 | if ((sc->sc_status.flags == 0) && (sc->sc_n_strokes == 0)) { |
---|
1040 | sc->sc_idlecount++; |
---|
1041 | if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) { |
---|
1042 | /* |
---|
1043 | * Use the last frame before we go idle for |
---|
1044 | * calibration on pads which do not send |
---|
1045 | * calibration frames. |
---|
1046 | */ |
---|
1047 | const struct fg_dev_params *params = |
---|
1048 | (const struct fg_dev_params *)sc->sc_params; |
---|
1049 | |
---|
1050 | DPRINTFN(ATP_LLEVEL_INFO, "idle\n"); |
---|
1051 | |
---|
1052 | if (params->prot < FG_TRACKPAD_TYPE_GEYSER3) { |
---|
1053 | memcpy(sc->sc_base_x, sc->sc_cur_x, |
---|
1054 | params->n_xsensors * sizeof(*(sc->sc_base_x))); |
---|
1055 | memcpy(sc->sc_base_y, sc->sc_cur_y, |
---|
1056 | params->n_ysensors * sizeof(*(sc->sc_base_y))); |
---|
1057 | } |
---|
1058 | |
---|
1059 | sc->sc_idlecount = 0; |
---|
1060 | usbd_transfer_start(sc->sc_xfer[ATP_RESET]); |
---|
1061 | } |
---|
1062 | } else { |
---|
1063 | sc->sc_idlecount = 0; |
---|
1064 | } |
---|
1065 | } |
---|
1066 | |
---|
1067 | /* |
---|
1068 | * Interpret the data from the X and Y pressure sensors. This function |
---|
1069 | * is called separately for the X and Y sensor arrays. The data in the |
---|
1070 | * USB packet is laid out in the following manner: |
---|
1071 | * |
---|
1072 | * sensor_data: |
---|
1073 | * --,--,Y1,Y2,--,Y3,Y4,--,Y5,...,Y10, ... X1,X2,--,X3,X4 |
---|
1074 | * indices: 0 1 2 3 4 5 6 7 8 ... 15 ... 20 21 22 23 24 |
---|
1075 | * |
---|
1076 | * '--' (in the above) indicates that the value is unimportant. |
---|
1077 | * |
---|
1078 | * Information about the above layout was obtained from the |
---|
1079 | * implementation of the AppleTouch driver in Linux. |
---|
1080 | * |
---|
1081 | * parameters: |
---|
1082 | * sensor_data |
---|
1083 | * raw sensor data from the USB packet. |
---|
1084 | * num |
---|
1085 | * The number of elements in the array 'arr'. |
---|
1086 | * axis |
---|
1087 | * Axis of data to fetch |
---|
1088 | * arr |
---|
1089 | * The array to be initialized with the readings. |
---|
1090 | * prot |
---|
1091 | * The protocol to use to interpret the data |
---|
1092 | */ |
---|
1093 | static void |
---|
1094 | fg_extract_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis, |
---|
1095 | int *arr, enum fountain_geyser_trackpad_type prot) |
---|
1096 | { |
---|
1097 | u_int i; |
---|
1098 | u_int di; /* index into sensor data */ |
---|
1099 | |
---|
1100 | switch (prot) { |
---|
1101 | case FG_TRACKPAD_TYPE_GEYSER1: |
---|
1102 | /* |
---|
1103 | * For Geyser 1, the sensors are laid out in pairs |
---|
1104 | * every 5 bytes. |
---|
1105 | */ |
---|
1106 | for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) { |
---|
1107 | arr[i] = sensor_data[di]; |
---|
1108 | arr[i+8] = sensor_data[di+2]; |
---|
1109 | if ((axis == X) && (num > 16)) |
---|
1110 | arr[i+16] = sensor_data[di+40]; |
---|
1111 | } |
---|
1112 | |
---|
1113 | break; |
---|
1114 | case FG_TRACKPAD_TYPE_GEYSER2: |
---|
1115 | for (i = 0, di = (axis == Y) ? 1 : 19; i < num; /* empty */ ) { |
---|
1116 | arr[i++] = sensor_data[di++]; |
---|
1117 | arr[i++] = sensor_data[di++]; |
---|
1118 | di++; |
---|
1119 | } |
---|
1120 | break; |
---|
1121 | case FG_TRACKPAD_TYPE_GEYSER3: |
---|
1122 | case FG_TRACKPAD_TYPE_GEYSER4: |
---|
1123 | for (i = 0, di = (axis == Y) ? 2 : 20; i < num; /* empty */ ) { |
---|
1124 | arr[i++] = sensor_data[di++]; |
---|
1125 | arr[i++] = sensor_data[di++]; |
---|
1126 | di++; |
---|
1127 | } |
---|
1128 | break; |
---|
1129 | default: |
---|
1130 | break; |
---|
1131 | } |
---|
1132 | } |
---|
1133 | |
---|
1134 | static void |
---|
1135 | fg_get_pressures(int *p, const int *cur, const int *base, int n) |
---|
1136 | { |
---|
1137 | int i; |
---|
1138 | |
---|
1139 | for (i = 0; i < n; i++) { |
---|
1140 | p[i] = cur[i] - base[i]; |
---|
1141 | if (p[i] > 127) |
---|
1142 | p[i] -= 256; |
---|
1143 | if (p[i] < -127) |
---|
1144 | p[i] += 256; |
---|
1145 | if (p[i] < 0) |
---|
1146 | p[i] = 0; |
---|
1147 | |
---|
1148 | /* |
---|
1149 | * Shave off pressures below the noise-pressure |
---|
1150 | * threshold; this will reduce the contribution from |
---|
1151 | * lower pressure readings. |
---|
1152 | */ |
---|
1153 | if ((u_int)p[i] <= FG_SENSOR_NOISE_THRESHOLD) |
---|
1154 | p[i] = 0; /* filter away noise */ |
---|
1155 | else |
---|
1156 | p[i] -= FG_SENSOR_NOISE_THRESHOLD; |
---|
1157 | } |
---|
1158 | } |
---|
1159 | |
---|
1160 | static void |
---|
1161 | fg_detect_pspans(int *p, u_int num_sensors, |
---|
1162 | u_int max_spans, /* max # of pspans permitted */ |
---|
1163 | fg_pspan *spans, /* finger spans */ |
---|
1164 | u_int *nspans_p) /* num spans detected */ |
---|
1165 | { |
---|
1166 | u_int i; |
---|
1167 | int maxp; /* max pressure seen within a span */ |
---|
1168 | u_int num_spans = 0; |
---|
1169 | |
---|
1170 | enum fg_pspan_state { |
---|
1171 | ATP_PSPAN_INACTIVE, |
---|
1172 | ATP_PSPAN_INCREASING, |
---|
1173 | ATP_PSPAN_DECREASING, |
---|
1174 | } state; /* state of the pressure span */ |
---|
1175 | |
---|
1176 | /* |
---|
1177 | * The following is a simple state machine to track |
---|
1178 | * the phase of the pressure span. |
---|
1179 | */ |
---|
1180 | memset(spans, 0, max_spans * sizeof(fg_pspan)); |
---|
1181 | maxp = 0; |
---|
1182 | state = ATP_PSPAN_INACTIVE; |
---|
1183 | for (i = 0; i < num_sensors; i++) { |
---|
1184 | if (num_spans >= max_spans) |
---|
1185 | break; |
---|
1186 | |
---|
1187 | if (p[i] == 0) { |
---|
1188 | if (state == ATP_PSPAN_INACTIVE) { |
---|
1189 | /* |
---|
1190 | * There is no pressure information for this |
---|
1191 | * sensor, and we aren't tracking a finger. |
---|
1192 | */ |
---|
1193 | continue; |
---|
1194 | } else { |
---|
1195 | state = ATP_PSPAN_INACTIVE; |
---|
1196 | maxp = 0; |
---|
1197 | num_spans++; |
---|
1198 | } |
---|
1199 | } else { |
---|
1200 | switch (state) { |
---|
1201 | case ATP_PSPAN_INACTIVE: |
---|
1202 | state = ATP_PSPAN_INCREASING; |
---|
1203 | maxp = p[i]; |
---|
1204 | break; |
---|
1205 | |
---|
1206 | case ATP_PSPAN_INCREASING: |
---|
1207 | if (p[i] > maxp) |
---|
1208 | maxp = p[i]; |
---|
1209 | else if (p[i] <= (maxp >> 1)) |
---|
1210 | state = ATP_PSPAN_DECREASING; |
---|
1211 | break; |
---|
1212 | |
---|
1213 | case ATP_PSPAN_DECREASING: |
---|
1214 | if (p[i] > p[i - 1]) { |
---|
1215 | /* |
---|
1216 | * This is the beginning of |
---|
1217 | * another span; change state |
---|
1218 | * to give the appearance that |
---|
1219 | * we're starting from an |
---|
1220 | * inactive span, and then |
---|
1221 | * re-process this reading in |
---|
1222 | * the next iteration. |
---|
1223 | */ |
---|
1224 | num_spans++; |
---|
1225 | state = ATP_PSPAN_INACTIVE; |
---|
1226 | maxp = 0; |
---|
1227 | i--; |
---|
1228 | continue; |
---|
1229 | } |
---|
1230 | break; |
---|
1231 | } |
---|
1232 | |
---|
1233 | /* Update the finger span with this reading. */ |
---|
1234 | spans[num_spans].width++; |
---|
1235 | spans[num_spans].cum += p[i]; |
---|
1236 | spans[num_spans].cog += p[i] * (i + 1); |
---|
1237 | } |
---|
1238 | } |
---|
1239 | if (state != ATP_PSPAN_INACTIVE) |
---|
1240 | num_spans++; /* close the last finger span */ |
---|
1241 | |
---|
1242 | /* post-process the spans */ |
---|
1243 | for (i = 0; i < num_spans; i++) { |
---|
1244 | /* filter away unwanted pressure spans */ |
---|
1245 | if ((spans[i].cum < FG_PSPAN_MIN_CUM_PRESSURE) || |
---|
1246 | (spans[i].width > FG_PSPAN_MAX_WIDTH)) { |
---|
1247 | if ((i + 1) < num_spans) { |
---|
1248 | memcpy(&spans[i], &spans[i + 1], |
---|
1249 | (num_spans - i - 1) * sizeof(fg_pspan)); |
---|
1250 | i--; |
---|
1251 | } |
---|
1252 | num_spans--; |
---|
1253 | continue; |
---|
1254 | } |
---|
1255 | |
---|
1256 | /* compute this span's representative location */ |
---|
1257 | spans[i].loc = spans[i].cog * FG_SCALE_FACTOR / |
---|
1258 | spans[i].cum; |
---|
1259 | |
---|
1260 | spans[i].matched = false; /* not yet matched against a stroke */ |
---|
1261 | } |
---|
1262 | |
---|
1263 | *nspans_p = num_spans; |
---|
1264 | } |
---|
1265 | |
---|
1266 | static void |
---|
1267 | wsp_interpret_sensor_data(struct atp_softc *sc, u_int data_len) |
---|
1268 | { |
---|
1269 | const struct wsp_dev_params *params = sc->sc_params; |
---|
1270 | wsp_finger_t fingers[WSP_MAX_FINGERS]; |
---|
1271 | struct wsp_finger_sensor_data *source_fingerp; |
---|
1272 | u_int n_source_fingers; |
---|
1273 | u_int n_fingers; |
---|
1274 | u_int i; |
---|
1275 | |
---|
1276 | /* validate sensor data length */ |
---|
1277 | if ((data_len < params->finger_data_offset) || |
---|
1278 | ((data_len - params->finger_data_offset) % |
---|
1279 | WSP_SIZEOF_FINGER_SENSOR_DATA) != 0) |
---|
1280 | return; |
---|
1281 | |
---|
1282 | /* compute number of source fingers */ |
---|
1283 | n_source_fingers = (data_len - params->finger_data_offset) / |
---|
1284 | WSP_SIZEOF_FINGER_SENSOR_DATA; |
---|
1285 | |
---|
1286 | if (n_source_fingers > WSP_MAX_FINGERS) |
---|
1287 | n_source_fingers = WSP_MAX_FINGERS; |
---|
1288 | |
---|
1289 | /* iterate over the source data collecting useful fingers */ |
---|
1290 | n_fingers = 0; |
---|
1291 | source_fingerp = (struct wsp_finger_sensor_data *)(sc->sc_sensor_data + |
---|
1292 | params->finger_data_offset); |
---|
1293 | |
---|
1294 | for (i = 0; i < n_source_fingers; i++, source_fingerp++) { |
---|
1295 | /* swap endianness, if any */ |
---|
1296 | if (le16toh(0x1234) != 0x1234) { |
---|
1297 | source_fingerp->origin = le16toh((uint16_t)source_fingerp->origin); |
---|
1298 | source_fingerp->abs_x = le16toh((uint16_t)source_fingerp->abs_x); |
---|
1299 | source_fingerp->abs_y = le16toh((uint16_t)source_fingerp->abs_y); |
---|
1300 | source_fingerp->rel_x = le16toh((uint16_t)source_fingerp->rel_x); |
---|
1301 | source_fingerp->rel_y = le16toh((uint16_t)source_fingerp->rel_y); |
---|
1302 | source_fingerp->tool_major = le16toh((uint16_t)source_fingerp->tool_major); |
---|
1303 | source_fingerp->tool_minor = le16toh((uint16_t)source_fingerp->tool_minor); |
---|
1304 | source_fingerp->orientation = le16toh((uint16_t)source_fingerp->orientation); |
---|
1305 | source_fingerp->touch_major = le16toh((uint16_t)source_fingerp->touch_major); |
---|
1306 | source_fingerp->touch_minor = le16toh((uint16_t)source_fingerp->touch_minor); |
---|
1307 | source_fingerp->multi = le16toh((uint16_t)source_fingerp->multi); |
---|
1308 | } |
---|
1309 | |
---|
1310 | /* check for minium threshold */ |
---|
1311 | if (source_fingerp->touch_major == 0) |
---|
1312 | continue; |
---|
1313 | |
---|
1314 | fingers[n_fingers].matched = false; |
---|
1315 | fingers[n_fingers].x = source_fingerp->abs_x; |
---|
1316 | fingers[n_fingers].y = -source_fingerp->abs_y; |
---|
1317 | |
---|
1318 | n_fingers++; |
---|
1319 | } |
---|
1320 | |
---|
1321 | if ((sc->sc_n_strokes == 0) && (n_fingers == 0)) |
---|
1322 | return; |
---|
1323 | |
---|
1324 | if (wsp_update_strokes(sc, fingers, n_fingers)) |
---|
1325 | sc->sc_status.flags |= MOUSE_POSCHANGED; |
---|
1326 | |
---|
1327 | switch(params->tp_type) { |
---|
1328 | case WSP_TRACKPAD_TYPE2: |
---|
1329 | sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE2_BUTTON_DATA_OFFSET]; |
---|
1330 | break; |
---|
1331 | case WSP_TRACKPAD_TYPE3: |
---|
1332 | sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE3_BUTTON_DATA_OFFSET]; |
---|
1333 | break; |
---|
1334 | default: |
---|
1335 | break; |
---|
1336 | } |
---|
1337 | sc->sc_status.button = sc->sc_ibtn ? MOUSE_BUTTON1DOWN : 0; |
---|
1338 | } |
---|
1339 | |
---|
1340 | /* |
---|
1341 | * Match a pressure-span against a stroke-component. If there is a |
---|
1342 | * match, update the component's state and return true. |
---|
1343 | */ |
---|
1344 | static boolean_t |
---|
1345 | fg_match_stroke_component(fg_stroke_component_t *component, |
---|
1346 | const fg_pspan *pspan, atp_stroke_type stroke_type) |
---|
1347 | { |
---|
1348 | int delta_mickeys; |
---|
1349 | u_int min_pressure; |
---|
1350 | |
---|
1351 | delta_mickeys = pspan->loc - component->loc; |
---|
1352 | |
---|
1353 | if (abs(delta_mickeys) > (int)FG_MAX_DELTA_MICKEYS) |
---|
1354 | return (false); /* the finger span is too far out; no match */ |
---|
1355 | |
---|
1356 | component->loc = pspan->loc; |
---|
1357 | |
---|
1358 | /* |
---|
1359 | * A sudden and significant increase in a pspan's cumulative |
---|
1360 | * pressure indicates the incidence of a new finger |
---|
1361 | * contact. This usually revises the pspan's |
---|
1362 | * centre-of-gravity, and hence the location of any/all |
---|
1363 | * matching stroke component(s). But such a change should |
---|
1364 | * *not* be interpreted as a movement. |
---|
1365 | */ |
---|
1366 | if (pspan->cum > ((3 * component->cum_pressure) >> 1)) |
---|
1367 | delta_mickeys = 0; |
---|
1368 | |
---|
1369 | component->cum_pressure = pspan->cum; |
---|
1370 | if (pspan->cum > component->max_cum_pressure) |
---|
1371 | component->max_cum_pressure = pspan->cum; |
---|
1372 | |
---|
1373 | /* |
---|
1374 | * Disregard the component's movement if its cumulative |
---|
1375 | * pressure drops below a fraction of the maximum; this |
---|
1376 | * fraction is determined based on the stroke's type. |
---|
1377 | */ |
---|
1378 | if (stroke_type == ATP_STROKE_TOUCH) |
---|
1379 | min_pressure = (3 * component->max_cum_pressure) >> 2; |
---|
1380 | else |
---|
1381 | min_pressure = component->max_cum_pressure >> 2; |
---|
1382 | if (component->cum_pressure < min_pressure) |
---|
1383 | delta_mickeys = 0; |
---|
1384 | |
---|
1385 | component->delta_mickeys = delta_mickeys; |
---|
1386 | return (true); |
---|
1387 | } |
---|
1388 | |
---|
1389 | static void |
---|
1390 | fg_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis, |
---|
1391 | fg_pspan *pspans, u_int n_pspans, u_int repeat_count) |
---|
1392 | { |
---|
1393 | atp_stroke_t *strokep; |
---|
1394 | u_int repeat_index = 0; |
---|
1395 | u_int i; |
---|
1396 | |
---|
1397 | /* Determine the index of the multi-span. */ |
---|
1398 | if (repeat_count) { |
---|
1399 | for (i = 0; i < n_pspans; i++) { |
---|
1400 | if (pspans[i].cum > pspans[repeat_index].cum) |
---|
1401 | repeat_index = i; |
---|
1402 | } |
---|
1403 | } |
---|
1404 | |
---|
1405 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { |
---|
1406 | if (strokep->components[axis].matched) |
---|
1407 | continue; /* skip matched components */ |
---|
1408 | |
---|
1409 | for (i = 0; i < n_pspans; i++) { |
---|
1410 | if (pspans[i].matched) |
---|
1411 | continue; /* skip matched pspans */ |
---|
1412 | |
---|
1413 | if (fg_match_stroke_component( |
---|
1414 | &strokep->components[axis], &pspans[i], |
---|
1415 | strokep->type)) { |
---|
1416 | |
---|
1417 | /* There is a match. */ |
---|
1418 | strokep->components[axis].matched = true; |
---|
1419 | |
---|
1420 | /* Take care to repeat at the multi-span. */ |
---|
1421 | if ((repeat_count > 0) && (i == repeat_index)) |
---|
1422 | repeat_count--; |
---|
1423 | else |
---|
1424 | pspans[i].matched = true; |
---|
1425 | |
---|
1426 | break; /* skip to the next strokep */ |
---|
1427 | } |
---|
1428 | } /* loop over pspans */ |
---|
1429 | } /* loop over strokes */ |
---|
1430 | } |
---|
1431 | |
---|
1432 | static boolean_t |
---|
1433 | wsp_match_strokes_against_fingers(struct atp_softc *sc, |
---|
1434 | wsp_finger_t *fingers, u_int n_fingers) |
---|
1435 | { |
---|
1436 | boolean_t movement = false; |
---|
1437 | atp_stroke_t *strokep; |
---|
1438 | u_int i; |
---|
1439 | |
---|
1440 | /* reset the matched status for all strokes */ |
---|
1441 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) |
---|
1442 | strokep->matched = false; |
---|
1443 | |
---|
1444 | for (i = 0; i != n_fingers; i++) { |
---|
1445 | u_int least_distance_sq = WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ; |
---|
1446 | atp_stroke_t *strokep_best = NULL; |
---|
1447 | |
---|
1448 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { |
---|
1449 | int instantaneous_dx; |
---|
1450 | int instantaneous_dy; |
---|
1451 | u_int d_squared; |
---|
1452 | |
---|
1453 | if (strokep->matched) |
---|
1454 | continue; |
---|
1455 | |
---|
1456 | instantaneous_dx = fingers[i].x - strokep->x; |
---|
1457 | instantaneous_dy = fingers[i].y - strokep->y; |
---|
1458 | |
---|
1459 | /* skip strokes which are far away */ |
---|
1460 | d_squared = |
---|
1461 | (instantaneous_dx * instantaneous_dx) + |
---|
1462 | (instantaneous_dy * instantaneous_dy); |
---|
1463 | |
---|
1464 | if (d_squared < least_distance_sq) { |
---|
1465 | least_distance_sq = d_squared; |
---|
1466 | strokep_best = strokep; |
---|
1467 | } |
---|
1468 | } |
---|
1469 | |
---|
1470 | strokep = strokep_best; |
---|
1471 | |
---|
1472 | if (strokep != NULL) { |
---|
1473 | fingers[i].matched = true; |
---|
1474 | |
---|
1475 | strokep->matched = true; |
---|
1476 | strokep->instantaneous_dx = fingers[i].x - strokep->x; |
---|
1477 | strokep->instantaneous_dy = fingers[i].y - strokep->y; |
---|
1478 | strokep->x = fingers[i].x; |
---|
1479 | strokep->y = fingers[i].y; |
---|
1480 | |
---|
1481 | atp_advance_stroke_state(sc, strokep, &movement); |
---|
1482 | } |
---|
1483 | } |
---|
1484 | return (movement); |
---|
1485 | } |
---|
1486 | |
---|
1487 | /* |
---|
1488 | * Update strokes by matching against current pressure-spans. |
---|
1489 | * Return true if any movement is detected. |
---|
1490 | */ |
---|
1491 | static boolean_t |
---|
1492 | fg_update_strokes(struct atp_softc *sc, fg_pspan *pspans_x, |
---|
1493 | u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans) |
---|
1494 | { |
---|
1495 | atp_stroke_t *strokep; |
---|
1496 | atp_stroke_t *strokep_next; |
---|
1497 | boolean_t movement = false; |
---|
1498 | u_int repeat_count = 0; |
---|
1499 | u_int i; |
---|
1500 | u_int j; |
---|
1501 | |
---|
1502 | /* Reset X and Y components of all strokes as unmatched. */ |
---|
1503 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { |
---|
1504 | strokep->components[X].matched = false; |
---|
1505 | strokep->components[Y].matched = false; |
---|
1506 | } |
---|
1507 | |
---|
1508 | /* |
---|
1509 | * Usually, the X and Y pspans come in pairs (the common case |
---|
1510 | * being a single pair). It is possible, however, that |
---|
1511 | * multiple contacts resolve to a single pspan along an |
---|
1512 | * axis, as illustrated in the following: |
---|
1513 | * |
---|
1514 | * F = finger-contact |
---|
1515 | * |
---|
1516 | * pspan pspan |
---|
1517 | * +-----------------------+ |
---|
1518 | * | . . | |
---|
1519 | * | . . | |
---|
1520 | * | . . | |
---|
1521 | * | . . | |
---|
1522 | * pspan |.........F......F | |
---|
1523 | * | | |
---|
1524 | * | | |
---|
1525 | * | | |
---|
1526 | * +-----------------------+ |
---|
1527 | * |
---|
1528 | * |
---|
1529 | * The above case can be detected by a difference in the |
---|
1530 | * number of X and Y pspans. When this happens, X and Y pspans |
---|
1531 | * aren't easy to pair or match against strokes. |
---|
1532 | * |
---|
1533 | * When X and Y pspans differ in number, the axis with the |
---|
1534 | * smaller number of pspans is regarded as having a repeating |
---|
1535 | * pspan (or a multi-pspan)--in the above illustration, the |
---|
1536 | * Y-axis has a repeating pspan. Our approach is to try to |
---|
1537 | * match the multi-pspan repeatedly against strokes. The |
---|
1538 | * difference between the number of X and Y pspans gives us a |
---|
1539 | * crude repeat_count for matching multi-pspans--i.e. the |
---|
1540 | * multi-pspan along the Y axis (above) has a repeat_count of 1. |
---|
1541 | */ |
---|
1542 | repeat_count = abs(n_xpspans - n_ypspans); |
---|
1543 | |
---|
1544 | fg_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans, |
---|
1545 | (((repeat_count != 0) && ((n_xpspans < n_ypspans))) ? |
---|
1546 | repeat_count : 0)); |
---|
1547 | fg_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans, |
---|
1548 | (((repeat_count != 0) && (n_ypspans < n_xpspans)) ? |
---|
1549 | repeat_count : 0)); |
---|
1550 | |
---|
1551 | /* Update the state of strokes based on the above pspan matches. */ |
---|
1552 | TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { |
---|
1553 | |
---|
1554 | if (strokep->components[X].matched && |
---|
1555 | strokep->components[Y].matched) { |
---|
1556 | strokep->matched = true; |
---|
1557 | strokep->instantaneous_dx = |
---|
1558 | strokep->components[X].delta_mickeys; |
---|
1559 | strokep->instantaneous_dy = |
---|
1560 | strokep->components[Y].delta_mickeys; |
---|
1561 | atp_advance_stroke_state(sc, strokep, &movement); |
---|
1562 | } else { |
---|
1563 | /* |
---|
1564 | * At least one component of this stroke |
---|
1565 | * didn't match against current pspans; |
---|
1566 | * terminate it. |
---|
1567 | */ |
---|
1568 | atp_terminate_stroke(sc, strokep); |
---|
1569 | } |
---|
1570 | } |
---|
1571 | |
---|
1572 | /* Add new strokes for pairs of unmatched pspans */ |
---|
1573 | for (i = 0; i < n_xpspans; i++) { |
---|
1574 | if (pspans_x[i].matched == false) break; |
---|
1575 | } |
---|
1576 | for (j = 0; j < n_ypspans; j++) { |
---|
1577 | if (pspans_y[j].matched == false) break; |
---|
1578 | } |
---|
1579 | if ((i < n_xpspans) && (j < n_ypspans)) { |
---|
1580 | #ifdef USB_DEBUG |
---|
1581 | if (atp_debug >= ATP_LLEVEL_INFO) { |
---|
1582 | printf("unmatched pspans:"); |
---|
1583 | for (; i < n_xpspans; i++) { |
---|
1584 | if (pspans_x[i].matched) |
---|
1585 | continue; |
---|
1586 | printf(" X:[loc:%u,cum:%u]", |
---|
1587 | pspans_x[i].loc, pspans_x[i].cum); |
---|
1588 | } |
---|
1589 | for (; j < n_ypspans; j++) { |
---|
1590 | if (pspans_y[j].matched) |
---|
1591 | continue; |
---|
1592 | printf(" Y:[loc:%u,cum:%u]", |
---|
1593 | pspans_y[j].loc, pspans_y[j].cum); |
---|
1594 | } |
---|
1595 | printf("\n"); |
---|
1596 | } |
---|
1597 | #endif /* USB_DEBUG */ |
---|
1598 | if ((n_xpspans == 1) && (n_ypspans == 1)) |
---|
1599 | /* The common case of a single pair of new pspans. */ |
---|
1600 | fg_add_stroke(sc, &pspans_x[0], &pspans_y[0]); |
---|
1601 | else |
---|
1602 | fg_add_new_strokes(sc, pspans_x, n_xpspans, |
---|
1603 | pspans_y, n_ypspans); |
---|
1604 | } |
---|
1605 | |
---|
1606 | #ifdef USB_DEBUG |
---|
1607 | if (atp_debug >= ATP_LLEVEL_INFO) { |
---|
1608 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { |
---|
1609 | printf(" %s%clc:%u,dm:%d,cum:%d,max:%d,%c" |
---|
1610 | ",%clc:%u,dm:%d,cum:%d,max:%d,%c", |
---|
1611 | (strokep->flags & ATSF_ZOMBIE) ? "zomb:" : "", |
---|
1612 | (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<', |
---|
1613 | strokep->components[X].loc, |
---|
1614 | strokep->components[X].delta_mickeys, |
---|
1615 | strokep->components[X].cum_pressure, |
---|
1616 | strokep->components[X].max_cum_pressure, |
---|
1617 | (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>', |
---|
1618 | (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<', |
---|
1619 | strokep->components[Y].loc, |
---|
1620 | strokep->components[Y].delta_mickeys, |
---|
1621 | strokep->components[Y].cum_pressure, |
---|
1622 | strokep->components[Y].max_cum_pressure, |
---|
1623 | (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>'); |
---|
1624 | } |
---|
1625 | if (TAILQ_FIRST(&sc->sc_stroke_used) != NULL) |
---|
1626 | printf("\n"); |
---|
1627 | } |
---|
1628 | #endif /* USB_DEBUG */ |
---|
1629 | return (movement); |
---|
1630 | } |
---|
1631 | |
---|
1632 | /* |
---|
1633 | * Update strokes by matching against current pressure-spans. |
---|
1634 | * Return true if any movement is detected. |
---|
1635 | */ |
---|
1636 | static boolean_t |
---|
1637 | wsp_update_strokes(struct atp_softc *sc, wsp_finger_t *fingers, u_int n_fingers) |
---|
1638 | { |
---|
1639 | boolean_t movement = false; |
---|
1640 | atp_stroke_t *strokep_next; |
---|
1641 | atp_stroke_t *strokep; |
---|
1642 | u_int i; |
---|
1643 | |
---|
1644 | if (sc->sc_n_strokes > 0) { |
---|
1645 | movement = wsp_match_strokes_against_fingers( |
---|
1646 | sc, fingers, n_fingers); |
---|
1647 | |
---|
1648 | /* handle zombie strokes */ |
---|
1649 | TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { |
---|
1650 | if (strokep->matched) |
---|
1651 | continue; |
---|
1652 | atp_terminate_stroke(sc, strokep); |
---|
1653 | } |
---|
1654 | } |
---|
1655 | |
---|
1656 | /* initialize unmatched fingers as strokes */ |
---|
1657 | for (i = 0; i != n_fingers; i++) { |
---|
1658 | if (fingers[i].matched) |
---|
1659 | continue; |
---|
1660 | |
---|
1661 | wsp_add_stroke(sc, fingers + i); |
---|
1662 | } |
---|
1663 | return (movement); |
---|
1664 | } |
---|
1665 | |
---|
1666 | /* Initialize a stroke using a pressure-span. */ |
---|
1667 | static void |
---|
1668 | fg_add_stroke(struct atp_softc *sc, const fg_pspan *pspan_x, |
---|
1669 | const fg_pspan *pspan_y) |
---|
1670 | { |
---|
1671 | atp_stroke_t *strokep; |
---|
1672 | |
---|
1673 | strokep = atp_alloc_stroke(sc); |
---|
1674 | if (strokep == NULL) |
---|
1675 | return; |
---|
1676 | |
---|
1677 | /* |
---|
1678 | * Strokes begin as potential touches. If a stroke survives |
---|
1679 | * longer than a threshold, or if it records significant |
---|
1680 | * cumulative movement, then it is considered a 'slide'. |
---|
1681 | */ |
---|
1682 | strokep->type = ATP_STROKE_TOUCH; |
---|
1683 | strokep->matched = false; |
---|
1684 | microtime(&strokep->ctime); |
---|
1685 | strokep->age = 1; /* number of interrupts */ |
---|
1686 | strokep->x = pspan_x->loc; |
---|
1687 | strokep->y = pspan_y->loc; |
---|
1688 | |
---|
1689 | strokep->components[X].loc = pspan_x->loc; |
---|
1690 | strokep->components[X].cum_pressure = pspan_x->cum; |
---|
1691 | strokep->components[X].max_cum_pressure = pspan_x->cum; |
---|
1692 | strokep->components[X].matched = true; |
---|
1693 | |
---|
1694 | strokep->components[Y].loc = pspan_y->loc; |
---|
1695 | strokep->components[Y].cum_pressure = pspan_y->cum; |
---|
1696 | strokep->components[Y].max_cum_pressure = pspan_y->cum; |
---|
1697 | strokep->components[Y].matched = true; |
---|
1698 | |
---|
1699 | if (sc->sc_n_strokes > 1) { |
---|
1700 | /* Reset double-tap-n-drag if we have more than one strokes. */ |
---|
1701 | sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; |
---|
1702 | } |
---|
1703 | |
---|
1704 | DPRINTFN(ATP_LLEVEL_INFO, "[%u,%u], time: %u,%ld\n", |
---|
1705 | strokep->components[X].loc, |
---|
1706 | strokep->components[Y].loc, |
---|
1707 | (u_int)strokep->ctime.tv_sec, |
---|
1708 | (unsigned long int)strokep->ctime.tv_usec); |
---|
1709 | } |
---|
1710 | |
---|
1711 | static void |
---|
1712 | fg_add_new_strokes(struct atp_softc *sc, fg_pspan *pspans_x, |
---|
1713 | u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans) |
---|
1714 | { |
---|
1715 | fg_pspan spans[2][FG_MAX_PSPANS_PER_AXIS]; |
---|
1716 | u_int nspans[2]; |
---|
1717 | u_int i; |
---|
1718 | u_int j; |
---|
1719 | |
---|
1720 | /* Copy unmatched pspans into the local arrays. */ |
---|
1721 | for (i = 0, nspans[X] = 0; i < n_xpspans; i++) { |
---|
1722 | if (pspans_x[i].matched == false) { |
---|
1723 | spans[X][nspans[X]] = pspans_x[i]; |
---|
1724 | nspans[X]++; |
---|
1725 | } |
---|
1726 | } |
---|
1727 | for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) { |
---|
1728 | if (pspans_y[j].matched == false) { |
---|
1729 | spans[Y][nspans[Y]] = pspans_y[j]; |
---|
1730 | nspans[Y]++; |
---|
1731 | } |
---|
1732 | } |
---|
1733 | |
---|
1734 | if (nspans[X] == nspans[Y]) { |
---|
1735 | /* Create new strokes from pairs of unmatched pspans */ |
---|
1736 | for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++) |
---|
1737 | fg_add_stroke(sc, &spans[X][i], &spans[Y][j]); |
---|
1738 | } else { |
---|
1739 | u_int cum = 0; |
---|
1740 | atp_axis repeat_axis; /* axis with multi-pspans */ |
---|
1741 | u_int repeat_count; /* repeat count for the multi-pspan*/ |
---|
1742 | u_int repeat_index = 0; /* index of the multi-span */ |
---|
1743 | |
---|
1744 | repeat_axis = (nspans[X] > nspans[Y]) ? Y : X; |
---|
1745 | repeat_count = abs(nspans[X] - nspans[Y]); |
---|
1746 | for (i = 0; i < nspans[repeat_axis]; i++) { |
---|
1747 | if (spans[repeat_axis][i].cum > cum) { |
---|
1748 | repeat_index = i; |
---|
1749 | cum = spans[repeat_axis][i].cum; |
---|
1750 | } |
---|
1751 | } |
---|
1752 | |
---|
1753 | /* Create new strokes from pairs of unmatched pspans */ |
---|
1754 | i = 0, j = 0; |
---|
1755 | for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) { |
---|
1756 | fg_add_stroke(sc, &spans[X][i], &spans[Y][j]); |
---|
1757 | |
---|
1758 | /* Take care to repeat at the multi-pspan. */ |
---|
1759 | if (repeat_count > 0) { |
---|
1760 | if ((repeat_axis == X) && |
---|
1761 | (repeat_index == i)) { |
---|
1762 | i--; /* counter loop increment */ |
---|
1763 | repeat_count--; |
---|
1764 | } else if ((repeat_axis == Y) && |
---|
1765 | (repeat_index == j)) { |
---|
1766 | j--; /* counter loop increment */ |
---|
1767 | repeat_count--; |
---|
1768 | } |
---|
1769 | } |
---|
1770 | } |
---|
1771 | } |
---|
1772 | } |
---|
1773 | |
---|
1774 | /* Initialize a stroke from an unmatched finger. */ |
---|
1775 | static void |
---|
1776 | wsp_add_stroke(struct atp_softc *sc, const wsp_finger_t *fingerp) |
---|
1777 | { |
---|
1778 | atp_stroke_t *strokep; |
---|
1779 | |
---|
1780 | strokep = atp_alloc_stroke(sc); |
---|
1781 | if (strokep == NULL) |
---|
1782 | return; |
---|
1783 | |
---|
1784 | /* |
---|
1785 | * Strokes begin as potential touches. If a stroke survives |
---|
1786 | * longer than a threshold, or if it records significant |
---|
1787 | * cumulative movement, then it is considered a 'slide'. |
---|
1788 | */ |
---|
1789 | strokep->type = ATP_STROKE_TOUCH; |
---|
1790 | strokep->matched = true; |
---|
1791 | microtime(&strokep->ctime); |
---|
1792 | strokep->age = 1; /* number of interrupts */ |
---|
1793 | strokep->x = fingerp->x; |
---|
1794 | strokep->y = fingerp->y; |
---|
1795 | |
---|
1796 | /* Reset double-tap-n-drag if we have more than one strokes. */ |
---|
1797 | if (sc->sc_n_strokes > 1) |
---|
1798 | sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; |
---|
1799 | |
---|
1800 | DPRINTFN(ATP_LLEVEL_INFO, "[%d,%d]\n", strokep->x, strokep->y); |
---|
1801 | } |
---|
1802 | |
---|
1803 | static void |
---|
1804 | atp_advance_stroke_state(struct atp_softc *sc, atp_stroke_t *strokep, |
---|
1805 | boolean_t *movementp) |
---|
1806 | { |
---|
1807 | /* Revitalize stroke if it had previously been marked as a zombie. */ |
---|
1808 | if (strokep->flags & ATSF_ZOMBIE) |
---|
1809 | strokep->flags &= ~ATSF_ZOMBIE; |
---|
1810 | |
---|
1811 | strokep->age++; |
---|
1812 | if (strokep->age <= atp_stroke_maturity_threshold) { |
---|
1813 | /* Avoid noise from immature strokes. */ |
---|
1814 | strokep->instantaneous_dx = 0; |
---|
1815 | strokep->instantaneous_dy = 0; |
---|
1816 | } |
---|
1817 | |
---|
1818 | if (atp_compute_stroke_movement(strokep)) |
---|
1819 | *movementp = true; |
---|
1820 | |
---|
1821 | if (strokep->type != ATP_STROKE_TOUCH) |
---|
1822 | return; |
---|
1823 | |
---|
1824 | /* Convert touch strokes to slides upon detecting movement or age. */ |
---|
1825 | if ((abs(strokep->cum_movement_x) > atp_slide_min_movement) || |
---|
1826 | (abs(strokep->cum_movement_y) > atp_slide_min_movement)) |
---|
1827 | atp_convert_to_slide(sc, strokep); |
---|
1828 | else { |
---|
1829 | /* Compute the stroke's age. */ |
---|
1830 | struct timeval tdiff; |
---|
1831 | getmicrotime(&tdiff); |
---|
1832 | if (timevalcmp(&tdiff, &strokep->ctime, >)) { |
---|
1833 | timevalsub(&tdiff, &strokep->ctime); |
---|
1834 | |
---|
1835 | if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) || |
---|
1836 | ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) && |
---|
1837 | (tdiff.tv_usec >= (atp_touch_timeout % 1000000)))) |
---|
1838 | atp_convert_to_slide(sc, strokep); |
---|
1839 | } |
---|
1840 | } |
---|
1841 | } |
---|
1842 | |
---|
1843 | static boolean_t |
---|
1844 | atp_stroke_has_small_movement(const atp_stroke_t *strokep) |
---|
1845 | { |
---|
1846 | return (((u_int)abs(strokep->instantaneous_dx) <= |
---|
1847 | atp_small_movement_threshold) && |
---|
1848 | ((u_int)abs(strokep->instantaneous_dy) <= |
---|
1849 | atp_small_movement_threshold)); |
---|
1850 | } |
---|
1851 | |
---|
1852 | /* |
---|
1853 | * Accumulate instantaneous changes into the stroke's 'pending' bucket; if |
---|
1854 | * the aggregate exceeds the small_movement_threshold, then retain |
---|
1855 | * instantaneous changes for later. |
---|
1856 | */ |
---|
1857 | static void |
---|
1858 | atp_update_pending_mickeys(atp_stroke_t *strokep) |
---|
1859 | { |
---|
1860 | /* accumulate instantaneous movement */ |
---|
1861 | strokep->pending_dx += strokep->instantaneous_dx; |
---|
1862 | strokep->pending_dy += strokep->instantaneous_dy; |
---|
1863 | |
---|
1864 | #define UPDATE_INSTANTANEOUS_AND_PENDING(I, P) \ |
---|
1865 | if (abs((P)) <= atp_small_movement_threshold) \ |
---|
1866 | (I) = 0; /* clobber small movement */ \ |
---|
1867 | else { \ |
---|
1868 | if ((I) > 0) { \ |
---|
1869 | /* \ |
---|
1870 | * Round up instantaneous movement to the nearest \ |
---|
1871 | * ceiling. This helps preserve small mickey \ |
---|
1872 | * movements from being lost in following scaling \ |
---|
1873 | * operation. \ |
---|
1874 | */ \ |
---|
1875 | (I) = (((I) + (atp_mickeys_scale_factor - 1)) / \ |
---|
1876 | atp_mickeys_scale_factor) * \ |
---|
1877 | atp_mickeys_scale_factor; \ |
---|
1878 | \ |
---|
1879 | /* \ |
---|
1880 | * Deduct the rounded mickeys from pending mickeys. \ |
---|
1881 | * Note: we multiply by 2 to offset the previous \ |
---|
1882 | * accumulation of instantaneous movement into \ |
---|
1883 | * pending. \ |
---|
1884 | */ \ |
---|
1885 | (P) -= ((I) << 1); \ |
---|
1886 | \ |
---|
1887 | /* truncate pending to 0 if it becomes negative. */ \ |
---|
1888 | (P) = imax((P), 0); \ |
---|
1889 | } else { \ |
---|
1890 | /* \ |
---|
1891 | * Round down instantaneous movement to the nearest \ |
---|
1892 | * ceiling. This helps preserve small mickey \ |
---|
1893 | * movements from being lost in following scaling \ |
---|
1894 | * operation. \ |
---|
1895 | */ \ |
---|
1896 | (I) = (((I) - (atp_mickeys_scale_factor - 1)) / \ |
---|
1897 | atp_mickeys_scale_factor) * \ |
---|
1898 | atp_mickeys_scale_factor; \ |
---|
1899 | \ |
---|
1900 | /* \ |
---|
1901 | * Deduct the rounded mickeys from pending mickeys. \ |
---|
1902 | * Note: we multiply by 2 to offset the previous \ |
---|
1903 | * accumulation of instantaneous movement into \ |
---|
1904 | * pending. \ |
---|
1905 | */ \ |
---|
1906 | (P) -= ((I) << 1); \ |
---|
1907 | \ |
---|
1908 | /* truncate pending to 0 if it becomes positive. */ \ |
---|
1909 | (P) = imin((P), 0); \ |
---|
1910 | } \ |
---|
1911 | } |
---|
1912 | |
---|
1913 | UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dx, |
---|
1914 | strokep->pending_dx); |
---|
1915 | UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dy, |
---|
1916 | strokep->pending_dy); |
---|
1917 | } |
---|
1918 | |
---|
1919 | /* |
---|
1920 | * Compute a smoothened value for the stroke's movement from |
---|
1921 | * instantaneous changes in the X and Y components. |
---|
1922 | */ |
---|
1923 | static boolean_t |
---|
1924 | atp_compute_stroke_movement(atp_stroke_t *strokep) |
---|
1925 | { |
---|
1926 | /* |
---|
1927 | * Short movements are added first to the 'pending' bucket, |
---|
1928 | * and then acted upon only when their aggregate exceeds a |
---|
1929 | * threshold. This has the effect of filtering away movement |
---|
1930 | * noise. |
---|
1931 | */ |
---|
1932 | if (atp_stroke_has_small_movement(strokep)) |
---|
1933 | atp_update_pending_mickeys(strokep); |
---|
1934 | else { /* large movement */ |
---|
1935 | /* clear away any pending mickeys if there are large movements*/ |
---|
1936 | strokep->pending_dx = 0; |
---|
1937 | strokep->pending_dy = 0; |
---|
1938 | } |
---|
1939 | |
---|
1940 | /* scale movement */ |
---|
1941 | strokep->movement_dx = (strokep->instantaneous_dx) / |
---|
1942 | (int)atp_mickeys_scale_factor; |
---|
1943 | strokep->movement_dy = (strokep->instantaneous_dy) / |
---|
1944 | (int)atp_mickeys_scale_factor; |
---|
1945 | |
---|
1946 | if ((abs(strokep->instantaneous_dx) >= ATP_FAST_MOVEMENT_TRESHOLD) || |
---|
1947 | (abs(strokep->instantaneous_dy) >= ATP_FAST_MOVEMENT_TRESHOLD)) { |
---|
1948 | strokep->movement_dx <<= 1; |
---|
1949 | strokep->movement_dy <<= 1; |
---|
1950 | } |
---|
1951 | |
---|
1952 | strokep->cum_movement_x += strokep->movement_dx; |
---|
1953 | strokep->cum_movement_y += strokep->movement_dy; |
---|
1954 | |
---|
1955 | return ((strokep->movement_dx != 0) || (strokep->movement_dy != 0)); |
---|
1956 | } |
---|
1957 | |
---|
1958 | /* |
---|
1959 | * Terminate a stroke. Aside from immature strokes, a slide or touch is |
---|
1960 | * retained as a zombies so as to reap all their termination siblings |
---|
1961 | * together; this helps establish the number of fingers involved at the |
---|
1962 | * end of a multi-touch gesture. |
---|
1963 | */ |
---|
1964 | static void |
---|
1965 | atp_terminate_stroke(struct atp_softc *sc, atp_stroke_t *strokep) |
---|
1966 | { |
---|
1967 | if (strokep->flags & ATSF_ZOMBIE) |
---|
1968 | return; |
---|
1969 | |
---|
1970 | /* Drop immature strokes rightaway. */ |
---|
1971 | if (strokep->age <= atp_stroke_maturity_threshold) { |
---|
1972 | atp_free_stroke(sc, strokep); |
---|
1973 | return; |
---|
1974 | } |
---|
1975 | |
---|
1976 | strokep->flags |= ATSF_ZOMBIE; |
---|
1977 | sc->sc_state |= ATP_ZOMBIES_EXIST; |
---|
1978 | |
---|
1979 | callout_reset(&sc->sc_callout, ATP_ZOMBIE_STROKE_REAP_INTERVAL, |
---|
1980 | atp_reap_sibling_zombies, sc); |
---|
1981 | |
---|
1982 | /* |
---|
1983 | * Reset the double-click-n-drag at the termination of any |
---|
1984 | * slide stroke. |
---|
1985 | */ |
---|
1986 | if (strokep->type == ATP_STROKE_SLIDE) |
---|
1987 | sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; |
---|
1988 | } |
---|
1989 | |
---|
1990 | static boolean_t |
---|
1991 | atp_is_horizontal_scroll(const atp_stroke_t *strokep) |
---|
1992 | { |
---|
1993 | if (abs(strokep->cum_movement_x) < atp_slide_min_movement) |
---|
1994 | return (false); |
---|
1995 | if (strokep->cum_movement_y == 0) |
---|
1996 | return (true); |
---|
1997 | return (abs(strokep->cum_movement_x / strokep->cum_movement_y) >= 4); |
---|
1998 | } |
---|
1999 | |
---|
2000 | static boolean_t |
---|
2001 | atp_is_vertical_scroll(const atp_stroke_t *strokep) |
---|
2002 | { |
---|
2003 | if (abs(strokep->cum_movement_y) < atp_slide_min_movement) |
---|
2004 | return (false); |
---|
2005 | if (strokep->cum_movement_x == 0) |
---|
2006 | return (true); |
---|
2007 | return (abs(strokep->cum_movement_y / strokep->cum_movement_x) >= 4); |
---|
2008 | } |
---|
2009 | |
---|
2010 | static void |
---|
2011 | atp_reap_sibling_zombies(void *arg) |
---|
2012 | { |
---|
2013 | struct atp_softc *sc = (struct atp_softc *)arg; |
---|
2014 | u_int8_t n_touches_reaped = 0; |
---|
2015 | u_int8_t n_slides_reaped = 0; |
---|
2016 | u_int8_t n_horizontal_scrolls = 0; |
---|
2017 | u_int8_t n_vertical_scrolls = 0; |
---|
2018 | int horizontal_scroll = 0; |
---|
2019 | int vertical_scroll = 0; |
---|
2020 | atp_stroke_t *strokep; |
---|
2021 | atp_stroke_t *strokep_next; |
---|
2022 | |
---|
2023 | DPRINTFN(ATP_LLEVEL_INFO, "\n"); |
---|
2024 | |
---|
2025 | TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { |
---|
2026 | if ((strokep->flags & ATSF_ZOMBIE) == 0) |
---|
2027 | continue; |
---|
2028 | |
---|
2029 | if (strokep->type == ATP_STROKE_TOUCH) { |
---|
2030 | n_touches_reaped++; |
---|
2031 | } else { |
---|
2032 | n_slides_reaped++; |
---|
2033 | |
---|
2034 | if (atp_is_horizontal_scroll(strokep)) { |
---|
2035 | n_horizontal_scrolls++; |
---|
2036 | horizontal_scroll += strokep->cum_movement_x; |
---|
2037 | } else if (atp_is_vertical_scroll(strokep)) { |
---|
2038 | n_vertical_scrolls++; |
---|
2039 | vertical_scroll += strokep->cum_movement_y; |
---|
2040 | } |
---|
2041 | } |
---|
2042 | |
---|
2043 | atp_free_stroke(sc, strokep); |
---|
2044 | } |
---|
2045 | |
---|
2046 | DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", |
---|
2047 | n_touches_reaped + n_slides_reaped); |
---|
2048 | sc->sc_state &= ~ATP_ZOMBIES_EXIST; |
---|
2049 | |
---|
2050 | /* No further processing necessary if physical button is depressed. */ |
---|
2051 | if (sc->sc_ibtn != 0) |
---|
2052 | return; |
---|
2053 | |
---|
2054 | if ((n_touches_reaped == 0) && (n_slides_reaped == 0)) |
---|
2055 | return; |
---|
2056 | |
---|
2057 | /* Add a pair of virtual button events (button-down and button-up) if |
---|
2058 | * the physical button isn't pressed. */ |
---|
2059 | if (n_touches_reaped != 0) { |
---|
2060 | if (n_touches_reaped < atp_tap_minimum) |
---|
2061 | return; |
---|
2062 | |
---|
2063 | switch (n_touches_reaped) { |
---|
2064 | case 1: |
---|
2065 | atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN); |
---|
2066 | microtime(&sc->sc_touch_reap_time); /* remember this time */ |
---|
2067 | break; |
---|
2068 | case 2: |
---|
2069 | atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN); |
---|
2070 | break; |
---|
2071 | case 3: |
---|
2072 | atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN); |
---|
2073 | break; |
---|
2074 | default: |
---|
2075 | /* we handle taps of only up to 3 fingers */ |
---|
2076 | return; |
---|
2077 | } |
---|
2078 | atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ |
---|
2079 | |
---|
2080 | } else if ((n_slides_reaped == 2) && (n_horizontal_scrolls == 2)) { |
---|
2081 | if (horizontal_scroll < 0) |
---|
2082 | atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON4DOWN); |
---|
2083 | else |
---|
2084 | atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON5DOWN); |
---|
2085 | atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ |
---|
2086 | } |
---|
2087 | } |
---|
2088 | |
---|
2089 | /* Switch a given touch stroke to being a slide. */ |
---|
2090 | static void |
---|
2091 | atp_convert_to_slide(struct atp_softc *sc, atp_stroke_t *strokep) |
---|
2092 | { |
---|
2093 | strokep->type = ATP_STROKE_SLIDE; |
---|
2094 | |
---|
2095 | /* Are we at the beginning of a double-click-n-drag? */ |
---|
2096 | if ((sc->sc_n_strokes == 1) && |
---|
2097 | ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) && |
---|
2098 | timevalcmp(&strokep->ctime, &sc->sc_touch_reap_time, >)) { |
---|
2099 | struct timeval delta; |
---|
2100 | struct timeval window = { |
---|
2101 | atp_double_tap_threshold / 1000000, |
---|
2102 | atp_double_tap_threshold % 1000000 |
---|
2103 | }; |
---|
2104 | |
---|
2105 | delta = strokep->ctime; |
---|
2106 | timevalsub(&delta, &sc->sc_touch_reap_time); |
---|
2107 | if (timevalcmp(&delta, &window, <=)) |
---|
2108 | sc->sc_state |= ATP_DOUBLE_TAP_DRAG; |
---|
2109 | } |
---|
2110 | } |
---|
2111 | |
---|
2112 | static void |
---|
2113 | atp_reset_buf(struct atp_softc *sc) |
---|
2114 | { |
---|
2115 | /* reset read queue */ |
---|
2116 | usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); |
---|
2117 | } |
---|
2118 | |
---|
2119 | static void |
---|
2120 | atp_add_to_queue(struct atp_softc *sc, int dx, int dy, int dz, |
---|
2121 | uint32_t buttons_in) |
---|
2122 | { |
---|
2123 | uint32_t buttons_out; |
---|
2124 | uint8_t buf[8]; |
---|
2125 | |
---|
2126 | dx = imin(dx, 254); dx = imax(dx, -256); |
---|
2127 | dy = imin(dy, 254); dy = imax(dy, -256); |
---|
2128 | dz = imin(dz, 126); dz = imax(dz, -128); |
---|
2129 | |
---|
2130 | buttons_out = MOUSE_MSC_BUTTONS; |
---|
2131 | if (buttons_in & MOUSE_BUTTON1DOWN) |
---|
2132 | buttons_out &= ~MOUSE_MSC_BUTTON1UP; |
---|
2133 | else if (buttons_in & MOUSE_BUTTON2DOWN) |
---|
2134 | buttons_out &= ~MOUSE_MSC_BUTTON2UP; |
---|
2135 | else if (buttons_in & MOUSE_BUTTON3DOWN) |
---|
2136 | buttons_out &= ~MOUSE_MSC_BUTTON3UP; |
---|
2137 | |
---|
2138 | DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n", |
---|
2139 | dx, dy, buttons_out); |
---|
2140 | |
---|
2141 | /* Encode the mouse data in standard format; refer to mouse(4) */ |
---|
2142 | buf[0] = sc->sc_mode.syncmask[1]; |
---|
2143 | buf[0] |= buttons_out; |
---|
2144 | buf[1] = dx >> 1; |
---|
2145 | buf[2] = dy >> 1; |
---|
2146 | buf[3] = dx - (dx >> 1); |
---|
2147 | buf[4] = dy - (dy >> 1); |
---|
2148 | /* Encode extra bytes for level 1 */ |
---|
2149 | if (sc->sc_mode.level == 1) { |
---|
2150 | buf[5] = dz >> 1; |
---|
2151 | buf[6] = dz - (dz >> 1); |
---|
2152 | buf[7] = (((~buttons_in) >> 3) & MOUSE_SYS_EXTBUTTONS); |
---|
2153 | } |
---|
2154 | |
---|
2155 | usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, |
---|
2156 | sc->sc_mode.packetsize, 1); |
---|
2157 | } |
---|
2158 | |
---|
2159 | static int |
---|
2160 | atp_probe(device_t self) |
---|
2161 | { |
---|
2162 | struct usb_attach_arg *uaa = device_get_ivars(self); |
---|
2163 | |
---|
2164 | if (uaa->usb_mode != USB_MODE_HOST) |
---|
2165 | return (ENXIO); |
---|
2166 | |
---|
2167 | if (uaa->info.bInterfaceClass != UICLASS_HID) |
---|
2168 | return (ENXIO); |
---|
2169 | /* |
---|
2170 | * Note: for some reason, the check |
---|
2171 | * (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) doesn't hold true |
---|
2172 | * for wellspring trackpads, so we've removed it from the common path. |
---|
2173 | */ |
---|
2174 | |
---|
2175 | if ((usbd_lookup_id_by_uaa(fg_devs, sizeof(fg_devs), uaa)) == 0) |
---|
2176 | return ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) ? |
---|
2177 | 0 : ENXIO); |
---|
2178 | |
---|
2179 | if ((usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)) == 0) |
---|
2180 | if (uaa->info.bIfaceIndex == WELLSPRING_INTERFACE_INDEX) |
---|
2181 | return (0); |
---|
2182 | |
---|
2183 | return (ENXIO); |
---|
2184 | } |
---|
2185 | |
---|
2186 | static int |
---|
2187 | atp_attach(device_t dev) |
---|
2188 | { |
---|
2189 | struct atp_softc *sc = device_get_softc(dev); |
---|
2190 | struct usb_attach_arg *uaa = device_get_ivars(dev); |
---|
2191 | usb_error_t err; |
---|
2192 | void *descriptor_ptr = NULL; |
---|
2193 | uint16_t descriptor_len; |
---|
2194 | unsigned long di; |
---|
2195 | |
---|
2196 | DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc); |
---|
2197 | |
---|
2198 | sc->sc_dev = dev; |
---|
2199 | sc->sc_usb_device = uaa->device; |
---|
2200 | |
---|
2201 | /* Get HID descriptor */ |
---|
2202 | if (usbd_req_get_hid_desc(uaa->device, NULL, &descriptor_ptr, |
---|
2203 | &descriptor_len, M_TEMP, uaa->info.bIfaceIndex) != |
---|
2204 | USB_ERR_NORMAL_COMPLETION) |
---|
2205 | return (ENXIO); |
---|
2206 | |
---|
2207 | /* Get HID report descriptor length */ |
---|
2208 | sc->sc_expected_sensor_data_len = hid_report_size(descriptor_ptr, |
---|
2209 | descriptor_len, hid_input, NULL); |
---|
2210 | free(descriptor_ptr, M_TEMP); |
---|
2211 | |
---|
2212 | if ((sc->sc_expected_sensor_data_len <= 0) || |
---|
2213 | (sc->sc_expected_sensor_data_len > ATP_SENSOR_DATA_BUF_MAX)) { |
---|
2214 | DPRINTF("atp_attach: datalength invalid or too large: %d\n", |
---|
2215 | sc->sc_expected_sensor_data_len); |
---|
2216 | return (ENXIO); |
---|
2217 | } |
---|
2218 | |
---|
2219 | /* |
---|
2220 | * By default the touchpad behaves like an HID device, sending |
---|
2221 | * packets with reportID = 2. Such reports contain only |
---|
2222 | * limited information--they encode movement deltas and button |
---|
2223 | * events,--but do not include data from the pressure |
---|
2224 | * sensors. The device input mode can be switched from HID |
---|
2225 | * reports to raw sensor data using vendor-specific USB |
---|
2226 | * control commands. |
---|
2227 | */ |
---|
2228 | if ((err = atp_set_device_mode(sc, RAW_SENSOR_MODE)) != 0) { |
---|
2229 | DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err); |
---|
2230 | return (ENXIO); |
---|
2231 | } |
---|
2232 | |
---|
2233 | mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE); |
---|
2234 | |
---|
2235 | di = USB_GET_DRIVER_INFO(uaa); |
---|
2236 | |
---|
2237 | sc->sc_family = DECODE_FAMILY_FROM_DRIVER_INFO(di); |
---|
2238 | |
---|
2239 | switch(sc->sc_family) { |
---|
2240 | case TRACKPAD_FAMILY_FOUNTAIN_GEYSER: |
---|
2241 | sc->sc_params = |
---|
2242 | &fg_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; |
---|
2243 | sc->sensor_data_interpreter = fg_interpret_sensor_data; |
---|
2244 | break; |
---|
2245 | case TRACKPAD_FAMILY_WELLSPRING: |
---|
2246 | sc->sc_params = |
---|
2247 | &wsp_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; |
---|
2248 | sc->sensor_data_interpreter = wsp_interpret_sensor_data; |
---|
2249 | break; |
---|
2250 | default: |
---|
2251 | goto detach; |
---|
2252 | } |
---|
2253 | |
---|
2254 | err = usbd_transfer_setup(uaa->device, |
---|
2255 | &uaa->info.bIfaceIndex, sc->sc_xfer, atp_xfer_config, |
---|
2256 | ATP_N_TRANSFER, sc, &sc->sc_mutex); |
---|
2257 | if (err) { |
---|
2258 | DPRINTF("error=%s\n", usbd_errstr(err)); |
---|
2259 | goto detach; |
---|
2260 | } |
---|
2261 | |
---|
2262 | if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex, |
---|
2263 | &atp_fifo_methods, &sc->sc_fifo, |
---|
2264 | device_get_unit(dev), -1, uaa->info.bIfaceIndex, |
---|
2265 | UID_ROOT, GID_OPERATOR, 0644)) { |
---|
2266 | goto detach; |
---|
2267 | } |
---|
2268 | |
---|
2269 | device_set_usb_desc(dev); |
---|
2270 | |
---|
2271 | sc->sc_hw.buttons = 3; |
---|
2272 | sc->sc_hw.iftype = MOUSE_IF_USB; |
---|
2273 | sc->sc_hw.type = MOUSE_PAD; |
---|
2274 | sc->sc_hw.model = MOUSE_MODEL_GENERIC; |
---|
2275 | sc->sc_hw.hwid = 0; |
---|
2276 | sc->sc_mode.protocol = MOUSE_PROTO_MSC; |
---|
2277 | sc->sc_mode.rate = -1; |
---|
2278 | sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; |
---|
2279 | sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; |
---|
2280 | sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; |
---|
2281 | sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; |
---|
2282 | sc->sc_mode.accelfactor = 0; |
---|
2283 | sc->sc_mode.level = 0; |
---|
2284 | |
---|
2285 | sc->sc_state = 0; |
---|
2286 | sc->sc_ibtn = 0; |
---|
2287 | |
---|
2288 | callout_init_mtx(&sc->sc_callout, &sc->sc_mutex, 0); |
---|
2289 | |
---|
2290 | return (0); |
---|
2291 | |
---|
2292 | detach: |
---|
2293 | atp_detach(dev); |
---|
2294 | return (ENOMEM); |
---|
2295 | } |
---|
2296 | |
---|
2297 | static int |
---|
2298 | atp_detach(device_t dev) |
---|
2299 | { |
---|
2300 | struct atp_softc *sc; |
---|
2301 | |
---|
2302 | sc = device_get_softc(dev); |
---|
2303 | atp_set_device_mode(sc, HID_MODE); |
---|
2304 | |
---|
2305 | mtx_lock(&sc->sc_mutex); |
---|
2306 | callout_drain(&sc->sc_callout); |
---|
2307 | if (sc->sc_state & ATP_ENABLED) |
---|
2308 | atp_disable(sc); |
---|
2309 | mtx_unlock(&sc->sc_mutex); |
---|
2310 | |
---|
2311 | usb_fifo_detach(&sc->sc_fifo); |
---|
2312 | |
---|
2313 | usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER); |
---|
2314 | |
---|
2315 | mtx_destroy(&sc->sc_mutex); |
---|
2316 | |
---|
2317 | return (0); |
---|
2318 | } |
---|
2319 | |
---|
2320 | static void |
---|
2321 | atp_intr(struct usb_xfer *xfer, usb_error_t error) |
---|
2322 | { |
---|
2323 | struct atp_softc *sc = usbd_xfer_softc(xfer); |
---|
2324 | struct usb_page_cache *pc; |
---|
2325 | int len; |
---|
2326 | |
---|
2327 | usbd_xfer_status(xfer, &len, NULL, NULL, NULL); |
---|
2328 | |
---|
2329 | switch (USB_GET_STATE(xfer)) { |
---|
2330 | case USB_ST_TRANSFERRED: |
---|
2331 | pc = usbd_xfer_get_frame(xfer, 0); |
---|
2332 | usbd_copy_out(pc, 0, sc->sc_sensor_data, len); |
---|
2333 | if (len < sc->sc_expected_sensor_data_len) { |
---|
2334 | /* make sure we don't process old data */ |
---|
2335 | memset(sc->sc_sensor_data + len, 0, |
---|
2336 | sc->sc_expected_sensor_data_len - len); |
---|
2337 | } |
---|
2338 | |
---|
2339 | sc->sc_status.flags &= ~(MOUSE_STDBUTTONSCHANGED | |
---|
2340 | MOUSE_POSCHANGED); |
---|
2341 | sc->sc_status.obutton = sc->sc_status.button; |
---|
2342 | |
---|
2343 | (sc->sensor_data_interpreter)(sc, len); |
---|
2344 | |
---|
2345 | if (sc->sc_status.button != 0) { |
---|
2346 | /* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */ |
---|
2347 | sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; |
---|
2348 | } else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) { |
---|
2349 | /* Assume a button-press with DOUBLE_TAP_N_DRAG. */ |
---|
2350 | sc->sc_status.button = MOUSE_BUTTON1DOWN; |
---|
2351 | } |
---|
2352 | |
---|
2353 | sc->sc_status.flags |= |
---|
2354 | sc->sc_status.button ^ sc->sc_status.obutton; |
---|
2355 | if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) { |
---|
2356 | DPRINTFN(ATP_LLEVEL_INFO, "button %s\n", |
---|
2357 | ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ? |
---|
2358 | "pressed" : "released")); |
---|
2359 | } |
---|
2360 | |
---|
2361 | if (sc->sc_status.flags & (MOUSE_POSCHANGED | |
---|
2362 | MOUSE_STDBUTTONSCHANGED)) { |
---|
2363 | |
---|
2364 | atp_stroke_t *strokep; |
---|
2365 | u_int8_t n_movements = 0; |
---|
2366 | int dx = 0; |
---|
2367 | int dy = 0; |
---|
2368 | int dz = 0; |
---|
2369 | |
---|
2370 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { |
---|
2371 | if (strokep->flags & ATSF_ZOMBIE) |
---|
2372 | continue; |
---|
2373 | |
---|
2374 | dx += strokep->movement_dx; |
---|
2375 | dy += strokep->movement_dy; |
---|
2376 | if (strokep->movement_dx || |
---|
2377 | strokep->movement_dy) |
---|
2378 | n_movements++; |
---|
2379 | } |
---|
2380 | |
---|
2381 | /* average movement if multiple strokes record motion.*/ |
---|
2382 | if (n_movements > 1) { |
---|
2383 | dx /= (int)n_movements; |
---|
2384 | dy /= (int)n_movements; |
---|
2385 | } |
---|
2386 | |
---|
2387 | /* detect multi-finger vertical scrolls */ |
---|
2388 | if (n_movements >= 2) { |
---|
2389 | boolean_t all_vertical_scrolls = true; |
---|
2390 | TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { |
---|
2391 | if (strokep->flags & ATSF_ZOMBIE) |
---|
2392 | continue; |
---|
2393 | |
---|
2394 | if (!atp_is_vertical_scroll(strokep)) |
---|
2395 | all_vertical_scrolls = false; |
---|
2396 | } |
---|
2397 | if (all_vertical_scrolls) { |
---|
2398 | dz = dy; |
---|
2399 | dy = dx = 0; |
---|
2400 | } |
---|
2401 | } |
---|
2402 | |
---|
2403 | sc->sc_status.dx += dx; |
---|
2404 | sc->sc_status.dy += dy; |
---|
2405 | sc->sc_status.dz += dz; |
---|
2406 | atp_add_to_queue(sc, dx, -dy, -dz, sc->sc_status.button); |
---|
2407 | } |
---|
2408 | |
---|
2409 | case USB_ST_SETUP: |
---|
2410 | tr_setup: |
---|
2411 | /* check if we can put more data into the FIFO */ |
---|
2412 | if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { |
---|
2413 | usbd_xfer_set_frame_len(xfer, 0, |
---|
2414 | sc->sc_expected_sensor_data_len); |
---|
2415 | usbd_transfer_submit(xfer); |
---|
2416 | } |
---|
2417 | break; |
---|
2418 | |
---|
2419 | default: /* Error */ |
---|
2420 | if (error != USB_ERR_CANCELLED) { |
---|
2421 | /* try clear stall first */ |
---|
2422 | usbd_xfer_set_stall(xfer); |
---|
2423 | goto tr_setup; |
---|
2424 | } |
---|
2425 | break; |
---|
2426 | } |
---|
2427 | } |
---|
2428 | |
---|
2429 | static void |
---|
2430 | atp_start_read(struct usb_fifo *fifo) |
---|
2431 | { |
---|
2432 | struct atp_softc *sc = usb_fifo_softc(fifo); |
---|
2433 | int rate; |
---|
2434 | |
---|
2435 | /* Check if we should override the default polling interval */ |
---|
2436 | rate = sc->sc_pollrate; |
---|
2437 | /* Range check rate */ |
---|
2438 | if (rate > 1000) |
---|
2439 | rate = 1000; |
---|
2440 | /* Check for set rate */ |
---|
2441 | if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) { |
---|
2442 | /* Stop current transfer, if any */ |
---|
2443 | usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]); |
---|
2444 | /* Set new interval */ |
---|
2445 | usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate); |
---|
2446 | /* Only set pollrate once */ |
---|
2447 | sc->sc_pollrate = 0; |
---|
2448 | } |
---|
2449 | |
---|
2450 | usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]); |
---|
2451 | } |
---|
2452 | |
---|
2453 | static void |
---|
2454 | atp_stop_read(struct usb_fifo *fifo) |
---|
2455 | { |
---|
2456 | struct atp_softc *sc = usb_fifo_softc(fifo); |
---|
2457 | usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]); |
---|
2458 | } |
---|
2459 | |
---|
2460 | static int |
---|
2461 | atp_open(struct usb_fifo *fifo, int fflags) |
---|
2462 | { |
---|
2463 | struct atp_softc *sc = usb_fifo_softc(fifo); |
---|
2464 | |
---|
2465 | /* check for duplicate open, should not happen */ |
---|
2466 | if (sc->sc_fflags & fflags) |
---|
2467 | return (EBUSY); |
---|
2468 | |
---|
2469 | /* check for first open */ |
---|
2470 | if (sc->sc_fflags == 0) { |
---|
2471 | int rc; |
---|
2472 | if ((rc = atp_enable(sc)) != 0) |
---|
2473 | return (rc); |
---|
2474 | } |
---|
2475 | |
---|
2476 | if (fflags & FREAD) { |
---|
2477 | if (usb_fifo_alloc_buffer(fifo, |
---|
2478 | ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) { |
---|
2479 | return (ENOMEM); |
---|
2480 | } |
---|
2481 | } |
---|
2482 | |
---|
2483 | sc->sc_fflags |= (fflags & (FREAD | FWRITE)); |
---|
2484 | return (0); |
---|
2485 | } |
---|
2486 | |
---|
2487 | static void |
---|
2488 | atp_close(struct usb_fifo *fifo, int fflags) |
---|
2489 | { |
---|
2490 | struct atp_softc *sc = usb_fifo_softc(fifo); |
---|
2491 | if (fflags & FREAD) |
---|
2492 | usb_fifo_free_buffer(fifo); |
---|
2493 | |
---|
2494 | sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); |
---|
2495 | if (sc->sc_fflags == 0) { |
---|
2496 | atp_disable(sc); |
---|
2497 | } |
---|
2498 | } |
---|
2499 | |
---|
2500 | static int |
---|
2501 | atp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) |
---|
2502 | { |
---|
2503 | struct atp_softc *sc = usb_fifo_softc(fifo); |
---|
2504 | mousemode_t mode; |
---|
2505 | int error = 0; |
---|
2506 | |
---|
2507 | mtx_lock(&sc->sc_mutex); |
---|
2508 | |
---|
2509 | switch(cmd) { |
---|
2510 | case MOUSE_GETHWINFO: |
---|
2511 | *(mousehw_t *)addr = sc->sc_hw; |
---|
2512 | break; |
---|
2513 | case MOUSE_GETMODE: |
---|
2514 | *(mousemode_t *)addr = sc->sc_mode; |
---|
2515 | break; |
---|
2516 | case MOUSE_SETMODE: |
---|
2517 | mode = *(mousemode_t *)addr; |
---|
2518 | |
---|
2519 | if (mode.level == -1) |
---|
2520 | /* Don't change the current setting */ |
---|
2521 | ; |
---|
2522 | else if ((mode.level < 0) || (mode.level > 1)) { |
---|
2523 | error = EINVAL; |
---|
2524 | break; |
---|
2525 | } |
---|
2526 | sc->sc_mode.level = mode.level; |
---|
2527 | sc->sc_pollrate = mode.rate; |
---|
2528 | sc->sc_hw.buttons = 3; |
---|
2529 | |
---|
2530 | if (sc->sc_mode.level == 0) { |
---|
2531 | sc->sc_mode.protocol = MOUSE_PROTO_MSC; |
---|
2532 | sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; |
---|
2533 | sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; |
---|
2534 | sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; |
---|
2535 | } else if (sc->sc_mode.level == 1) { |
---|
2536 | sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; |
---|
2537 | sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; |
---|
2538 | sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; |
---|
2539 | sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; |
---|
2540 | } |
---|
2541 | atp_reset_buf(sc); |
---|
2542 | break; |
---|
2543 | case MOUSE_GETLEVEL: |
---|
2544 | *(int *)addr = sc->sc_mode.level; |
---|
2545 | break; |
---|
2546 | case MOUSE_SETLEVEL: |
---|
2547 | if ((*(int *)addr < 0) || (*(int *)addr > 1)) { |
---|
2548 | error = EINVAL; |
---|
2549 | break; |
---|
2550 | } |
---|
2551 | sc->sc_mode.level = *(int *)addr; |
---|
2552 | sc->sc_hw.buttons = 3; |
---|
2553 | |
---|
2554 | if (sc->sc_mode.level == 0) { |
---|
2555 | sc->sc_mode.protocol = MOUSE_PROTO_MSC; |
---|
2556 | sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; |
---|
2557 | sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; |
---|
2558 | sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; |
---|
2559 | } else if (sc->sc_mode.level == 1) { |
---|
2560 | sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; |
---|
2561 | sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; |
---|
2562 | sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; |
---|
2563 | sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; |
---|
2564 | } |
---|
2565 | atp_reset_buf(sc); |
---|
2566 | break; |
---|
2567 | case MOUSE_GETSTATUS: { |
---|
2568 | mousestatus_t *status = (mousestatus_t *)addr; |
---|
2569 | |
---|
2570 | *status = sc->sc_status; |
---|
2571 | sc->sc_status.obutton = sc->sc_status.button; |
---|
2572 | sc->sc_status.button = 0; |
---|
2573 | sc->sc_status.dx = 0; |
---|
2574 | sc->sc_status.dy = 0; |
---|
2575 | sc->sc_status.dz = 0; |
---|
2576 | |
---|
2577 | if (status->dx || status->dy || status->dz) |
---|
2578 | status->flags |= MOUSE_POSCHANGED; |
---|
2579 | if (status->button != status->obutton) |
---|
2580 | status->flags |= MOUSE_BUTTONSCHANGED; |
---|
2581 | break; |
---|
2582 | } |
---|
2583 | |
---|
2584 | default: |
---|
2585 | error = ENOTTY; |
---|
2586 | break; |
---|
2587 | } |
---|
2588 | |
---|
2589 | mtx_unlock(&sc->sc_mutex); |
---|
2590 | return (error); |
---|
2591 | } |
---|
2592 | |
---|
2593 | static int |
---|
2594 | atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS) |
---|
2595 | { |
---|
2596 | int error; |
---|
2597 | u_int tmp; |
---|
2598 | |
---|
2599 | tmp = atp_mickeys_scale_factor; |
---|
2600 | error = sysctl_handle_int(oidp, &tmp, 0, req); |
---|
2601 | if (error != 0 || req->newptr == NULL) |
---|
2602 | return (error); |
---|
2603 | |
---|
2604 | if (tmp == atp_mickeys_scale_factor) |
---|
2605 | return (0); /* no change */ |
---|
2606 | if ((tmp == 0) || (tmp > (10 * ATP_SCALE_FACTOR))) |
---|
2607 | return (EINVAL); |
---|
2608 | |
---|
2609 | atp_mickeys_scale_factor = tmp; |
---|
2610 | DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n", |
---|
2611 | ATP_DRIVER_NAME, tmp); |
---|
2612 | |
---|
2613 | return (0); |
---|
2614 | } |
---|
2615 | |
---|
2616 | static devclass_t atp_devclass; |
---|
2617 | |
---|
2618 | static device_method_t atp_methods[] = { |
---|
2619 | DEVMETHOD(device_probe, atp_probe), |
---|
2620 | DEVMETHOD(device_attach, atp_attach), |
---|
2621 | DEVMETHOD(device_detach, atp_detach), |
---|
2622 | |
---|
2623 | DEVMETHOD_END |
---|
2624 | }; |
---|
2625 | |
---|
2626 | static driver_t atp_driver = { |
---|
2627 | .name = ATP_DRIVER_NAME, |
---|
2628 | .methods = atp_methods, |
---|
2629 | .size = sizeof(struct atp_softc) |
---|
2630 | }; |
---|
2631 | |
---|
2632 | DRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0); |
---|
2633 | MODULE_DEPEND(atp, usb, 1, 1, 1); |
---|
2634 | MODULE_VERSION(atp, 1); |
---|
2635 | USB_PNP_HOST_INFO(fg_devs); |
---|
2636 | USB_PNP_HOST_INFO(wsp_devs); |
---|