atp.c revision 200241
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 200241 2009-12-08 00:52:59Z rpaulo $");
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,
344199680Sthompsa	ATP_RESET,
345199086Srpaulo	ATP_N_TRANSFER,
346199086Srpaulo};
347199086Srpaulo
348199086Srpaulostruct atp_softc {
349199086Srpaulo	device_t               sc_dev;
350199086Srpaulo	struct usb_device     *sc_usb_device;
351199086Srpaulo#define MODE_LENGTH 8
352199086Srpaulo	char                   sc_mode_bytes[MODE_LENGTH]; /* device mode */
353199086Srpaulo	struct mtx             sc_mutex; /* for synchronization */
354199086Srpaulo	struct usb_xfer       *sc_xfer[ATP_N_TRANSFER];
355199086Srpaulo	struct usb_fifo_sc     sc_fifo;
356199086Srpaulo
357199086Srpaulo	struct atp_dev_params *sc_params;
358199086Srpaulo
359199086Srpaulo	mousehw_t              sc_hw;
360199086Srpaulo	mousemode_t            sc_mode;
361199086Srpaulo	u_int                  sc_pollrate;
362199086Srpaulo	mousestatus_t          sc_status;
363199086Srpaulo	u_int                  sc_state;
364199086Srpaulo#define ATP_ENABLED            0x01
365199086Srpaulo#define ATP_ZOMBIES_EXIST      0x02
366199086Srpaulo#define ATP_DOUBLE_TAP_DRAG    0x04
367199151Snwhitehorn#define ATP_VALID              0x08
368199086Srpaulo
369199086Srpaulo	u_int                  sc_left_margin;
370199086Srpaulo	u_int                  sc_right_margin;
371199086Srpaulo
372199086Srpaulo	atp_stroke             sc_strokes[ATP_MAX_STROKES];
373199086Srpaulo	u_int                  sc_n_strokes;
374199086Srpaulo
375199086Srpaulo	int8_t                *sensor_data; /* from interrupt packet */
376199086Srpaulo	int                   *base_x;      /* base sensor readings */
377199086Srpaulo	int                   *base_y;
378199086Srpaulo	int                   *cur_x;       /* current sensor readings */
379199086Srpaulo	int                   *cur_y;
380199086Srpaulo	int                   *pressure_x;  /* computed pressures */
381199086Srpaulo	int                   *pressure_y;
382199086Srpaulo
383199086Srpaulo	u_int                  sc_idlecount; /* preceding idle interrupts */
384199086Srpaulo#define ATP_IDLENESS_THRESHOLD 10
385199086Srpaulo
386199086Srpaulo	struct timeval         sc_reap_time;
387199086Srpaulo	struct timeval         sc_reap_ctime; /*ctime of siblings to be reaped*/
388199086Srpaulo};
389199086Srpaulo
390199086Srpaulo/*
391199086Srpaulo * The last byte of the sensor data contains status bits; the
392199086Srpaulo * following values define the meanings of these bits.
393199086Srpaulo */
394199086Srpauloenum atp_status_bits {
395199086Srpaulo	ATP_STATUS_BUTTON      = (uint8_t)0x01, /* The button was pressed */
396199086Srpaulo	ATP_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/
397199086Srpaulo};
398199086Srpaulo
399199086Srpaulotypedef enum interface_mode {
400199086Srpaulo	RAW_SENSOR_MODE = (uint8_t)0x04,
401199086Srpaulo	HID_MODE        = (uint8_t)0x08
402199086Srpaulo} interface_mode;
403199086Srpaulo
404199086Srpaulo/*
405199086Srpaulo * function prototypes
406199086Srpaulo */
407199086Srpaulostatic usb_fifo_cmd_t   atp_start_read;
408199086Srpaulostatic usb_fifo_cmd_t   atp_stop_read;
409199086Srpaulostatic usb_fifo_open_t  atp_open;
410199086Srpaulostatic usb_fifo_close_t atp_close;
411199086Srpaulostatic usb_fifo_ioctl_t atp_ioctl;
412199086Srpaulo
413199086Srpaulostatic struct usb_fifo_methods atp_fifo_methods = {
414199086Srpaulo	.f_open       = &atp_open,
415199086Srpaulo	.f_close      = &atp_close,
416199086Srpaulo	.f_ioctl      = &atp_ioctl,
417199086Srpaulo	.f_start_read = &atp_start_read,
418199086Srpaulo	.f_stop_read  = &atp_stop_read,
419199086Srpaulo	.basename[0]  = ATP_DRIVER_NAME,
420199086Srpaulo};
421199086Srpaulo
422199086Srpaulo/* device initialization and shutdown */
423199086Srpaulostatic usb_error_t   atp_req_get_report(struct usb_device *udev, void *data);
424199086Srpaulostatic int           atp_set_device_mode(device_t dev, interface_mode mode);
425199680Sthompsastatic void          atp_reset_callback(struct usb_xfer *, usb_error_t);
426199086Srpaulostatic int           atp_enable(struct atp_softc *sc);
427199086Srpaulostatic void          atp_disable(struct atp_softc *sc);
428199086Srpaulostatic int           atp_softc_populate(struct atp_softc *);
429199086Srpaulostatic void          atp_softc_unpopulate(struct atp_softc *);
430199086Srpaulo
431199086Srpaulo/* sensor interpretation */
432199151Snwhitehornstatic __inline void atp_interpret_sensor_data(const int8_t *, u_int, atp_axis,
433199151Snwhitehorn			 int *, atp_protocol);
434199086Srpaulostatic __inline void atp_get_pressures(int *, const int *, const int *, int);
435199086Srpaulostatic void          atp_detect_pspans(int *, u_int, u_int, atp_pspan *,
436199086Srpaulo			 u_int *);
437199086Srpaulo
438199086Srpaulo/* movement detection */
439199086Srpaulostatic boolean_t     atp_match_stroke_component(atp_stroke_component *,
440200241Srpaulo                         const atp_pspan *, atp_stroke_type);
441199086Srpaulostatic void          atp_match_strokes_against_pspans(struct atp_softc *,
442199086Srpaulo			 atp_axis, atp_pspan *, u_int, u_int);
443199086Srpaulostatic boolean_t     atp_update_strokes(struct atp_softc *,
444199086Srpaulo			 atp_pspan *, u_int, atp_pspan *, u_int);
445199086Srpaulostatic __inline void atp_add_stroke(struct atp_softc *, const atp_pspan *,
446199086Srpaulo			 const atp_pspan *);
447199086Srpaulostatic void          atp_add_new_strokes(struct atp_softc *, atp_pspan *,
448199086Srpaulo			 u_int, atp_pspan *, u_int);
449199086Srpaulostatic void          atp_advance_stroke_state(struct atp_softc *,
450199086Srpaulo			 atp_stroke *, boolean_t *);
451199086Srpaulostatic void          atp_terminate_stroke(struct atp_softc *, u_int);
452199086Srpaulostatic __inline boolean_t atp_stroke_has_small_movement(const atp_stroke *);
453199086Srpaulostatic __inline void atp_update_pending_mickeys(atp_stroke_component *);
454199086Srpaulostatic void          atp_compute_smoothening_scale_ratio(atp_stroke *, int *,
455199086Srpaulo			 int *);
456199086Srpaulostatic boolean_t     atp_compute_stroke_movement(atp_stroke *);
457199086Srpaulo
458199086Srpaulo/* tap detection */
459199086Srpaulostatic __inline void atp_setup_reap_time(struct atp_softc *, struct timeval *);
460199086Srpaulostatic void          atp_reap_zombies(struct atp_softc *, u_int *, u_int *);
461200241Srpaulostatic void          atp_convert_to_slide(struct atp_softc *, atp_stroke *);
462199086Srpaulo
463199086Srpaulo/* updating fifo */
464199086Srpaulostatic void          atp_reset_buf(struct atp_softc *sc);
465199086Srpaulostatic void          atp_add_to_queue(struct atp_softc *, int, int, uint32_t);
466199086Srpaulo
467199086Srpaulo
468199086Srpaulousb_error_t
469199086Srpauloatp_req_get_report(struct usb_device *udev, void *data)
470199086Srpaulo{
471199086Srpaulo	struct usb_device_request req;
472199086Srpaulo
473199086Srpaulo	req.bmRequestType = UT_READ_CLASS_INTERFACE;
474199086Srpaulo	req.bRequest = UR_GET_REPORT;
475199086Srpaulo	USETW2(req.wValue, (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
476199086Srpaulo	USETW(req.wIndex, 0);
477199086Srpaulo	USETW(req.wLength, MODE_LENGTH);
478199086Srpaulo
479199086Srpaulo	return (usbd_do_request(udev, NULL /* mutex */, &req, data));
480199086Srpaulo}
481199086Srpaulo
482199086Srpaulostatic int
483199086Srpauloatp_set_device_mode(device_t dev, interface_mode mode)
484199086Srpaulo{
485199086Srpaulo	struct atp_softc     *sc;
486199086Srpaulo	usb_device_request_t  req;
487199086Srpaulo	usb_error_t           err;
488199086Srpaulo
489199086Srpaulo	if ((mode != RAW_SENSOR_MODE) && (mode != HID_MODE))
490199086Srpaulo		return (ENXIO);
491199086Srpaulo
492199086Srpaulo	sc = device_get_softc(dev);
493199086Srpaulo
494199086Srpaulo	sc->sc_mode_bytes[0] = mode;
495199086Srpaulo	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
496199086Srpaulo	req.bRequest = UR_SET_REPORT;
497199086Srpaulo	USETW2(req.wValue, (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
498199086Srpaulo	USETW(req.wIndex, 0);
499199086Srpaulo	USETW(req.wLength, MODE_LENGTH);
500199086Srpaulo	err = usbd_do_request(sc->sc_usb_device, NULL, &req, sc->sc_mode_bytes);
501199086Srpaulo	if (err != USB_ERR_NORMAL_COMPLETION)
502199086Srpaulo		return (ENXIO);
503199086Srpaulo
504199086Srpaulo	return (0);
505199086Srpaulo}
506199086Srpaulo
507199680Sthompsavoid
508199680Sthompsaatp_reset_callback(struct usb_xfer *xfer, usb_error_t error)
509199680Sthompsa{
510199680Sthompsa	usb_device_request_t   req;
511199680Sthompsa	struct usb_page_cache *pc;
512199680Sthompsa	struct atp_softc      *sc = usbd_xfer_softc(xfer);
513199680Sthompsa
514199680Sthompsa	switch (USB_GET_STATE(xfer)) {
515199680Sthompsa	case USB_ST_SETUP:
516199680Sthompsa		sc->sc_mode_bytes[0] = RAW_SENSOR_MODE;
517199680Sthompsa		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
518199680Sthompsa		req.bRequest = UR_SET_REPORT;
519199680Sthompsa		USETW2(req.wValue,
520199680Sthompsa		    (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */);
521199680Sthompsa		USETW(req.wIndex, 0);
522199680Sthompsa		USETW(req.wLength, MODE_LENGTH);
523199680Sthompsa
524199680Sthompsa		pc = usbd_xfer_get_frame(xfer, 0);
525199680Sthompsa		usbd_copy_in(pc, 0, &req, sizeof(req));
526199680Sthompsa		pc = usbd_xfer_get_frame(xfer, 1);
527199680Sthompsa		usbd_copy_in(pc, 0, sc->sc_mode_bytes, MODE_LENGTH);
528199680Sthompsa
529199680Sthompsa		usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
530199680Sthompsa		usbd_xfer_set_frame_len(xfer, 1, MODE_LENGTH);
531199680Sthompsa		usbd_xfer_set_frames(xfer, 2);
532199680Sthompsa		usbd_transfer_submit(xfer);
533199680Sthompsa		break;
534199680Sthompsa
535199680Sthompsa	case USB_ST_TRANSFERRED:
536199680Sthompsa	default:
537199680Sthompsa		break;
538199680Sthompsa	}
539199680Sthompsa}
540199680Sthompsa
541199086Srpaulostatic int
542199086Srpauloatp_enable(struct atp_softc *sc)
543199086Srpaulo{
544199086Srpaulo	/* Allocate the dynamic buffers */
545199086Srpaulo	if (atp_softc_populate(sc) != 0) {
546199086Srpaulo		atp_softc_unpopulate(sc);
547199086Srpaulo		return (ENOMEM);
548199086Srpaulo	}
549199086Srpaulo
550199086Srpaulo	/* reset status */
551199086Srpaulo	memset(sc->sc_strokes, 0, sizeof(sc->sc_strokes));
552199086Srpaulo	sc->sc_n_strokes = 0;
553199086Srpaulo	memset(&sc->sc_status, 0, sizeof(sc->sc_status));
554199086Srpaulo	sc->sc_idlecount = 0;
555199086Srpaulo	sc->sc_state |= ATP_ENABLED;
556199086Srpaulo
557199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n");
558199086Srpaulo	return (0);
559199086Srpaulo}
560199086Srpaulo
561199086Srpaulostatic void
562199086Srpauloatp_disable(struct atp_softc *sc)
563199086Srpaulo{
564199086Srpaulo	atp_softc_unpopulate(sc);
565199086Srpaulo
566199151Snwhitehorn	sc->sc_state &= ~(ATP_ENABLED | ATP_VALID);
567199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n");
568199086Srpaulo}
569199086Srpaulo
570199086Srpaulo/* Allocate dynamic memory for some fields in softc. */
571199086Srpaulostatic int
572199086Srpauloatp_softc_populate(struct atp_softc *sc)
573199086Srpaulo{
574199086Srpaulo	const struct atp_dev_params *params = sc->sc_params;
575199086Srpaulo
576199086Srpaulo	if (params == NULL) {
577199086Srpaulo		DPRINTF("params uninitialized!\n");
578199086Srpaulo		return (ENXIO);
579199086Srpaulo	}
580199086Srpaulo	if (params->data_len) {
581199086Srpaulo		sc->sensor_data = malloc(params->data_len * sizeof(int8_t),
582199086Srpaulo		    M_USB, M_WAITOK);
583199086Srpaulo		if (sc->sensor_data == NULL) {
584199086Srpaulo			DPRINTF("mem for sensor_data\n");
585199086Srpaulo			return (ENXIO);
586199086Srpaulo		}
587199086Srpaulo	}
588199086Srpaulo
589199086Srpaulo	if (params->n_xsensors != 0) {
590199086Srpaulo		sc->base_x = malloc(params->n_xsensors * sizeof(*(sc->base_x)),
591199086Srpaulo		    M_USB, M_WAITOK);
592199086Srpaulo		if (sc->base_x == NULL) {
593199086Srpaulo			DPRINTF("mem for sc->base_x\n");
594199086Srpaulo			return (ENXIO);
595199086Srpaulo		}
596199086Srpaulo
597199086Srpaulo		sc->cur_x = malloc(params->n_xsensors * sizeof(*(sc->cur_x)),
598199086Srpaulo		    M_USB, M_WAITOK);
599199086Srpaulo		if (sc->cur_x == NULL) {
600199086Srpaulo			DPRINTF("mem for sc->cur_x\n");
601199086Srpaulo			return (ENXIO);
602199086Srpaulo		}
603199086Srpaulo
604199086Srpaulo		sc->pressure_x =
605199086Srpaulo			malloc(params->n_xsensors * sizeof(*(sc->pressure_x)),
606199086Srpaulo			    M_USB, M_WAITOK);
607199086Srpaulo		if (sc->pressure_x == NULL) {
608199086Srpaulo			DPRINTF("mem. for pressure_x\n");
609199086Srpaulo			return (ENXIO);
610199086Srpaulo		}
611199086Srpaulo	}
612199086Srpaulo
613199086Srpaulo	if (params->n_ysensors != 0) {
614199086Srpaulo		sc->base_y = malloc(params->n_ysensors * sizeof(*(sc->base_y)),
615199086Srpaulo		    M_USB, M_WAITOK);
616199086Srpaulo		if (sc->base_y == NULL) {
617199086Srpaulo			DPRINTF("mem for base_y\n");
618199086Srpaulo			return (ENXIO);
619199086Srpaulo		}
620199086Srpaulo
621199086Srpaulo		sc->cur_y = malloc(params->n_ysensors * sizeof(*(sc->cur_y)),
622199086Srpaulo		    M_USB, M_WAITOK);
623199086Srpaulo		if (sc->cur_y == NULL) {
624199086Srpaulo			DPRINTF("mem for cur_y\n");
625199086Srpaulo			return (ENXIO);
626199086Srpaulo		}
627199086Srpaulo
628199086Srpaulo		sc->pressure_y =
629199086Srpaulo			malloc(params->n_ysensors * sizeof(*(sc->pressure_y)),
630199086Srpaulo			    M_USB, M_WAITOK);
631199086Srpaulo		if (sc->pressure_y == NULL) {
632199086Srpaulo			DPRINTF("mem. for pressure_y\n");
633199086Srpaulo			return (ENXIO);
634199086Srpaulo		}
635199086Srpaulo	}
636199086Srpaulo
637199086Srpaulo	return (0);
638199086Srpaulo}
639199086Srpaulo
640199086Srpaulo/* Free dynamic memory allocated for some fields in softc. */
641199086Srpaulostatic void
642199086Srpauloatp_softc_unpopulate(struct atp_softc *sc)
643199086Srpaulo{
644199086Srpaulo	const struct atp_dev_params *params = sc->sc_params;
645199086Srpaulo
646199086Srpaulo	if (params == NULL) {
647199086Srpaulo		return;
648199086Srpaulo	}
649199086Srpaulo	if (params->n_xsensors != 0) {
650199086Srpaulo		if (sc->base_x != NULL) {
651199086Srpaulo			free(sc->base_x, M_USB);
652199086Srpaulo			sc->base_x = NULL;
653199086Srpaulo		}
654199086Srpaulo
655199086Srpaulo		if (sc->cur_x != NULL) {
656199086Srpaulo			free(sc->cur_x, M_USB);
657199086Srpaulo			sc->cur_x = NULL;
658199086Srpaulo		}
659199086Srpaulo
660199086Srpaulo		if (sc->pressure_x != NULL) {
661199086Srpaulo			free(sc->pressure_x, M_USB);
662199086Srpaulo			sc->pressure_x = NULL;
663199086Srpaulo		}
664199086Srpaulo	}
665199086Srpaulo	if (params->n_ysensors != 0) {
666199086Srpaulo		if (sc->base_y != NULL) {
667199086Srpaulo			free(sc->base_y, M_USB);
668199086Srpaulo			sc->base_y = NULL;
669199086Srpaulo		}
670199086Srpaulo
671199086Srpaulo		if (sc->cur_y != NULL) {
672199086Srpaulo			free(sc->cur_y, M_USB);
673199086Srpaulo			sc->cur_y = NULL;
674199086Srpaulo		}
675199086Srpaulo
676199086Srpaulo		if (sc->pressure_y != NULL) {
677199086Srpaulo			free(sc->pressure_y, M_USB);
678199086Srpaulo			sc->pressure_y = NULL;
679199086Srpaulo		}
680199086Srpaulo	}
681199086Srpaulo	if (sc->sensor_data != NULL) {
682199086Srpaulo		free(sc->sensor_data, M_USB);
683199086Srpaulo		sc->sensor_data = NULL;
684199086Srpaulo	}
685199086Srpaulo}
686199086Srpaulo
687199086Srpaulo/*
688199086Srpaulo * Interpret the data from the X and Y pressure sensors. This function
689199086Srpaulo * is called separately for the X and Y sensor arrays. The data in the
690199086Srpaulo * USB packet is laid out in the following manner:
691199086Srpaulo *
692199086Srpaulo * sensor_data:
693199086Srpaulo *            --,--,Y1,Y2,--,Y3,Y4,--,Y5,...,Y10, ... X1,X2,--,X3,X4
694199086Srpaulo *  indices:   0  1  2  3  4  5  6  7  8 ...  15  ... 20 21 22 23 24
695199086Srpaulo *
696199086Srpaulo * '--' (in the above) indicates that the value is unimportant.
697199086Srpaulo *
698199086Srpaulo * Information about the above layout was obtained from the
699199086Srpaulo * implementation of the AppleTouch driver in Linux.
700199086Srpaulo *
701199086Srpaulo * parameters:
702199086Srpaulo *   sensor_data
703199086Srpaulo *       raw sensor data from the USB packet.
704199086Srpaulo *   num
705199086Srpaulo *       The number of elements in the array 'arr'.
706199151Snwhitehorn *   axis
707199151Snwhitehorn *       Axis of data to fetch
708199086Srpaulo *   arr
709199086Srpaulo *       The array to be initialized with the readings.
710199151Snwhitehorn *   prot
711199151Snwhitehorn *       The protocol to use to interpret the data
712199086Srpaulo */
713199086Srpaulostatic __inline void
714199151Snwhitehornatp_interpret_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis,
715199151Snwhitehorn    int	*arr, atp_protocol prot)
716199086Srpaulo{
717199086Srpaulo	u_int i;
718199086Srpaulo	u_int di;   /* index into sensor data */
719199086Srpaulo
720199151Snwhitehorn	switch (prot) {
721199151Snwhitehorn	case ATP_PROT_GEYSER1:
722199151Snwhitehorn		/*
723199151Snwhitehorn		 * For Geyser 1, the sensors are laid out in pairs
724199151Snwhitehorn		 * every 5 bytes.
725199151Snwhitehorn		 */
726199151Snwhitehorn		for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) {
727199151Snwhitehorn			arr[i] = sensor_data[di];
728199151Snwhitehorn			arr[i+8] = sensor_data[di+2];
729200241Srpaulo			if (axis == X && num > 16)
730199151Snwhitehorn				arr[i+16] = sensor_data[di+40];
731199151Snwhitehorn		}
732199151Snwhitehorn
733199151Snwhitehorn		break;
734199151Snwhitehorn	case ATP_PROT_GEYSER2:
735199151Snwhitehorn	case ATP_PROT_GEYSER3:
736199151Snwhitehorn		for (i = 0, di = (axis == Y) ? 2 : 20; i < num; /* empty */ ) {
737199151Snwhitehorn			arr[i++] = sensor_data[di++];
738199151Snwhitehorn			arr[i++] = sensor_data[di++];
739199151Snwhitehorn			di++;
740199151Snwhitehorn		}
741199151Snwhitehorn		break;
742199086Srpaulo	}
743199086Srpaulo}
744199086Srpaulo
745199086Srpaulostatic __inline void
746199086Srpauloatp_get_pressures(int *p, const int *cur, const int *base, int n)
747199086Srpaulo{
748199086Srpaulo	int i;
749199086Srpaulo
750199086Srpaulo	for (i = 0; i < n; i++) {
751199086Srpaulo		p[i] = cur[i] - base[i];
752199086Srpaulo		if (p[i] > 127)
753199086Srpaulo			p[i] -= 256;
754199086Srpaulo		if (p[i] < -127)
755199086Srpaulo			p[i] += 256;
756199086Srpaulo		if (p[i] < 0)
757199086Srpaulo			p[i] = 0;
758199086Srpaulo
759199086Srpaulo		/*
760199086Srpaulo		 * Shave off pressures below the noise-pressure
761199086Srpaulo		 * threshold; this will reduce the contribution from
762199086Srpaulo		 * lower pressure readings.
763199086Srpaulo		 */
764199086Srpaulo		if (p[i] <= atp_sensor_noise_threshold)
765199086Srpaulo			p[i] = 0; /* filter away noise */
766199086Srpaulo		else
767199086Srpaulo			p[i] -= atp_sensor_noise_threshold;
768199086Srpaulo	}
769199086Srpaulo}
770199086Srpaulo
771199086Srpaulostatic void
772199086Srpauloatp_detect_pspans(int *p, u_int num_sensors,
773199086Srpaulo    u_int       max_spans, /* max # of pspans permitted */
774199086Srpaulo    atp_pspan  *spans,     /* finger spans */
775199086Srpaulo    u_int      *nspans_p)  /* num spans detected */
776199086Srpaulo{
777199086Srpaulo	u_int i;
778199086Srpaulo	int   maxp;             /* max pressure seen within a span */
779199086Srpaulo	u_int num_spans = 0;
780199086Srpaulo
781199086Srpaulo	enum atp_pspan_state {
782199086Srpaulo		ATP_PSPAN_INACTIVE,
783199086Srpaulo		ATP_PSPAN_INCREASING,
784199086Srpaulo		ATP_PSPAN_DECREASING,
785199086Srpaulo	} state; /* state of the pressure span */
786199086Srpaulo
787199086Srpaulo	/*
788199086Srpaulo	 * The following is a simple state machine to track
789199086Srpaulo	 * the phase of the pressure span.
790199086Srpaulo	 */
791199086Srpaulo	memset(spans, 0, max_spans * sizeof(atp_pspan));
792199086Srpaulo	maxp = 0;
793199086Srpaulo	state = ATP_PSPAN_INACTIVE;
794199086Srpaulo	for (i = 0; i < num_sensors; i++) {
795199086Srpaulo		if (num_spans >= max_spans)
796199086Srpaulo			break;
797199086Srpaulo
798199086Srpaulo		if (p[i] == 0) {
799199086Srpaulo			if (state == ATP_PSPAN_INACTIVE) {
800199086Srpaulo				/*
801199086Srpaulo				 * There is no pressure information for this
802199086Srpaulo				 * sensor, and we aren't tracking a finger.
803199086Srpaulo				 */
804199086Srpaulo				continue;
805199086Srpaulo			} else {
806199086Srpaulo				state = ATP_PSPAN_INACTIVE;
807199086Srpaulo				maxp = 0;
808199086Srpaulo				num_spans++;
809199086Srpaulo			}
810199086Srpaulo		} else {
811199086Srpaulo			switch (state) {
812199086Srpaulo			case ATP_PSPAN_INACTIVE:
813199086Srpaulo				state = ATP_PSPAN_INCREASING;
814199086Srpaulo				maxp  = p[i];
815199086Srpaulo				break;
816199086Srpaulo
817199086Srpaulo			case ATP_PSPAN_INCREASING:
818199086Srpaulo				if (p[i] > maxp)
819199086Srpaulo					maxp = p[i];
820199086Srpaulo				else if (p[i] <= (maxp >> 1))
821199086Srpaulo					state = ATP_PSPAN_DECREASING;
822199086Srpaulo				break;
823199086Srpaulo
824199086Srpaulo			case ATP_PSPAN_DECREASING:
825199086Srpaulo				if (p[i] > p[i - 1]) {
826199086Srpaulo					/*
827199086Srpaulo					 * This is the beginning of
828199086Srpaulo					 * another span; change state
829199086Srpaulo					 * to give the appearance that
830199086Srpaulo					 * we're starting from an
831199086Srpaulo					 * inactive span, and then
832199086Srpaulo					 * re-process this reading in
833199086Srpaulo					 * the next iteration.
834199086Srpaulo					 */
835199086Srpaulo					num_spans++;
836199086Srpaulo					state = ATP_PSPAN_INACTIVE;
837199086Srpaulo					maxp  = 0;
838199086Srpaulo					i--;
839199086Srpaulo					continue;
840199086Srpaulo				}
841199086Srpaulo				break;
842199086Srpaulo			}
843199086Srpaulo
844199086Srpaulo			/* Update the finger span with this reading. */
845199086Srpaulo			spans[num_spans].width++;
846199086Srpaulo			spans[num_spans].cum += p[i];
847199086Srpaulo			spans[num_spans].cog += p[i] * (i + 1);
848199086Srpaulo		}
849199086Srpaulo	}
850199086Srpaulo	if (state != ATP_PSPAN_INACTIVE)
851199086Srpaulo		num_spans++;    /* close the last finger span */
852199086Srpaulo
853199086Srpaulo	/* post-process the spans */
854199086Srpaulo	for (i = 0; i < num_spans; i++) {
855199086Srpaulo		/* filter away unwanted pressure spans */
856199086Srpaulo		if ((spans[i].cum < atp_pspan_min_cum_pressure) ||
857199086Srpaulo		    (spans[i].width > atp_pspan_max_width)) {
858199086Srpaulo			if ((i + 1) < num_spans) {
859199086Srpaulo				memcpy(&spans[i], &spans[i + 1],
860199086Srpaulo				    (num_spans - i - 1) * sizeof(atp_pspan));
861199086Srpaulo				i--;
862199086Srpaulo			}
863199086Srpaulo			num_spans--;
864199086Srpaulo			continue;
865199086Srpaulo		}
866199086Srpaulo
867199086Srpaulo		/* compute this span's representative location */
868199086Srpaulo		spans[i].loc = spans[i].cog * atp_mickeys_scale_factor /
869199086Srpaulo			spans[i].cum;
870199086Srpaulo
871199086Srpaulo		spans[i].matched = FALSE; /* not yet matched against a stroke */
872199086Srpaulo	}
873199086Srpaulo
874199086Srpaulo	*nspans_p = num_spans;
875199086Srpaulo}
876199086Srpaulo
877199086Srpaulo/*
878199086Srpaulo * Match a pressure-span against a stroke-component. If there is a
879199086Srpaulo * match, update the component's state and return TRUE.
880199086Srpaulo */
881199086Srpaulostatic boolean_t
882199086Srpauloatp_match_stroke_component(atp_stroke_component *component,
883200241Srpaulo    const atp_pspan *pspan, atp_stroke_type stroke_type)
884199086Srpaulo{
885200241Srpaulo	int   delta_mickeys;
886200241Srpaulo	u_int min_pressure;
887199086Srpaulo
888200241Srpaulo	delta_mickeys = pspan->loc - component->loc;
889200241Srpaulo
890199086Srpaulo	if (abs(delta_mickeys) > atp_max_delta_mickeys)
891199086Srpaulo		return (FALSE); /* the finger span is too far out; no match */
892199086Srpaulo
893199086Srpaulo	component->loc          = pspan->loc;
894200241Srpaulo
895200241Srpaulo	/*
896200241Srpaulo	 * A sudden and significant increase in a pspan's cumulative
897200241Srpaulo	 * pressure indicates the incidence of a new finger
898200241Srpaulo	 * contact. This usually revises the pspan's
899200241Srpaulo	 * centre-of-gravity, and hence the location of any/all
900200241Srpaulo	 * matching stroke component(s). But such a change should
901200241Srpaulo	 * *not* be interpreted as a movement.
902200241Srpaulo	 */
903200241Srpaulo        if (pspan->cum > ((3 * component->cum_pressure) >> 1))
904200241Srpaulo		delta_mickeys = 0;
905200241Srpaulo
906199086Srpaulo	component->cum_pressure = pspan->cum;
907199086Srpaulo	if (pspan->cum > component->max_cum_pressure)
908199086Srpaulo		component->max_cum_pressure = pspan->cum;
909199086Srpaulo
910199086Srpaulo	/*
911200241Srpaulo	 * Disregard the component's movement if its cumulative
912200241Srpaulo	 * pressure drops below a fraction of the maximum; this
913200241Srpaulo	 * fraction is determined based on the stroke's type.
914199086Srpaulo	 */
915200241Srpaulo	if (stroke_type == ATP_STROKE_TOUCH)
916200241Srpaulo		min_pressure = (3 * component->max_cum_pressure) >> 2;
917200241Srpaulo	else
918200241Srpaulo		min_pressure = component->max_cum_pressure >> 2;
919200241Srpaulo	if (component->cum_pressure < min_pressure)
920199086Srpaulo		delta_mickeys = 0;
921199086Srpaulo
922199086Srpaulo	component->delta_mickeys = delta_mickeys;
923199086Srpaulo	return (TRUE);
924199086Srpaulo}
925199086Srpaulo
926199086Srpaulostatic void
927199086Srpauloatp_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis,
928199086Srpaulo    atp_pspan *pspans, u_int n_pspans, u_int repeat_count)
929199086Srpaulo{
930199086Srpaulo	u_int i, j;
931199086Srpaulo	u_int repeat_index = 0;
932199086Srpaulo
933199086Srpaulo	/* Determine the index of the multi-span. */
934199086Srpaulo	if (repeat_count) {
935199086Srpaulo		u_int cum = 0;
936199086Srpaulo		for (i = 0; i < n_pspans; i++) {
937199086Srpaulo			if (pspans[i].cum > cum) {
938199086Srpaulo				repeat_index = i;
939199086Srpaulo				cum = pspans[i].cum;
940199086Srpaulo			}
941199086Srpaulo		}
942199086Srpaulo	}
943199086Srpaulo
944199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
945199086Srpaulo		atp_stroke *stroke  = &sc->sc_strokes[i];
946199086Srpaulo		if (stroke->components[axis].matched)
947199086Srpaulo			continue; /* skip matched components */
948199086Srpaulo
949199086Srpaulo		for (j = 0; j < n_pspans; j++) {
950199086Srpaulo			if (pspans[j].matched)
951199086Srpaulo				continue; /* skip matched pspans */
952199086Srpaulo
953199086Srpaulo			if (atp_match_stroke_component(
954200241Srpaulo				    &stroke->components[axis], &pspans[j],
955200241Srpaulo				    stroke->type)) {
956199086Srpaulo				/* There is a match. */
957199086Srpaulo				stroke->components[axis].matched = TRUE;
958199086Srpaulo
959199086Srpaulo				/* Take care to repeat at the multi-span. */
960199086Srpaulo				if ((repeat_count > 0) && (j == repeat_index))
961199086Srpaulo					repeat_count--;
962199086Srpaulo				else
963199086Srpaulo					pspans[j].matched = TRUE;
964199086Srpaulo
965199086Srpaulo				break; /* skip to the next stroke */
966199086Srpaulo			}
967199086Srpaulo		} /* loop over pspans */
968199086Srpaulo	} /* loop over strokes */
969199086Srpaulo}
970199086Srpaulo
971199086Srpaulo/*
972199086Srpaulo * Update strokes by matching against current pressure-spans.
973199086Srpaulo * Return TRUE if any movement is detected.
974199086Srpaulo */
975199086Srpaulostatic boolean_t
976199086Srpauloatp_update_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
977199086Srpaulo    u_int n_xpspans, atp_pspan *pspans_y, u_int n_ypspans)
978199086Srpaulo{
979199086Srpaulo	u_int       i, j;
980199086Srpaulo	atp_stroke *stroke;
981199086Srpaulo	boolean_t   movement = FALSE;
982199086Srpaulo	u_int       repeat_count = 0;
983199086Srpaulo
984199086Srpaulo	/* Reset X and Y components of all strokes as unmatched. */
985199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
986199086Srpaulo		stroke = &sc->sc_strokes[i];
987199086Srpaulo		stroke->components[X].matched = FALSE;
988199086Srpaulo		stroke->components[Y].matched = FALSE;
989199086Srpaulo	}
990199086Srpaulo
991199086Srpaulo	/*
992199086Srpaulo	 * Usually, the X and Y pspans come in pairs (the common case
993199086Srpaulo	 * being a single pair). It is possible, however, that
994199086Srpaulo	 * multiple contacts resolve to a single pspan along an
995199086Srpaulo	 * axis, as illustrated in the following:
996199086Srpaulo	 *
997199086Srpaulo	 *   F = finger-contact
998199086Srpaulo	 *
999199086Srpaulo	 *                pspan  pspan
1000199086Srpaulo	 *        +-----------------------+
1001199086Srpaulo	 *        |         .      .      |
1002199086Srpaulo	 *        |         .      .      |
1003199086Srpaulo	 *        |         .      .      |
1004199086Srpaulo	 *        |         .      .      |
1005199086Srpaulo	 *  pspan |.........F......F      |
1006199086Srpaulo	 *        |                       |
1007199086Srpaulo	 *        |                       |
1008199086Srpaulo	 *        |                       |
1009199086Srpaulo	 *        +-----------------------+
1010199086Srpaulo	 *
1011199086Srpaulo	 *
1012199086Srpaulo	 * The above case can be detected by a difference in the
1013199086Srpaulo	 * number of X and Y pspans. When this happens, X and Y pspans
1014199086Srpaulo	 * aren't easy to pair or match against strokes.
1015199086Srpaulo	 *
1016199086Srpaulo	 * When X and Y pspans differ in number, the axis with the
1017199086Srpaulo	 * smaller number of pspans is regarded as having a repeating
1018199086Srpaulo	 * pspan (or a multi-pspan)--in the above illustration, the
1019199086Srpaulo	 * Y-axis has a repeating pspan. Our approach is to try to
1020199086Srpaulo	 * match the multi-pspan repeatedly against strokes. The
1021199086Srpaulo	 * difference between the number of X and Y pspans gives us a
1022199086Srpaulo	 * crude repeat_count for matching multi-pspans--i.e. the
1023199086Srpaulo	 * multi-pspan along the Y axis (above) has a repeat_count of 1.
1024199086Srpaulo	 */
1025199086Srpaulo	repeat_count = abs(n_xpspans - n_ypspans);
1026199086Srpaulo
1027199086Srpaulo	atp_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans,
1028199086Srpaulo	    (((repeat_count != 0) && ((n_xpspans < n_ypspans))) ?
1029199086Srpaulo		repeat_count : 0));
1030199086Srpaulo	atp_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans,
1031199086Srpaulo	    (((repeat_count != 0) && (n_ypspans < n_xpspans)) ?
1032199086Srpaulo		repeat_count : 0));
1033199086Srpaulo
1034199086Srpaulo	/* Update the state of strokes based on the above pspan matches. */
1035199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1036199086Srpaulo		stroke = &sc->sc_strokes[i];
1037199086Srpaulo		if (stroke->components[X].matched &&
1038199086Srpaulo		    stroke->components[Y].matched) {
1039199086Srpaulo			atp_advance_stroke_state(sc, stroke, &movement);
1040199086Srpaulo		} else {
1041199086Srpaulo			/*
1042199086Srpaulo			 * At least one component of this stroke
1043199086Srpaulo			 * didn't match against current pspans;
1044199086Srpaulo			 * terminate it.
1045199086Srpaulo			 */
1046199086Srpaulo			atp_terminate_stroke(sc, i);
1047199086Srpaulo		}
1048199086Srpaulo	}
1049199086Srpaulo
1050199086Srpaulo	/* Add new strokes for pairs of unmatched pspans */
1051199086Srpaulo	for (i = 0; i < n_xpspans; i++) {
1052199086Srpaulo		if (pspans_x[i].matched == FALSE) break;
1053199086Srpaulo	}
1054199086Srpaulo	for (j = 0; j < n_ypspans; j++) {
1055199086Srpaulo		if (pspans_y[j].matched == FALSE) break;
1056199086Srpaulo	}
1057199086Srpaulo	if ((i < n_xpspans) && (j < n_ypspans)) {
1058199086Srpaulo#if USB_DEBUG
1059199086Srpaulo		if (atp_debug >= ATP_LLEVEL_INFO) {
1060199086Srpaulo			printf("unmatched pspans:");
1061199086Srpaulo			for (; i < n_xpspans; i++) {
1062199086Srpaulo				if (pspans_x[i].matched)
1063199086Srpaulo					continue;
1064199086Srpaulo				printf(" X:[loc:%u,cum:%u]",
1065199086Srpaulo				    pspans_x[i].loc, pspans_x[i].cum);
1066199086Srpaulo			}
1067199086Srpaulo			for (; j < n_ypspans; j++) {
1068199086Srpaulo				if (pspans_y[j].matched)
1069199086Srpaulo					continue;
1070199086Srpaulo				printf(" Y:[loc:%u,cum:%u]",
1071199086Srpaulo				    pspans_y[j].loc, pspans_y[j].cum);
1072199086Srpaulo			}
1073199086Srpaulo			printf("\n");
1074199086Srpaulo		}
1075199086Srpaulo#endif /* #if USB_DEBUG */
1076199086Srpaulo		if ((n_xpspans == 1) && (n_ypspans == 1))
1077199086Srpaulo			/* The common case of a single pair of new pspans. */
1078199086Srpaulo			atp_add_stroke(sc, &pspans_x[0], &pspans_y[0]);
1079199086Srpaulo		else
1080199086Srpaulo			atp_add_new_strokes(sc,
1081199086Srpaulo			    pspans_x, n_xpspans,
1082199086Srpaulo			    pspans_y, n_ypspans);
1083199086Srpaulo	}
1084199086Srpaulo
1085199086Srpaulo#if USB_DEBUG
1086199086Srpaulo	if (atp_debug >= ATP_LLEVEL_INFO) {
1087199086Srpaulo		for (i = 0; i < sc->sc_n_strokes; i++) {
1088199086Srpaulo			atp_stroke *stroke = &sc->sc_strokes[i];
1089199086Srpaulo
1090200241Srpaulo			printf(" %s%clc:%u,dm:%d,pnd:%d,cum:%d,max:%d,mv:%d%c"
1091200241Srpaulo			    ",%clc:%u,dm:%d,pnd:%d,cum:%d,max:%d,mv:%d%c",
1092199086Srpaulo			    (stroke->flags & ATSF_ZOMBIE) ? "zomb:" : "",
1093199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
1094199086Srpaulo			    stroke->components[X].loc,
1095199086Srpaulo			    stroke->components[X].delta_mickeys,
1096199086Srpaulo			    stroke->components[X].pending,
1097200241Srpaulo			    stroke->components[X].cum_pressure,
1098200241Srpaulo			    stroke->components[X].max_cum_pressure,
1099199086Srpaulo			    stroke->components[X].movement,
1100199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? ']' : '>',
1101199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? '[' : '<',
1102199086Srpaulo			    stroke->components[Y].loc,
1103199086Srpaulo			    stroke->components[Y].delta_mickeys,
1104199086Srpaulo			    stroke->components[Y].pending,
1105200241Srpaulo			    stroke->components[Y].cum_pressure,
1106200241Srpaulo			    stroke->components[Y].max_cum_pressure,
1107199086Srpaulo			    stroke->components[Y].movement,
1108199086Srpaulo			    (stroke->type == ATP_STROKE_TOUCH) ? ']' : '>');
1109199086Srpaulo		}
1110199086Srpaulo		if (sc->sc_n_strokes)
1111199086Srpaulo			printf("\n");
1112199086Srpaulo	}
1113199086Srpaulo#endif /* #if USB_DEBUG */
1114199086Srpaulo
1115199086Srpaulo	return (movement);
1116199086Srpaulo}
1117199086Srpaulo
1118199086Srpaulo/* Initialize a stroke using a pressure-span. */
1119199086Srpaulostatic __inline void
1120199086Srpauloatp_add_stroke(struct atp_softc *sc, const atp_pspan *pspan_x,
1121199086Srpaulo    const atp_pspan *pspan_y)
1122199086Srpaulo{
1123199086Srpaulo	atp_stroke *stroke;
1124199086Srpaulo
1125199086Srpaulo	if (sc->sc_n_strokes >= ATP_MAX_STROKES)
1126199086Srpaulo		return;
1127199086Srpaulo	stroke = &sc->sc_strokes[sc->sc_n_strokes];
1128199086Srpaulo
1129199086Srpaulo	memset(stroke, 0, sizeof(atp_stroke));
1130199086Srpaulo
1131199086Srpaulo	/*
1132199086Srpaulo	 * Strokes begin as potential touches. If a stroke survives
1133199086Srpaulo	 * longer than a threshold, or if it records significant
1134199086Srpaulo	 * cumulative movement, then it is considered a 'slide'.
1135199086Srpaulo	 */
1136199086Srpaulo	stroke->type = ATP_STROKE_TOUCH;
1137199086Srpaulo	microtime(&stroke->ctime);
1138199086Srpaulo	stroke->age  = 1;       /* Unit: interrupts */
1139199086Srpaulo
1140199086Srpaulo	stroke->components[X].loc              = pspan_x->loc;
1141199086Srpaulo	stroke->components[X].cum_pressure     = pspan_x->cum;
1142199086Srpaulo	stroke->components[X].max_cum_pressure = pspan_x->cum;
1143199086Srpaulo	stroke->components[X].matched          = TRUE;
1144199086Srpaulo
1145199086Srpaulo	stroke->components[Y].loc              = pspan_y->loc;
1146199086Srpaulo	stroke->components[Y].cum_pressure     = pspan_y->cum;
1147199086Srpaulo	stroke->components[Y].max_cum_pressure = pspan_y->cum;
1148199086Srpaulo	stroke->components[Y].matched          = TRUE;
1149199086Srpaulo
1150199086Srpaulo	sc->sc_n_strokes++;
1151199086Srpaulo	if (sc->sc_n_strokes > 1) {
1152199086Srpaulo		/* Reset double-tap-n-drag if we have more than one strokes. */
1153199086Srpaulo		sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1154199086Srpaulo	}
1155199086Srpaulo
1156199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "[%u,%u], time: %u,%ld\n",
1157199086Srpaulo	    stroke->components[X].loc,
1158199086Srpaulo	    stroke->components[Y].loc,
1159199086Srpaulo	    (unsigned int)stroke->ctime.tv_sec,
1160199086Srpaulo	    (unsigned long int)stroke->ctime.tv_usec);
1161199086Srpaulo}
1162199086Srpaulo
1163199086Srpaulostatic void
1164199086Srpauloatp_add_new_strokes(struct atp_softc *sc, atp_pspan *pspans_x,
1165199086Srpaulo    u_int n_xpspans, atp_pspan *pspans_y, u_int n_ypspans)
1166199086Srpaulo{
1167199086Srpaulo	int       i, j;
1168199086Srpaulo	atp_pspan spans[2][ATP_MAX_PSPANS_PER_AXIS];
1169199086Srpaulo	u_int     nspans[2];
1170199086Srpaulo
1171199086Srpaulo	/* Copy unmatched pspans into the local arrays. */
1172199086Srpaulo	for (i = 0, nspans[X] = 0; i < n_xpspans; i++) {
1173199086Srpaulo		if (pspans_x[i].matched == FALSE) {
1174199086Srpaulo			spans[X][nspans[X]] = pspans_x[i];
1175199086Srpaulo			nspans[X]++;
1176199086Srpaulo		}
1177199086Srpaulo	}
1178199086Srpaulo	for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) {
1179199086Srpaulo		if (pspans_y[j].matched == FALSE) {
1180199086Srpaulo			spans[Y][nspans[Y]] = pspans_y[j];
1181199086Srpaulo			nspans[Y]++;
1182199086Srpaulo		}
1183199086Srpaulo	}
1184199086Srpaulo
1185199086Srpaulo	if (nspans[X] == nspans[Y]) {
1186199086Srpaulo		/* Create new strokes from pairs of unmatched pspans */
1187199086Srpaulo		for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++)
1188199086Srpaulo			atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
1189199086Srpaulo	} else {
1190199086Srpaulo		u_int    cum = 0;
1191199086Srpaulo		atp_axis repeat_axis;      /* axis with multi-pspans */
1192199086Srpaulo		u_int    repeat_count;     /* repeat count for the multi-pspan*/
1193199086Srpaulo		u_int    repeat_index = 0; /* index of the multi-span */
1194199086Srpaulo
1195199086Srpaulo		repeat_axis  = (nspans[X] > nspans[Y]) ? Y : X;
1196199086Srpaulo		repeat_count = abs(nspans[X] - nspans[Y]);
1197199086Srpaulo		for (i = 0; i < nspans[repeat_axis]; i++) {
1198199086Srpaulo			if (spans[repeat_axis][i].cum > cum) {
1199199086Srpaulo				repeat_index = i;
1200199086Srpaulo				cum = spans[repeat_axis][i].cum;
1201199086Srpaulo			}
1202199086Srpaulo		}
1203199086Srpaulo
1204199086Srpaulo		/* Create new strokes from pairs of unmatched pspans */
1205199086Srpaulo		i = 0, j = 0;
1206199086Srpaulo		for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) {
1207199086Srpaulo			atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
1208199086Srpaulo
1209199086Srpaulo			/* Take care to repeat at the multi-pspan. */
1210199086Srpaulo			if (repeat_count > 0) {
1211199086Srpaulo				if ((repeat_axis == X) &&
1212199086Srpaulo				    (repeat_index == i)) {
1213199086Srpaulo					i--; /* counter loop increment */
1214199086Srpaulo					repeat_count--;
1215199086Srpaulo				} else if ((repeat_axis == Y) &&
1216199086Srpaulo				    (repeat_index == j)) {
1217199086Srpaulo					j--; /* counter loop increment */
1218199086Srpaulo					repeat_count--;
1219199086Srpaulo				}
1220199086Srpaulo			}
1221199086Srpaulo		}
1222199086Srpaulo	}
1223199086Srpaulo}
1224199086Srpaulo
1225199086Srpaulo/*
1226199086Srpaulo * Advance the state of this stroke--and update the out-parameter
1227199086Srpaulo * 'movement' as a side-effect.
1228199086Srpaulo */
1229199086Srpaulovoid
1230199086Srpauloatp_advance_stroke_state(struct atp_softc *sc, atp_stroke *stroke,
1231199086Srpaulo    boolean_t *movement)
1232199086Srpaulo{
1233199086Srpaulo	stroke->age++;
1234199086Srpaulo	if (stroke->age <= atp_stroke_maturity_threshold) {
1235199086Srpaulo		/* Avoid noise from immature strokes. */
1236199086Srpaulo		stroke->components[X].delta_mickeys = 0;
1237199086Srpaulo		stroke->components[Y].delta_mickeys = 0;
1238199086Srpaulo	}
1239199086Srpaulo
1240199086Srpaulo	/* Revitalize stroke if it had previously been marked as a zombie. */
1241199086Srpaulo	if (stroke->flags & ATSF_ZOMBIE)
1242199086Srpaulo		stroke->flags &= ~ATSF_ZOMBIE;
1243199086Srpaulo
1244199086Srpaulo	if (atp_compute_stroke_movement(stroke))
1245199086Srpaulo		*movement = TRUE;
1246199086Srpaulo
1247200241Srpaulo	if (stroke->type != ATP_STROKE_TOUCH)
1248200241Srpaulo		return;
1249200241Srpaulo
1250199086Srpaulo	/* Convert touch strokes to slides upon detecting movement or age. */
1251200241Srpaulo	if (stroke->cum_movement >= atp_slide_min_movement) {
1252200241Srpaulo		atp_convert_to_slide(sc, stroke);
1253200241Srpaulo	} else {
1254200241Srpaulo		/* If a touch stroke is found to be older than the
1255200241Srpaulo		 * touch-timeout threshold, it should be converted to
1256200241Srpaulo		 * a slide; except if there is a co-incident sibling
1257200241Srpaulo		 * with a later creation time.
1258200241Srpaulo		 *
1259200241Srpaulo		 * When multiple fingers make contact with the
1260200241Srpaulo		 * touchpad, they are likely to be separated in their
1261200241Srpaulo		 * times of incidence.  During a multi-finger tap,
1262200241Srpaulo		 * therefore, the last finger to make
1263200241Srpaulo		 * contact--i.e. the one with the latest
1264200241Srpaulo		 * 'ctime'--should be used to determine how the
1265200241Srpaulo		 * touch-siblings get treated; otherwise older
1266200241Srpaulo		 * siblings may lapse the touch-timeout and get
1267200241Srpaulo		 * converted into slides prematurely.  The following
1268200241Srpaulo		 * loop determines if there exists another touch
1269200241Srpaulo		 * stroke with a larger 'ctime' than the current
1270200241Srpaulo		 * stroke (NOTE: zombies with a larger 'ctime' are
1271200241Srpaulo		 * also considered) .
1272200241Srpaulo		 */
1273199086Srpaulo
1274200241Srpaulo		u_int i;
1275200241Srpaulo		for (i = 0; i < sc->sc_n_strokes; i++) {
1276200241Srpaulo			if ((&sc->sc_strokes[i] == stroke) ||
1277200241Srpaulo			    (sc->sc_strokes[i].type != ATP_STROKE_TOUCH))
1278200241Srpaulo				continue;
1279200241Srpaulo
1280200241Srpaulo			if (timevalcmp(&sc->sc_strokes[i].ctime,
1281200241Srpaulo				&stroke->ctime, >))
1282200241Srpaulo				break;
1283199086Srpaulo		}
1284200241Srpaulo		if (i == sc->sc_n_strokes) {
1285200241Srpaulo			/* Found no other touch stroke with a larger 'ctime'. */
1286200241Srpaulo			struct timeval tdiff;
1287199086Srpaulo
1288200241Srpaulo			/* Compute the stroke's age. */
1289200241Srpaulo			getmicrotime(&tdiff);
1290200241Srpaulo			if (timevalcmp(&tdiff, &stroke->ctime, >))
1291200241Srpaulo				timevalsub(&tdiff, &stroke->ctime);
1292200241Srpaulo			else {
1293200241Srpaulo				/*
1294200241Srpaulo				 * If we are here, it is because getmicrotime
1295200241Srpaulo				 * reported the current time as being behind
1296200241Srpaulo				 * the stroke's start time; getmicrotime can
1297200241Srpaulo				 * be imprecise.
1298200241Srpaulo				 */
1299200241Srpaulo				tdiff.tv_sec  = 0;
1300200241Srpaulo				tdiff.tv_usec = 0;
1301200241Srpaulo			}
1302199086Srpaulo
1303200241Srpaulo			if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) ||
1304200241Srpaulo			    ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) &&
1305200241Srpaulo				(tdiff.tv_usec >=
1306200241Srpaulo				    (atp_touch_timeout % 1000000))))
1307200241Srpaulo				atp_convert_to_slide(sc, stroke);
1308199086Srpaulo		}
1309199086Srpaulo	}
1310199086Srpaulo}
1311199086Srpaulo
1312200241Srpaulo/* Switch a given touch stroke to being a slide. */
1313200241Srpaulovoid
1314200241Srpauloatp_convert_to_slide(struct atp_softc *sc, atp_stroke *stroke)
1315200241Srpaulo{
1316200241Srpaulo	stroke->type = ATP_STROKE_SLIDE;
1317200241Srpaulo
1318200241Srpaulo	/* Are we at the beginning of a double-click-n-drag? */
1319200241Srpaulo	if ((sc->sc_n_strokes == 1) &&
1320200241Srpaulo	    ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) &&
1321200241Srpaulo	    timevalcmp(&stroke->ctime, &sc->sc_reap_time, >)) {
1322200241Srpaulo		struct timeval delta;
1323200241Srpaulo		struct timeval window = {
1324200241Srpaulo			atp_double_tap_threshold / 1000000,
1325200241Srpaulo			atp_double_tap_threshold % 1000000
1326200241Srpaulo		};
1327200241Srpaulo
1328200241Srpaulo		delta = stroke->ctime;
1329200241Srpaulo		timevalsub(&delta, &sc->sc_reap_time);
1330200241Srpaulo		if (timevalcmp(&delta, &window, <=))
1331200241Srpaulo			sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
1332200241Srpaulo	}
1333200241Srpaulo}
1334200241Srpaulo
1335199086Srpaulo/*
1336199086Srpaulo * Terminate a stroke. While SLIDE strokes are dropped, TOUCH strokes
1337199086Srpaulo * are retained as zombies so as to reap all their siblings together;
1338199086Srpaulo * this helps establish the number of fingers involved in the tap.
1339199086Srpaulo */
1340199086Srpaulostatic void
1341199086Srpauloatp_terminate_stroke(struct atp_softc *sc,
1342199086Srpaulo    u_int index) /* index of the stroke to be terminated */
1343199086Srpaulo{
1344199086Srpaulo	atp_stroke *s = &sc->sc_strokes[index];
1345199086Srpaulo
1346199086Srpaulo	if (s->flags & ATSF_ZOMBIE) {
1347199086Srpaulo		return;
1348199086Srpaulo	}
1349199086Srpaulo
1350199086Srpaulo	if ((s->type == ATP_STROKE_TOUCH) &&
1351199086Srpaulo	    (s->age > atp_stroke_maturity_threshold)) {
1352199086Srpaulo		s->flags |= ATSF_ZOMBIE;
1353199086Srpaulo
1354199086Srpaulo		/* If no zombies exist, then prepare to reap zombies later. */
1355199086Srpaulo		if ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) {
1356199086Srpaulo			atp_setup_reap_time(sc, &s->ctime);
1357199086Srpaulo			sc->sc_state |= ATP_ZOMBIES_EXIST;
1358199086Srpaulo		}
1359199086Srpaulo	} else {
1360199086Srpaulo		/* Drop this stroke. */
1361199086Srpaulo		memcpy(&sc->sc_strokes[index], &sc->sc_strokes[index + 1],
1362199086Srpaulo		    (sc->sc_n_strokes - index - 1) * sizeof(atp_stroke));
1363199086Srpaulo		sc->sc_n_strokes--;
1364199086Srpaulo
1365199086Srpaulo		/*
1366199086Srpaulo		 * Reset the double-click-n-drag at the termination of
1367199086Srpaulo		 * any slide stroke.
1368199086Srpaulo		 */
1369199086Srpaulo		sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1370199086Srpaulo	}
1371199086Srpaulo}
1372199086Srpaulo
1373199086Srpaulostatic __inline boolean_t
1374199086Srpauloatp_stroke_has_small_movement(const atp_stroke *stroke)
1375199086Srpaulo{
1376199086Srpaulo	return ((abs(stroke->components[X].delta_mickeys) <=
1377199086Srpaulo		atp_small_movement_threshold) &&
1378199086Srpaulo	    (abs(stroke->components[Y].delta_mickeys) <=
1379199086Srpaulo		atp_small_movement_threshold));
1380199086Srpaulo}
1381199086Srpaulo
1382199086Srpaulo/*
1383199086Srpaulo * Accumulate delta_mickeys into the component's 'pending' bucket; if
1384199086Srpaulo * the aggregate exceeds the small_movement_threshold, then retain
1385199086Srpaulo * delta_mickeys for later.
1386199086Srpaulo */
1387199086Srpaulostatic __inline void
1388199086Srpauloatp_update_pending_mickeys(atp_stroke_component *component)
1389199086Srpaulo{
1390199086Srpaulo	component->pending += component->delta_mickeys;
1391199086Srpaulo	if (abs(component->pending) <= atp_small_movement_threshold)
1392199086Srpaulo		component->delta_mickeys = 0;
1393199086Srpaulo	else {
1394199086Srpaulo		/*
1395199086Srpaulo		 * Penalise pending mickeys for having accumulated
1396199086Srpaulo		 * over short deltas. This operation has the effect of
1397199086Srpaulo		 * scaling down the cumulative contribution of short
1398199086Srpaulo		 * movements.
1399199086Srpaulo		 */
1400199086Srpaulo		component->pending -= (component->delta_mickeys << 1);
1401199086Srpaulo	}
1402199086Srpaulo}
1403199086Srpaulo
1404199086Srpaulo
1405199086Srpaulostatic void
1406199086Srpauloatp_compute_smoothening_scale_ratio(atp_stroke *stroke, int *numerator,
1407199086Srpaulo    int *denominator)
1408199086Srpaulo{
1409199086Srpaulo	int   dxdt;
1410199086Srpaulo	int   dydt;
1411199086Srpaulo	u_int vel_squared; /* Square of the velocity vector's magnitude. */
1412199086Srpaulo	u_int vel_squared_smooth;
1413199086Srpaulo
1414199086Srpaulo	/* Table holding (10 * sqrt(x)) for x between 1 and 256. */
1415199086Srpaulo	static uint8_t sqrt_table[256] = {
1416199086Srpaulo		10, 14, 17, 20, 22, 24, 26, 28,
1417199086Srpaulo		30, 31, 33, 34, 36, 37, 38, 40,
1418199086Srpaulo		41, 42, 43, 44, 45, 46, 47, 48,
1419199086Srpaulo		50, 50, 51, 52, 53, 54, 55, 56,
1420199086Srpaulo		57, 58, 59, 60, 60, 61, 62, 63,
1421199086Srpaulo		64, 64, 65, 66, 67, 67, 68, 69,
1422199086Srpaulo		70, 70, 71, 72, 72, 73, 74, 74,
1423199086Srpaulo		75, 76, 76, 77, 78, 78, 79, 80,
1424199086Srpaulo		80, 81, 81, 82, 83, 83, 84, 84,
1425199086Srpaulo		85, 86, 86, 87, 87, 88, 88, 89,
1426199086Srpaulo		90, 90, 91, 91, 92, 92, 93, 93,
1427199086Srpaulo		94, 94, 95, 95, 96, 96, 97, 97,
1428199086Srpaulo		98, 98, 99, 100, 100, 100, 101, 101,
1429199086Srpaulo		102, 102, 103, 103, 104, 104, 105, 105,
1430199086Srpaulo		106, 106, 107, 107, 108, 108, 109, 109,
1431199086Srpaulo		110, 110, 110, 111, 111, 112, 112, 113,
1432199086Srpaulo		113, 114, 114, 114, 115, 115, 116, 116,
1433199086Srpaulo		117, 117, 117, 118, 118, 119, 119, 120,
1434199086Srpaulo		120, 120, 121, 121, 122, 122, 122, 123,
1435199086Srpaulo		123, 124, 124, 124, 125, 125, 126, 126,
1436199086Srpaulo		126, 127, 127, 128, 128, 128, 129, 129,
1437199086Srpaulo		130, 130, 130, 131, 131, 131, 132, 132,
1438199086Srpaulo		133, 133, 133, 134, 134, 134, 135, 135,
1439199086Srpaulo		136, 136, 136, 137, 137, 137, 138, 138,
1440199086Srpaulo		138, 139, 139, 140, 140, 140, 141, 141,
1441199086Srpaulo		141, 142, 142, 142, 143, 143, 143, 144,
1442199086Srpaulo		144, 144, 145, 145, 145, 146, 146, 146,
1443199086Srpaulo		147, 147, 147, 148, 148, 148, 149, 149,
1444199086Srpaulo		150, 150, 150, 150, 151, 151, 151, 152,
1445199086Srpaulo		152, 152, 153, 153, 153, 154, 154, 154,
1446199086Srpaulo		155, 155, 155, 156, 156, 156, 157, 157,
1447199086Srpaulo		157, 158, 158, 158, 159, 159, 159, 160
1448199086Srpaulo	};
1449199086Srpaulo	const u_int N = sizeof(sqrt_table) / sizeof(sqrt_table[0]);
1450199086Srpaulo
1451199086Srpaulo	dxdt = stroke->components[X].delta_mickeys;
1452199086Srpaulo	dydt = stroke->components[Y].delta_mickeys;
1453199086Srpaulo
1454199086Srpaulo	*numerator = 0, *denominator = 0; /* default values. */
1455199086Srpaulo
1456199086Srpaulo	/* Compute a smoothened magnitude_squared of the stroke's velocity. */
1457199086Srpaulo	vel_squared = dxdt * dxdt + dydt * dydt;
1458199086Srpaulo	vel_squared_smooth = (3 * stroke->velocity_squared + vel_squared) >> 2;
1459199086Srpaulo	stroke->velocity_squared = vel_squared_smooth; /* retained as history */
1460199086Srpaulo	if ((vel_squared == 0) || (vel_squared_smooth == 0))
1461199086Srpaulo		return; /* returning (numerator == 0) will imply zero movement*/
1462199086Srpaulo
1463199086Srpaulo	/*
1464199086Srpaulo	 * In order to determine the overall movement scale factor,
1465199086Srpaulo	 * we're actually interested in the effect of smoothening upon
1466199086Srpaulo	 * the *magnitude* of velocity; i.e. we need to compute the
1467199086Srpaulo	 * square-root of (vel_squared_smooth / vel_squared) in the
1468199086Srpaulo	 * form of a numerator and denominator.
1469199086Srpaulo	 */
1470199086Srpaulo
1471199086Srpaulo	/* Keep within the bounds of the square-root table. */
1472199086Srpaulo	while ((vel_squared > N) || (vel_squared_smooth > N)) {
1473199086Srpaulo		/* Dividing uniformly by 2 won't disturb the final ratio. */
1474199086Srpaulo		vel_squared        >>= 1;
1475199086Srpaulo		vel_squared_smooth >>= 1;
1476199086Srpaulo	}
1477199086Srpaulo
1478199086Srpaulo	*numerator   = sqrt_table[vel_squared_smooth - 1];
1479199086Srpaulo	*denominator = sqrt_table[vel_squared - 1];
1480199086Srpaulo}
1481199086Srpaulo
1482199086Srpaulo/*
1483199086Srpaulo * Compute a smoothened value for the stroke's movement from
1484199086Srpaulo * delta_mickeys in the X and Y components.
1485199086Srpaulo */
1486199086Srpaulostatic boolean_t
1487199086Srpauloatp_compute_stroke_movement(atp_stroke *stroke)
1488199086Srpaulo{
1489199086Srpaulo	int   num;              /* numerator of scale ratio */
1490199086Srpaulo	int   denom;            /* denominator of scale ratio */
1491199086Srpaulo
1492199086Srpaulo	/*
1493199086Srpaulo	 * Short movements are added first to the 'pending' bucket,
1494199086Srpaulo	 * and then acted upon only when their aggregate exceeds a
1495199086Srpaulo	 * threshold. This has the effect of filtering away movement
1496199086Srpaulo	 * noise.
1497199086Srpaulo	 */
1498199086Srpaulo	if (atp_stroke_has_small_movement(stroke)) {
1499199086Srpaulo		atp_update_pending_mickeys(&stroke->components[X]);
1500199086Srpaulo		atp_update_pending_mickeys(&stroke->components[Y]);
1501199086Srpaulo	} else {                /* large movement */
1502199086Srpaulo		/* clear away any pending mickeys if there are large movements*/
1503199086Srpaulo		stroke->components[X].pending = 0;
1504199086Srpaulo		stroke->components[Y].pending = 0;
1505199086Srpaulo	}
1506199086Srpaulo
1507199086Srpaulo	/* Get the scale ratio and smoothen movement. */
1508199086Srpaulo	atp_compute_smoothening_scale_ratio(stroke, &num, &denom);
1509199086Srpaulo	if ((num == 0) || (denom == 0)) {
1510199086Srpaulo		stroke->components[X].movement = 0;
1511199086Srpaulo		stroke->components[Y].movement = 0;
1512199086Srpaulo		stroke->velocity_squared >>= 1; /* Erode velocity_squared. */
1513199086Srpaulo	} else {
1514199086Srpaulo		stroke->components[X].movement =
1515199086Srpaulo			(stroke->components[X].delta_mickeys * num) / denom;
1516199086Srpaulo		stroke->components[Y].movement =
1517199086Srpaulo			(stroke->components[Y].delta_mickeys * num) / denom;
1518199086Srpaulo
1519199086Srpaulo		stroke->cum_movement +=
1520199086Srpaulo			abs(stroke->components[X].movement) +
1521199086Srpaulo			abs(stroke->components[Y].movement);
1522199086Srpaulo	}
1523199086Srpaulo
1524199086Srpaulo	return ((stroke->components[X].movement != 0) ||
1525199086Srpaulo	    (stroke->components[Y].movement != 0));
1526199086Srpaulo}
1527199086Srpaulo
1528199086Srpaulostatic __inline void
1529199086Srpauloatp_setup_reap_time(struct atp_softc *sc, struct timeval *tvp)
1530199086Srpaulo{
1531199086Srpaulo	struct timeval reap_window = {
1532199086Srpaulo		ATP_ZOMBIE_STROKE_REAP_WINDOW / 1000000,
1533199086Srpaulo		ATP_ZOMBIE_STROKE_REAP_WINDOW % 1000000
1534199086Srpaulo	};
1535199086Srpaulo
1536199086Srpaulo	microtime(&sc->sc_reap_time);
1537199086Srpaulo	timevaladd(&sc->sc_reap_time, &reap_window);
1538199086Srpaulo
1539199086Srpaulo	sc->sc_reap_ctime = *tvp; /* ctime to reap */
1540199086Srpaulo}
1541199086Srpaulo
1542199086Srpaulostatic void
1543199086Srpauloatp_reap_zombies(struct atp_softc *sc, u_int *n_reaped, u_int *reaped_xlocs)
1544199086Srpaulo{
1545199086Srpaulo	u_int       i;
1546199086Srpaulo	atp_stroke *stroke;
1547199086Srpaulo
1548199086Srpaulo	*n_reaped = 0;
1549199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1550199086Srpaulo		struct timeval  tdiff;
1551199086Srpaulo
1552199086Srpaulo		stroke = &sc->sc_strokes[i];
1553199086Srpaulo
1554199086Srpaulo		if ((stroke->flags & ATSF_ZOMBIE) == 0)
1555199086Srpaulo			continue;
1556199086Srpaulo
1557199086Srpaulo		/* Compare this stroke's ctime with the ctime being reaped. */
1558199086Srpaulo		if (timevalcmp(&stroke->ctime, &sc->sc_reap_ctime, >=)) {
1559199086Srpaulo			tdiff = stroke->ctime;
1560199086Srpaulo			timevalsub(&tdiff, &sc->sc_reap_ctime);
1561199086Srpaulo		} else {
1562199086Srpaulo			tdiff = sc->sc_reap_ctime;
1563199086Srpaulo			timevalsub(&tdiff, &stroke->ctime);
1564199086Srpaulo		}
1565199086Srpaulo
1566199086Srpaulo		if ((tdiff.tv_sec > (ATP_COINCIDENCE_THRESHOLD / 1000000)) ||
1567199086Srpaulo		    ((tdiff.tv_sec == (ATP_COINCIDENCE_THRESHOLD / 1000000)) &&
1568199086Srpaulo		     (tdiff.tv_usec > (ATP_COINCIDENCE_THRESHOLD % 1000000)))) {
1569199086Srpaulo			continue; /* Skip non-siblings. */
1570199086Srpaulo		}
1571199086Srpaulo
1572199086Srpaulo		/*
1573199086Srpaulo		 * Reap this sibling zombie stroke.
1574199086Srpaulo		 */
1575199086Srpaulo
1576199086Srpaulo		if (reaped_xlocs != NULL)
1577199086Srpaulo			reaped_xlocs[*n_reaped] = stroke->components[X].loc;
1578199086Srpaulo
1579199086Srpaulo		/* Erase the stroke from the sc. */
1580199086Srpaulo		memcpy(&stroke[i], &stroke[i + 1],
1581199086Srpaulo		    (sc->sc_n_strokes - i - 1) * sizeof(atp_stroke));
1582199086Srpaulo		sc->sc_n_strokes--;
1583199086Srpaulo
1584199086Srpaulo		*n_reaped += 1;
1585199086Srpaulo		--i; /* Decr. i to keep it unchanged for the next iteration */
1586199086Srpaulo	}
1587199086Srpaulo
1588199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", *n_reaped);
1589199086Srpaulo
1590199086Srpaulo	/* There could still be zombies remaining in the system. */
1591199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1592199086Srpaulo		stroke = &sc->sc_strokes[i];
1593199086Srpaulo		if (stroke->flags & ATSF_ZOMBIE) {
1594199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO, "zombies remain!\n");
1595199086Srpaulo			atp_setup_reap_time(sc, &stroke->ctime);
1596199086Srpaulo			return;
1597199086Srpaulo		}
1598199086Srpaulo	}
1599199086Srpaulo
1600199086Srpaulo	/* If we reach here, then no more zombies remain. */
1601199086Srpaulo	sc->sc_state &= ~ATP_ZOMBIES_EXIST;
1602199086Srpaulo}
1603199086Srpaulo
1604199086Srpaulo
1605199086Srpaulo/* Device methods. */
1606199086Srpaulostatic device_probe_t  atp_probe;
1607199086Srpaulostatic device_attach_t atp_attach;
1608199086Srpaulostatic device_detach_t atp_detach;
1609199086Srpaulostatic usb_callback_t  atp_intr;
1610199086Srpaulo
1611199086Srpaulostatic const struct usb_config atp_config[ATP_N_TRANSFER] = {
1612199086Srpaulo	[ATP_INTR_DT] = {
1613199086Srpaulo		.type      = UE_INTERRUPT,
1614199086Srpaulo		.endpoint  = UE_ADDR_ANY,
1615199086Srpaulo		.direction = UE_DIR_IN,
1616199086Srpaulo		.flags = {
1617199086Srpaulo			.pipe_bof = 1,
1618199086Srpaulo			.short_xfer_ok = 1,
1619199086Srpaulo		},
1620199086Srpaulo		.bufsize   = 0, /* use wMaxPacketSize */
1621199086Srpaulo		.callback  = &atp_intr,
1622199086Srpaulo	},
1623199680Sthompsa	[ATP_RESET] = {
1624199680Sthompsa		.type      = UE_CONTROL,
1625199680Sthompsa		.endpoint  = 0, /* Control pipe */
1626199680Sthompsa		.direction = UE_DIR_ANY,
1627199680Sthompsa		.bufsize = sizeof(struct usb_device_request) + MODE_LENGTH,
1628199680Sthompsa		.callback  = &atp_reset_callback,
1629199680Sthompsa		.interval = 0,  /* no pre-delay */
1630199680Sthompsa	},
1631199086Srpaulo};
1632199086Srpaulo
1633199086Srpaulostatic int
1634199086Srpauloatp_probe(device_t self)
1635199086Srpaulo{
1636199086Srpaulo	struct usb_attach_arg *uaa = device_get_ivars(self);
1637199086Srpaulo
1638199086Srpaulo	if (uaa->usb_mode != USB_MODE_HOST)
1639199086Srpaulo		return (ENXIO);
1640199086Srpaulo
1641199086Srpaulo	if ((uaa->info.bInterfaceClass != UICLASS_HID) ||
1642199086Srpaulo	    (uaa->info.bInterfaceProtocol != UIPROTO_MOUSE))
1643199086Srpaulo		return (ENXIO);
1644199086Srpaulo
1645199680Sthompsa	return (usbd_lookup_id_by_uaa(atp_devs, sizeof(atp_devs), uaa));
1646199086Srpaulo}
1647199086Srpaulo
1648199086Srpaulostatic int
1649199086Srpauloatp_attach(device_t dev)
1650199086Srpaulo{
1651199086Srpaulo	struct atp_softc      *sc = device_get_softc(dev);
1652199086Srpaulo	struct usb_attach_arg *uaa = device_get_ivars(dev);
1653199086Srpaulo	usb_error_t            err;
1654199086Srpaulo
1655199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc);
1656199086Srpaulo
1657199086Srpaulo	sc->sc_dev        = dev;
1658199086Srpaulo	sc->sc_usb_device = uaa->device;
1659199086Srpaulo
1660199086Srpaulo	/*
1661199086Srpaulo	 * By default the touchpad behaves like an HID device, sending
1662199086Srpaulo	 * packets with reportID = 2. Such reports contain only
1663199086Srpaulo	 * limited information--they encode movement deltas and button
1664199086Srpaulo	 * events,--but do not include data from the pressure
1665199086Srpaulo	 * sensors. The device input mode can be switched from HID
1666199086Srpaulo	 * reports to raw sensor data using vendor-specific USB
1667199086Srpaulo	 * control commands; but first the mode must be read.
1668199086Srpaulo	 */
1669199086Srpaulo	err = atp_req_get_report(sc->sc_usb_device, sc->sc_mode_bytes);
1670199086Srpaulo	if (err != USB_ERR_NORMAL_COMPLETION) {
1671199086Srpaulo		DPRINTF("failed to read device mode (%d)\n", err);
1672199086Srpaulo		return (ENXIO);
1673199086Srpaulo	}
1674199086Srpaulo
1675199086Srpaulo	if (atp_set_device_mode(dev, RAW_SENSOR_MODE) != 0) {
1676199086Srpaulo		DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err);
1677199086Srpaulo		return (ENXIO);
1678199086Srpaulo	}
1679199086Srpaulo
1680199086Srpaulo	mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE);
1681199086Srpaulo
1682199086Srpaulo	err = usbd_transfer_setup(uaa->device,
1683199086Srpaulo	    &uaa->info.bIfaceIndex, sc->sc_xfer, atp_config,
1684199086Srpaulo	    ATP_N_TRANSFER, sc, &sc->sc_mutex);
1685199086Srpaulo
1686199086Srpaulo	if (err) {
1687199086Srpaulo		DPRINTF("error=%s\n", usbd_errstr(err));
1688199086Srpaulo		goto detach;
1689199086Srpaulo	}
1690199086Srpaulo
1691199086Srpaulo	if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex,
1692199086Srpaulo		&atp_fifo_methods, &sc->sc_fifo,
1693199086Srpaulo		device_get_unit(dev), 0 - 1, uaa->info.bIfaceIndex,
1694199086Srpaulo		UID_ROOT, GID_OPERATOR, 0644)) {
1695199086Srpaulo		goto detach;
1696199086Srpaulo	}
1697199086Srpaulo
1698199086Srpaulo	device_set_usb_desc(dev);
1699199086Srpaulo
1700199086Srpaulo	sc->sc_params           = &atp_dev_params[uaa->driver_info];
1701199086Srpaulo
1702199086Srpaulo	sc->sc_hw.buttons       = 3;
1703199086Srpaulo	sc->sc_hw.iftype        = MOUSE_IF_USB;
1704199086Srpaulo	sc->sc_hw.type          = MOUSE_PAD;
1705199086Srpaulo	sc->sc_hw.model         = MOUSE_MODEL_GENERIC;
1706199086Srpaulo	sc->sc_hw.hwid          = 0;
1707199086Srpaulo	sc->sc_mode.protocol    = MOUSE_PROTO_MSC;
1708199086Srpaulo	sc->sc_mode.rate        = -1;
1709199086Srpaulo	sc->sc_mode.resolution  = MOUSE_RES_UNKNOWN;
1710199086Srpaulo	sc->sc_mode.accelfactor = 0;
1711199086Srpaulo	sc->sc_mode.level       = 0;
1712199086Srpaulo	sc->sc_mode.packetsize  = MOUSE_MSC_PACKETSIZE;
1713199086Srpaulo	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
1714199086Srpaulo	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
1715199086Srpaulo
1716199086Srpaulo	sc->sc_state            = 0;
1717199086Srpaulo
1718199086Srpaulo	sc->sc_left_margin  = atp_mickeys_scale_factor;
1719199086Srpaulo	sc->sc_right_margin = (sc->sc_params->n_xsensors - 1) *
1720199086Srpaulo		atp_mickeys_scale_factor;
1721199086Srpaulo
1722199086Srpaulo	return (0);
1723199086Srpaulo
1724199086Srpaulodetach:
1725199086Srpaulo	atp_detach(dev);
1726199086Srpaulo	return (ENOMEM);
1727199086Srpaulo}
1728199086Srpaulo
1729199086Srpaulostatic int
1730199086Srpauloatp_detach(device_t dev)
1731199086Srpaulo{
1732199086Srpaulo	struct atp_softc *sc;
1733199086Srpaulo
1734199086Srpaulo	sc = device_get_softc(dev);
1735199086Srpaulo	if (sc->sc_state & ATP_ENABLED) {
1736199086Srpaulo		mtx_lock(&sc->sc_mutex);
1737199086Srpaulo		atp_disable(sc);
1738199086Srpaulo		mtx_unlock(&sc->sc_mutex);
1739199086Srpaulo	}
1740199086Srpaulo
1741199086Srpaulo	usb_fifo_detach(&sc->sc_fifo);
1742199086Srpaulo
1743199086Srpaulo	usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER);
1744199086Srpaulo
1745199086Srpaulo	mtx_destroy(&sc->sc_mutex);
1746199086Srpaulo
1747199086Srpaulo	return (0);
1748199086Srpaulo}
1749199086Srpaulo
1750199086Srpaulostatic void
1751199086Srpauloatp_intr(struct usb_xfer *xfer, usb_error_t error)
1752199086Srpaulo{
1753199086Srpaulo	struct atp_softc      *sc = usbd_xfer_softc(xfer);
1754199086Srpaulo	int                    len;
1755199086Srpaulo	struct usb_page_cache *pc;
1756199086Srpaulo	uint8_t                status_bits;
1757199086Srpaulo	atp_pspan  pspans_x[ATP_MAX_PSPANS_PER_AXIS];
1758199086Srpaulo	atp_pspan  pspans_y[ATP_MAX_PSPANS_PER_AXIS];
1759199086Srpaulo	u_int      n_xpspans = 0, n_ypspans = 0;
1760199086Srpaulo	u_int      reaped_xlocs[ATP_MAX_STROKES];
1761199086Srpaulo	u_int      tap_fingers = 0;
1762199086Srpaulo
1763199086Srpaulo	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
1764199086Srpaulo
1765199086Srpaulo	switch (USB_GET_STATE(xfer)) {
1766199086Srpaulo	case USB_ST_TRANSFERRED:
1767199086Srpaulo		if (len > sc->sc_params->data_len) {
1768199086Srpaulo			DPRINTFN(ATP_LLEVEL_ERROR,
1769199086Srpaulo			    "truncating large packet from %u to %u bytes\n",
1770199086Srpaulo			    len, sc->sc_params->data_len);
1771199086Srpaulo			len = sc->sc_params->data_len;
1772199086Srpaulo		}
1773199151Snwhitehorn		if (len < sc->sc_params->data_len)
1774199086Srpaulo			goto tr_setup;
1775199086Srpaulo
1776199086Srpaulo		pc = usbd_xfer_get_frame(xfer, 0);
1777199086Srpaulo		usbd_copy_out(pc, 0, sc->sensor_data, sc->sc_params->data_len);
1778199086Srpaulo
1779199086Srpaulo		/* Interpret sensor data */
1780199086Srpaulo		atp_interpret_sensor_data(sc->sensor_data,
1781199151Snwhitehorn		    sc->sc_params->n_xsensors, X, sc->cur_x,
1782199151Snwhitehorn		    sc->sc_params->prot);
1783199086Srpaulo		atp_interpret_sensor_data(sc->sensor_data,
1784199151Snwhitehorn		    sc->sc_params->n_ysensors, Y,  sc->cur_y,
1785199151Snwhitehorn		    sc->sc_params->prot);
1786199086Srpaulo
1787199086Srpaulo		/*
1788199086Srpaulo		 * If this is the initial update (from an untouched
1789199086Srpaulo		 * pad), we should set the base values for the sensor
1790199086Srpaulo		 * data; deltas with respect to these base values can
1791199086Srpaulo		 * be used as pressure readings subsequently.
1792199086Srpaulo		 */
1793199086Srpaulo		status_bits = sc->sensor_data[sc->sc_params->data_len - 1];
1794199151Snwhitehorn		if ((sc->sc_params->prot == ATP_PROT_GEYSER3 &&
1795200241Srpaulo		    (status_bits & ATP_STATUS_BASE_UPDATE)) ||
1796199151Snwhitehorn		    !(sc->sc_state & ATP_VALID)) {
1797199086Srpaulo			memcpy(sc->base_x, sc->cur_x,
1798199086Srpaulo			    sc->sc_params->n_xsensors * sizeof(*(sc->base_x)));
1799199086Srpaulo			memcpy(sc->base_y, sc->cur_y,
1800199086Srpaulo			    sc->sc_params->n_ysensors * sizeof(*(sc->base_y)));
1801199151Snwhitehorn			sc->sc_state |= ATP_VALID;
1802199086Srpaulo			goto tr_setup;
1803199086Srpaulo		}
1804199086Srpaulo
1805199086Srpaulo		/* Get pressure readings and detect p-spans for both axes. */
1806199086Srpaulo		atp_get_pressures(sc->pressure_x, sc->cur_x, sc->base_x,
1807199086Srpaulo		    sc->sc_params->n_xsensors);
1808199086Srpaulo		atp_detect_pspans(sc->pressure_x, sc->sc_params->n_xsensors,
1809199086Srpaulo		    ATP_MAX_PSPANS_PER_AXIS,
1810199086Srpaulo		    pspans_x, &n_xpspans);
1811199086Srpaulo		atp_get_pressures(sc->pressure_y, sc->cur_y, sc->base_y,
1812199086Srpaulo		    sc->sc_params->n_ysensors);
1813199086Srpaulo		atp_detect_pspans(sc->pressure_y, sc->sc_params->n_ysensors,
1814199086Srpaulo		    ATP_MAX_PSPANS_PER_AXIS,
1815199086Srpaulo		    pspans_y, &n_ypspans);
1816199086Srpaulo
1817199086Srpaulo		/* Update strokes with new pspans to detect movements. */
1818199086Srpaulo		sc->sc_status.flags &= ~MOUSE_POSCHANGED;
1819199086Srpaulo		if (atp_update_strokes(sc,
1820199086Srpaulo			pspans_x, n_xpspans,
1821199086Srpaulo			pspans_y, n_ypspans))
1822199086Srpaulo			sc->sc_status.flags |= MOUSE_POSCHANGED;
1823199086Srpaulo
1824199086Srpaulo		/* Reap zombies if it is time. */
1825199086Srpaulo		if (sc->sc_state & ATP_ZOMBIES_EXIST) {
1826199086Srpaulo			struct timeval now;
1827199086Srpaulo
1828199086Srpaulo			getmicrotime(&now);
1829199086Srpaulo			if (timevalcmp(&now, &sc->sc_reap_time, >=))
1830199086Srpaulo				atp_reap_zombies(sc, &tap_fingers,
1831199086Srpaulo				    reaped_xlocs);
1832199086Srpaulo		}
1833199086Srpaulo
1834199086Srpaulo		sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED;
1835199086Srpaulo		sc->sc_status.obutton = sc->sc_status.button;
1836199086Srpaulo
1837199086Srpaulo		/* Get the state of the physical buttton. */
1838199086Srpaulo		sc->sc_status.button = (status_bits & ATP_STATUS_BUTTON) ?
1839199086Srpaulo			MOUSE_BUTTON1DOWN : 0;
1840199086Srpaulo		if (sc->sc_status.button != 0) {
1841199086Srpaulo			/* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */
1842199086Srpaulo			sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1843199086Srpaulo		} else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) {
1844199086Srpaulo			/* Assume a button-press with DOUBLE_TAP_N_DRAG. */
1845199086Srpaulo			sc->sc_status.button = MOUSE_BUTTON1DOWN;
1846199086Srpaulo		}
1847199086Srpaulo
1848199086Srpaulo		sc->sc_status.flags |=
1849199086Srpaulo			sc->sc_status.button ^ sc->sc_status.obutton;
1850199086Srpaulo		if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) {
1851199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO, "button %s\n",
1852199086Srpaulo			    ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ?
1853199086Srpaulo				"pressed" : "released"));
1854199086Srpaulo		} else if ((sc->sc_status.obutton == 0) &&
1855199086Srpaulo		    (sc->sc_status.button == 0) &&
1856199086Srpaulo		    (tap_fingers != 0)) {
1857199086Srpaulo			/* Ignore single-finger taps at the edges. */
1858199086Srpaulo			if ((tap_fingers == 1) &&
1859199086Srpaulo			    ((reaped_xlocs[0] <= sc->sc_left_margin) ||
1860199086Srpaulo				(reaped_xlocs[0] > sc->sc_right_margin))) {
1861199086Srpaulo				tap_fingers = 0;
1862199086Srpaulo			}
1863199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO,
1864199086Srpaulo			    "tap_fingers: %u\n", tap_fingers);
1865199086Srpaulo		}
1866199086Srpaulo
1867199086Srpaulo		if (sc->sc_status.flags &
1868199086Srpaulo		    (MOUSE_POSCHANGED | MOUSE_STDBUTTONSCHANGED)) {
1869199086Srpaulo			int   dx, dy;
1870199086Srpaulo			u_int n_movements;
1871199086Srpaulo
1872199086Srpaulo			dx = 0, dy = 0, n_movements = 0;
1873199086Srpaulo			for (u_int i = 0; i < sc->sc_n_strokes; i++) {
1874199086Srpaulo				atp_stroke *stroke = &sc->sc_strokes[i];
1875199086Srpaulo
1876199086Srpaulo				if ((stroke->components[X].movement) ||
1877199086Srpaulo				    (stroke->components[Y].movement)) {
1878199086Srpaulo					dx += stroke->components[X].movement;
1879199086Srpaulo					dy += stroke->components[Y].movement;
1880199086Srpaulo					n_movements++;
1881199086Srpaulo				}
1882199086Srpaulo			}
1883199086Srpaulo			/*
1884199086Srpaulo			 * Disregard movement if multiple
1885199086Srpaulo			 * strokes record motion.
1886199086Srpaulo			 */
1887199086Srpaulo			if (n_movements != 1)
1888199086Srpaulo				dx = 0, dy = 0;
1889199086Srpaulo
1890199086Srpaulo			sc->sc_status.dx += dx;
1891199086Srpaulo			sc->sc_status.dy += dy;
1892199086Srpaulo			atp_add_to_queue(sc, dx, -dy, sc->sc_status.button);
1893199086Srpaulo		}
1894199086Srpaulo
1895199086Srpaulo		if (tap_fingers != 0) {
1896199086Srpaulo			/* Add a pair of events (button-down and button-up). */
1897199086Srpaulo			switch (tap_fingers) {
1898199086Srpaulo			case 1: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON1DOWN);
1899199086Srpaulo				break;
1900199086Srpaulo			case 2: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON2DOWN);
1901199086Srpaulo				break;
1902199086Srpaulo			case 3: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON3DOWN);
1903199086Srpaulo				break;
1904199086Srpaulo			default: break;/* handle taps of only up to 3 fingers */
1905199086Srpaulo			}
1906199086Srpaulo			atp_add_to_queue(sc, 0, 0, 0); /* button release */
1907199086Srpaulo		}
1908199086Srpaulo
1909199086Srpaulo		/*
1910199086Srpaulo		 * The device continues to trigger interrupts at a
1911199086Srpaulo		 * fast rate even after touchpad activity has
1912199086Srpaulo		 * stopped. Upon detecting that the device has
1913199086Srpaulo		 * remained idle beyond a threshold, we reinitialize
1914199086Srpaulo		 * it to silence the interrupts.
1915199086Srpaulo		 */
1916199086Srpaulo		if ((sc->sc_status.flags  == 0) &&
1917199086Srpaulo		    (sc->sc_n_strokes     == 0) &&
1918199086Srpaulo		    (sc->sc_status.button == 0)) {
1919199086Srpaulo			sc->sc_idlecount++;
1920199086Srpaulo			if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) {
1921199086Srpaulo				DPRINTFN(ATP_LLEVEL_INFO, "idle\n");
1922199948Snwhitehorn
1923199948Snwhitehorn				/*
1924199948Snwhitehorn				 * Use the last frame before we go idle for
1925199948Snwhitehorn				 * calibration on pads which do not send
1926199948Snwhitehorn				 * calibration frames.
1927199948Snwhitehorn				 */
1928199948Snwhitehorn				if (sc->sc_params->prot < ATP_PROT_GEYSER3) {
1929199948Snwhitehorn					memcpy(sc->base_x, sc->cur_x,
1930199948Snwhitehorn					    sc->sc_params->n_xsensors *
1931199948Snwhitehorn					    sizeof(*(sc->base_x)));
1932199948Snwhitehorn					memcpy(sc->base_y, sc->cur_y,
1933199948Snwhitehorn					    sc->sc_params->n_ysensors *
1934199948Snwhitehorn					    sizeof(*(sc->base_y)));
1935199948Snwhitehorn				}
1936199948Snwhitehorn
1937199151Snwhitehorn				sc->sc_idlecount = 0;
1938199680Sthompsa				usbd_transfer_start(sc->sc_xfer[ATP_RESET]);
1939199086Srpaulo			}
1940199086Srpaulo		} else {
1941199086Srpaulo			sc->sc_idlecount = 0;
1942199086Srpaulo		}
1943199086Srpaulo
1944199086Srpaulo	case USB_ST_SETUP:
1945199086Srpaulo	tr_setup:
1946199086Srpaulo		/* check if we can put more data into the FIFO */
1947199086Srpaulo		if (usb_fifo_put_bytes_max(
1948199086Srpaulo			    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
1949199086Srpaulo			usbd_xfer_set_frame_len(xfer, 0,
1950199151Snwhitehorn			    sc->sc_params->data_len);
1951199086Srpaulo			usbd_transfer_submit(xfer);
1952199086Srpaulo		}
1953199086Srpaulo		break;
1954199086Srpaulo
1955199086Srpaulo	default:                        /* Error */
1956199086Srpaulo		if (error != USB_ERR_CANCELLED) {
1957199086Srpaulo			/* try clear stall first */
1958199086Srpaulo			usbd_xfer_set_stall(xfer);
1959199086Srpaulo			goto tr_setup;
1960199086Srpaulo		}
1961199086Srpaulo		break;
1962199086Srpaulo	}
1963199086Srpaulo
1964199086Srpaulo	return;
1965199086Srpaulo}
1966199086Srpaulo
1967199086Srpaulostatic void
1968199086Srpauloatp_add_to_queue(struct atp_softc *sc, int dx, int dy, uint32_t buttons_in)
1969199086Srpaulo{
1970199086Srpaulo	uint32_t buttons_out;
1971199086Srpaulo	uint8_t  buf[8];
1972199086Srpaulo
1973199086Srpaulo	dx = imin(dx,  254); dx = imax(dx, -256);
1974199086Srpaulo	dy = imin(dy,  254); dy = imax(dy, -256);
1975199086Srpaulo
1976199086Srpaulo	buttons_out = MOUSE_MSC_BUTTONS;
1977199086Srpaulo	if (buttons_in & MOUSE_BUTTON1DOWN)
1978199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON1UP;
1979199086Srpaulo	else if (buttons_in & MOUSE_BUTTON2DOWN)
1980199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON2UP;
1981199086Srpaulo	else if (buttons_in & MOUSE_BUTTON3DOWN)
1982199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON3UP;
1983199086Srpaulo
1984199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n",
1985199086Srpaulo	    dx, dy, buttons_out);
1986199086Srpaulo
1987199086Srpaulo	/* Encode the mouse data in standard format; refer to mouse(4) */
1988199086Srpaulo	buf[0] = sc->sc_mode.syncmask[1];
1989199086Srpaulo	buf[0] |= buttons_out;
1990199086Srpaulo	buf[1] = dx >> 1;
1991199086Srpaulo	buf[2] = dy >> 1;
1992199086Srpaulo	buf[3] = dx - (dx >> 1);
1993199086Srpaulo	buf[4] = dy - (dy >> 1);
1994199086Srpaulo	/* Encode extra bytes for level 1 */
1995199086Srpaulo	if (sc->sc_mode.level == 1) {
1996199086Srpaulo		buf[5] = 0;                    /* dz */
1997199086Srpaulo		buf[6] = 0;                    /* dz - (dz / 2) */
1998199086Srpaulo		buf[7] = MOUSE_SYS_EXTBUTTONS; /* Extra buttons all up. */
1999199086Srpaulo	}
2000199086Srpaulo
2001199086Srpaulo	usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
2002199086Srpaulo	    sc->sc_mode.packetsize, 1);
2003199086Srpaulo}
2004199086Srpaulo
2005199086Srpaulostatic void
2006199086Srpauloatp_reset_buf(struct atp_softc *sc)
2007199086Srpaulo{
2008199086Srpaulo	/* reset read queue */
2009199086Srpaulo	usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
2010199086Srpaulo}
2011199086Srpaulo
2012199086Srpaulostatic void
2013199086Srpauloatp_start_read(struct usb_fifo *fifo)
2014199086Srpaulo{
2015199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
2016199086Srpaulo	int rate;
2017199086Srpaulo
2018199086Srpaulo	/* Check if we should override the default polling interval */
2019199086Srpaulo	rate = sc->sc_pollrate;
2020199086Srpaulo	/* Range check rate */
2021199086Srpaulo	if (rate > 1000)
2022199086Srpaulo		rate = 1000;
2023199086Srpaulo	/* Check for set rate */
2024199086Srpaulo	if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) {
2025199086Srpaulo		/* Stop current transfer, if any */
2026199086Srpaulo		usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
2027199086Srpaulo		/* Set new interval */
2028199086Srpaulo		usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate);
2029199086Srpaulo		/* Only set pollrate once */
2030199086Srpaulo		sc->sc_pollrate = 0;
2031199086Srpaulo	}
2032199086Srpaulo
2033199086Srpaulo	usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]);
2034199086Srpaulo}
2035199086Srpaulo
2036199086Srpaulostatic void
2037199086Srpauloatp_stop_read(struct usb_fifo *fifo)
2038199086Srpaulo{
2039199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
2040199086Srpaulo
2041199086Srpaulo	usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
2042199086Srpaulo}
2043199086Srpaulo
2044199086Srpaulo
2045199086Srpaulostatic int
2046199086Srpauloatp_open(struct usb_fifo *fifo, int fflags)
2047199086Srpaulo{
2048199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "\n");
2049199086Srpaulo
2050199086Srpaulo	if (fflags & FREAD) {
2051199086Srpaulo		struct atp_softc *sc = usb_fifo_softc(fifo);
2052199086Srpaulo		int rc;
2053199086Srpaulo
2054199086Srpaulo		if (sc->sc_state & ATP_ENABLED)
2055199086Srpaulo			return (EBUSY);
2056199086Srpaulo
2057199086Srpaulo		if (usb_fifo_alloc_buffer(fifo,
2058199086Srpaulo			ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) {
2059199086Srpaulo			return (ENOMEM);
2060199086Srpaulo		}
2061199086Srpaulo
2062199086Srpaulo		rc = atp_enable(sc);
2063199086Srpaulo		if (rc != 0) {
2064199086Srpaulo			usb_fifo_free_buffer(fifo);
2065199086Srpaulo			return (rc);
2066199086Srpaulo		}
2067199086Srpaulo	}
2068199086Srpaulo
2069199086Srpaulo	return (0);
2070199086Srpaulo}
2071199086Srpaulo
2072199086Srpaulostatic void
2073199086Srpauloatp_close(struct usb_fifo *fifo, int fflags)
2074199086Srpaulo{
2075199086Srpaulo	if (fflags & FREAD) {
2076199086Srpaulo		struct atp_softc *sc = usb_fifo_softc(fifo);
2077199086Srpaulo
2078199086Srpaulo		atp_disable(sc);
2079199086Srpaulo		usb_fifo_free_buffer(fifo);
2080199086Srpaulo	}
2081199086Srpaulo}
2082199086Srpaulo
2083199086Srpauloint
2084199086Srpauloatp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
2085199086Srpaulo{
2086199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
2087199086Srpaulo	mousemode_t mode;
2088199086Srpaulo	int error = 0;
2089199086Srpaulo
2090199086Srpaulo	mtx_lock(&sc->sc_mutex);
2091199086Srpaulo
2092199086Srpaulo	switch(cmd) {
2093199086Srpaulo	case MOUSE_GETHWINFO:
2094199086Srpaulo		*(mousehw_t *)addr = sc->sc_hw;
2095199086Srpaulo		break;
2096199086Srpaulo	case MOUSE_GETMODE:
2097199086Srpaulo		*(mousemode_t *)addr = sc->sc_mode;
2098199086Srpaulo		break;
2099199086Srpaulo	case MOUSE_SETMODE:
2100199086Srpaulo		mode = *(mousemode_t *)addr;
2101199086Srpaulo
2102199086Srpaulo		if (mode.level == -1)
2103199086Srpaulo			/* Don't change the current setting */
2104199086Srpaulo			;
2105199086Srpaulo		else if ((mode.level < 0) || (mode.level > 1)) {
2106199086Srpaulo			error = EINVAL;
2107199086Srpaulo			goto done;
2108199086Srpaulo		}
2109199086Srpaulo		sc->sc_mode.level = mode.level;
2110199086Srpaulo		sc->sc_pollrate   = mode.rate;
2111199086Srpaulo		sc->sc_hw.buttons = 3;
2112199086Srpaulo
2113199086Srpaulo		if (sc->sc_mode.level == 0) {
2114199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
2115199086Srpaulo			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
2116199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
2117199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
2118199086Srpaulo		} else if (sc->sc_mode.level == 1) {
2119199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
2120199086Srpaulo			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
2121199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
2122199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
2123199086Srpaulo		}
2124199086Srpaulo		atp_reset_buf(sc);
2125199086Srpaulo		break;
2126199086Srpaulo	case MOUSE_GETLEVEL:
2127199086Srpaulo		*(int *)addr = sc->sc_mode.level;
2128199086Srpaulo		break;
2129199086Srpaulo	case MOUSE_SETLEVEL:
2130199086Srpaulo		if (*(int *)addr < 0 || *(int *)addr > 1) {
2131199086Srpaulo			error = EINVAL;
2132199086Srpaulo			goto done;
2133199086Srpaulo		}
2134199086Srpaulo		sc->sc_mode.level = *(int *)addr;
2135199086Srpaulo		sc->sc_hw.buttons = 3;
2136199086Srpaulo
2137199086Srpaulo		if (sc->sc_mode.level == 0) {
2138199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
2139199086Srpaulo			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
2140199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
2141199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
2142199086Srpaulo		} else if (sc->sc_mode.level == 1) {
2143199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
2144199086Srpaulo			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
2145199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
2146199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
2147199086Srpaulo		}
2148199086Srpaulo		atp_reset_buf(sc);
2149199086Srpaulo		break;
2150199086Srpaulo	case MOUSE_GETSTATUS: {
2151199086Srpaulo		mousestatus_t *status = (mousestatus_t *)addr;
2152199086Srpaulo
2153199086Srpaulo		*status = sc->sc_status;
2154199086Srpaulo		sc->sc_status.obutton = sc->sc_status.button;
2155199086Srpaulo		sc->sc_status.button  = 0;
2156199086Srpaulo		sc->sc_status.dx = 0;
2157199086Srpaulo		sc->sc_status.dy = 0;
2158199086Srpaulo		sc->sc_status.dz = 0;
2159199086Srpaulo
2160199086Srpaulo		if (status->dx || status->dy || status->dz)
2161199086Srpaulo			status->flags |= MOUSE_POSCHANGED;
2162199086Srpaulo		if (status->button != status->obutton)
2163199086Srpaulo			status->flags |= MOUSE_BUTTONSCHANGED;
2164199086Srpaulo		break;
2165199086Srpaulo	}
2166199086Srpaulo	default:
2167199086Srpaulo		error = ENOTTY;
2168199086Srpaulo	}
2169199086Srpaulo
2170199086Srpaulodone:
2171199086Srpaulo	mtx_unlock(&sc->sc_mutex);
2172199086Srpaulo	return (error);
2173199086Srpaulo}
2174199086Srpaulo
2175199086Srpaulostatic int
2176199086Srpauloatp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS)
2177199086Srpaulo{
2178199086Srpaulo	int error;
2179199086Srpaulo	u_int tmp;
2180199086Srpaulo	u_int prev_mickeys_scale_factor;
2181199086Srpaulo
2182199086Srpaulo	prev_mickeys_scale_factor = atp_mickeys_scale_factor;
2183199086Srpaulo
2184199086Srpaulo	tmp = atp_mickeys_scale_factor;
2185199086Srpaulo	error = sysctl_handle_int(oidp, &tmp, 0, req);
2186199086Srpaulo	if (error != 0 || req->newptr == NULL)
2187199086Srpaulo		return (error);
2188199086Srpaulo
2189199086Srpaulo	if (tmp == prev_mickeys_scale_factor)
2190199086Srpaulo		return (0);     /* no change */
2191199086Srpaulo
2192199086Srpaulo	atp_mickeys_scale_factor = tmp;
2193199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n",
2194199086Srpaulo	    ATP_DRIVER_NAME, tmp);
2195199086Srpaulo
2196199086Srpaulo	/* Update dependent thresholds. */
2197199086Srpaulo	if (atp_small_movement_threshold == (prev_mickeys_scale_factor >> 3))
2198199086Srpaulo		atp_small_movement_threshold = atp_mickeys_scale_factor >> 3;
2199199086Srpaulo	if (atp_max_delta_mickeys == ((3 * prev_mickeys_scale_factor) >> 1))
2200199086Srpaulo		atp_max_delta_mickeys = ((3 * atp_mickeys_scale_factor) >>1);
2201199086Srpaulo	if (atp_slide_min_movement == (prev_mickeys_scale_factor >> 3))
2202199086Srpaulo		atp_slide_min_movement = atp_mickeys_scale_factor >> 3;
2203199086Srpaulo
2204199086Srpaulo	return (0);
2205199086Srpaulo}
2206199086Srpaulo
2207199086Srpaulostatic device_method_t atp_methods[] = {
2208199086Srpaulo	/* Device interface */
2209199086Srpaulo	DEVMETHOD(device_probe,  atp_probe),
2210199086Srpaulo	DEVMETHOD(device_attach, atp_attach),
2211199086Srpaulo	DEVMETHOD(device_detach, atp_detach),
2212199086Srpaulo	{ 0, 0 }
2213199086Srpaulo};
2214199086Srpaulo
2215199086Srpaulostatic driver_t atp_driver = {
2216199086Srpaulo	ATP_DRIVER_NAME,
2217199086Srpaulo	atp_methods,
2218199086Srpaulo	sizeof(struct atp_softc)
2219199086Srpaulo};
2220199086Srpaulo
2221199086Srpaulostatic devclass_t atp_devclass;
2222199086Srpaulo
2223199086SrpauloDRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0);
2224199086SrpauloMODULE_DEPEND(atp, usb, 1, 1, 1);
2225