atp.c revision 199151
1199086Srpaulo/*-
2199086Srpaulo * Copyright (c) 2009 Rohit Grover
3199086Srpaulo * All rights reserved.
4199086Srpaulo *
5199086Srpaulo * Redistribution and use in source and binary forms, with or without
6199086Srpaulo * modification, are permitted provided that the following conditions
7199086Srpaulo * are met:
8199086Srpaulo * 1. Redistributions of source code must retain the above copyright
9199086Srpaulo *    notice, this list of conditions and the following disclaimer.
10199086Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11199086Srpaulo *    notice, this list of conditions and the following disclaimer in the
12199086Srpaulo *    documentation and/or other materials provided with the distribution.
13199086Srpaulo *
14199086Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15199086Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199086Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199086Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18199086Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199086Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199086Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199086Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199086Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199086Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199086Srpaulo * SUCH DAMAGE.
25199086Srpaulo */
26199086Srpaulo
27199086Srpaulo#include <sys/cdefs.h>
28199086Srpaulo__FBSDID("$FreeBSD: head/sys/dev/usb/input/atp.c 199151 2009-11-10 19:14:06Z nwhitehorn $");
29199086Srpaulo
30199086Srpaulo#include <sys/param.h>
31199086Srpaulo#include <sys/systm.h>
32199086Srpaulo#include <sys/kernel.h>
33199086Srpaulo#include <sys/malloc.h>
34199086Srpaulo#include <sys/module.h>
35199086Srpaulo#include <sys/lock.h>
36199086Srpaulo#include <sys/mutex.h>
37199086Srpaulo#include <sys/bus.h>
38199086Srpaulo#include <sys/conf.h>
39199086Srpaulo#include <sys/fcntl.h>
40199086Srpaulo#include <sys/file.h>
41199086Srpaulo#include <sys/selinfo.h>
42199086Srpaulo#include <sys/poll.h>
43199086Srpaulo#include <sys/sysctl.h>
44199086Srpaulo#include <sys/uio.h>
45199086Srpaulo
46199086Srpaulo#include <dev/usb/usb.h>
47199086Srpaulo#include <dev/usb/usbdi.h>
48199086Srpaulo#include <dev/usb/usbdi_util.h>
49199086Srpaulo#include <dev/usb/usbhid.h>
50199086Srpaulo#include "usbdevs.h"
51199086Srpaulo
52199086Srpaulo#define USB_DEBUG_VAR atp_debug
53199086Srpaulo#include <dev/usb/usb_debug.h>
54199086Srpaulo
55199086Srpaulo#include <sys/mouse.h>
56199086Srpaulo
57199086Srpaulo#define ATP_DRIVER_NAME "atp"
58199086Srpaulo
59199086Srpaulo/*
60199086Srpaulo * Driver specific options: the following options may be set by
61199086Srpaulo * `options' statements in the kernel configuration file.
62199086Srpaulo */
63199086Srpaulo
64199086Srpaulo/* The multiplier used to translate sensor reported positions to mickeys. */
65199086Srpaulo#ifndef ATP_SCALE_FACTOR
66199086Srpaulo#define ATP_SCALE_FACTOR 48
67199086Srpaulo#endif
68199086Srpaulo
69199086Srpaulo/*
70199086Srpaulo * This is the age (in microseconds) beyond which a touch is
71199086Srpaulo * considered to be a slide; and therefore a tap event isn't registered.
72199086Srpaulo */
73199086Srpaulo#ifndef ATP_TOUCH_TIMEOUT
74199086Srpaulo#define ATP_TOUCH_TIMEOUT 125000
75199086Srpaulo#endif
76199086Srpaulo
77199086Srpaulo/*
78199086Srpaulo * A double-tap followed by a single-finger slide is treated as a
79199086Srpaulo * special gesture. The driver responds to this gesture by assuming a
80199086Srpaulo * virtual button-press for the lifetime of the slide. The following
81199086Srpaulo * threshold is the maximum time gap (in microseconds) between the two
82199086Srpaulo * tap events preceding the slide for such a gesture.
83199086Srpaulo */
84199086Srpaulo#ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD
85199086Srpaulo#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000
86199086Srpaulo#endif
87199086Srpaulo
88199086Srpaulo/*
89199086Srpaulo * The device provides us only with pressure readings from an array of
90199086Srpaulo * X and Y sensors; for our algorithms, we need to interpret groups
91199086Srpaulo * (typically pairs) of X and Y readings as being related to a single
92199086Srpaulo * finger stroke. We can relate X and Y readings based on their times
93199086Srpaulo * of incidence. The coincidence window should be at least 10000us
94199086Srpaulo * since it is used against values from getmicrotime(), which has a
95199086Srpaulo * precision of around 10ms.
96199086Srpaulo */
97199086Srpaulo#ifndef ATP_COINCIDENCE_THRESHOLD
98199086Srpaulo#define ATP_COINCIDENCE_THRESHOLD  40000 /* unit: microseconds */
99199086Srpaulo#if ATP_COINCIDENCE_THRESHOLD > 100000
100199086Srpaulo#error "ATP_COINCIDENCE_THRESHOLD too large"
101199086Srpaulo#endif
102199086Srpaulo#endif /* #ifndef ATP_COINCIDENCE_THRESHOLD */
103199086Srpaulo
104199086Srpaulo/*
105199086Srpaulo * The wait duration (in microseconds) after losing a touch contact
106199086Srpaulo * before zombied strokes are reaped and turned into button events.
107199086Srpaulo */
108199086Srpaulo#define ATP_ZOMBIE_STROKE_REAP_WINDOW   50000
109199086Srpaulo#if ATP_ZOMBIE_STROKE_REAP_WINDOW > 100000
110199086Srpaulo#error "ATP_ZOMBIE_STROKE_REAP_WINDOW too large"
111199086Srpaulo#endif
112199086Srpaulo
113199086Srpaulo/* end of driver specific options */
114199086Srpaulo
115199086Srpaulo
116199086Srpaulo/* Tunables */
117199086SrpauloSYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB atp");
118199086Srpaulo
119199086Srpaulo#if USB_DEBUG
120199086Srpauloenum atp_log_level {
121199086Srpaulo	ATP_LLEVEL_DISABLED = 0,
122199086Srpaulo	ATP_LLEVEL_ERROR,
123199086Srpaulo	ATP_LLEVEL_DEBUG,       /* for troubleshooting */
124199086Srpaulo	ATP_LLEVEL_INFO,        /* for diagnostics */
125199086Srpaulo};
126199086Srpaulostatic int atp_debug = ATP_LLEVEL_ERROR; /* the default is to only log errors */
127199086SrpauloSYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RW,
128199086Srpaulo    &atp_debug, ATP_LLEVEL_ERROR, "ATP debug level");
129199086Srpaulo#endif /* #if USB_DEBUG */
130199086Srpaulo
131199086Srpaulostatic u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT;
132199086SrpauloSYSCTL_INT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW, &atp_touch_timeout,
133199086Srpaulo    125000, "age threshold (in micros) for a touch");
134199086Srpaulo
135199086Srpaulostatic u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD;
136199086SrpauloSYSCTL_INT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RW,
137199086Srpaulo    &atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD,
138199086Srpaulo    "maximum time (in micros) between a double-tap");
139199086Srpaulo
140199086Srpaulostatic u_int atp_mickeys_scale_factor = ATP_SCALE_FACTOR;
141199086Srpaulostatic int atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS);
142199086SrpauloSYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RW,
143199086Srpaulo    &atp_mickeys_scale_factor, sizeof(atp_mickeys_scale_factor),
144199086Srpaulo    atp_sysctl_scale_factor_handler, "IU", "movement scale factor");
145199086Srpaulo
146199086Srpaulostatic u_int atp_small_movement_threshold = ATP_SCALE_FACTOR >> 3;
147199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RW,
148199086Srpaulo    &atp_small_movement_threshold, ATP_SCALE_FACTOR >> 3,
149199086Srpaulo    "the small movement black-hole for filtering noise");
150199086Srpaulo/*
151199086Srpaulo * The movement threshold for a stroke; this is the maximum difference
152199086Srpaulo * in position which will be resolved as a continuation of a stroke
153199086Srpaulo * component.
154199086Srpaulo */
155199086Srpaulostatic u_int atp_max_delta_mickeys = ((3 * ATP_SCALE_FACTOR) >> 1);
156199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, max_delta_mickeys, CTLFLAG_RW,
157199086Srpaulo    &atp_max_delta_mickeys, ((3 * ATP_SCALE_FACTOR) >> 1),
158199086Srpaulo    "max. mickeys-delta which will match against an existing stroke");
159199086Srpaulo/*
160199086Srpaulo * Strokes which accumulate at least this amount of absolute movement
161199086Srpaulo * from the aggregate of their components are considered as
162199086Srpaulo * slides. Unit: mickeys.
163199086Srpaulo */
164199086Srpaulostatic u_int atp_slide_min_movement = (ATP_SCALE_FACTOR >> 3);
165199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RW,
166199086Srpaulo    &atp_slide_min_movement, (ATP_SCALE_FACTOR >> 3),
167199086Srpaulo    "strokes with at least this amt. of movement are considered slides");
168199086Srpaulo
169199086Srpaulo/*
170199086Srpaulo * The minimum age of a stroke for it to be considered mature; this
171199086Srpaulo * helps filter movements (noise) from immature strokes. Units: interrupts.
172199086Srpaulo */
173199086Srpaulostatic u_int atp_stroke_maturity_threshold = 2;
174199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RW,
175199086Srpaulo    &atp_stroke_maturity_threshold, 2,
176199086Srpaulo    "the minimum age of a stroke for it to be considered mature");
177199086Srpaulo
178199086Srpaulo/* Accept pressure readings from sensors only if above this value. */
179199086Srpaulostatic u_int atp_sensor_noise_threshold = 2;
180199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, sensor_noise_threshold, CTLFLAG_RW,
181199086Srpaulo    &atp_sensor_noise_threshold, 2,
182199086Srpaulo    "accept pressure readings from sensors only if above this value");
183199086Srpaulo
184199086Srpaulo/* Ignore pressure spans with cumulative press. below this value. */
185199086Srpaulostatic u_int atp_pspan_min_cum_pressure = 10;
186199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, pspan_min_cum_pressure, CTLFLAG_RW,
187199086Srpaulo    &atp_pspan_min_cum_pressure, 10,
188199086Srpaulo    "ignore pressure spans with cumulative press. below this value");
189199086Srpaulo
190199086Srpaulo/* Maximum allowed width for pressure-spans.*/
191199086Srpaulostatic u_int atp_pspan_max_width = 4;
192199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, pspan_max_width, CTLFLAG_RW,
193199086Srpaulo    &atp_pspan_max_width, 4,
194199086Srpaulo    "maximum allowed width (in sensors) for pressure-spans");
195199086Srpaulo
196199151Snwhitehorn/* We support three payload protocols */
197199151Snwhitehorntypedef enum {
198199151Snwhitehorn	ATP_PROT_GEYSER1,
199199151Snwhitehorn	ATP_PROT_GEYSER2,
200199151Snwhitehorn	ATP_PROT_GEYSER3,
201199151Snwhitehorn} atp_protocol;
202199086Srpaulo
203199086Srpaulo/* Define the various flavours of devices supported by this driver. */
204199086Srpauloenum {
205199086Srpaulo	ATP_DEV_PARAMS_0,
206199151Snwhitehorn	ATP_DEV_PARAMS_PBOOK,
207199151Snwhitehorn	ATP_DEV_PARAMS_PBOOK_15A,
208199151Snwhitehorn	ATP_DEV_PARAMS_PBOOK_17,
209199086Srpaulo	ATP_N_DEV_PARAMS
210199086Srpaulo};
211199086Srpaulostruct atp_dev_params {
212199086Srpaulo	u_int            data_len;   /* for sensor data */
213199086Srpaulo	u_int            n_xsensors;
214199086Srpaulo	u_int            n_ysensors;
215199151Snwhitehorn	atp_protocol     prot;
216199086Srpaulo} atp_dev_params[ATP_N_DEV_PARAMS] = {
217199086Srpaulo	[ATP_DEV_PARAMS_0] = {
218199086Srpaulo		.data_len   = 64,
219199086Srpaulo		.n_xsensors = 20,
220199151Snwhitehorn		.n_ysensors = 10,
221199151Snwhitehorn		.prot       = ATP_PROT_GEYSER3
222199086Srpaulo	},
223199151Snwhitehorn	[ATP_DEV_PARAMS_PBOOK] = {
224199151Snwhitehorn		.data_len   = 81,
225199151Snwhitehorn		.n_xsensors = 16,
226199151Snwhitehorn		.n_ysensors = 16,
227199151Snwhitehorn		.prot       = ATP_PROT_GEYSER1
228199151Snwhitehorn	},
229199151Snwhitehorn	[ATP_DEV_PARAMS_PBOOK_15A] = {
230199151Snwhitehorn		.data_len   = 64,
231199151Snwhitehorn		.n_xsensors = 15,
232199151Snwhitehorn		.n_ysensors = 9,
233199151Snwhitehorn		.prot       = ATP_PROT_GEYSER2
234199151Snwhitehorn	},
235199151Snwhitehorn	[ATP_DEV_PARAMS_PBOOK_17] = {
236199151Snwhitehorn		.data_len   = 81,
237199151Snwhitehorn		.n_xsensors = 26,
238199151Snwhitehorn		.n_ysensors = 16,
239199151Snwhitehorn		.prot       = ATP_PROT_GEYSER1
240199151Snwhitehorn	},
241199086Srpaulo};
242199086Srpaulo
243199086Srpaulostatic const struct usb_device_id atp_devs[] = {
244199086Srpaulo	/* Core Duo MacBook & MacBook Pro */
245199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x0217, ATP_DEV_PARAMS_0) },
246199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x0218, ATP_DEV_PARAMS_0) },
247199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x0219, ATP_DEV_PARAMS_0) },
248199086Srpaulo
249199086Srpaulo	/* Core2 Duo MacBook & MacBook Pro */
250199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x021a, ATP_DEV_PARAMS_0) },
251199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x021b, ATP_DEV_PARAMS_0) },
252199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x021c, ATP_DEV_PARAMS_0) },
253199086Srpaulo
254199086Srpaulo	/* Core2 Duo MacBook3,1 */
255199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x0229, ATP_DEV_PARAMS_0) },
256199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x022a, ATP_DEV_PARAMS_0) },
257199086Srpaulo	{ USB_VPI(USB_VENDOR_APPLE, 0x022b, ATP_DEV_PARAMS_0) },
258199151Snwhitehorn
259199151Snwhitehorn	/* 12 inch PowerBook and iBook */
260199151Snwhitehorn	{ USB_VPI(USB_VENDOR_APPLE, 0x030a, ATP_DEV_PARAMS_PBOOK) },
261199151Snwhitehorn	{ USB_VPI(USB_VENDOR_APPLE, 0x030b, ATP_DEV_PARAMS_PBOOK) },
262199151Snwhitehorn
263199151Snwhitehorn	/* 15 inch PowerBook */
264199151Snwhitehorn	{ USB_VPI(USB_VENDOR_APPLE, 0x020e, ATP_DEV_PARAMS_PBOOK) },
265199151Snwhitehorn	{ USB_VPI(USB_VENDOR_APPLE, 0x020f, ATP_DEV_PARAMS_PBOOK) },
266199151Snwhitehorn	{ USB_VPI(USB_VENDOR_APPLE, 0x0215, ATP_DEV_PARAMS_PBOOK_15A) },
267199151Snwhitehorn
268199151Snwhitehorn	/* 17 inch PowerBook */
269199151Snwhitehorn	{ USB_VPI(USB_VENDOR_APPLE, 0x020d, ATP_DEV_PARAMS_PBOOK_17) },
270199151Snwhitehorn
271199086Srpaulo};
272199086Srpaulo
273199086Srpaulo/*
274199086Srpaulo * The following structure captures the state of a pressure span along
275199086Srpaulo * an axis. Each contact with the touchpad results in separate
276199086Srpaulo * pressure spans along the two axes.
277199086Srpaulo */
278199086Srpaulotypedef struct atp_pspan {
279199086Srpaulo	u_int width;   /* in units of sensors */
280199086Srpaulo	u_int cum;     /* cumulative compression (from all sensors) */
281199086Srpaulo	u_int cog;     /* center of gravity */
282199086Srpaulo	u_int loc;     /* location (scaled using the mickeys factor) */
283199086Srpaulo	boolean_t matched; /* to track pspans as they match against strokes. */
284199086Srpaulo} atp_pspan;
285199086Srpaulo
286199086Srpaulotypedef enum atp_stroke_type {
287199086Srpaulo	ATP_STROKE_TOUCH,
288199086Srpaulo	ATP_STROKE_SLIDE,
289199086Srpaulo} atp_stroke_type;
290199086Srpaulo
291199086Srpaulo#define ATP_MAX_PSPANS_PER_AXIS 3
292199086Srpaulo
293199086Srpaulotypedef struct atp_stroke_component {
294199086Srpaulo	/* Fields encapsulating the pressure-span. */
295199086Srpaulo	u_int loc;              /* location (scaled) */
296199086Srpaulo	u_int cum_pressure;     /* cumulative compression */
297199086Srpaulo	u_int max_cum_pressure; /* max cumulative compression */
298199086Srpaulo	boolean_t matched; /*to track components as they match against pspans.*/
299199086Srpaulo
300199086Srpaulo	/* Fields containing information about movement. */
301199086Srpaulo	int   delta_mickeys;    /* change in location (un-smoothened movement)*/
302199086Srpaulo	int   pending;          /* cum. of pending short movements */
303199086Srpaulo	int   movement;         /* current smoothened movement */
304199086Srpaulo} atp_stroke_component;
305199086Srpaulo
306199086Srpaulotypedef enum atp_axis {
307199086Srpaulo	X = 0,
308199086Srpaulo	Y = 1
309199086Srpaulo} atp_axis;
310199086Srpaulo
311199086Srpaulo#define ATP_MAX_STROKES         (2 * ATP_MAX_PSPANS_PER_AXIS)
312199086Srpaulo
313199086Srpaulo/*
314199086Srpaulo * The following structure captures a finger contact with the
315199086Srpaulo * touchpad. A stroke comprises two p-span components and some state.
316199086Srpaulo */
317199086Srpaulotypedef struct atp_stroke {
318199086Srpaulo	atp_stroke_type      type;
319199086Srpaulo	struct timeval       ctime; /* create time; for coincident siblings. */
320199086Srpaulo	u_int                age;   /*
321199086Srpaulo				     * Unit: interrupts; we maintain
322199086Srpaulo				     * this value in addition to
323199086Srpaulo				     * 'ctime' in order to avoid the
324199086Srpaulo				     * expensive call to microtime()
325199086Srpaulo				     * at every interrupt.
326199086Srpaulo				     */
327199086Srpaulo
328199086Srpaulo	atp_stroke_component components[2];
329199086Srpaulo	u_int                velocity_squared; /*
330199086Srpaulo						* Average magnitude (squared)
331199086Srpaulo						* of recent velocity.
332199086Srpaulo						*/
333199086Srpaulo	u_int                cum_movement; /* cum. absolute movement so far */
334199086Srpaulo
335199086Srpaulo	uint32_t             flags;  /* the state of this stroke */
336199086Srpaulo#define ATSF_ZOMBIE          0x1
337199086Srpaulo} atp_stroke;
338199086Srpaulo
339199086Srpaulo#define ATP_FIFO_BUF_SIZE        8 /* bytes */
340199086Srpaulo#define ATP_FIFO_QUEUE_MAXLEN   50 /* units */
341199086Srpaulo
342199086Srpauloenum {
343199086Srpaulo	ATP_INTR_DT,
344199086Srpaulo	ATP_N_TRANSFER,
345199086Srpaulo};
346199086Srpaulo
347199086Srpaulostruct atp_softc {
348199086Srpaulo	device_t               sc_dev;
349199086Srpaulo	struct usb_device     *sc_usb_device;
350199086Srpaulo#define MODE_LENGTH 8
351199086Srpaulo	char                   sc_mode_bytes[MODE_LENGTH]; /* device mode */
352199086Srpaulo	struct mtx             sc_mutex; /* for synchronization */
353199086Srpaulo	struct usb_xfer       *sc_xfer[ATP_N_TRANSFER];
354199086Srpaulo	struct usb_fifo_sc     sc_fifo;
355199086Srpaulo
356199086Srpaulo	struct atp_dev_params *sc_params;
357199086Srpaulo
358199086Srpaulo	mousehw_t              sc_hw;
359199086Srpaulo	mousemode_t            sc_mode;
360199086Srpaulo	u_int                  sc_pollrate;
361199086Srpaulo	mousestatus_t          sc_status;
362199086Srpaulo	u_int                  sc_state;
363199086Srpaulo#define ATP_ENABLED            0x01
364199086Srpaulo#define ATP_ZOMBIES_EXIST      0x02
365199086Srpaulo#define ATP_DOUBLE_TAP_DRAG    0x04
366199151Snwhitehorn#define ATP_VALID              0x08
367199086Srpaulo
368199086Srpaulo	u_int                  sc_left_margin;
369199086Srpaulo	u_int                  sc_right_margin;
370199086Srpaulo
371199086Srpaulo	atp_stroke             sc_strokes[ATP_MAX_STROKES];
372199086Srpaulo	u_int                  sc_n_strokes;
373199086Srpaulo
374199086Srpaulo	int8_t                *sensor_data; /* from interrupt packet */
375199086Srpaulo	int                   *base_x;      /* base sensor readings */
376199086Srpaulo	int                   *base_y;
377199086Srpaulo	int                   *cur_x;       /* current sensor readings */
378199086Srpaulo	int                   *cur_y;
379199086Srpaulo	int                   *pressure_x;  /* computed pressures */
380199086Srpaulo	int                   *pressure_y;
381199086Srpaulo
382199086Srpaulo	u_int                  sc_idlecount; /* preceding idle interrupts */
383199086Srpaulo#define ATP_IDLENESS_THRESHOLD 10
384199086Srpaulo
385199086Srpaulo	struct timeval         sc_reap_time;
386199086Srpaulo	struct timeval         sc_reap_ctime; /*ctime of siblings to be reaped*/
387199086Srpaulo};
388199086Srpaulo
389199086Srpaulo/*
390199086Srpaulo * The last byte of the sensor data contains status bits; the
391199086Srpaulo * following values define the meanings of these bits.
392199086Srpaulo */
393199086Srpauloenum atp_status_bits {
394199086Srpaulo	ATP_STATUS_BUTTON      = (uint8_t)0x01, /* The button was pressed */
395199086Srpaulo	ATP_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/
396199086Srpaulo};
397199086Srpaulo
398199086Srpaulotypedef enum interface_mode {
399199086Srpaulo	RAW_SENSOR_MODE = (uint8_t)0x04,
400199086Srpaulo	HID_MODE        = (uint8_t)0x08
401199086Srpaulo} interface_mode;
402199086Srpaulo
403199086Srpaulo/*
404199086Srpaulo * function prototypes
405199086Srpaulo */
406199086Srpaulostatic usb_fifo_cmd_t   atp_start_read;
407199086Srpaulostatic usb_fifo_cmd_t   atp_stop_read;
408199086Srpaulostatic usb_fifo_open_t  atp_open;
409199086Srpaulostatic usb_fifo_close_t atp_close;
410199086Srpaulostatic usb_fifo_ioctl_t atp_ioctl;
411199086Srpaulo
412199086Srpaulostatic struct usb_fifo_methods atp_fifo_methods = {
413199086Srpaulo	.f_open       = &atp_open,
414199086Srpaulo	.f_close      = &atp_close,
415199086Srpaulo	.f_ioctl      = &atp_ioctl,
416199086Srpaulo	.f_start_read = &atp_start_read,
417199086Srpaulo	.f_stop_read  = &atp_stop_read,
418199086Srpaulo	.basename[0]  = ATP_DRIVER_NAME,
419199086Srpaulo};
420199086Srpaulo
421199086Srpaulo/* device initialization and shutdown */
422199086Srpaulostatic usb_error_t   atp_req_get_report(struct usb_device *udev, void *data);
423199086Srpaulostatic int           atp_set_device_mode(device_t dev, interface_mode mode);
424199086Srpaulostatic int           atp_enable(struct atp_softc *sc);
425199086Srpaulostatic void          atp_disable(struct atp_softc *sc);
426199086Srpaulostatic int           atp_softc_populate(struct atp_softc *);
427199086Srpaulostatic void          atp_softc_unpopulate(struct atp_softc *);
428199086Srpaulo
429199086Srpaulo/* sensor interpretation */
430199151Snwhitehornstatic __inline void atp_interpret_sensor_data(const int8_t *, u_int, atp_axis,
431199151Snwhitehorn			 int *, atp_protocol);
432199086Srpaulostatic __inline void atp_get_pressures(int *, const int *, const int *, int);
433199086Srpaulostatic void          atp_detect_pspans(int *, u_int, u_int, atp_pspan *,
434199086Srpaulo			 u_int *);
435199086Srpaulo
436199086Srpaulo/* movement detection */
437199086Srpaulostatic boolean_t     atp_match_stroke_component(atp_stroke_component *,
438199086Srpaulo			 const atp_pspan *);
439199086Srpaulostatic void          atp_match_strokes_against_pspans(struct atp_softc *,
440199086Srpaulo			 atp_axis, atp_pspan *, u_int, u_int);
441199086Srpaulostatic boolean_t     atp_update_strokes(struct atp_softc *,
442199086Srpaulo			 atp_pspan *, u_int, atp_pspan *, u_int);
443199086Srpaulostatic __inline void atp_add_stroke(struct atp_softc *, const atp_pspan *,
444199086Srpaulo			 const atp_pspan *);
445199086Srpaulostatic void          atp_add_new_strokes(struct atp_softc *, atp_pspan *,
446199086Srpaulo			 u_int, atp_pspan *, u_int);
447199086Srpaulostatic void          atp_advance_stroke_state(struct atp_softc *,
448199086Srpaulo			 atp_stroke *, boolean_t *);
449199086Srpaulostatic void          atp_terminate_stroke(struct atp_softc *, u_int);
450199086Srpaulostatic __inline boolean_t atp_stroke_has_small_movement(const atp_stroke *);
451199086Srpaulostatic __inline void atp_update_pending_mickeys(atp_stroke_component *);
452199086Srpaulostatic void          atp_compute_smoothening_scale_ratio(atp_stroke *, int *,
453199086Srpaulo			 int *);
454199086Srpaulostatic boolean_t     atp_compute_stroke_movement(atp_stroke *);
455199086Srpaulo
456199086Srpaulo/* tap detection */
457199086Srpaulostatic __inline void atp_setup_reap_time(struct atp_softc *, struct timeval *);
458199086Srpaulostatic void          atp_reap_zombies(struct atp_softc *, u_int *, u_int *);
459199086Srpaulo
460199086Srpaulo/* updating fifo */
461199086Srpaulostatic void          atp_reset_buf(struct atp_softc *sc);
462199086Srpaulostatic void          atp_add_to_queue(struct atp_softc *, int, int, uint32_t);
463199086Srpaulo
464199086Srpaulo
465199086Srpaulousb_error_t
466199086Srpauloatp_req_get_report(struct usb_device *udev, void *data)
467199086Srpaulo{
468199086Srpaulo	struct usb_device_request req;
469199086Srpaulo
470199086Srpaulo	req.bmRequestType = UT_READ_CLASS_INTERFACE;
471199086Srpaulo	req.bRequest = UR_GET_REPORT;
472199086Srpaulo	USETW2(req.wValue, (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
473199086Srpaulo	USETW(req.wIndex, 0);
474199086Srpaulo	USETW(req.wLength, MODE_LENGTH);
475199086Srpaulo
476199086Srpaulo	return (usbd_do_request(udev, NULL /* mutex */, &req, data));
477199086Srpaulo}
478199086Srpaulo
479199086Srpaulostatic int
480199086Srpauloatp_set_device_mode(device_t dev, interface_mode mode)
481199086Srpaulo{
482199086Srpaulo	struct atp_softc     *sc;
483199086Srpaulo	usb_device_request_t  req;
484199086Srpaulo	usb_error_t           err;
485199086Srpaulo
486199086Srpaulo	if ((mode != RAW_SENSOR_MODE) && (mode != HID_MODE))
487199086Srpaulo		return (ENXIO);
488199086Srpaulo
489199086Srpaulo	sc = device_get_softc(dev);
490199086Srpaulo
491199086Srpaulo	sc->sc_mode_bytes[0] = mode;
492199086Srpaulo	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
493199086Srpaulo	req.bRequest = UR_SET_REPORT;
494199086Srpaulo	USETW2(req.wValue, (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
495199086Srpaulo	USETW(req.wIndex, 0);
496199086Srpaulo	USETW(req.wLength, MODE_LENGTH);
497199086Srpaulo	err = usbd_do_request(sc->sc_usb_device, NULL, &req, sc->sc_mode_bytes);
498199086Srpaulo	if (err != USB_ERR_NORMAL_COMPLETION)
499199086Srpaulo		return (ENXIO);
500199086Srpaulo
501199086Srpaulo	return (0);
502199086Srpaulo}
503199086Srpaulo
504199086Srpaulostatic int
505199086Srpauloatp_enable(struct atp_softc *sc)
506199086Srpaulo{
507199086Srpaulo	/* Allocate the dynamic buffers */
508199086Srpaulo	if (atp_softc_populate(sc) != 0) {
509199086Srpaulo		atp_softc_unpopulate(sc);
510199086Srpaulo		return (ENOMEM);
511199086Srpaulo	}
512199086Srpaulo
513199086Srpaulo	/* reset status */
514199086Srpaulo	memset(sc->sc_strokes, 0, sizeof(sc->sc_strokes));
515199086Srpaulo	sc->sc_n_strokes = 0;
516199086Srpaulo	memset(&sc->sc_status, 0, sizeof(sc->sc_status));
517199086Srpaulo	sc->sc_idlecount = 0;
518199086Srpaulo	sc->sc_state |= ATP_ENABLED;
519199086Srpaulo
520199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n");
521199086Srpaulo	return (0);
522199086Srpaulo}
523199086Srpaulo
524199086Srpaulostatic void
525199086Srpauloatp_disable(struct atp_softc *sc)
526199086Srpaulo{
527199086Srpaulo	atp_softc_unpopulate(sc);
528199086Srpaulo
529199151Snwhitehorn	sc->sc_state &= ~(ATP_ENABLED | ATP_VALID);
530199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n");
531199086Srpaulo}
532199086Srpaulo
533199086Srpaulo/* Allocate dynamic memory for some fields in softc. */
534199086Srpaulostatic int
535199086Srpauloatp_softc_populate(struct atp_softc *sc)
536199086Srpaulo{
537199086Srpaulo	const struct atp_dev_params *params = sc->sc_params;
538199086Srpaulo
539199086Srpaulo	if (params == NULL) {
540199086Srpaulo		DPRINTF("params uninitialized!\n");
541199086Srpaulo		return (ENXIO);
542199086Srpaulo	}
543199086Srpaulo	if (params->data_len) {
544199086Srpaulo		sc->sensor_data = malloc(params->data_len * sizeof(int8_t),
545199086Srpaulo		    M_USB, M_WAITOK);
546199086Srpaulo		if (sc->sensor_data == NULL) {
547199086Srpaulo			DPRINTF("mem for sensor_data\n");
548199086Srpaulo			return (ENXIO);
549199086Srpaulo		}
550199086Srpaulo	}
551199086Srpaulo
552199086Srpaulo	if (params->n_xsensors != 0) {
553199086Srpaulo		sc->base_x = malloc(params->n_xsensors * sizeof(*(sc->base_x)),
554199086Srpaulo		    M_USB, M_WAITOK);
555199086Srpaulo		if (sc->base_x == NULL) {
556199086Srpaulo			DPRINTF("mem for sc->base_x\n");
557199086Srpaulo			return (ENXIO);
558199086Srpaulo		}
559199086Srpaulo
560199086Srpaulo		sc->cur_x = malloc(params->n_xsensors * sizeof(*(sc->cur_x)),
561199086Srpaulo		    M_USB, M_WAITOK);
562199086Srpaulo		if (sc->cur_x == NULL) {
563199086Srpaulo			DPRINTF("mem for sc->cur_x\n");
564199086Srpaulo			return (ENXIO);
565199086Srpaulo		}
566199086Srpaulo
567199086Srpaulo		sc->pressure_x =
568199086Srpaulo			malloc(params->n_xsensors * sizeof(*(sc->pressure_x)),
569199086Srpaulo			    M_USB, M_WAITOK);
570199086Srpaulo		if (sc->pressure_x == NULL) {
571199086Srpaulo			DPRINTF("mem. for pressure_x\n");
572199086Srpaulo			return (ENXIO);
573199086Srpaulo		}
574199086Srpaulo	}
575199086Srpaulo
576199086Srpaulo	if (params->n_ysensors != 0) {
577199086Srpaulo		sc->base_y = malloc(params->n_ysensors * sizeof(*(sc->base_y)),
578199086Srpaulo		    M_USB, M_WAITOK);
579199086Srpaulo		if (sc->base_y == NULL) {
580199086Srpaulo			DPRINTF("mem for base_y\n");
581199086Srpaulo			return (ENXIO);
582199086Srpaulo		}
583199086Srpaulo
584199086Srpaulo		sc->cur_y = malloc(params->n_ysensors * sizeof(*(sc->cur_y)),
585199086Srpaulo		    M_USB, M_WAITOK);
586199086Srpaulo		if (sc->cur_y == NULL) {
587199086Srpaulo			DPRINTF("mem for cur_y\n");
588199086Srpaulo			return (ENXIO);
589199086Srpaulo		}
590199086Srpaulo
591199086Srpaulo		sc->pressure_y =
592199086Srpaulo			malloc(params->n_ysensors * sizeof(*(sc->pressure_y)),
593199086Srpaulo			    M_USB, M_WAITOK);
594199086Srpaulo		if (sc->pressure_y == NULL) {
595199086Srpaulo			DPRINTF("mem. for pressure_y\n");
596199086Srpaulo			return (ENXIO);
597199086Srpaulo		}
598199086Srpaulo	}
599199086Srpaulo
600199086Srpaulo	return (0);
601199086Srpaulo}
602199086Srpaulo
603199086Srpaulo/* Free dynamic memory allocated for some fields in softc. */
604199086Srpaulostatic void
605199086Srpauloatp_softc_unpopulate(struct atp_softc *sc)
606199086Srpaulo{
607199086Srpaulo	const struct atp_dev_params *params = sc->sc_params;
608199086Srpaulo
609199086Srpaulo	if (params == NULL) {
610199086Srpaulo		return;
611199086Srpaulo	}
612199086Srpaulo	if (params->n_xsensors != 0) {
613199086Srpaulo		if (sc->base_x != NULL) {
614199086Srpaulo			free(sc->base_x, M_USB);
615199086Srpaulo			sc->base_x = NULL;
616199086Srpaulo		}
617199086Srpaulo
618199086Srpaulo		if (sc->cur_x != NULL) {
619199086Srpaulo			free(sc->cur_x, M_USB);
620199086Srpaulo			sc->cur_x = NULL;
621199086Srpaulo		}
622199086Srpaulo
623199086Srpaulo		if (sc->pressure_x != NULL) {
624199086Srpaulo			free(sc->pressure_x, M_USB);
625199086Srpaulo			sc->pressure_x = NULL;
626199086Srpaulo		}
627199086Srpaulo	}
628199086Srpaulo	if (params->n_ysensors != 0) {
629199086Srpaulo		if (sc->base_y != NULL) {
630199086Srpaulo			free(sc->base_y, M_USB);
631199086Srpaulo			sc->base_y = NULL;
632199086Srpaulo		}
633199086Srpaulo
634199086Srpaulo		if (sc->cur_y != NULL) {
635199086Srpaulo			free(sc->cur_y, M_USB);
636199086Srpaulo			sc->cur_y = NULL;
637199086Srpaulo		}
638199086Srpaulo
639199086Srpaulo		if (sc->pressure_y != NULL) {
640199086Srpaulo			free(sc->pressure_y, M_USB);
641199086Srpaulo			sc->pressure_y = NULL;
642199086Srpaulo		}
643199086Srpaulo	}
644199086Srpaulo	if (sc->sensor_data != NULL) {
645199086Srpaulo		free(sc->sensor_data, M_USB);
646199086Srpaulo		sc->sensor_data = NULL;
647199086Srpaulo	}
648199086Srpaulo}
649199086Srpaulo
650199086Srpaulo/*
651199086Srpaulo * Interpret the data from the X and Y pressure sensors. This function
652199086Srpaulo * is called separately for the X and Y sensor arrays. The data in the
653199086Srpaulo * USB packet is laid out in the following manner:
654199086Srpaulo *
655199086Srpaulo * sensor_data:
656199086Srpaulo *            --,--,Y1,Y2,--,Y3,Y4,--,Y5,...,Y10, ... X1,X2,--,X3,X4
657199086Srpaulo *  indices:   0  1  2  3  4  5  6  7  8 ...  15  ... 20 21 22 23 24
658199086Srpaulo *
659199086Srpaulo * '--' (in the above) indicates that the value is unimportant.
660199086Srpaulo *
661199086Srpaulo * Information about the above layout was obtained from the
662199086Srpaulo * implementation of the AppleTouch driver in Linux.
663199086Srpaulo *
664199086Srpaulo * parameters:
665199086Srpaulo *   sensor_data
666199086Srpaulo *       raw sensor data from the USB packet.
667199086Srpaulo *   num
668199086Srpaulo *       The number of elements in the array 'arr'.
669199151Snwhitehorn *   axis
670199151Snwhitehorn *       Axis of data to fetch
671199086Srpaulo *   arr
672199086Srpaulo *       The array to be initialized with the readings.
673199151Snwhitehorn *   prot
674199151Snwhitehorn *       The protocol to use to interpret the data
675199086Srpaulo */
676199086Srpaulostatic __inline void
677199151Snwhitehornatp_interpret_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis,
678199151Snwhitehorn    int	*arr, atp_protocol prot)
679199086Srpaulo{
680199086Srpaulo	u_int i;
681199086Srpaulo	u_int di;   /* index into sensor data */
682199086Srpaulo
683199151Snwhitehorn	switch (prot) {
684199151Snwhitehorn	case ATP_PROT_GEYSER1:
685199151Snwhitehorn		/*
686199151Snwhitehorn		 * For Geyser 1, the sensors are laid out in pairs
687199151Snwhitehorn		 * every 5 bytes.
688199151Snwhitehorn		 */
689199151Snwhitehorn		for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) {
690199151Snwhitehorn			arr[i] = sensor_data[di];
691199151Snwhitehorn			arr[i+8] = sensor_data[di+2];
692199151Snwhitehorn			if (axis == X && num > 16)
693199151Snwhitehorn				arr[i+16] = sensor_data[di+40];
694199151Snwhitehorn		}
695199151Snwhitehorn
696199151Snwhitehorn		break;
697199151Snwhitehorn	case ATP_PROT_GEYSER2:
698199151Snwhitehorn	case ATP_PROT_GEYSER3:
699199151Snwhitehorn		for (i = 0, di = (axis == Y) ? 2 : 20; i < num; /* empty */ ) {
700199151Snwhitehorn			arr[i++] = sensor_data[di++];
701199151Snwhitehorn			arr[i++] = sensor_data[di++];
702199151Snwhitehorn			di++;
703199151Snwhitehorn		}
704199151Snwhitehorn		break;
705199086Srpaulo	}
706199086Srpaulo}
707199086Srpaulo
708199086Srpaulostatic __inline void
709199086Srpauloatp_get_pressures(int *p, const int *cur, const int *base, int n)
710199086Srpaulo{
711199086Srpaulo	int i;
712199086Srpaulo
713199086Srpaulo	for (i = 0; i < n; i++) {
714199086Srpaulo		p[i] = cur[i] - base[i];
715199086Srpaulo		if (p[i] > 127)
716199086Srpaulo			p[i] -= 256;
717199086Srpaulo		if (p[i] < -127)
718199086Srpaulo			p[i] += 256;
719199086Srpaulo		if (p[i] < 0)
720199086Srpaulo			p[i] = 0;
721199086Srpaulo
722199086Srpaulo		/*
723199086Srpaulo		 * Shave off pressures below the noise-pressure
724199086Srpaulo		 * threshold; this will reduce the contribution from
725199086Srpaulo		 * lower pressure readings.
726199086Srpaulo		 */
727199086Srpaulo		if (p[i] <= atp_sensor_noise_threshold)
728199086Srpaulo			p[i] = 0; /* filter away noise */
729199086Srpaulo		else
730199086Srpaulo			p[i] -= atp_sensor_noise_threshold;
731199086Srpaulo	}
732199086Srpaulo}
733199086Srpaulo
734199086Srpaulostatic void
735199086Srpauloatp_detect_pspans(int *p, u_int num_sensors,
736199086Srpaulo    u_int       max_spans, /* max # of pspans permitted */
737199086Srpaulo    atp_pspan  *spans,     /* finger spans */
738199086Srpaulo    u_int      *nspans_p)  /* num spans detected */
739199086Srpaulo{
740199086Srpaulo	u_int i;
741199086Srpaulo	int   maxp;             /* max pressure seen within a span */
742199086Srpaulo	u_int num_spans = 0;
743199086Srpaulo
744199086Srpaulo	enum atp_pspan_state {
745199086Srpaulo		ATP_PSPAN_INACTIVE,
746199086Srpaulo		ATP_PSPAN_INCREASING,
747199086Srpaulo		ATP_PSPAN_DECREASING,
748199086Srpaulo	} state; /* state of the pressure span */
749199086Srpaulo
750199086Srpaulo	/*
751199086Srpaulo	 * The following is a simple state machine to track
752199086Srpaulo	 * the phase of the pressure span.
753199086Srpaulo	 */
754199086Srpaulo	memset(spans, 0, max_spans * sizeof(atp_pspan));
755199086Srpaulo	maxp = 0;
756199086Srpaulo	state = ATP_PSPAN_INACTIVE;
757199086Srpaulo	for (i = 0; i < num_sensors; i++) {
758199086Srpaulo		if (num_spans >= max_spans)
759199086Srpaulo			break;
760199086Srpaulo
761199086Srpaulo		if (p[i] == 0) {
762199086Srpaulo			if (state == ATP_PSPAN_INACTIVE) {
763199086Srpaulo				/*
764199086Srpaulo				 * There is no pressure information for this
765199086Srpaulo				 * sensor, and we aren't tracking a finger.
766199086Srpaulo				 */
767199086Srpaulo				continue;
768199086Srpaulo			} else {
769199086Srpaulo				state = ATP_PSPAN_INACTIVE;
770199086Srpaulo				maxp = 0;
771199086Srpaulo				num_spans++;
772199086Srpaulo			}
773199086Srpaulo		} else {
774199086Srpaulo			switch (state) {
775199086Srpaulo			case ATP_PSPAN_INACTIVE:
776199086Srpaulo				state = ATP_PSPAN_INCREASING;
777199086Srpaulo				maxp  = p[i];
778199086Srpaulo				break;
779199086Srpaulo
780199086Srpaulo			case ATP_PSPAN_INCREASING:
781199086Srpaulo				if (p[i] > maxp)
782199086Srpaulo					maxp = p[i];
783199086Srpaulo				else if (p[i] <= (maxp >> 1))
784199086Srpaulo					state = ATP_PSPAN_DECREASING;
785199086Srpaulo				break;
786199086Srpaulo
787199086Srpaulo			case ATP_PSPAN_DECREASING:
788199086Srpaulo				if (p[i] > p[i - 1]) {
789199086Srpaulo					/*
790199086Srpaulo					 * This is the beginning of
791199086Srpaulo					 * another span; change state
792199086Srpaulo					 * to give the appearance that
793199086Srpaulo					 * we're starting from an
794199086Srpaulo					 * inactive span, and then
795199086Srpaulo					 * re-process this reading in
796199086Srpaulo					 * the next iteration.
797199086Srpaulo					 */
798199086Srpaulo					num_spans++;
799199086Srpaulo					state = ATP_PSPAN_INACTIVE;
800199086Srpaulo					maxp  = 0;
801199086Srpaulo					i--;
802199086Srpaulo					continue;
803199086Srpaulo				}
804199086Srpaulo				break;
805199086Srpaulo			}
806199086Srpaulo
807199086Srpaulo			/* Update the finger span with this reading. */
808199086Srpaulo			spans[num_spans].width++;
809199086Srpaulo			spans[num_spans].cum += p[i];
810199086Srpaulo			spans[num_spans].cog += p[i] * (i + 1);
811199086Srpaulo		}
812199086Srpaulo	}
813199086Srpaulo	if (state != ATP_PSPAN_INACTIVE)
814199086Srpaulo		num_spans++;    /* close the last finger span */
815199086Srpaulo
816199086Srpaulo	/* post-process the spans */
817199086Srpaulo	for (i = 0; i < num_spans; i++) {
818199086Srpaulo		/* filter away unwanted pressure spans */
819199086Srpaulo		if ((spans[i].cum < atp_pspan_min_cum_pressure) ||
820199086Srpaulo		    (spans[i].width > atp_pspan_max_width)) {
821199086Srpaulo			if ((i + 1) < num_spans) {
822199086Srpaulo				memcpy(&spans[i], &spans[i + 1],
823199086Srpaulo				    (num_spans - i - 1) * sizeof(atp_pspan));
824199086Srpaulo				i--;
825199086Srpaulo			}
826199086Srpaulo			num_spans--;
827199086Srpaulo			continue;
828199086Srpaulo		}
829199086Srpaulo
830199086Srpaulo		/* compute this span's representative location */
831199086Srpaulo		spans[i].loc = spans[i].cog * atp_mickeys_scale_factor /
832199086Srpaulo			spans[i].cum;
833199086Srpaulo
834199086Srpaulo		spans[i].matched = FALSE; /* not yet matched against a stroke */
835199086Srpaulo	}
836199086Srpaulo
837199086Srpaulo	*nspans_p = num_spans;
838199086Srpaulo}
839199086Srpaulo
840199086Srpaulo/*
841199086Srpaulo * Match a pressure-span against a stroke-component. If there is a
842199086Srpaulo * match, update the component's state and return TRUE.
843199086Srpaulo */
844199086Srpaulostatic boolean_t
845199086Srpauloatp_match_stroke_component(atp_stroke_component *component,
846199086Srpaulo    const atp_pspan *pspan)
847199086Srpaulo{
848199086Srpaulo	int delta_mickeys = pspan->loc - component->loc;
849199086Srpaulo
850199086Srpaulo	if (abs(delta_mickeys) > atp_max_delta_mickeys)
851199086Srpaulo		return (FALSE); /* the finger span is too far out; no match */
852199086Srpaulo
853199086Srpaulo	component->loc          = pspan->loc;
854199086Srpaulo	component->cum_pressure = pspan->cum;
855199086Srpaulo	if (pspan->cum > component->max_cum_pressure)
856199086Srpaulo		component->max_cum_pressure = pspan->cum;
857199086Srpaulo
858199086Srpaulo	/*
859199086Srpaulo	 * If the cumulative pressure drops below a quarter of the max,
860199086Srpaulo	 * then disregard the component's movement.
861199086Srpaulo	 */
862199086Srpaulo	if (component->cum_pressure < (component->max_cum_pressure >> 2))
863199086Srpaulo		delta_mickeys = 0;
864199086Srpaulo
865199086Srpaulo	component->delta_mickeys = delta_mickeys;
866199086Srpaulo	return (TRUE);
867199086Srpaulo}
868199086Srpaulo
869199086Srpaulostatic void
870199086Srpauloatp_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis,
871199086Srpaulo    atp_pspan *pspans, u_int n_pspans, u_int repeat_count)
872199086Srpaulo{
873199086Srpaulo	u_int i, j;
874199086Srpaulo	u_int repeat_index = 0;
875199086Srpaulo
876199086Srpaulo	/* Determine the index of the multi-span. */
877199086Srpaulo	if (repeat_count) {
878199086Srpaulo		u_int cum = 0;
879199086Srpaulo		for (i = 0; i < n_pspans; i++) {
880199086Srpaulo			if (pspans[i].cum > cum) {
881199086Srpaulo				repeat_index = i;
882199086Srpaulo				cum = pspans[i].cum;
883199086Srpaulo			}
884199086Srpaulo		}
885199086Srpaulo	}
886199086Srpaulo
887199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
888199086Srpaulo		atp_stroke *stroke  = &sc->sc_strokes[i];
889199086Srpaulo		if (stroke->components[axis].matched)
890199086Srpaulo			continue; /* skip matched components */
891199086Srpaulo
892199086Srpaulo		for (j = 0; j < n_pspans; j++) {
893199086Srpaulo			if (pspans[j].matched)
894199086Srpaulo				continue; /* skip matched pspans */
895199086Srpaulo
896199086Srpaulo			if (atp_match_stroke_component(
897199086Srpaulo				    &stroke->components[axis], &pspans[j])) {
898199086Srpaulo				/* There is a match. */
899199086Srpaulo				stroke->components[axis].matched = TRUE;
900199086Srpaulo
901199086Srpaulo				/* Take care to repeat at the multi-span. */
902199086Srpaulo				if ((repeat_count > 0) && (j == repeat_index))
903199086Srpaulo					repeat_count--;
904199086Srpaulo				else
905199086Srpaulo					pspans[j].matched = TRUE;
906199086Srpaulo
907199086Srpaulo				break; /* skip to the next stroke */
908199086Srpaulo			}
909199086Srpaulo		} /* loop over pspans */
910199086Srpaulo	} /* loop over strokes */
911199086Srpaulo}
912199086Srpaulo
913199086Srpaulo/*
914199086Srpaulo * Update strokes by matching against current pressure-spans.
915199086Srpaulo * Return TRUE if any movement is detected.
916199086Srpaulo */
917199086Srpaulostatic boolean_t
918199086Srpauloatp_update_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
919199086Srpaulo    u_int n_xpspans, atp_pspan *pspans_y, u_int n_ypspans)
920199086Srpaulo{
921199086Srpaulo	u_int       i, j;
922199086Srpaulo	atp_stroke *stroke;
923199086Srpaulo	boolean_t   movement = FALSE;
924199086Srpaulo	u_int       repeat_count = 0;
925199086Srpaulo
926199086Srpaulo	/* Reset X and Y components of all strokes as unmatched. */
927199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
928199086Srpaulo		stroke = &sc->sc_strokes[i];
929199086Srpaulo		stroke->components[X].matched = FALSE;
930199086Srpaulo		stroke->components[Y].matched = FALSE;
931199086Srpaulo	}
932199086Srpaulo
933199086Srpaulo	/*
934199086Srpaulo	 * Usually, the X and Y pspans come in pairs (the common case
935199086Srpaulo	 * being a single pair). It is possible, however, that
936199086Srpaulo	 * multiple contacts resolve to a single pspan along an
937199086Srpaulo	 * axis, as illustrated in the following:
938199086Srpaulo	 *
939199086Srpaulo	 *   F = finger-contact
940199086Srpaulo	 *
941199086Srpaulo	 *                pspan  pspan
942199086Srpaulo	 *        +-----------------------+
943199086Srpaulo	 *        |         .      .      |
944199086Srpaulo	 *        |         .      .      |
945199086Srpaulo	 *        |         .      .      |
946199086Srpaulo	 *        |         .      .      |
947199086Srpaulo	 *  pspan |.........F......F      |
948199086Srpaulo	 *        |                       |
949199086Srpaulo	 *        |                       |
950199086Srpaulo	 *        |                       |
951199086Srpaulo	 *        +-----------------------+
952199086Srpaulo	 *
953199086Srpaulo	 *
954199086Srpaulo	 * The above case can be detected by a difference in the
955199086Srpaulo	 * number of X and Y pspans. When this happens, X and Y pspans
956199086Srpaulo	 * aren't easy to pair or match against strokes.
957199086Srpaulo	 *
958199086Srpaulo	 * When X and Y pspans differ in number, the axis with the
959199086Srpaulo	 * smaller number of pspans is regarded as having a repeating
960199086Srpaulo	 * pspan (or a multi-pspan)--in the above illustration, the
961199086Srpaulo	 * Y-axis has a repeating pspan. Our approach is to try to
962199086Srpaulo	 * match the multi-pspan repeatedly against strokes. The
963199086Srpaulo	 * difference between the number of X and Y pspans gives us a
964199086Srpaulo	 * crude repeat_count for matching multi-pspans--i.e. the
965199086Srpaulo	 * multi-pspan along the Y axis (above) has a repeat_count of 1.
966199086Srpaulo	 */
967199086Srpaulo	repeat_count = abs(n_xpspans - n_ypspans);
968199086Srpaulo
969199086Srpaulo	atp_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans,
970199086Srpaulo	    (((repeat_count != 0) && ((n_xpspans < n_ypspans))) ?
971199086Srpaulo		repeat_count : 0));
972199086Srpaulo	atp_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans,
973199086Srpaulo	    (((repeat_count != 0) && (n_ypspans < n_xpspans)) ?
974199086Srpaulo		repeat_count : 0));
975199086Srpaulo
976199086Srpaulo	/* Update the state of strokes based on the above pspan matches. */
977199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
978199086Srpaulo		stroke = &sc->sc_strokes[i];
979199086Srpaulo		if (stroke->components[X].matched &&
980199086Srpaulo		    stroke->components[Y].matched) {
981199086Srpaulo			atp_advance_stroke_state(sc, stroke, &movement);
982199086Srpaulo		} else {
983199086Srpaulo			/*
984199086Srpaulo			 * At least one component of this stroke
985199086Srpaulo			 * didn't match against current pspans;
986199086Srpaulo			 * terminate it.
987199086Srpaulo			 */
988199086Srpaulo			atp_terminate_stroke(sc, i);
989199086Srpaulo		}
990199086Srpaulo	}
991199086Srpaulo
992199086Srpaulo	/* Add new strokes for pairs of unmatched pspans */
993199086Srpaulo	for (i = 0; i < n_xpspans; i++) {
994199086Srpaulo		if (pspans_x[i].matched == FALSE) break;
995199086Srpaulo	}
996199086Srpaulo	for (j = 0; j < n_ypspans; j++) {
997199086Srpaulo		if (pspans_y[j].matched == FALSE) break;
998199086Srpaulo	}
999199086Srpaulo	if ((i < n_xpspans) && (j < n_ypspans)) {
1000199086Srpaulo#if USB_DEBUG
1001199086Srpaulo		if (atp_debug >= ATP_LLEVEL_INFO) {
1002199086Srpaulo			printf("unmatched pspans:");
1003199086Srpaulo			for (; i < n_xpspans; i++) {
1004199086Srpaulo				if (pspans_x[i].matched)
1005199086Srpaulo					continue;
1006199086Srpaulo				printf(" X:[loc:%u,cum:%u]",
1007199086Srpaulo				    pspans_x[i].loc, pspans_x[i].cum);
1008199086Srpaulo			}
1009199086Srpaulo			for (; j < n_ypspans; j++) {
1010199086Srpaulo				if (pspans_y[j].matched)
1011199086Srpaulo					continue;
1012199086Srpaulo				printf(" Y:[loc:%u,cum:%u]",
1013199086Srpaulo				    pspans_y[j].loc, pspans_y[j].cum);
1014199086Srpaulo			}
1015199086Srpaulo			printf("\n");
1016199086Srpaulo		}
1017199086Srpaulo#endif /* #if USB_DEBUG */
1018199086Srpaulo		if ((n_xpspans == 1) && (n_ypspans == 1))
1019199086Srpaulo			/* The common case of a single pair of new pspans. */
1020199086Srpaulo			atp_add_stroke(sc, &pspans_x[0], &pspans_y[0]);
1021199086Srpaulo		else
1022199086Srpaulo			atp_add_new_strokes(sc,
1023199086Srpaulo			    pspans_x, n_xpspans,
1024199086Srpaulo			    pspans_y, n_ypspans);
1025199086Srpaulo	}
1026199086Srpaulo
1027199086Srpaulo#if USB_DEBUG
1028199086Srpaulo	if (atp_debug >= ATP_LLEVEL_INFO) {
1029199086Srpaulo		for (i = 0; i < sc->sc_n_strokes; i++) {
1030199086Srpaulo			atp_stroke *stroke = &sc->sc_strokes[i];
1031199086Srpaulo
1032199086Srpaulo			printf(" %s%clc:%u,dm:%d,pnd:%d,mv:%d%c"
1033199086Srpaulo			    ",%clc:%u,dm:%d,pnd:%d,mv:%d%c",
1034199086Srpaulo			    (stroke->flags & ATSF_ZOMBIE) ? "zomb:" : "",
1035199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
1036199086Srpaulo			    stroke->components[X].loc,
1037199086Srpaulo			    stroke->components[X].delta_mickeys,
1038199086Srpaulo			    stroke->components[X].pending,
1039199086Srpaulo			    stroke->components[X].movement,
1040199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? ']' : '>',
1041199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
1042199086Srpaulo			    stroke->components[Y].loc,
1043199086Srpaulo			    stroke->components[Y].delta_mickeys,
1044199086Srpaulo			    stroke->components[Y].pending,
1045199086Srpaulo			    stroke->components[Y].movement,
1046199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? ']' : '>');
1047199086Srpaulo		}
1048199086Srpaulo		if (sc->sc_n_strokes)
1049199086Srpaulo			printf("\n");
1050199086Srpaulo	}
1051199086Srpaulo#endif /* #if USB_DEBUG */
1052199086Srpaulo
1053199086Srpaulo	return (movement);
1054199086Srpaulo}
1055199086Srpaulo
1056199086Srpaulo/* Initialize a stroke using a pressure-span. */
1057199086Srpaulostatic __inline void
1058199086Srpauloatp_add_stroke(struct atp_softc *sc, const atp_pspan *pspan_x,
1059199086Srpaulo    const atp_pspan *pspan_y)
1060199086Srpaulo{
1061199086Srpaulo	atp_stroke *stroke;
1062199086Srpaulo
1063199086Srpaulo	if (sc->sc_n_strokes >= ATP_MAX_STROKES)
1064199086Srpaulo		return;
1065199086Srpaulo	stroke = &sc->sc_strokes[sc->sc_n_strokes];
1066199086Srpaulo
1067199086Srpaulo	memset(stroke, 0, sizeof(atp_stroke));
1068199086Srpaulo
1069199086Srpaulo	/*
1070199086Srpaulo	 * Strokes begin as potential touches. If a stroke survives
1071199086Srpaulo	 * longer than a threshold, or if it records significant
1072199086Srpaulo	 * cumulative movement, then it is considered a 'slide'.
1073199086Srpaulo	 */
1074199086Srpaulo	stroke->type = ATP_STROKE_TOUCH;
1075199086Srpaulo	microtime(&stroke->ctime);
1076199086Srpaulo	stroke->age  = 1;       /* Unit: interrupts */
1077199086Srpaulo
1078199086Srpaulo	stroke->components[X].loc              = pspan_x->loc;
1079199086Srpaulo	stroke->components[X].cum_pressure     = pspan_x->cum;
1080199086Srpaulo	stroke->components[X].max_cum_pressure = pspan_x->cum;
1081199086Srpaulo	stroke->components[X].matched          = TRUE;
1082199086Srpaulo
1083199086Srpaulo	stroke->components[Y].loc              = pspan_y->loc;
1084199086Srpaulo	stroke->components[Y].cum_pressure     = pspan_y->cum;
1085199086Srpaulo	stroke->components[Y].max_cum_pressure = pspan_y->cum;
1086199086Srpaulo	stroke->components[Y].matched          = TRUE;
1087199086Srpaulo
1088199086Srpaulo	sc->sc_n_strokes++;
1089199086Srpaulo	if (sc->sc_n_strokes > 1) {
1090199086Srpaulo		/* Reset double-tap-n-drag if we have more than one strokes. */
1091199086Srpaulo		sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1092199086Srpaulo	}
1093199086Srpaulo
1094199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "[%u,%u], time: %u,%ld\n",
1095199086Srpaulo	    stroke->components[X].loc,
1096199086Srpaulo	    stroke->components[Y].loc,
1097199086Srpaulo	    (unsigned int)stroke->ctime.tv_sec,
1098199086Srpaulo	    (unsigned long int)stroke->ctime.tv_usec);
1099199086Srpaulo}
1100199086Srpaulo
1101199086Srpaulostatic void
1102199086Srpauloatp_add_new_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
1103199086Srpaulo    u_int n_xpspans, atp_pspan *pspans_y, u_int n_ypspans)
1104199086Srpaulo{
1105199086Srpaulo	int       i, j;
1106199086Srpaulo	atp_pspan spans[2][ATP_MAX_PSPANS_PER_AXIS];
1107199086Srpaulo	u_int     nspans[2];
1108199086Srpaulo
1109199086Srpaulo	/* Copy unmatched pspans into the local arrays. */
1110199086Srpaulo	for (i = 0, nspans[X] = 0; i < n_xpspans; i++) {
1111199086Srpaulo		if (pspans_x[i].matched == FALSE) {
1112199086Srpaulo			spans[X][nspans[X]] = pspans_x[i];
1113199086Srpaulo			nspans[X]++;
1114199086Srpaulo		}
1115199086Srpaulo	}
1116199086Srpaulo	for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) {
1117199086Srpaulo		if (pspans_y[j].matched == FALSE) {
1118199086Srpaulo			spans[Y][nspans[Y]] = pspans_y[j];
1119199086Srpaulo			nspans[Y]++;
1120199086Srpaulo		}
1121199086Srpaulo	}
1122199086Srpaulo
1123199086Srpaulo	if (nspans[X] == nspans[Y]) {
1124199086Srpaulo		/* Create new strokes from pairs of unmatched pspans */
1125199086Srpaulo		for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++)
1126199086Srpaulo			atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
1127199086Srpaulo	} else {
1128199086Srpaulo		u_int    cum = 0;
1129199086Srpaulo		atp_axis repeat_axis;      /* axis with multi-pspans */
1130199086Srpaulo		u_int    repeat_count;     /* repeat count for the multi-pspan*/
1131199086Srpaulo		u_int    repeat_index = 0; /* index of the multi-span */
1132199086Srpaulo
1133199086Srpaulo		repeat_axis  = (nspans[X] > nspans[Y]) ? Y : X;
1134199086Srpaulo		repeat_count = abs(nspans[X] - nspans[Y]);
1135199086Srpaulo		for (i = 0; i < nspans[repeat_axis]; i++) {
1136199086Srpaulo			if (spans[repeat_axis][i].cum > cum) {
1137199086Srpaulo				repeat_index = i;
1138199086Srpaulo				cum = spans[repeat_axis][i].cum;
1139199086Srpaulo			}
1140199086Srpaulo		}
1141199086Srpaulo
1142199086Srpaulo		/* Create new strokes from pairs of unmatched pspans */
1143199086Srpaulo		i = 0, j = 0;
1144199086Srpaulo		for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) {
1145199086Srpaulo			atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
1146199086Srpaulo
1147199086Srpaulo			/* Take care to repeat at the multi-pspan. */
1148199086Srpaulo			if (repeat_count > 0) {
1149199086Srpaulo				if ((repeat_axis == X) &&
1150199086Srpaulo				    (repeat_index == i)) {
1151199086Srpaulo					i--; /* counter loop increment */
1152199086Srpaulo					repeat_count--;
1153199086Srpaulo				} else if ((repeat_axis == Y) &&
1154199086Srpaulo				    (repeat_index == j)) {
1155199086Srpaulo					j--; /* counter loop increment */
1156199086Srpaulo					repeat_count--;
1157199086Srpaulo				}
1158199086Srpaulo			}
1159199086Srpaulo		}
1160199086Srpaulo	}
1161199086Srpaulo}
1162199086Srpaulo
1163199086Srpaulo/*
1164199086Srpaulo * Advance the state of this stroke--and update the out-parameter
1165199086Srpaulo * 'movement' as a side-effect.
1166199086Srpaulo */
1167199086Srpaulovoid
1168199086Srpauloatp_advance_stroke_state(struct atp_softc *sc, atp_stroke *stroke,
1169199086Srpaulo    boolean_t *movement)
1170199086Srpaulo{
1171199086Srpaulo	stroke->age++;
1172199086Srpaulo	if (stroke->age <= atp_stroke_maturity_threshold) {
1173199086Srpaulo		/* Avoid noise from immature strokes. */
1174199086Srpaulo		stroke->components[X].delta_mickeys = 0;
1175199086Srpaulo		stroke->components[Y].delta_mickeys = 0;
1176199086Srpaulo	}
1177199086Srpaulo
1178199086Srpaulo	/* Revitalize stroke if it had previously been marked as a zombie. */
1179199086Srpaulo	if (stroke->flags & ATSF_ZOMBIE)
1180199086Srpaulo		stroke->flags &= ~ATSF_ZOMBIE;
1181199086Srpaulo
1182199086Srpaulo	if (atp_compute_stroke_movement(stroke))
1183199086Srpaulo		*movement = TRUE;
1184199086Srpaulo
1185199086Srpaulo	/* Convert touch strokes to slides upon detecting movement or age. */
1186199086Srpaulo	if (stroke->type == ATP_STROKE_TOUCH) {
1187199086Srpaulo		struct timeval tdiff;
1188199086Srpaulo
1189199086Srpaulo		/* Compute the stroke's age. */
1190199086Srpaulo		getmicrotime(&tdiff);
1191199086Srpaulo		if (timevalcmp(&tdiff, &stroke->ctime, >))
1192199086Srpaulo			timevalsub(&tdiff, &stroke->ctime);
1193199086Srpaulo		else {
1194199086Srpaulo			/*
1195199086Srpaulo			 * If we are here, it is because getmicrotime
1196199086Srpaulo			 * reported the current time as being behind
1197199086Srpaulo			 * the stroke's start time; getmicrotime can
1198199086Srpaulo			 * be imprecise.
1199199086Srpaulo			 */
1200199086Srpaulo			tdiff.tv_sec  = 0;
1201199086Srpaulo			tdiff.tv_usec = 0;
1202199086Srpaulo		}
1203199086Srpaulo
1204199086Srpaulo		if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) ||
1205199086Srpaulo		    ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) &&
1206199086Srpaulo			(tdiff.tv_usec > atp_touch_timeout)) ||
1207199086Srpaulo		    (stroke->cum_movement >= atp_slide_min_movement)) {
1208199086Srpaulo			/* Switch this stroke to being a slide. */
1209199086Srpaulo			stroke->type = ATP_STROKE_SLIDE;
1210199086Srpaulo
1211199086Srpaulo			/* Are we at the beginning of a double-click-n-drag? */
1212199086Srpaulo			if ((sc->sc_n_strokes == 1) &&
1213199086Srpaulo			    ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) &&
1214199086Srpaulo			    timevalcmp(&stroke->ctime, &sc->sc_reap_time, >)) {
1215199086Srpaulo				struct timeval delta;
1216199086Srpaulo				struct timeval window = {
1217199086Srpaulo					atp_double_tap_threshold / 1000000,
1218199086Srpaulo					atp_double_tap_threshold % 1000000
1219199086Srpaulo				};
1220199086Srpaulo
1221199086Srpaulo				delta = stroke->ctime;
1222199086Srpaulo				timevalsub(&delta, &sc->sc_reap_time);
1223199086Srpaulo				if (timevalcmp(&delta, &window, <=))
1224199086Srpaulo					sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
1225199086Srpaulo			}
1226199086Srpaulo		}
1227199086Srpaulo	}
1228199086Srpaulo}
1229199086Srpaulo
1230199086Srpaulo/*
1231199086Srpaulo * Terminate a stroke. While SLIDE strokes are dropped, TOUCH strokes
1232199086Srpaulo * are retained as zombies so as to reap all their siblings together;
1233199086Srpaulo * this helps establish the number of fingers involved in the tap.
1234199086Srpaulo */
1235199086Srpaulostatic void
1236199086Srpauloatp_terminate_stroke(struct atp_softc *sc,
1237199086Srpaulo    u_int index) /* index of the stroke to be terminated */
1238199086Srpaulo{
1239199086Srpaulo	atp_stroke *s = &sc->sc_strokes[index];
1240199086Srpaulo
1241199086Srpaulo	if (s->flags & ATSF_ZOMBIE) {
1242199086Srpaulo		return;
1243199086Srpaulo	}
1244199086Srpaulo
1245199086Srpaulo	if ((s->type == ATP_STROKE_TOUCH) &&
1246199086Srpaulo	    (s->age > atp_stroke_maturity_threshold)) {
1247199086Srpaulo		s->flags |= ATSF_ZOMBIE;
1248199086Srpaulo
1249199086Srpaulo		/* If no zombies exist, then prepare to reap zombies later. */
1250199086Srpaulo		if ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) {
1251199086Srpaulo			atp_setup_reap_time(sc, &s->ctime);
1252199086Srpaulo			sc->sc_state |= ATP_ZOMBIES_EXIST;
1253199086Srpaulo		}
1254199086Srpaulo	} else {
1255199086Srpaulo		/* Drop this stroke. */
1256199086Srpaulo		memcpy(&sc->sc_strokes[index], &sc->sc_strokes[index + 1],
1257199086Srpaulo		    (sc->sc_n_strokes - index - 1) * sizeof(atp_stroke));
1258199086Srpaulo		sc->sc_n_strokes--;
1259199086Srpaulo
1260199086Srpaulo		/*
1261199086Srpaulo		 * Reset the double-click-n-drag at the termination of
1262199086Srpaulo		 * any slide stroke.
1263199086Srpaulo		 */
1264199086Srpaulo		sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1265199086Srpaulo	}
1266199086Srpaulo}
1267199086Srpaulo
1268199086Srpaulostatic __inline boolean_t
1269199086Srpauloatp_stroke_has_small_movement(const atp_stroke *stroke)
1270199086Srpaulo{
1271199086Srpaulo	return ((abs(stroke->components[X].delta_mickeys) <=
1272199086Srpaulo		atp_small_movement_threshold) &&
1273199086Srpaulo	    (abs(stroke->components[Y].delta_mickeys) <=
1274199086Srpaulo		atp_small_movement_threshold));
1275199086Srpaulo}
1276199086Srpaulo
1277199086Srpaulo/*
1278199086Srpaulo * Accumulate delta_mickeys into the component's 'pending' bucket; if
1279199086Srpaulo * the aggregate exceeds the small_movement_threshold, then retain
1280199086Srpaulo * delta_mickeys for later.
1281199086Srpaulo */
1282199086Srpaulostatic __inline void
1283199086Srpauloatp_update_pending_mickeys(atp_stroke_component *component)
1284199086Srpaulo{
1285199086Srpaulo	component->pending += component->delta_mickeys;
1286199086Srpaulo	if (abs(component->pending) <= atp_small_movement_threshold)
1287199086Srpaulo		component->delta_mickeys = 0;
1288199086Srpaulo	else {
1289199086Srpaulo		/*
1290199086Srpaulo		 * Penalise pending mickeys for having accumulated
1291199086Srpaulo		 * over short deltas. This operation has the effect of
1292199086Srpaulo		 * scaling down the cumulative contribution of short
1293199086Srpaulo		 * movements.
1294199086Srpaulo		 */
1295199086Srpaulo		component->pending -= (component->delta_mickeys << 1);
1296199086Srpaulo	}
1297199086Srpaulo}
1298199086Srpaulo
1299199086Srpaulo
1300199086Srpaulostatic void
1301199086Srpauloatp_compute_smoothening_scale_ratio(atp_stroke *stroke, int *numerator,
1302199086Srpaulo    int *denominator)
1303199086Srpaulo{
1304199086Srpaulo	int   dxdt;
1305199086Srpaulo	int   dydt;
1306199086Srpaulo	u_int vel_squared; /* Square of the velocity vector's magnitude. */
1307199086Srpaulo	u_int vel_squared_smooth;
1308199086Srpaulo
1309199086Srpaulo	/* Table holding (10 * sqrt(x)) for x between 1 and 256. */
1310199086Srpaulo	static uint8_t sqrt_table[256] = {
1311199086Srpaulo		10, 14, 17, 20, 22, 24, 26, 28,
1312199086Srpaulo		30, 31, 33, 34, 36, 37, 38, 40,
1313199086Srpaulo		41, 42, 43, 44, 45, 46, 47, 48,
1314199086Srpaulo		50, 50, 51, 52, 53, 54, 55, 56,
1315199086Srpaulo		57, 58, 59, 60, 60, 61, 62, 63,
1316199086Srpaulo		64, 64, 65, 66, 67, 67, 68, 69,
1317199086Srpaulo		70, 70, 71, 72, 72, 73, 74, 74,
1318199086Srpaulo		75, 76, 76, 77, 78, 78, 79, 80,
1319199086Srpaulo		80, 81, 81, 82, 83, 83, 84, 84,
1320199086Srpaulo		85, 86, 86, 87, 87, 88, 88, 89,
1321199086Srpaulo		90, 90, 91, 91, 92, 92, 93, 93,
1322199086Srpaulo		94, 94, 95, 95, 96, 96, 97, 97,
1323199086Srpaulo		98, 98, 99, 100, 100, 100, 101, 101,
1324199086Srpaulo		102, 102, 103, 103, 104, 104, 105, 105,
1325199086Srpaulo		106, 106, 107, 107, 108, 108, 109, 109,
1326199086Srpaulo		110, 110, 110, 111, 111, 112, 112, 113,
1327199086Srpaulo		113, 114, 114, 114, 115, 115, 116, 116,
1328199086Srpaulo		117, 117, 117, 118, 118, 119, 119, 120,
1329199086Srpaulo		120, 120, 121, 121, 122, 122, 122, 123,
1330199086Srpaulo		123, 124, 124, 124, 125, 125, 126, 126,
1331199086Srpaulo		126, 127, 127, 128, 128, 128, 129, 129,
1332199086Srpaulo		130, 130, 130, 131, 131, 131, 132, 132,
1333199086Srpaulo		133, 133, 133, 134, 134, 134, 135, 135,
1334199086Srpaulo		136, 136, 136, 137, 137, 137, 138, 138,
1335199086Srpaulo		138, 139, 139, 140, 140, 140, 141, 141,
1336199086Srpaulo		141, 142, 142, 142, 143, 143, 143, 144,
1337199086Srpaulo		144, 144, 145, 145, 145, 146, 146, 146,
1338199086Srpaulo		147, 147, 147, 148, 148, 148, 149, 149,
1339199086Srpaulo		150, 150, 150, 150, 151, 151, 151, 152,
1340199086Srpaulo		152, 152, 153, 153, 153, 154, 154, 154,
1341199086Srpaulo		155, 155, 155, 156, 156, 156, 157, 157,
1342199086Srpaulo		157, 158, 158, 158, 159, 159, 159, 160
1343199086Srpaulo	};
1344199086Srpaulo	const u_int N = sizeof(sqrt_table) / sizeof(sqrt_table[0]);
1345199086Srpaulo
1346199086Srpaulo	dxdt = stroke->components[X].delta_mickeys;
1347199086Srpaulo	dydt = stroke->components[Y].delta_mickeys;
1348199086Srpaulo
1349199086Srpaulo	*numerator = 0, *denominator = 0; /* default values. */
1350199086Srpaulo
1351199086Srpaulo	/* Compute a smoothened magnitude_squared of the stroke's velocity. */
1352199086Srpaulo	vel_squared = dxdt * dxdt + dydt * dydt;
1353199086Srpaulo	vel_squared_smooth = (3 * stroke->velocity_squared + vel_squared) >> 2;
1354199086Srpaulo	stroke->velocity_squared = vel_squared_smooth; /* retained as history */
1355199086Srpaulo	if ((vel_squared == 0) || (vel_squared_smooth == 0))
1356199086Srpaulo		return; /* returning (numerator == 0) will imply zero movement*/
1357199086Srpaulo
1358199086Srpaulo	/*
1359199086Srpaulo	 * In order to determine the overall movement scale factor,
1360199086Srpaulo	 * we're actually interested in the effect of smoothening upon
1361199086Srpaulo	 * the *magnitude* of velocity; i.e. we need to compute the
1362199086Srpaulo	 * square-root of (vel_squared_smooth / vel_squared) in the
1363199086Srpaulo	 * form of a numerator and denominator.
1364199086Srpaulo	 */
1365199086Srpaulo
1366199086Srpaulo	/* Keep within the bounds of the square-root table. */
1367199086Srpaulo	while ((vel_squared > N) || (vel_squared_smooth > N)) {
1368199086Srpaulo		/* Dividing uniformly by 2 won't disturb the final ratio. */
1369199086Srpaulo		vel_squared        >>= 1;
1370199086Srpaulo		vel_squared_smooth >>= 1;
1371199086Srpaulo	}
1372199086Srpaulo
1373199086Srpaulo	*numerator   = sqrt_table[vel_squared_smooth - 1];
1374199086Srpaulo	*denominator = sqrt_table[vel_squared - 1];
1375199086Srpaulo}
1376199086Srpaulo
1377199086Srpaulo/*
1378199086Srpaulo * Compute a smoothened value for the stroke's movement from
1379199086Srpaulo * delta_mickeys in the X and Y components.
1380199086Srpaulo */
1381199086Srpaulostatic boolean_t
1382199086Srpauloatp_compute_stroke_movement(atp_stroke *stroke)
1383199086Srpaulo{
1384199086Srpaulo	int   num;              /* numerator of scale ratio */
1385199086Srpaulo	int   denom;            /* denominator of scale ratio */
1386199086Srpaulo
1387199086Srpaulo	/*
1388199086Srpaulo	 * Short movements are added first to the 'pending' bucket,
1389199086Srpaulo	 * and then acted upon only when their aggregate exceeds a
1390199086Srpaulo	 * threshold. This has the effect of filtering away movement
1391199086Srpaulo	 * noise.
1392199086Srpaulo	 */
1393199086Srpaulo	if (atp_stroke_has_small_movement(stroke)) {
1394199086Srpaulo		atp_update_pending_mickeys(&stroke->components[X]);
1395199086Srpaulo		atp_update_pending_mickeys(&stroke->components[Y]);
1396199086Srpaulo	} else {                /* large movement */
1397199086Srpaulo		/* clear away any pending mickeys if there are large movements*/
1398199086Srpaulo		stroke->components[X].pending = 0;
1399199086Srpaulo		stroke->components[Y].pending = 0;
1400199086Srpaulo	}
1401199086Srpaulo
1402199086Srpaulo	/* Get the scale ratio and smoothen movement. */
1403199086Srpaulo	atp_compute_smoothening_scale_ratio(stroke, &num, &denom);
1404199086Srpaulo	if ((num == 0) || (denom == 0)) {
1405199086Srpaulo		stroke->components[X].movement = 0;
1406199086Srpaulo		stroke->components[Y].movement = 0;
1407199086Srpaulo		stroke->velocity_squared >>= 1; /* Erode velocity_squared. */
1408199086Srpaulo	} else {
1409199086Srpaulo		stroke->components[X].movement =
1410199086Srpaulo			(stroke->components[X].delta_mickeys * num) / denom;
1411199086Srpaulo		stroke->components[Y].movement =
1412199086Srpaulo			(stroke->components[Y].delta_mickeys * num) / denom;
1413199086Srpaulo
1414199086Srpaulo		stroke->cum_movement +=
1415199086Srpaulo			abs(stroke->components[X].movement) +
1416199086Srpaulo			abs(stroke->components[Y].movement);
1417199086Srpaulo	}
1418199086Srpaulo
1419199086Srpaulo	return ((stroke->components[X].movement != 0) ||
1420199086Srpaulo	    (stroke->components[Y].movement != 0));
1421199086Srpaulo}
1422199086Srpaulo
1423199086Srpaulostatic __inline void
1424199086Srpauloatp_setup_reap_time(struct atp_softc *sc, struct timeval *tvp)
1425199086Srpaulo{
1426199086Srpaulo	struct timeval reap_window = {
1427199086Srpaulo		ATP_ZOMBIE_STROKE_REAP_WINDOW / 1000000,
1428199086Srpaulo		ATP_ZOMBIE_STROKE_REAP_WINDOW % 1000000
1429199086Srpaulo	};
1430199086Srpaulo
1431199086Srpaulo	microtime(&sc->sc_reap_time);
1432199086Srpaulo	timevaladd(&sc->sc_reap_time, &reap_window);
1433199086Srpaulo
1434199086Srpaulo	sc->sc_reap_ctime = *tvp; /* ctime to reap */
1435199086Srpaulo}
1436199086Srpaulo
1437199086Srpaulostatic void
1438199086Srpauloatp_reap_zombies(struct atp_softc *sc, u_int *n_reaped, u_int *reaped_xlocs)
1439199086Srpaulo{
1440199086Srpaulo	u_int       i;
1441199086Srpaulo	atp_stroke *stroke;
1442199086Srpaulo
1443199086Srpaulo	*n_reaped = 0;
1444199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1445199086Srpaulo		struct timeval  tdiff;
1446199086Srpaulo
1447199086Srpaulo		stroke = &sc->sc_strokes[i];
1448199086Srpaulo
1449199086Srpaulo		if ((stroke->flags & ATSF_ZOMBIE) == 0)
1450199086Srpaulo			continue;
1451199086Srpaulo
1452199086Srpaulo		/* Compare this stroke's ctime with the ctime being reaped. */
1453199086Srpaulo		if (timevalcmp(&stroke->ctime, &sc->sc_reap_ctime, >=)) {
1454199086Srpaulo			tdiff = stroke->ctime;
1455199086Srpaulo			timevalsub(&tdiff, &sc->sc_reap_ctime);
1456199086Srpaulo		} else {
1457199086Srpaulo			tdiff = sc->sc_reap_ctime;
1458199086Srpaulo			timevalsub(&tdiff, &stroke->ctime);
1459199086Srpaulo		}
1460199086Srpaulo
1461199086Srpaulo		if ((tdiff.tv_sec > (ATP_COINCIDENCE_THRESHOLD / 1000000)) ||
1462199086Srpaulo		    ((tdiff.tv_sec == (ATP_COINCIDENCE_THRESHOLD / 1000000)) &&
1463199086Srpaulo		     (tdiff.tv_usec > (ATP_COINCIDENCE_THRESHOLD % 1000000)))) {
1464199086Srpaulo			continue; /* Skip non-siblings. */
1465199086Srpaulo		}
1466199086Srpaulo
1467199086Srpaulo		/*
1468199086Srpaulo		 * Reap this sibling zombie stroke.
1469199086Srpaulo		 */
1470199086Srpaulo
1471199086Srpaulo		if (reaped_xlocs != NULL)
1472199086Srpaulo			reaped_xlocs[*n_reaped] = stroke->components[X].loc;
1473199086Srpaulo
1474199086Srpaulo		/* Erase the stroke from the sc. */
1475199086Srpaulo		memcpy(&stroke[i], &stroke[i + 1],
1476199086Srpaulo		    (sc->sc_n_strokes - i - 1) * sizeof(atp_stroke));
1477199086Srpaulo		sc->sc_n_strokes--;
1478199086Srpaulo
1479199086Srpaulo		*n_reaped += 1;
1480199086Srpaulo		--i; /* Decr. i to keep it unchanged for the next iteration */
1481199086Srpaulo	}
1482199086Srpaulo
1483199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", *n_reaped);
1484199086Srpaulo
1485199086Srpaulo	/* There could still be zombies remaining in the system. */
1486199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1487199086Srpaulo		stroke = &sc->sc_strokes[i];
1488199086Srpaulo		if (stroke->flags & ATSF_ZOMBIE) {
1489199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO, "zombies remain!\n");
1490199086Srpaulo			atp_setup_reap_time(sc, &stroke->ctime);
1491199086Srpaulo			return;
1492199086Srpaulo		}
1493199086Srpaulo	}
1494199086Srpaulo
1495199086Srpaulo	/* If we reach here, then no more zombies remain. */
1496199086Srpaulo	sc->sc_state &= ~ATP_ZOMBIES_EXIST;
1497199086Srpaulo}
1498199086Srpaulo
1499199086Srpaulo
1500199086Srpaulo/* Device methods. */
1501199086Srpaulostatic device_probe_t  atp_probe;
1502199086Srpaulostatic device_attach_t atp_attach;
1503199086Srpaulostatic device_detach_t atp_detach;
1504199086Srpaulostatic usb_callback_t  atp_intr;
1505199086Srpaulo
1506199086Srpaulostatic const struct usb_config atp_config[ATP_N_TRANSFER] = {
1507199086Srpaulo	[ATP_INTR_DT] = {
1508199086Srpaulo		.type      = UE_INTERRUPT,
1509199086Srpaulo		.endpoint  = UE_ADDR_ANY,
1510199086Srpaulo		.direction = UE_DIR_IN,
1511199086Srpaulo		.flags = {
1512199086Srpaulo			.pipe_bof = 1,
1513199086Srpaulo			.short_xfer_ok = 1,
1514199086Srpaulo		},
1515199086Srpaulo		.bufsize   = 0, /* use wMaxPacketSize */
1516199086Srpaulo		.callback  = &atp_intr,
1517199086Srpaulo	},
1518199086Srpaulo};
1519199086Srpaulo
1520199086Srpaulostatic int
1521199086Srpauloatp_probe(device_t self)
1522199086Srpaulo{
1523199086Srpaulo	struct usb_attach_arg *uaa = device_get_ivars(self);
1524199086Srpaulo
1525199086Srpaulo	if (uaa->usb_mode != USB_MODE_HOST)
1526199086Srpaulo		return (ENXIO);
1527199086Srpaulo
1528199086Srpaulo	if ((uaa->info.bInterfaceClass != UICLASS_HID) ||
1529199086Srpaulo	    (uaa->info.bInterfaceProtocol != UIPROTO_MOUSE))
1530199086Srpaulo		return (ENXIO);
1531199086Srpaulo
1532199086Srpaulo	if (usbd_lookup_id_by_uaa(atp_devs, sizeof(atp_devs), uaa) == 0)
1533199086Srpaulo		return BUS_PROBE_SPECIFIC;
1534199086Srpaulo	else
1535199086Srpaulo		return ENXIO;
1536199086Srpaulo}
1537199086Srpaulo
1538199086Srpaulostatic int
1539199086Srpauloatp_attach(device_t dev)
1540199086Srpaulo{
1541199086Srpaulo	struct atp_softc      *sc = device_get_softc(dev);
1542199086Srpaulo	struct usb_attach_arg *uaa = device_get_ivars(dev);
1543199086Srpaulo	usb_error_t            err;
1544199086Srpaulo
1545199086Srpaulo	/* ensure that the probe was successful */
1546199086Srpaulo	if (uaa->driver_info >= ATP_N_DEV_PARAMS) {
1547199086Srpaulo		DPRINTF("device probe returned bad id: %lu\n",
1548199086Srpaulo		    uaa->driver_info);
1549199086Srpaulo		return (ENXIO);
1550199086Srpaulo	}
1551199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc);
1552199086Srpaulo
1553199086Srpaulo	sc->sc_dev        = dev;
1554199086Srpaulo	sc->sc_usb_device = uaa->device;
1555199086Srpaulo
1556199086Srpaulo	/*
1557199086Srpaulo	 * By default the touchpad behaves like an HID device, sending
1558199086Srpaulo	 * packets with reportID = 2. Such reports contain only
1559199086Srpaulo	 * limited information--they encode movement deltas and button
1560199086Srpaulo	 * events,--but do not include data from the pressure
1561199086Srpaulo	 * sensors. The device input mode can be switched from HID
1562199086Srpaulo	 * reports to raw sensor data using vendor-specific USB
1563199086Srpaulo	 * control commands; but first the mode must be read.
1564199086Srpaulo	 */
1565199086Srpaulo	err = atp_req_get_report(sc->sc_usb_device, sc->sc_mode_bytes);
1566199086Srpaulo	if (err != USB_ERR_NORMAL_COMPLETION) {
1567199086Srpaulo		DPRINTF("failed to read device mode (%d)\n", err);
1568199086Srpaulo		return (ENXIO);
1569199086Srpaulo	}
1570199086Srpaulo
1571199086Srpaulo	if (atp_set_device_mode(dev, RAW_SENSOR_MODE) != 0) {
1572199086Srpaulo		DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err);
1573199086Srpaulo		return (ENXIO);
1574199086Srpaulo	}
1575199086Srpaulo
1576199086Srpaulo	mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE);
1577199086Srpaulo
1578199086Srpaulo	err = usbd_transfer_setup(uaa->device,
1579199086Srpaulo	    &uaa->info.bIfaceIndex, sc->sc_xfer, atp_config,
1580199086Srpaulo	    ATP_N_TRANSFER, sc, &sc->sc_mutex);
1581199086Srpaulo
1582199086Srpaulo	if (err) {
1583199086Srpaulo		DPRINTF("error=%s\n", usbd_errstr(err));
1584199086Srpaulo		goto detach;
1585199086Srpaulo	}
1586199086Srpaulo
1587199086Srpaulo	if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex,
1588199086Srpaulo		&atp_fifo_methods, &sc->sc_fifo,
1589199086Srpaulo		device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
1590199086Srpaulo		UID_ROOT, GID_OPERATOR, 0644)) {
1591199086Srpaulo		goto detach;
1592199086Srpaulo	}
1593199086Srpaulo
1594199086Srpaulo	device_set_usb_desc(dev);
1595199086Srpaulo
1596199086Srpaulo	sc->sc_params           = &atp_dev_params[uaa->driver_info];
1597199086Srpaulo
1598199086Srpaulo	sc->sc_hw.buttons       = 3;
1599199086Srpaulo	sc->sc_hw.iftype        = MOUSE_IF_USB;
1600199086Srpaulo	sc->sc_hw.type          = MOUSE_PAD;
1601199086Srpaulo	sc->sc_hw.model         = MOUSE_MODEL_GENERIC;
1602199086Srpaulo	sc->sc_hw.hwid          = 0;
1603199086Srpaulo	sc->sc_mode.protocol    = MOUSE_PROTO_MSC;
1604199086Srpaulo	sc->sc_mode.rate        = -1;
1605199086Srpaulo	sc->sc_mode.resolution  = MOUSE_RES_UNKNOWN;
1606199086Srpaulo	sc->sc_mode.accelfactor = 0;
1607199086Srpaulo	sc->sc_mode.level       = 0;
1608199086Srpaulo	sc->sc_mode.packetsize  = MOUSE_MSC_PACKETSIZE;
1609199086Srpaulo	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
1610199086Srpaulo	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
1611199086Srpaulo
1612199086Srpaulo	sc->sc_state            = 0;
1613199086Srpaulo
1614199086Srpaulo	sc->sc_left_margin  = atp_mickeys_scale_factor;
1615199086Srpaulo	sc->sc_right_margin = (sc->sc_params->n_xsensors - 1) *
1616199086Srpaulo		atp_mickeys_scale_factor;
1617199086Srpaulo
1618199086Srpaulo	return (0);
1619199086Srpaulo
1620199086Srpaulodetach:
1621199086Srpaulo	atp_detach(dev);
1622199086Srpaulo	return (ENOMEM);
1623199086Srpaulo}
1624199086Srpaulo
1625199086Srpaulostatic int
1626199086Srpauloatp_detach(device_t dev)
1627199086Srpaulo{
1628199086Srpaulo	struct atp_softc *sc;
1629199086Srpaulo	int err;
1630199086Srpaulo
1631199086Srpaulo	sc = device_get_softc(dev);
1632199086Srpaulo	if (sc->sc_state & ATP_ENABLED) {
1633199086Srpaulo		mtx_lock(&sc->sc_mutex);
1634199086Srpaulo		atp_disable(sc);
1635199086Srpaulo		mtx_unlock(&sc->sc_mutex);
1636199086Srpaulo	}
1637199086Srpaulo
1638199086Srpaulo	usb_fifo_detach(&sc->sc_fifo);
1639199086Srpaulo
1640199086Srpaulo	usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER);
1641199086Srpaulo
1642199086Srpaulo	mtx_destroy(&sc->sc_mutex);
1643199086Srpaulo
1644199086Srpaulo	err = atp_set_device_mode(dev, HID_MODE);
1645199086Srpaulo	if (err != 0) {
1646199086Srpaulo		DPRINTF("failed to reset mode to 'HID' (%d)\n", err);
1647199086Srpaulo		return (err);
1648199086Srpaulo	}
1649199086Srpaulo
1650199086Srpaulo	return (0);
1651199086Srpaulo}
1652199086Srpaulo
1653199086Srpaulostatic void
1654199086Srpauloatp_intr(struct usb_xfer *xfer, usb_error_t error)
1655199086Srpaulo{
1656199086Srpaulo	struct atp_softc      *sc = usbd_xfer_softc(xfer);
1657199086Srpaulo	int                    len;
1658199086Srpaulo	struct usb_page_cache *pc;
1659199086Srpaulo	uint8_t                status_bits;
1660199086Srpaulo	atp_pspan  pspans_x[ATP_MAX_PSPANS_PER_AXIS];
1661199086Srpaulo	atp_pspan  pspans_y[ATP_MAX_PSPANS_PER_AXIS];
1662199086Srpaulo	u_int      n_xpspans = 0, n_ypspans = 0;
1663199086Srpaulo	u_int      reaped_xlocs[ATP_MAX_STROKES];
1664199086Srpaulo	u_int      tap_fingers = 0;
1665199086Srpaulo
1666199086Srpaulo	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
1667199086Srpaulo
1668199086Srpaulo	switch (USB_GET_STATE(xfer)) {
1669199086Srpaulo	case USB_ST_TRANSFERRED:
1670199086Srpaulo		if (len > sc->sc_params->data_len) {
1671199086Srpaulo			DPRINTFN(ATP_LLEVEL_ERROR,
1672199086Srpaulo			    "truncating large packet from %u to %u bytes\n",
1673199086Srpaulo			    len, sc->sc_params->data_len);
1674199086Srpaulo			len = sc->sc_params->data_len;
1675199086Srpaulo		}
1676199151Snwhitehorn		if (len < sc->sc_params->data_len)
1677199086Srpaulo			goto tr_setup;
1678199086Srpaulo
1679199086Srpaulo		pc = usbd_xfer_get_frame(xfer, 0);
1680199086Srpaulo		usbd_copy_out(pc, 0, sc->sensor_data, sc->sc_params->data_len);
1681199086Srpaulo
1682199086Srpaulo		/* Interpret sensor data */
1683199086Srpaulo		atp_interpret_sensor_data(sc->sensor_data,
1684199151Snwhitehorn		    sc->sc_params->n_xsensors, X, sc->cur_x,
1685199151Snwhitehorn		    sc->sc_params->prot);
1686199086Srpaulo		atp_interpret_sensor_data(sc->sensor_data,
1687199151Snwhitehorn		    sc->sc_params->n_ysensors, Y,  sc->cur_y,
1688199151Snwhitehorn		    sc->sc_params->prot);
1689199086Srpaulo
1690199086Srpaulo		/*
1691199086Srpaulo		 * If this is the initial update (from an untouched
1692199086Srpaulo		 * pad), we should set the base values for the sensor
1693199086Srpaulo		 * data; deltas with respect to these base values can
1694199086Srpaulo		 * be used as pressure readings subsequently.
1695199086Srpaulo		 */
1696199086Srpaulo		status_bits = sc->sensor_data[sc->sc_params->data_len - 1];
1697199151Snwhitehorn		if ((sc->sc_params->prot == ATP_PROT_GEYSER3 &&
1698199151Snwhitehorn		    (status_bits & ATP_STATUS_BASE_UPDATE)) ||
1699199151Snwhitehorn		    !(sc->sc_state & ATP_VALID)) {
1700199086Srpaulo			memcpy(sc->base_x, sc->cur_x,
1701199086Srpaulo			    sc->sc_params->n_xsensors * sizeof(*(sc->base_x)));
1702199086Srpaulo			memcpy(sc->base_y, sc->cur_y,
1703199086Srpaulo			    sc->sc_params->n_ysensors * sizeof(*(sc->base_y)));
1704199151Snwhitehorn			sc->sc_state |= ATP_VALID;
1705199086Srpaulo			goto tr_setup;
1706199086Srpaulo		}
1707199086Srpaulo
1708199086Srpaulo		/* Get pressure readings and detect p-spans for both axes. */
1709199086Srpaulo		atp_get_pressures(sc->pressure_x, sc->cur_x, sc->base_x,
1710199086Srpaulo		    sc->sc_params->n_xsensors);
1711199086Srpaulo		atp_detect_pspans(sc->pressure_x, sc->sc_params->n_xsensors,
1712199086Srpaulo		    ATP_MAX_PSPANS_PER_AXIS,
1713199086Srpaulo		    pspans_x, &n_xpspans);
1714199086Srpaulo		atp_get_pressures(sc->pressure_y, sc->cur_y, sc->base_y,
1715199086Srpaulo		    sc->sc_params->n_ysensors);
1716199086Srpaulo		atp_detect_pspans(sc->pressure_y, sc->sc_params->n_ysensors,
1717199086Srpaulo		    ATP_MAX_PSPANS_PER_AXIS,
1718199086Srpaulo		    pspans_y, &n_ypspans);
1719199086Srpaulo
1720199086Srpaulo		/* Update strokes with new pspans to detect movements. */
1721199086Srpaulo		sc->sc_status.flags &= ~MOUSE_POSCHANGED;
1722199086Srpaulo		if (atp_update_strokes(sc,
1723199086Srpaulo			pspans_x, n_xpspans,
1724199086Srpaulo			pspans_y, n_ypspans))
1725199086Srpaulo			sc->sc_status.flags |= MOUSE_POSCHANGED;
1726199086Srpaulo
1727199086Srpaulo		/* Reap zombies if it is time. */
1728199086Srpaulo		if (sc->sc_state & ATP_ZOMBIES_EXIST) {
1729199086Srpaulo			struct timeval now;
1730199086Srpaulo
1731199086Srpaulo			getmicrotime(&now);
1732199086Srpaulo			if (timevalcmp(&now, &sc->sc_reap_time, >=))
1733199086Srpaulo				atp_reap_zombies(sc, &tap_fingers,
1734199086Srpaulo				    reaped_xlocs);
1735199086Srpaulo		}
1736199086Srpaulo
1737199086Srpaulo		sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED;
1738199086Srpaulo		sc->sc_status.obutton = sc->sc_status.button;
1739199086Srpaulo
1740199086Srpaulo		/* Get the state of the physical buttton. */
1741199086Srpaulo		sc->sc_status.button = (status_bits & ATP_STATUS_BUTTON) ?
1742199086Srpaulo			MOUSE_BUTTON1DOWN : 0;
1743199086Srpaulo		if (sc->sc_status.button != 0) {
1744199086Srpaulo			/* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */
1745199086Srpaulo			sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1746199086Srpaulo		} else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) {
1747199086Srpaulo			/* Assume a button-press with DOUBLE_TAP_N_DRAG. */
1748199086Srpaulo			sc->sc_status.button = MOUSE_BUTTON1DOWN;
1749199086Srpaulo		}
1750199086Srpaulo
1751199086Srpaulo		sc->sc_status.flags |=
1752199086Srpaulo			sc->sc_status.button ^ sc->sc_status.obutton;
1753199086Srpaulo		if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) {
1754199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO, "button %s\n",
1755199086Srpaulo			    ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ?
1756199086Srpaulo				"pressed" : "released"));
1757199086Srpaulo		} else if ((sc->sc_status.obutton == 0) &&
1758199086Srpaulo		    (sc->sc_status.button == 0) &&
1759199086Srpaulo		    (tap_fingers != 0)) {
1760199086Srpaulo			/* Ignore single-finger taps at the edges. */
1761199086Srpaulo			if ((tap_fingers == 1) &&
1762199086Srpaulo			    ((reaped_xlocs[0] <= sc->sc_left_margin) ||
1763199086Srpaulo				(reaped_xlocs[0] > sc->sc_right_margin))) {
1764199086Srpaulo				tap_fingers = 0;
1765199086Srpaulo			}
1766199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO,
1767199086Srpaulo			    "tap_fingers: %u\n", tap_fingers);
1768199086Srpaulo		}
1769199086Srpaulo
1770199086Srpaulo		if (sc->sc_status.flags &
1771199086Srpaulo		    (MOUSE_POSCHANGED | MOUSE_STDBUTTONSCHANGED)) {
1772199086Srpaulo			int   dx, dy;
1773199086Srpaulo			u_int n_movements;
1774199086Srpaulo
1775199086Srpaulo			dx = 0, dy = 0, n_movements = 0;
1776199086Srpaulo			for (u_int i = 0; i < sc->sc_n_strokes; i++) {
1777199086Srpaulo				atp_stroke *stroke = &sc->sc_strokes[i];
1778199086Srpaulo
1779199086Srpaulo				if ((stroke->components[X].movement) ||
1780199086Srpaulo				    (stroke->components[Y].movement)) {
1781199086Srpaulo					dx += stroke->components[X].movement;
1782199086Srpaulo					dy += stroke->components[Y].movement;
1783199086Srpaulo					n_movements++;
1784199086Srpaulo				}
1785199086Srpaulo			}
1786199086Srpaulo			/*
1787199086Srpaulo			 * Disregard movement if multiple
1788199086Srpaulo			 * strokes record motion.
1789199086Srpaulo			 */
1790199086Srpaulo			if (n_movements != 1)
1791199086Srpaulo				dx = 0, dy = 0;
1792199086Srpaulo
1793199086Srpaulo			sc->sc_status.dx += dx;
1794199086Srpaulo			sc->sc_status.dy += dy;
1795199086Srpaulo			atp_add_to_queue(sc, dx, -dy, sc->sc_status.button);
1796199086Srpaulo		}
1797199086Srpaulo
1798199086Srpaulo		if (tap_fingers != 0) {
1799199086Srpaulo			/* Add a pair of events (button-down and button-up). */
1800199086Srpaulo			switch (tap_fingers) {
1801199086Srpaulo			case 1: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON1DOWN);
1802199086Srpaulo				break;
1803199086Srpaulo			case 2: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON2DOWN);
1804199086Srpaulo				break;
1805199086Srpaulo			case 3: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON3DOWN);
1806199086Srpaulo				break;
1807199086Srpaulo			default: break;/* handle taps of only up to 3 fingers */
1808199086Srpaulo			}
1809199086Srpaulo			atp_add_to_queue(sc, 0, 0, 0); /* button release */
1810199086Srpaulo		}
1811199086Srpaulo
1812199086Srpaulo		/*
1813199086Srpaulo		 * The device continues to trigger interrupts at a
1814199086Srpaulo		 * fast rate even after touchpad activity has
1815199086Srpaulo		 * stopped. Upon detecting that the device has
1816199086Srpaulo		 * remained idle beyond a threshold, we reinitialize
1817199086Srpaulo		 * it to silence the interrupts.
1818199086Srpaulo		 */
1819199086Srpaulo		if ((sc->sc_status.flags  == 0) &&
1820199086Srpaulo		    (sc->sc_n_strokes     == 0) &&
1821199086Srpaulo		    (sc->sc_status.button == 0)) {
1822199086Srpaulo			sc->sc_idlecount++;
1823199086Srpaulo			if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) {
1824199086Srpaulo				DPRINTFN(ATP_LLEVEL_INFO, "idle\n");
1825199151Snwhitehorn				sc->sc_idlecount = 0;
1826199151Snwhitehorn
1827199151Snwhitehorn				mtx_unlock(&sc->sc_mutex);
1828199086Srpaulo				atp_set_device_mode(sc->sc_dev,RAW_SENSOR_MODE);
1829199151Snwhitehorn				mtx_lock(&sc->sc_mutex);
1830199086Srpaulo			}
1831199086Srpaulo		} else {
1832199086Srpaulo			sc->sc_idlecount = 0;
1833199086Srpaulo		}
1834199086Srpaulo
1835199086Srpaulo	case USB_ST_SETUP:
1836199086Srpaulo	tr_setup:
1837199086Srpaulo		/* check if we can put more data into the FIFO */
1838199086Srpaulo		if (usb_fifo_put_bytes_max(
1839199086Srpaulo			    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
1840199086Srpaulo			usbd_xfer_set_frame_len(xfer, 0,
1841199151Snwhitehorn			    sc->sc_params->data_len);
1842199086Srpaulo			usbd_transfer_submit(xfer);
1843199086Srpaulo		}
1844199086Srpaulo		break;
1845199086Srpaulo
1846199086Srpaulo	default:                        /* Error */
1847199086Srpaulo		if (error != USB_ERR_CANCELLED) {
1848199086Srpaulo			/* try clear stall first */
1849199086Srpaulo			usbd_xfer_set_stall(xfer);
1850199086Srpaulo			goto tr_setup;
1851199086Srpaulo		}
1852199086Srpaulo		break;
1853199086Srpaulo	}
1854199086Srpaulo
1855199086Srpaulo	return;
1856199086Srpaulo}
1857199086Srpaulo
1858199086Srpaulostatic void
1859199086Srpauloatp_add_to_queue(struct atp_softc *sc, int dx, int dy, uint32_t buttons_in)
1860199086Srpaulo{
1861199086Srpaulo	uint32_t buttons_out;
1862199086Srpaulo	uint8_t  buf[8];
1863199086Srpaulo
1864199086Srpaulo	dx = imin(dx,  254); dx = imax(dx, -256);
1865199086Srpaulo	dy = imin(dy,  254); dy = imax(dy, -256);
1866199086Srpaulo
1867199086Srpaulo	buttons_out = MOUSE_MSC_BUTTONS;
1868199086Srpaulo	if (buttons_in & MOUSE_BUTTON1DOWN)
1869199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON1UP;
1870199086Srpaulo	else if (buttons_in & MOUSE_BUTTON2DOWN)
1871199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON2UP;
1872199086Srpaulo	else if (buttons_in & MOUSE_BUTTON3DOWN)
1873199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON3UP;
1874199086Srpaulo
1875199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n",
1876199086Srpaulo	    dx, dy, buttons_out);
1877199086Srpaulo
1878199086Srpaulo	/* Encode the mouse data in standard format; refer to mouse(4) */
1879199086Srpaulo	buf[0] = sc->sc_mode.syncmask[1];
1880199086Srpaulo	buf[0] |= buttons_out;
1881199086Srpaulo	buf[1] = dx >> 1;
1882199086Srpaulo	buf[2] = dy >> 1;
1883199086Srpaulo	buf[3] = dx - (dx >> 1);
1884199086Srpaulo	buf[4] = dy - (dy >> 1);
1885199086Srpaulo	/* Encode extra bytes for level 1 */
1886199086Srpaulo	if (sc->sc_mode.level == 1) {
1887199086Srpaulo		buf[5] = 0;                    /* dz */
1888199086Srpaulo		buf[6] = 0;                    /* dz - (dz / 2) */
1889199086Srpaulo		buf[7] = MOUSE_SYS_EXTBUTTONS; /* Extra buttons all up. */
1890199086Srpaulo	}
1891199086Srpaulo
1892199086Srpaulo	usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
1893199086Srpaulo	    sc->sc_mode.packetsize, 1);
1894199086Srpaulo}
1895199086Srpaulo
1896199086Srpaulostatic void
1897199086Srpauloatp_reset_buf(struct atp_softc *sc)
1898199086Srpaulo{
1899199086Srpaulo	/* reset read queue */
1900199086Srpaulo	usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
1901199086Srpaulo}
1902199086Srpaulo
1903199086Srpaulostatic void
1904199086Srpauloatp_start_read(struct usb_fifo *fifo)
1905199086Srpaulo{
1906199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
1907199086Srpaulo	int rate;
1908199086Srpaulo
1909199086Srpaulo	/* Check if we should override the default polling interval */
1910199086Srpaulo	rate = sc->sc_pollrate;
1911199086Srpaulo	/* Range check rate */
1912199086Srpaulo	if (rate > 1000)
1913199086Srpaulo		rate = 1000;
1914199086Srpaulo	/* Check for set rate */
1915199086Srpaulo	if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) {
1916199086Srpaulo		/* Stop current transfer, if any */
1917199086Srpaulo		usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
1918199086Srpaulo		/* Set new interval */
1919199086Srpaulo		usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate);
1920199086Srpaulo		/* Only set pollrate once */
1921199086Srpaulo		sc->sc_pollrate = 0;
1922199086Srpaulo	}
1923199086Srpaulo
1924199086Srpaulo	usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]);
1925199086Srpaulo}
1926199086Srpaulo
1927199086Srpaulostatic void
1928199086Srpauloatp_stop_read(struct usb_fifo *fifo)
1929199086Srpaulo{
1930199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
1931199086Srpaulo
1932199086Srpaulo	usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
1933199086Srpaulo}
1934199086Srpaulo
1935199086Srpaulo
1936199086Srpaulostatic int
1937199086Srpauloatp_open(struct usb_fifo *fifo, int fflags)
1938199086Srpaulo{
1939199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "\n");
1940199086Srpaulo
1941199086Srpaulo	if (fflags & FREAD) {
1942199086Srpaulo		struct atp_softc *sc = usb_fifo_softc(fifo);
1943199086Srpaulo		int rc;
1944199086Srpaulo
1945199086Srpaulo		if (sc->sc_state & ATP_ENABLED)
1946199086Srpaulo			return (EBUSY);
1947199086Srpaulo
1948199086Srpaulo		if (usb_fifo_alloc_buffer(fifo,
1949199086Srpaulo			ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) {
1950199086Srpaulo			return (ENOMEM);
1951199086Srpaulo		}
1952199086Srpaulo
1953199086Srpaulo		rc = atp_enable(sc);
1954199086Srpaulo		if (rc != 0) {
1955199086Srpaulo			usb_fifo_free_buffer(fifo);
1956199086Srpaulo			return (rc);
1957199086Srpaulo		}
1958199086Srpaulo	}
1959199086Srpaulo
1960199086Srpaulo	return (0);
1961199086Srpaulo}
1962199086Srpaulo
1963199086Srpaulostatic void
1964199086Srpauloatp_close(struct usb_fifo *fifo, int fflags)
1965199086Srpaulo{
1966199086Srpaulo	if (fflags & FREAD) {
1967199086Srpaulo		struct atp_softc *sc = usb_fifo_softc(fifo);
1968199086Srpaulo
1969199086Srpaulo		atp_disable(sc);
1970199086Srpaulo		usb_fifo_free_buffer(fifo);
1971199086Srpaulo	}
1972199086Srpaulo}
1973199086Srpaulo
1974199086Srpauloint
1975199086Srpauloatp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
1976199086Srpaulo{
1977199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
1978199086Srpaulo	mousemode_t mode;
1979199086Srpaulo	int error = 0;
1980199086Srpaulo
1981199086Srpaulo	mtx_lock(&sc->sc_mutex);
1982199086Srpaulo
1983199086Srpaulo	switch(cmd) {
1984199086Srpaulo	case MOUSE_GETHWINFO:
1985199086Srpaulo		*(mousehw_t *)addr = sc->sc_hw;
1986199086Srpaulo		break;
1987199086Srpaulo	case MOUSE_GETMODE:
1988199086Srpaulo		*(mousemode_t *)addr = sc->sc_mode;
1989199086Srpaulo		break;
1990199086Srpaulo	case MOUSE_SETMODE:
1991199086Srpaulo		mode = *(mousemode_t *)addr;
1992199086Srpaulo
1993199086Srpaulo		if (mode.level == -1)
1994199086Srpaulo			/* Don't change the current setting */
1995199086Srpaulo			;
1996199086Srpaulo		else if ((mode.level < 0) || (mode.level > 1)) {
1997199086Srpaulo			error = EINVAL;
1998199086Srpaulo			goto done;
1999199086Srpaulo		}
2000199086Srpaulo		sc->sc_mode.level = mode.level;
2001199086Srpaulo		sc->sc_pollrate   = mode.rate;
2002199086Srpaulo		sc->sc_hw.buttons = 3;
2003199086Srpaulo
2004199086Srpaulo		if (sc->sc_mode.level == 0) {
2005199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
2006199086Srpaulo			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
2007199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
2008199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
2009199086Srpaulo		} else if (sc->sc_mode.level == 1) {
2010199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
2011199086Srpaulo			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
2012199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
2013199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
2014199086Srpaulo		}
2015199086Srpaulo		atp_reset_buf(sc);
2016199086Srpaulo		break;
2017199086Srpaulo	case MOUSE_GETLEVEL:
2018199086Srpaulo		*(int *)addr = sc->sc_mode.level;
2019199086Srpaulo		break;
2020199086Srpaulo	case MOUSE_SETLEVEL:
2021199086Srpaulo		if (*(int *)addr < 0 || *(int *)addr > 1) {
2022199086Srpaulo			error = EINVAL;
2023199086Srpaulo			goto done;
2024199086Srpaulo		}
2025199086Srpaulo		sc->sc_mode.level = *(int *)addr;
2026199086Srpaulo		sc->sc_hw.buttons = 3;
2027199086Srpaulo
2028199086Srpaulo		if (sc->sc_mode.level == 0) {
2029199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
2030199086Srpaulo			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
2031199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
2032199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
2033199086Srpaulo		} else if (sc->sc_mode.level == 1) {
2034199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
2035199086Srpaulo			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
2036199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
2037199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
2038199086Srpaulo		}
2039199086Srpaulo		atp_reset_buf(sc);
2040199086Srpaulo		break;
2041199086Srpaulo	case MOUSE_GETSTATUS: {
2042199086Srpaulo		mousestatus_t *status = (mousestatus_t *)addr;
2043199086Srpaulo
2044199086Srpaulo		*status = sc->sc_status;
2045199086Srpaulo		sc->sc_status.obutton = sc->sc_status.button;
2046199086Srpaulo		sc->sc_status.button  = 0;
2047199086Srpaulo		sc->sc_status.dx = 0;
2048199086Srpaulo		sc->sc_status.dy = 0;
2049199086Srpaulo		sc->sc_status.dz = 0;
2050199086Srpaulo
2051199086Srpaulo		if (status->dx || status->dy || status->dz)
2052199086Srpaulo			status->flags |= MOUSE_POSCHANGED;
2053199086Srpaulo		if (status->button != status->obutton)
2054199086Srpaulo			status->flags |= MOUSE_BUTTONSCHANGED;
2055199086Srpaulo		break;
2056199086Srpaulo	}
2057199086Srpaulo	default:
2058199086Srpaulo		error = ENOTTY;
2059199086Srpaulo	}
2060199086Srpaulo
2061199086Srpaulodone:
2062199086Srpaulo	mtx_unlock(&sc->sc_mutex);
2063199086Srpaulo	return (error);
2064199086Srpaulo}
2065199086Srpaulo
2066199086Srpaulostatic int
2067199086Srpauloatp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS)
2068199086Srpaulo{
2069199086Srpaulo	int error;
2070199086Srpaulo	u_int tmp;
2071199086Srpaulo	u_int prev_mickeys_scale_factor;
2072199086Srpaulo
2073199086Srpaulo	prev_mickeys_scale_factor = atp_mickeys_scale_factor;
2074199086Srpaulo
2075199086Srpaulo	tmp = atp_mickeys_scale_factor;
2076199086Srpaulo	error = sysctl_handle_int(oidp, &tmp, 0, req);
2077199086Srpaulo	if (error != 0 || req->newptr == NULL)
2078199086Srpaulo		return (error);
2079199086Srpaulo
2080199086Srpaulo	if (tmp == prev_mickeys_scale_factor)
2081199086Srpaulo		return (0);     /* no change */
2082199086Srpaulo
2083199086Srpaulo	atp_mickeys_scale_factor = tmp;
2084199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n",
2085199086Srpaulo	    ATP_DRIVER_NAME, tmp);
2086199086Srpaulo
2087199086Srpaulo	/* Update dependent thresholds. */
2088199086Srpaulo	if (atp_small_movement_threshold == (prev_mickeys_scale_factor >> 3))
2089199086Srpaulo		atp_small_movement_threshold = atp_mickeys_scale_factor >> 3;
2090199086Srpaulo	if (atp_max_delta_mickeys == ((3 * prev_mickeys_scale_factor) >> 1))
2091199086Srpaulo		atp_max_delta_mickeys = ((3 * atp_mickeys_scale_factor) >>1);
2092199086Srpaulo	if (atp_slide_min_movement == (prev_mickeys_scale_factor >> 3))
2093199086Srpaulo		atp_slide_min_movement = atp_mickeys_scale_factor >> 3;
2094199086Srpaulo
2095199086Srpaulo	return (0);
2096199086Srpaulo}
2097199086Srpaulo
2098199086Srpaulostatic device_method_t atp_methods[] = {
2099199086Srpaulo	/* Device interface */
2100199086Srpaulo	DEVMETHOD(device_probe,  atp_probe),
2101199086Srpaulo	DEVMETHOD(device_attach, atp_attach),
2102199086Srpaulo	DEVMETHOD(device_detach, atp_detach),
2103199086Srpaulo	{ 0, 0 }
2104199086Srpaulo};
2105199086Srpaulo
2106199086Srpaulostatic driver_t atp_driver = {
2107199086Srpaulo	ATP_DRIVER_NAME,
2108199086Srpaulo	atp_methods,
2109199086Srpaulo	sizeof(struct atp_softc)
2110199086Srpaulo};
2111199086Srpaulo
2112199086Srpaulostatic devclass_t atp_devclass;
2113199086Srpaulo
2114199086SrpauloDRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0);
2115199086SrpauloMODULE_DEPEND(atp, usb, 1, 1, 1);
2116