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$");
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 */
117227309Sedstatic SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB atp");
118199086Srpaulo
119207077Sthompsa#ifdef 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");
129207077Sthompsa#endif /* USB_DEBUG */
130199086Srpaulo
131199086Srpaulostatic u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT;
132217323SmdfSYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW,
133217323Smdf    &atp_touch_timeout, 125000, "age threshold (in micros) for a touch");
134199086Srpaulo
135199086Srpaulostatic u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD;
136217323SmdfSYSCTL_UINT(_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
243223486Shselaskystatic const STRUCT_USB_HOST_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		 */
764233774Shselasky		if ((u_int)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
890233774Shselasky	if ((u_int)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)) {
1058207077Sthompsa#ifdef 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		}
1075207077Sthompsa#endif /* 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
1085207077Sthompsa#ifdef 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	}
1113207077Sthompsa#endif /* 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	atp_pspan spans[2][ATP_MAX_PSPANS_PER_AXIS];
1168233774Shselasky	u_int nspans[2];
1169233774Shselasky	u_int i;
1170233774Shselasky	u_int j;
1171199086Srpaulo
1172199086Srpaulo	/* Copy unmatched pspans into the local arrays. */
1173199086Srpaulo	for (i = 0, nspans[X] = 0; i < n_xpspans; i++) {
1174199086Srpaulo		if (pspans_x[i].matched == FALSE) {
1175199086Srpaulo			spans[X][nspans[X]] = pspans_x[i];
1176199086Srpaulo			nspans[X]++;
1177199086Srpaulo		}
1178199086Srpaulo	}
1179199086Srpaulo	for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) {
1180199086Srpaulo		if (pspans_y[j].matched == FALSE) {
1181199086Srpaulo			spans[Y][nspans[Y]] = pspans_y[j];
1182199086Srpaulo			nspans[Y]++;
1183199086Srpaulo		}
1184199086Srpaulo	}
1185199086Srpaulo
1186199086Srpaulo	if (nspans[X] == nspans[Y]) {
1187199086Srpaulo		/* Create new strokes from pairs of unmatched pspans */
1188199086Srpaulo		for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++)
1189199086Srpaulo			atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
1190199086Srpaulo	} else {
1191199086Srpaulo		u_int    cum = 0;
1192199086Srpaulo		atp_axis repeat_axis;      /* axis with multi-pspans */
1193199086Srpaulo		u_int    repeat_count;     /* repeat count for the multi-pspan*/
1194199086Srpaulo		u_int    repeat_index = 0; /* index of the multi-span */
1195199086Srpaulo
1196199086Srpaulo		repeat_axis  = (nspans[X] > nspans[Y]) ? Y : X;
1197199086Srpaulo		repeat_count = abs(nspans[X] - nspans[Y]);
1198199086Srpaulo		for (i = 0; i < nspans[repeat_axis]; i++) {
1199199086Srpaulo			if (spans[repeat_axis][i].cum > cum) {
1200199086Srpaulo				repeat_index = i;
1201199086Srpaulo				cum = spans[repeat_axis][i].cum;
1202199086Srpaulo			}
1203199086Srpaulo		}
1204199086Srpaulo
1205199086Srpaulo		/* Create new strokes from pairs of unmatched pspans */
1206199086Srpaulo		i = 0, j = 0;
1207199086Srpaulo		for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) {
1208199086Srpaulo			atp_add_stroke(sc, &spans[X][i], &spans[Y][j]);
1209199086Srpaulo
1210199086Srpaulo			/* Take care to repeat at the multi-pspan. */
1211199086Srpaulo			if (repeat_count > 0) {
1212199086Srpaulo				if ((repeat_axis == X) &&
1213199086Srpaulo				    (repeat_index == i)) {
1214199086Srpaulo					i--; /* counter loop increment */
1215199086Srpaulo					repeat_count--;
1216199086Srpaulo				} else if ((repeat_axis == Y) &&
1217199086Srpaulo				    (repeat_index == j)) {
1218199086Srpaulo					j--; /* counter loop increment */
1219199086Srpaulo					repeat_count--;
1220199086Srpaulo				}
1221199086Srpaulo			}
1222199086Srpaulo		}
1223199086Srpaulo	}
1224199086Srpaulo}
1225199086Srpaulo
1226199086Srpaulo/*
1227199086Srpaulo * Advance the state of this stroke--and update the out-parameter
1228199086Srpaulo * 'movement' as a side-effect.
1229199086Srpaulo */
1230199086Srpaulovoid
1231199086Srpauloatp_advance_stroke_state(struct atp_softc *sc, atp_stroke *stroke,
1232199086Srpaulo    boolean_t *movement)
1233199086Srpaulo{
1234199086Srpaulo	stroke->age++;
1235199086Srpaulo	if (stroke->age <= atp_stroke_maturity_threshold) {
1236199086Srpaulo		/* Avoid noise from immature strokes. */
1237199086Srpaulo		stroke->components[X].delta_mickeys = 0;
1238199086Srpaulo		stroke->components[Y].delta_mickeys = 0;
1239199086Srpaulo	}
1240199086Srpaulo
1241199086Srpaulo	/* Revitalize stroke if it had previously been marked as a zombie. */
1242199086Srpaulo	if (stroke->flags & ATSF_ZOMBIE)
1243199086Srpaulo		stroke->flags &= ~ATSF_ZOMBIE;
1244199086Srpaulo
1245199086Srpaulo	if (atp_compute_stroke_movement(stroke))
1246199086Srpaulo		*movement = TRUE;
1247199086Srpaulo
1248200241Srpaulo	if (stroke->type != ATP_STROKE_TOUCH)
1249200241Srpaulo		return;
1250200241Srpaulo
1251199086Srpaulo	/* Convert touch strokes to slides upon detecting movement or age. */
1252200241Srpaulo	if (stroke->cum_movement >= atp_slide_min_movement) {
1253200241Srpaulo		atp_convert_to_slide(sc, stroke);
1254200241Srpaulo	} else {
1255200241Srpaulo		/* If a touch stroke is found to be older than the
1256200241Srpaulo		 * touch-timeout threshold, it should be converted to
1257200241Srpaulo		 * a slide; except if there is a co-incident sibling
1258200241Srpaulo		 * with a later creation time.
1259200241Srpaulo		 *
1260200241Srpaulo		 * When multiple fingers make contact with the
1261200241Srpaulo		 * touchpad, they are likely to be separated in their
1262200241Srpaulo		 * times of incidence.  During a multi-finger tap,
1263200241Srpaulo		 * therefore, the last finger to make
1264200241Srpaulo		 * contact--i.e. the one with the latest
1265200241Srpaulo		 * 'ctime'--should be used to determine how the
1266200241Srpaulo		 * touch-siblings get treated; otherwise older
1267200241Srpaulo		 * siblings may lapse the touch-timeout and get
1268200241Srpaulo		 * converted into slides prematurely.  The following
1269200241Srpaulo		 * loop determines if there exists another touch
1270200241Srpaulo		 * stroke with a larger 'ctime' than the current
1271200241Srpaulo		 * stroke (NOTE: zombies with a larger 'ctime' are
1272200241Srpaulo		 * also considered) .
1273200241Srpaulo		 */
1274199086Srpaulo
1275200241Srpaulo		u_int i;
1276200241Srpaulo		for (i = 0; i < sc->sc_n_strokes; i++) {
1277200241Srpaulo			if ((&sc->sc_strokes[i] == stroke) ||
1278200241Srpaulo			    (sc->sc_strokes[i].type != ATP_STROKE_TOUCH))
1279200241Srpaulo				continue;
1280200241Srpaulo
1281200241Srpaulo			if (timevalcmp(&sc->sc_strokes[i].ctime,
1282200241Srpaulo				&stroke->ctime, >))
1283200241Srpaulo				break;
1284199086Srpaulo		}
1285200241Srpaulo		if (i == sc->sc_n_strokes) {
1286200241Srpaulo			/* Found no other touch stroke with a larger 'ctime'. */
1287200241Srpaulo			struct timeval tdiff;
1288199086Srpaulo
1289200241Srpaulo			/* Compute the stroke's age. */
1290200241Srpaulo			getmicrotime(&tdiff);
1291200241Srpaulo			if (timevalcmp(&tdiff, &stroke->ctime, >))
1292200241Srpaulo				timevalsub(&tdiff, &stroke->ctime);
1293200241Srpaulo			else {
1294200241Srpaulo				/*
1295200241Srpaulo				 * If we are here, it is because getmicrotime
1296200241Srpaulo				 * reported the current time as being behind
1297200241Srpaulo				 * the stroke's start time; getmicrotime can
1298200241Srpaulo				 * be imprecise.
1299200241Srpaulo				 */
1300200241Srpaulo				tdiff.tv_sec  = 0;
1301200241Srpaulo				tdiff.tv_usec = 0;
1302200241Srpaulo			}
1303199086Srpaulo
1304200241Srpaulo			if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) ||
1305200241Srpaulo			    ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) &&
1306200241Srpaulo				(tdiff.tv_usec >=
1307200241Srpaulo				    (atp_touch_timeout % 1000000))))
1308200241Srpaulo				atp_convert_to_slide(sc, stroke);
1309199086Srpaulo		}
1310199086Srpaulo	}
1311199086Srpaulo}
1312199086Srpaulo
1313200241Srpaulo/* Switch a given touch stroke to being a slide. */
1314200241Srpaulovoid
1315200241Srpauloatp_convert_to_slide(struct atp_softc *sc, atp_stroke *stroke)
1316200241Srpaulo{
1317200241Srpaulo	stroke->type = ATP_STROKE_SLIDE;
1318200241Srpaulo
1319200241Srpaulo	/* Are we at the beginning of a double-click-n-drag? */
1320200241Srpaulo	if ((sc->sc_n_strokes == 1) &&
1321200241Srpaulo	    ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) &&
1322200241Srpaulo	    timevalcmp(&stroke->ctime, &sc->sc_reap_time, >)) {
1323200241Srpaulo		struct timeval delta;
1324200241Srpaulo		struct timeval window = {
1325200241Srpaulo			atp_double_tap_threshold / 1000000,
1326200241Srpaulo			atp_double_tap_threshold % 1000000
1327200241Srpaulo		};
1328200241Srpaulo
1329200241Srpaulo		delta = stroke->ctime;
1330200241Srpaulo		timevalsub(&delta, &sc->sc_reap_time);
1331200241Srpaulo		if (timevalcmp(&delta, &window, <=))
1332200241Srpaulo			sc->sc_state |= ATP_DOUBLE_TAP_DRAG;
1333200241Srpaulo	}
1334200241Srpaulo}
1335200241Srpaulo
1336199086Srpaulo/*
1337199086Srpaulo * Terminate a stroke. While SLIDE strokes are dropped, TOUCH strokes
1338199086Srpaulo * are retained as zombies so as to reap all their siblings together;
1339199086Srpaulo * this helps establish the number of fingers involved in the tap.
1340199086Srpaulo */
1341199086Srpaulostatic void
1342199086Srpauloatp_terminate_stroke(struct atp_softc *sc,
1343199086Srpaulo    u_int index) /* index of the stroke to be terminated */
1344199086Srpaulo{
1345199086Srpaulo	atp_stroke *s = &sc->sc_strokes[index];
1346199086Srpaulo
1347199086Srpaulo	if (s->flags & ATSF_ZOMBIE) {
1348199086Srpaulo		return;
1349199086Srpaulo	}
1350199086Srpaulo
1351199086Srpaulo	if ((s->type == ATP_STROKE_TOUCH) &&
1352199086Srpaulo	    (s->age > atp_stroke_maturity_threshold)) {
1353199086Srpaulo		s->flags |= ATSF_ZOMBIE;
1354199086Srpaulo
1355199086Srpaulo		/* If no zombies exist, then prepare to reap zombies later. */
1356199086Srpaulo		if ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) {
1357199086Srpaulo			atp_setup_reap_time(sc, &s->ctime);
1358199086Srpaulo			sc->sc_state |= ATP_ZOMBIES_EXIST;
1359199086Srpaulo		}
1360199086Srpaulo	} else {
1361199086Srpaulo		/* Drop this stroke. */
1362199086Srpaulo		memcpy(&sc->sc_strokes[index], &sc->sc_strokes[index + 1],
1363199086Srpaulo		    (sc->sc_n_strokes - index - 1) * sizeof(atp_stroke));
1364199086Srpaulo		sc->sc_n_strokes--;
1365199086Srpaulo
1366199086Srpaulo		/*
1367199086Srpaulo		 * Reset the double-click-n-drag at the termination of
1368199086Srpaulo		 * any slide stroke.
1369199086Srpaulo		 */
1370199086Srpaulo		sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1371199086Srpaulo	}
1372199086Srpaulo}
1373199086Srpaulo
1374199086Srpaulostatic __inline boolean_t
1375199086Srpauloatp_stroke_has_small_movement(const atp_stroke *stroke)
1376199086Srpaulo{
1377233774Shselasky	return (((u_int)abs(stroke->components[X].delta_mickeys) <=
1378199086Srpaulo		atp_small_movement_threshold) &&
1379233774Shselasky	    ((u_int)abs(stroke->components[Y].delta_mickeys) <=
1380199086Srpaulo		atp_small_movement_threshold));
1381199086Srpaulo}
1382199086Srpaulo
1383199086Srpaulo/*
1384199086Srpaulo * Accumulate delta_mickeys into the component's 'pending' bucket; if
1385199086Srpaulo * the aggregate exceeds the small_movement_threshold, then retain
1386199086Srpaulo * delta_mickeys for later.
1387199086Srpaulo */
1388199086Srpaulostatic __inline void
1389199086Srpauloatp_update_pending_mickeys(atp_stroke_component *component)
1390199086Srpaulo{
1391199086Srpaulo	component->pending += component->delta_mickeys;
1392233774Shselasky	if ((u_int)abs(component->pending) <= atp_small_movement_threshold)
1393199086Srpaulo		component->delta_mickeys = 0;
1394199086Srpaulo	else {
1395199086Srpaulo		/*
1396199086Srpaulo		 * Penalise pending mickeys for having accumulated
1397199086Srpaulo		 * over short deltas. This operation has the effect of
1398199086Srpaulo		 * scaling down the cumulative contribution of short
1399199086Srpaulo		 * movements.
1400199086Srpaulo		 */
1401199086Srpaulo		component->pending -= (component->delta_mickeys << 1);
1402199086Srpaulo	}
1403199086Srpaulo}
1404199086Srpaulo
1405199086Srpaulo
1406199086Srpaulostatic void
1407199086Srpauloatp_compute_smoothening_scale_ratio(atp_stroke *stroke, int *numerator,
1408199086Srpaulo    int *denominator)
1409199086Srpaulo{
1410199086Srpaulo	int   dxdt;
1411199086Srpaulo	int   dydt;
1412199086Srpaulo	u_int vel_squared; /* Square of the velocity vector's magnitude. */
1413199086Srpaulo	u_int vel_squared_smooth;
1414199086Srpaulo
1415199086Srpaulo	/* Table holding (10 * sqrt(x)) for x between 1 and 256. */
1416199086Srpaulo	static uint8_t sqrt_table[256] = {
1417199086Srpaulo		10, 14, 17, 20, 22, 24, 26, 28,
1418199086Srpaulo		30, 31, 33, 34, 36, 37, 38, 40,
1419199086Srpaulo		41, 42, 43, 44, 45, 46, 47, 48,
1420199086Srpaulo		50, 50, 51, 52, 53, 54, 55, 56,
1421199086Srpaulo		57, 58, 59, 60, 60, 61, 62, 63,
1422199086Srpaulo		64, 64, 65, 66, 67, 67, 68, 69,
1423199086Srpaulo		70, 70, 71, 72, 72, 73, 74, 74,
1424199086Srpaulo		75, 76, 76, 77, 78, 78, 79, 80,
1425199086Srpaulo		80, 81, 81, 82, 83, 83, 84, 84,
1426199086Srpaulo		85, 86, 86, 87, 87, 88, 88, 89,
1427199086Srpaulo		90, 90, 91, 91, 92, 92, 93, 93,
1428199086Srpaulo		94, 94, 95, 95, 96, 96, 97, 97,
1429199086Srpaulo		98, 98, 99, 100, 100, 100, 101, 101,
1430199086Srpaulo		102, 102, 103, 103, 104, 104, 105, 105,
1431199086Srpaulo		106, 106, 107, 107, 108, 108, 109, 109,
1432199086Srpaulo		110, 110, 110, 111, 111, 112, 112, 113,
1433199086Srpaulo		113, 114, 114, 114, 115, 115, 116, 116,
1434199086Srpaulo		117, 117, 117, 118, 118, 119, 119, 120,
1435199086Srpaulo		120, 120, 121, 121, 122, 122, 122, 123,
1436199086Srpaulo		123, 124, 124, 124, 125, 125, 126, 126,
1437199086Srpaulo		126, 127, 127, 128, 128, 128, 129, 129,
1438199086Srpaulo		130, 130, 130, 131, 131, 131, 132, 132,
1439199086Srpaulo		133, 133, 133, 134, 134, 134, 135, 135,
1440199086Srpaulo		136, 136, 136, 137, 137, 137, 138, 138,
1441199086Srpaulo		138, 139, 139, 140, 140, 140, 141, 141,
1442199086Srpaulo		141, 142, 142, 142, 143, 143, 143, 144,
1443199086Srpaulo		144, 144, 145, 145, 145, 146, 146, 146,
1444199086Srpaulo		147, 147, 147, 148, 148, 148, 149, 149,
1445199086Srpaulo		150, 150, 150, 150, 151, 151, 151, 152,
1446199086Srpaulo		152, 152, 153, 153, 153, 154, 154, 154,
1447199086Srpaulo		155, 155, 155, 156, 156, 156, 157, 157,
1448199086Srpaulo		157, 158, 158, 158, 159, 159, 159, 160
1449199086Srpaulo	};
1450199086Srpaulo	const u_int N = sizeof(sqrt_table) / sizeof(sqrt_table[0]);
1451199086Srpaulo
1452199086Srpaulo	dxdt = stroke->components[X].delta_mickeys;
1453199086Srpaulo	dydt = stroke->components[Y].delta_mickeys;
1454199086Srpaulo
1455199086Srpaulo	*numerator = 0, *denominator = 0; /* default values. */
1456199086Srpaulo
1457199086Srpaulo	/* Compute a smoothened magnitude_squared of the stroke's velocity. */
1458199086Srpaulo	vel_squared = dxdt * dxdt + dydt * dydt;
1459199086Srpaulo	vel_squared_smooth = (3 * stroke->velocity_squared + vel_squared) >> 2;
1460199086Srpaulo	stroke->velocity_squared = vel_squared_smooth; /* retained as history */
1461199086Srpaulo	if ((vel_squared == 0) || (vel_squared_smooth == 0))
1462199086Srpaulo		return; /* returning (numerator == 0) will imply zero movement*/
1463199086Srpaulo
1464199086Srpaulo	/*
1465199086Srpaulo	 * In order to determine the overall movement scale factor,
1466199086Srpaulo	 * we're actually interested in the effect of smoothening upon
1467199086Srpaulo	 * the *magnitude* of velocity; i.e. we need to compute the
1468199086Srpaulo	 * square-root of (vel_squared_smooth / vel_squared) in the
1469199086Srpaulo	 * form of a numerator and denominator.
1470199086Srpaulo	 */
1471199086Srpaulo
1472199086Srpaulo	/* Keep within the bounds of the square-root table. */
1473199086Srpaulo	while ((vel_squared > N) || (vel_squared_smooth > N)) {
1474199086Srpaulo		/* Dividing uniformly by 2 won't disturb the final ratio. */
1475199086Srpaulo		vel_squared        >>= 1;
1476199086Srpaulo		vel_squared_smooth >>= 1;
1477199086Srpaulo	}
1478199086Srpaulo
1479199086Srpaulo	*numerator   = sqrt_table[vel_squared_smooth - 1];
1480199086Srpaulo	*denominator = sqrt_table[vel_squared - 1];
1481199086Srpaulo}
1482199086Srpaulo
1483199086Srpaulo/*
1484199086Srpaulo * Compute a smoothened value for the stroke's movement from
1485199086Srpaulo * delta_mickeys in the X and Y components.
1486199086Srpaulo */
1487199086Srpaulostatic boolean_t
1488199086Srpauloatp_compute_stroke_movement(atp_stroke *stroke)
1489199086Srpaulo{
1490199086Srpaulo	int   num;              /* numerator of scale ratio */
1491199086Srpaulo	int   denom;            /* denominator of scale ratio */
1492199086Srpaulo
1493199086Srpaulo	/*
1494199086Srpaulo	 * Short movements are added first to the 'pending' bucket,
1495199086Srpaulo	 * and then acted upon only when their aggregate exceeds a
1496199086Srpaulo	 * threshold. This has the effect of filtering away movement
1497199086Srpaulo	 * noise.
1498199086Srpaulo	 */
1499199086Srpaulo	if (atp_stroke_has_small_movement(stroke)) {
1500199086Srpaulo		atp_update_pending_mickeys(&stroke->components[X]);
1501199086Srpaulo		atp_update_pending_mickeys(&stroke->components[Y]);
1502199086Srpaulo	} else {                /* large movement */
1503199086Srpaulo		/* clear away any pending mickeys if there are large movements*/
1504199086Srpaulo		stroke->components[X].pending = 0;
1505199086Srpaulo		stroke->components[Y].pending = 0;
1506199086Srpaulo	}
1507199086Srpaulo
1508199086Srpaulo	/* Get the scale ratio and smoothen movement. */
1509199086Srpaulo	atp_compute_smoothening_scale_ratio(stroke, &num, &denom);
1510199086Srpaulo	if ((num == 0) || (denom == 0)) {
1511199086Srpaulo		stroke->components[X].movement = 0;
1512199086Srpaulo		stroke->components[Y].movement = 0;
1513199086Srpaulo		stroke->velocity_squared >>= 1; /* Erode velocity_squared. */
1514199086Srpaulo	} else {
1515199086Srpaulo		stroke->components[X].movement =
1516199086Srpaulo			(stroke->components[X].delta_mickeys * num) / denom;
1517199086Srpaulo		stroke->components[Y].movement =
1518199086Srpaulo			(stroke->components[Y].delta_mickeys * num) / denom;
1519199086Srpaulo
1520199086Srpaulo		stroke->cum_movement +=
1521199086Srpaulo			abs(stroke->components[X].movement) +
1522199086Srpaulo			abs(stroke->components[Y].movement);
1523199086Srpaulo	}
1524199086Srpaulo
1525199086Srpaulo	return ((stroke->components[X].movement != 0) ||
1526199086Srpaulo	    (stroke->components[Y].movement != 0));
1527199086Srpaulo}
1528199086Srpaulo
1529199086Srpaulostatic __inline void
1530199086Srpauloatp_setup_reap_time(struct atp_softc *sc, struct timeval *tvp)
1531199086Srpaulo{
1532199086Srpaulo	struct timeval reap_window = {
1533199086Srpaulo		ATP_ZOMBIE_STROKE_REAP_WINDOW / 1000000,
1534199086Srpaulo		ATP_ZOMBIE_STROKE_REAP_WINDOW % 1000000
1535199086Srpaulo	};
1536199086Srpaulo
1537199086Srpaulo	microtime(&sc->sc_reap_time);
1538199086Srpaulo	timevaladd(&sc->sc_reap_time, &reap_window);
1539199086Srpaulo
1540199086Srpaulo	sc->sc_reap_ctime = *tvp; /* ctime to reap */
1541199086Srpaulo}
1542199086Srpaulo
1543199086Srpaulostatic void
1544199086Srpauloatp_reap_zombies(struct atp_softc *sc, u_int *n_reaped, u_int *reaped_xlocs)
1545199086Srpaulo{
1546199086Srpaulo	u_int       i;
1547199086Srpaulo	atp_stroke *stroke;
1548199086Srpaulo
1549199086Srpaulo	*n_reaped = 0;
1550199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1551199086Srpaulo		struct timeval  tdiff;
1552199086Srpaulo
1553199086Srpaulo		stroke = &sc->sc_strokes[i];
1554199086Srpaulo
1555199086Srpaulo		if ((stroke->flags & ATSF_ZOMBIE) == 0)
1556199086Srpaulo			continue;
1557199086Srpaulo
1558199086Srpaulo		/* Compare this stroke's ctime with the ctime being reaped. */
1559199086Srpaulo		if (timevalcmp(&stroke->ctime, &sc->sc_reap_ctime, >=)) {
1560199086Srpaulo			tdiff = stroke->ctime;
1561199086Srpaulo			timevalsub(&tdiff, &sc->sc_reap_ctime);
1562199086Srpaulo		} else {
1563199086Srpaulo			tdiff = sc->sc_reap_ctime;
1564199086Srpaulo			timevalsub(&tdiff, &stroke->ctime);
1565199086Srpaulo		}
1566199086Srpaulo
1567199086Srpaulo		if ((tdiff.tv_sec > (ATP_COINCIDENCE_THRESHOLD / 1000000)) ||
1568199086Srpaulo		    ((tdiff.tv_sec == (ATP_COINCIDENCE_THRESHOLD / 1000000)) &&
1569199086Srpaulo		     (tdiff.tv_usec > (ATP_COINCIDENCE_THRESHOLD % 1000000)))) {
1570199086Srpaulo			continue; /* Skip non-siblings. */
1571199086Srpaulo		}
1572199086Srpaulo
1573199086Srpaulo		/*
1574199086Srpaulo		 * Reap this sibling zombie stroke.
1575199086Srpaulo		 */
1576199086Srpaulo
1577199086Srpaulo		if (reaped_xlocs != NULL)
1578199086Srpaulo			reaped_xlocs[*n_reaped] = stroke->components[X].loc;
1579199086Srpaulo
1580199086Srpaulo		/* Erase the stroke from the sc. */
1581199086Srpaulo		memcpy(&stroke[i], &stroke[i + 1],
1582199086Srpaulo		    (sc->sc_n_strokes - i - 1) * sizeof(atp_stroke));
1583199086Srpaulo		sc->sc_n_strokes--;
1584199086Srpaulo
1585199086Srpaulo		*n_reaped += 1;
1586199086Srpaulo		--i; /* Decr. i to keep it unchanged for the next iteration */
1587199086Srpaulo	}
1588199086Srpaulo
1589199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", *n_reaped);
1590199086Srpaulo
1591199086Srpaulo	/* There could still be zombies remaining in the system. */
1592199086Srpaulo	for (i = 0; i < sc->sc_n_strokes; i++) {
1593199086Srpaulo		stroke = &sc->sc_strokes[i];
1594199086Srpaulo		if (stroke->flags & ATSF_ZOMBIE) {
1595199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO, "zombies remain!\n");
1596199086Srpaulo			atp_setup_reap_time(sc, &stroke->ctime);
1597199086Srpaulo			return;
1598199086Srpaulo		}
1599199086Srpaulo	}
1600199086Srpaulo
1601199086Srpaulo	/* If we reach here, then no more zombies remain. */
1602199086Srpaulo	sc->sc_state &= ~ATP_ZOMBIES_EXIST;
1603199086Srpaulo}
1604199086Srpaulo
1605199086Srpaulo
1606199086Srpaulo/* Device methods. */
1607199086Srpaulostatic device_probe_t  atp_probe;
1608199086Srpaulostatic device_attach_t atp_attach;
1609199086Srpaulostatic device_detach_t atp_detach;
1610199086Srpaulostatic usb_callback_t  atp_intr;
1611199086Srpaulo
1612199086Srpaulostatic const struct usb_config atp_config[ATP_N_TRANSFER] = {
1613199086Srpaulo	[ATP_INTR_DT] = {
1614199086Srpaulo		.type      = UE_INTERRUPT,
1615199086Srpaulo		.endpoint  = UE_ADDR_ANY,
1616199086Srpaulo		.direction = UE_DIR_IN,
1617199086Srpaulo		.flags = {
1618199086Srpaulo			.pipe_bof = 1,
1619199086Srpaulo			.short_xfer_ok = 1,
1620199086Srpaulo		},
1621199086Srpaulo		.bufsize   = 0, /* use wMaxPacketSize */
1622199086Srpaulo		.callback  = &atp_intr,
1623199086Srpaulo	},
1624199680Sthompsa	[ATP_RESET] = {
1625199680Sthompsa		.type      = UE_CONTROL,
1626199680Sthompsa		.endpoint  = 0, /* Control pipe */
1627199680Sthompsa		.direction = UE_DIR_ANY,
1628199680Sthompsa		.bufsize = sizeof(struct usb_device_request) + MODE_LENGTH,
1629199680Sthompsa		.callback  = &atp_reset_callback,
1630199680Sthompsa		.interval = 0,  /* no pre-delay */
1631199680Sthompsa	},
1632199086Srpaulo};
1633199086Srpaulo
1634199086Srpaulostatic int
1635199086Srpauloatp_probe(device_t self)
1636199086Srpaulo{
1637199086Srpaulo	struct usb_attach_arg *uaa = device_get_ivars(self);
1638199086Srpaulo
1639199086Srpaulo	if (uaa->usb_mode != USB_MODE_HOST)
1640199086Srpaulo		return (ENXIO);
1641199086Srpaulo
1642199086Srpaulo	if ((uaa->info.bInterfaceClass != UICLASS_HID) ||
1643199086Srpaulo	    (uaa->info.bInterfaceProtocol != UIPROTO_MOUSE))
1644199086Srpaulo		return (ENXIO);
1645199086Srpaulo
1646199680Sthompsa	return (usbd_lookup_id_by_uaa(atp_devs, sizeof(atp_devs), uaa));
1647199086Srpaulo}
1648199086Srpaulo
1649199086Srpaulostatic int
1650199086Srpauloatp_attach(device_t dev)
1651199086Srpaulo{
1652199086Srpaulo	struct atp_softc      *sc = device_get_softc(dev);
1653199086Srpaulo	struct usb_attach_arg *uaa = device_get_ivars(dev);
1654199086Srpaulo	usb_error_t            err;
1655199086Srpaulo
1656199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc);
1657199086Srpaulo
1658199086Srpaulo	sc->sc_dev        = dev;
1659199086Srpaulo	sc->sc_usb_device = uaa->device;
1660199086Srpaulo
1661199086Srpaulo	/*
1662199086Srpaulo	 * By default the touchpad behaves like an HID device, sending
1663199086Srpaulo	 * packets with reportID = 2. Such reports contain only
1664199086Srpaulo	 * limited information--they encode movement deltas and button
1665199086Srpaulo	 * events,--but do not include data from the pressure
1666199086Srpaulo	 * sensors. The device input mode can be switched from HID
1667199086Srpaulo	 * reports to raw sensor data using vendor-specific USB
1668199086Srpaulo	 * control commands; but first the mode must be read.
1669199086Srpaulo	 */
1670199086Srpaulo	err = atp_req_get_report(sc->sc_usb_device, sc->sc_mode_bytes);
1671199086Srpaulo	if (err != USB_ERR_NORMAL_COMPLETION) {
1672199086Srpaulo		DPRINTF("failed to read device mode (%d)\n", err);
1673199086Srpaulo		return (ENXIO);
1674199086Srpaulo	}
1675199086Srpaulo
1676199086Srpaulo	if (atp_set_device_mode(dev, RAW_SENSOR_MODE) != 0) {
1677199086Srpaulo		DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err);
1678199086Srpaulo		return (ENXIO);
1679199086Srpaulo	}
1680199086Srpaulo
1681199086Srpaulo	mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE);
1682199086Srpaulo
1683199086Srpaulo	err = usbd_transfer_setup(uaa->device,
1684199086Srpaulo	    &uaa->info.bIfaceIndex, sc->sc_xfer, atp_config,
1685199086Srpaulo	    ATP_N_TRANSFER, sc, &sc->sc_mutex);
1686199086Srpaulo
1687199086Srpaulo	if (err) {
1688199086Srpaulo		DPRINTF("error=%s\n", usbd_errstr(err));
1689199086Srpaulo		goto detach;
1690199086Srpaulo	}
1691199086Srpaulo
1692199086Srpaulo	if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex,
1693199086Srpaulo		&atp_fifo_methods, &sc->sc_fifo,
1694233774Shselasky		device_get_unit(dev), -1, uaa->info.bIfaceIndex,
1695199086Srpaulo		UID_ROOT, GID_OPERATOR, 0644)) {
1696199086Srpaulo		goto detach;
1697199086Srpaulo	}
1698199086Srpaulo
1699199086Srpaulo	device_set_usb_desc(dev);
1700199086Srpaulo
1701199086Srpaulo	sc->sc_params           = &atp_dev_params[uaa->driver_info];
1702199086Srpaulo
1703199086Srpaulo	sc->sc_hw.buttons       = 3;
1704199086Srpaulo	sc->sc_hw.iftype        = MOUSE_IF_USB;
1705199086Srpaulo	sc->sc_hw.type          = MOUSE_PAD;
1706199086Srpaulo	sc->sc_hw.model         = MOUSE_MODEL_GENERIC;
1707199086Srpaulo	sc->sc_hw.hwid          = 0;
1708199086Srpaulo	sc->sc_mode.protocol    = MOUSE_PROTO_MSC;
1709199086Srpaulo	sc->sc_mode.rate        = -1;
1710199086Srpaulo	sc->sc_mode.resolution  = MOUSE_RES_UNKNOWN;
1711199086Srpaulo	sc->sc_mode.accelfactor = 0;
1712199086Srpaulo	sc->sc_mode.level       = 0;
1713199086Srpaulo	sc->sc_mode.packetsize  = MOUSE_MSC_PACKETSIZE;
1714199086Srpaulo	sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
1715199086Srpaulo	sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
1716199086Srpaulo
1717199086Srpaulo	sc->sc_state            = 0;
1718199086Srpaulo
1719199086Srpaulo	sc->sc_left_margin  = atp_mickeys_scale_factor;
1720199086Srpaulo	sc->sc_right_margin = (sc->sc_params->n_xsensors - 1) *
1721199086Srpaulo		atp_mickeys_scale_factor;
1722199086Srpaulo
1723199086Srpaulo	return (0);
1724199086Srpaulo
1725199086Srpaulodetach:
1726199086Srpaulo	atp_detach(dev);
1727199086Srpaulo	return (ENOMEM);
1728199086Srpaulo}
1729199086Srpaulo
1730199086Srpaulostatic int
1731199086Srpauloatp_detach(device_t dev)
1732199086Srpaulo{
1733199086Srpaulo	struct atp_softc *sc;
1734199086Srpaulo
1735199086Srpaulo	sc = device_get_softc(dev);
1736199086Srpaulo	if (sc->sc_state & ATP_ENABLED) {
1737199086Srpaulo		mtx_lock(&sc->sc_mutex);
1738199086Srpaulo		atp_disable(sc);
1739199086Srpaulo		mtx_unlock(&sc->sc_mutex);
1740199086Srpaulo	}
1741199086Srpaulo
1742199086Srpaulo	usb_fifo_detach(&sc->sc_fifo);
1743199086Srpaulo
1744199086Srpaulo	usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER);
1745199086Srpaulo
1746199086Srpaulo	mtx_destroy(&sc->sc_mutex);
1747199086Srpaulo
1748199086Srpaulo	return (0);
1749199086Srpaulo}
1750199086Srpaulo
1751199086Srpaulostatic void
1752199086Srpauloatp_intr(struct usb_xfer *xfer, usb_error_t error)
1753199086Srpaulo{
1754199086Srpaulo	struct atp_softc      *sc = usbd_xfer_softc(xfer);
1755199086Srpaulo	int                    len;
1756199086Srpaulo	struct usb_page_cache *pc;
1757199086Srpaulo	uint8_t                status_bits;
1758199086Srpaulo	atp_pspan  pspans_x[ATP_MAX_PSPANS_PER_AXIS];
1759199086Srpaulo	atp_pspan  pspans_y[ATP_MAX_PSPANS_PER_AXIS];
1760199086Srpaulo	u_int      n_xpspans = 0, n_ypspans = 0;
1761199086Srpaulo	u_int      reaped_xlocs[ATP_MAX_STROKES];
1762199086Srpaulo	u_int      tap_fingers = 0;
1763199086Srpaulo
1764199086Srpaulo	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
1765199086Srpaulo
1766199086Srpaulo	switch (USB_GET_STATE(xfer)) {
1767199086Srpaulo	case USB_ST_TRANSFERRED:
1768233774Shselasky		if (len > (int)sc->sc_params->data_len) {
1769199086Srpaulo			DPRINTFN(ATP_LLEVEL_ERROR,
1770199086Srpaulo			    "truncating large packet from %u to %u bytes\n",
1771199086Srpaulo			    len, sc->sc_params->data_len);
1772199086Srpaulo			len = sc->sc_params->data_len;
1773199086Srpaulo		}
1774233774Shselasky		if (len < (int)sc->sc_params->data_len)
1775199086Srpaulo			goto tr_setup;
1776199086Srpaulo
1777199086Srpaulo		pc = usbd_xfer_get_frame(xfer, 0);
1778199086Srpaulo		usbd_copy_out(pc, 0, sc->sensor_data, sc->sc_params->data_len);
1779199086Srpaulo
1780199086Srpaulo		/* Interpret sensor data */
1781199086Srpaulo		atp_interpret_sensor_data(sc->sensor_data,
1782199151Snwhitehorn		    sc->sc_params->n_xsensors, X, sc->cur_x,
1783199151Snwhitehorn		    sc->sc_params->prot);
1784199086Srpaulo		atp_interpret_sensor_data(sc->sensor_data,
1785199151Snwhitehorn		    sc->sc_params->n_ysensors, Y,  sc->cur_y,
1786199151Snwhitehorn		    sc->sc_params->prot);
1787199086Srpaulo
1788199086Srpaulo		/*
1789199086Srpaulo		 * If this is the initial update (from an untouched
1790199086Srpaulo		 * pad), we should set the base values for the sensor
1791199086Srpaulo		 * data; deltas with respect to these base values can
1792199086Srpaulo		 * be used as pressure readings subsequently.
1793199086Srpaulo		 */
1794199086Srpaulo		status_bits = sc->sensor_data[sc->sc_params->data_len - 1];
1795199151Snwhitehorn		if ((sc->sc_params->prot == ATP_PROT_GEYSER3 &&
1796200241Srpaulo		    (status_bits & ATP_STATUS_BASE_UPDATE)) ||
1797199151Snwhitehorn		    !(sc->sc_state & ATP_VALID)) {
1798199086Srpaulo			memcpy(sc->base_x, sc->cur_x,
1799199086Srpaulo			    sc->sc_params->n_xsensors * sizeof(*(sc->base_x)));
1800199086Srpaulo			memcpy(sc->base_y, sc->cur_y,
1801199086Srpaulo			    sc->sc_params->n_ysensors * sizeof(*(sc->base_y)));
1802199151Snwhitehorn			sc->sc_state |= ATP_VALID;
1803199086Srpaulo			goto tr_setup;
1804199086Srpaulo		}
1805199086Srpaulo
1806199086Srpaulo		/* Get pressure readings and detect p-spans for both axes. */
1807199086Srpaulo		atp_get_pressures(sc->pressure_x, sc->cur_x, sc->base_x,
1808199086Srpaulo		    sc->sc_params->n_xsensors);
1809199086Srpaulo		atp_detect_pspans(sc->pressure_x, sc->sc_params->n_xsensors,
1810199086Srpaulo		    ATP_MAX_PSPANS_PER_AXIS,
1811199086Srpaulo		    pspans_x, &n_xpspans);
1812199086Srpaulo		atp_get_pressures(sc->pressure_y, sc->cur_y, sc->base_y,
1813199086Srpaulo		    sc->sc_params->n_ysensors);
1814199086Srpaulo		atp_detect_pspans(sc->pressure_y, sc->sc_params->n_ysensors,
1815199086Srpaulo		    ATP_MAX_PSPANS_PER_AXIS,
1816199086Srpaulo		    pspans_y, &n_ypspans);
1817199086Srpaulo
1818199086Srpaulo		/* Update strokes with new pspans to detect movements. */
1819199086Srpaulo		sc->sc_status.flags &= ~MOUSE_POSCHANGED;
1820199086Srpaulo		if (atp_update_strokes(sc,
1821199086Srpaulo			pspans_x, n_xpspans,
1822199086Srpaulo			pspans_y, n_ypspans))
1823199086Srpaulo			sc->sc_status.flags |= MOUSE_POSCHANGED;
1824199086Srpaulo
1825199086Srpaulo		/* Reap zombies if it is time. */
1826199086Srpaulo		if (sc->sc_state & ATP_ZOMBIES_EXIST) {
1827199086Srpaulo			struct timeval now;
1828199086Srpaulo
1829199086Srpaulo			getmicrotime(&now);
1830199086Srpaulo			if (timevalcmp(&now, &sc->sc_reap_time, >=))
1831199086Srpaulo				atp_reap_zombies(sc, &tap_fingers,
1832199086Srpaulo				    reaped_xlocs);
1833199086Srpaulo		}
1834199086Srpaulo
1835199086Srpaulo		sc->sc_status.flags &= ~MOUSE_STDBUTTONSCHANGED;
1836199086Srpaulo		sc->sc_status.obutton = sc->sc_status.button;
1837199086Srpaulo
1838199086Srpaulo		/* Get the state of the physical buttton. */
1839199086Srpaulo		sc->sc_status.button = (status_bits & ATP_STATUS_BUTTON) ?
1840199086Srpaulo			MOUSE_BUTTON1DOWN : 0;
1841199086Srpaulo		if (sc->sc_status.button != 0) {
1842199086Srpaulo			/* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */
1843199086Srpaulo			sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG;
1844199086Srpaulo		} else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) {
1845199086Srpaulo			/* Assume a button-press with DOUBLE_TAP_N_DRAG. */
1846199086Srpaulo			sc->sc_status.button = MOUSE_BUTTON1DOWN;
1847199086Srpaulo		}
1848199086Srpaulo
1849199086Srpaulo		sc->sc_status.flags |=
1850199086Srpaulo			sc->sc_status.button ^ sc->sc_status.obutton;
1851199086Srpaulo		if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) {
1852199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO, "button %s\n",
1853199086Srpaulo			    ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ?
1854199086Srpaulo				"pressed" : "released"));
1855199086Srpaulo		} else if ((sc->sc_status.obutton == 0) &&
1856199086Srpaulo		    (sc->sc_status.button == 0) &&
1857199086Srpaulo		    (tap_fingers != 0)) {
1858199086Srpaulo			/* Ignore single-finger taps at the edges. */
1859199086Srpaulo			if ((tap_fingers == 1) &&
1860199086Srpaulo			    ((reaped_xlocs[0] <= sc->sc_left_margin) ||
1861199086Srpaulo				(reaped_xlocs[0] > sc->sc_right_margin))) {
1862199086Srpaulo				tap_fingers = 0;
1863199086Srpaulo			}
1864199086Srpaulo			DPRINTFN(ATP_LLEVEL_INFO,
1865199086Srpaulo			    "tap_fingers: %u\n", tap_fingers);
1866199086Srpaulo		}
1867199086Srpaulo
1868199086Srpaulo		if (sc->sc_status.flags &
1869199086Srpaulo		    (MOUSE_POSCHANGED | MOUSE_STDBUTTONSCHANGED)) {
1870199086Srpaulo			int   dx, dy;
1871199086Srpaulo			u_int n_movements;
1872199086Srpaulo
1873199086Srpaulo			dx = 0, dy = 0, n_movements = 0;
1874199086Srpaulo			for (u_int i = 0; i < sc->sc_n_strokes; i++) {
1875199086Srpaulo				atp_stroke *stroke = &sc->sc_strokes[i];
1876199086Srpaulo
1877199086Srpaulo				if ((stroke->components[X].movement) ||
1878199086Srpaulo				    (stroke->components[Y].movement)) {
1879199086Srpaulo					dx += stroke->components[X].movement;
1880199086Srpaulo					dy += stroke->components[Y].movement;
1881199086Srpaulo					n_movements++;
1882199086Srpaulo				}
1883199086Srpaulo			}
1884199086Srpaulo			/*
1885199086Srpaulo			 * Disregard movement if multiple
1886199086Srpaulo			 * strokes record motion.
1887199086Srpaulo			 */
1888199086Srpaulo			if (n_movements != 1)
1889199086Srpaulo				dx = 0, dy = 0;
1890199086Srpaulo
1891199086Srpaulo			sc->sc_status.dx += dx;
1892199086Srpaulo			sc->sc_status.dy += dy;
1893199086Srpaulo			atp_add_to_queue(sc, dx, -dy, sc->sc_status.button);
1894199086Srpaulo		}
1895199086Srpaulo
1896199086Srpaulo		if (tap_fingers != 0) {
1897199086Srpaulo			/* Add a pair of events (button-down and button-up). */
1898199086Srpaulo			switch (tap_fingers) {
1899199086Srpaulo			case 1: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON1DOWN);
1900199086Srpaulo				break;
1901199086Srpaulo			case 2: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON2DOWN);
1902199086Srpaulo				break;
1903199086Srpaulo			case 3: atp_add_to_queue(sc, 0, 0, MOUSE_BUTTON3DOWN);
1904199086Srpaulo				break;
1905199086Srpaulo			default: break;/* handle taps of only up to 3 fingers */
1906199086Srpaulo			}
1907199086Srpaulo			atp_add_to_queue(sc, 0, 0, 0); /* button release */
1908199086Srpaulo		}
1909199086Srpaulo
1910199086Srpaulo		/*
1911199086Srpaulo		 * The device continues to trigger interrupts at a
1912199086Srpaulo		 * fast rate even after touchpad activity has
1913199086Srpaulo		 * stopped. Upon detecting that the device has
1914199086Srpaulo		 * remained idle beyond a threshold, we reinitialize
1915199086Srpaulo		 * it to silence the interrupts.
1916199086Srpaulo		 */
1917199086Srpaulo		if ((sc->sc_status.flags  == 0) &&
1918199086Srpaulo		    (sc->sc_n_strokes     == 0) &&
1919199086Srpaulo		    (sc->sc_status.button == 0)) {
1920199086Srpaulo			sc->sc_idlecount++;
1921199086Srpaulo			if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) {
1922199086Srpaulo				DPRINTFN(ATP_LLEVEL_INFO, "idle\n");
1923199948Snwhitehorn
1924199948Snwhitehorn				/*
1925199948Snwhitehorn				 * Use the last frame before we go idle for
1926199948Snwhitehorn				 * calibration on pads which do not send
1927199948Snwhitehorn				 * calibration frames.
1928199948Snwhitehorn				 */
1929199948Snwhitehorn				if (sc->sc_params->prot < ATP_PROT_GEYSER3) {
1930199948Snwhitehorn					memcpy(sc->base_x, sc->cur_x,
1931199948Snwhitehorn					    sc->sc_params->n_xsensors *
1932199948Snwhitehorn					    sizeof(*(sc->base_x)));
1933199948Snwhitehorn					memcpy(sc->base_y, sc->cur_y,
1934199948Snwhitehorn					    sc->sc_params->n_ysensors *
1935199948Snwhitehorn					    sizeof(*(sc->base_y)));
1936199948Snwhitehorn				}
1937199948Snwhitehorn
1938199151Snwhitehorn				sc->sc_idlecount = 0;
1939199680Sthompsa				usbd_transfer_start(sc->sc_xfer[ATP_RESET]);
1940199086Srpaulo			}
1941199086Srpaulo		} else {
1942199086Srpaulo			sc->sc_idlecount = 0;
1943199086Srpaulo		}
1944199086Srpaulo
1945199086Srpaulo	case USB_ST_SETUP:
1946199086Srpaulo	tr_setup:
1947199086Srpaulo		/* check if we can put more data into the FIFO */
1948199086Srpaulo		if (usb_fifo_put_bytes_max(
1949199086Srpaulo			    sc->sc_fifo.fp[USB_FIFO_RX]) != 0) {
1950199086Srpaulo			usbd_xfer_set_frame_len(xfer, 0,
1951199151Snwhitehorn			    sc->sc_params->data_len);
1952199086Srpaulo			usbd_transfer_submit(xfer);
1953199086Srpaulo		}
1954199086Srpaulo		break;
1955199086Srpaulo
1956199086Srpaulo	default:                        /* Error */
1957199086Srpaulo		if (error != USB_ERR_CANCELLED) {
1958199086Srpaulo			/* try clear stall first */
1959199086Srpaulo			usbd_xfer_set_stall(xfer);
1960199086Srpaulo			goto tr_setup;
1961199086Srpaulo		}
1962199086Srpaulo		break;
1963199086Srpaulo	}
1964199086Srpaulo
1965199086Srpaulo	return;
1966199086Srpaulo}
1967199086Srpaulo
1968199086Srpaulostatic void
1969199086Srpauloatp_add_to_queue(struct atp_softc *sc, int dx, int dy, uint32_t buttons_in)
1970199086Srpaulo{
1971199086Srpaulo	uint32_t buttons_out;
1972199086Srpaulo	uint8_t  buf[8];
1973199086Srpaulo
1974199086Srpaulo	dx = imin(dx,  254); dx = imax(dx, -256);
1975199086Srpaulo	dy = imin(dy,  254); dy = imax(dy, -256);
1976199086Srpaulo
1977199086Srpaulo	buttons_out = MOUSE_MSC_BUTTONS;
1978199086Srpaulo	if (buttons_in & MOUSE_BUTTON1DOWN)
1979199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON1UP;
1980199086Srpaulo	else if (buttons_in & MOUSE_BUTTON2DOWN)
1981199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON2UP;
1982199086Srpaulo	else if (buttons_in & MOUSE_BUTTON3DOWN)
1983199086Srpaulo		buttons_out &= ~MOUSE_MSC_BUTTON3UP;
1984199086Srpaulo
1985199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n",
1986199086Srpaulo	    dx, dy, buttons_out);
1987199086Srpaulo
1988199086Srpaulo	/* Encode the mouse data in standard format; refer to mouse(4) */
1989199086Srpaulo	buf[0] = sc->sc_mode.syncmask[1];
1990199086Srpaulo	buf[0] |= buttons_out;
1991199086Srpaulo	buf[1] = dx >> 1;
1992199086Srpaulo	buf[2] = dy >> 1;
1993199086Srpaulo	buf[3] = dx - (dx >> 1);
1994199086Srpaulo	buf[4] = dy - (dy >> 1);
1995199086Srpaulo	/* Encode extra bytes for level 1 */
1996199086Srpaulo	if (sc->sc_mode.level == 1) {
1997199086Srpaulo		buf[5] = 0;                    /* dz */
1998199086Srpaulo		buf[6] = 0;                    /* dz - (dz / 2) */
1999199086Srpaulo		buf[7] = MOUSE_SYS_EXTBUTTONS; /* Extra buttons all up. */
2000199086Srpaulo	}
2001199086Srpaulo
2002199086Srpaulo	usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf,
2003199086Srpaulo	    sc->sc_mode.packetsize, 1);
2004199086Srpaulo}
2005199086Srpaulo
2006199086Srpaulostatic void
2007199086Srpauloatp_reset_buf(struct atp_softc *sc)
2008199086Srpaulo{
2009199086Srpaulo	/* reset read queue */
2010199086Srpaulo	usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]);
2011199086Srpaulo}
2012199086Srpaulo
2013199086Srpaulostatic void
2014199086Srpauloatp_start_read(struct usb_fifo *fifo)
2015199086Srpaulo{
2016199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
2017199086Srpaulo	int rate;
2018199086Srpaulo
2019199086Srpaulo	/* Check if we should override the default polling interval */
2020199086Srpaulo	rate = sc->sc_pollrate;
2021199086Srpaulo	/* Range check rate */
2022199086Srpaulo	if (rate > 1000)
2023199086Srpaulo		rate = 1000;
2024199086Srpaulo	/* Check for set rate */
2025199086Srpaulo	if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) {
2026199086Srpaulo		/* Stop current transfer, if any */
2027199086Srpaulo		usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
2028199086Srpaulo		/* Set new interval */
2029199086Srpaulo		usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate);
2030199086Srpaulo		/* Only set pollrate once */
2031199086Srpaulo		sc->sc_pollrate = 0;
2032199086Srpaulo	}
2033199086Srpaulo
2034199086Srpaulo	usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]);
2035199086Srpaulo}
2036199086Srpaulo
2037199086Srpaulostatic void
2038199086Srpauloatp_stop_read(struct usb_fifo *fifo)
2039199086Srpaulo{
2040199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
2041199086Srpaulo
2042199086Srpaulo	usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]);
2043199086Srpaulo}
2044199086Srpaulo
2045199086Srpaulo
2046199086Srpaulostatic int
2047199086Srpauloatp_open(struct usb_fifo *fifo, int fflags)
2048199086Srpaulo{
2049199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "\n");
2050199086Srpaulo
2051199086Srpaulo	if (fflags & FREAD) {
2052199086Srpaulo		struct atp_softc *sc = usb_fifo_softc(fifo);
2053199086Srpaulo		int rc;
2054199086Srpaulo
2055199086Srpaulo		if (sc->sc_state & ATP_ENABLED)
2056199086Srpaulo			return (EBUSY);
2057199086Srpaulo
2058199086Srpaulo		if (usb_fifo_alloc_buffer(fifo,
2059199086Srpaulo			ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) {
2060199086Srpaulo			return (ENOMEM);
2061199086Srpaulo		}
2062199086Srpaulo
2063199086Srpaulo		rc = atp_enable(sc);
2064199086Srpaulo		if (rc != 0) {
2065199086Srpaulo			usb_fifo_free_buffer(fifo);
2066199086Srpaulo			return (rc);
2067199086Srpaulo		}
2068199086Srpaulo	}
2069199086Srpaulo
2070199086Srpaulo	return (0);
2071199086Srpaulo}
2072199086Srpaulo
2073199086Srpaulostatic void
2074199086Srpauloatp_close(struct usb_fifo *fifo, int fflags)
2075199086Srpaulo{
2076199086Srpaulo	if (fflags & FREAD) {
2077199086Srpaulo		struct atp_softc *sc = usb_fifo_softc(fifo);
2078199086Srpaulo
2079199086Srpaulo		atp_disable(sc);
2080199086Srpaulo		usb_fifo_free_buffer(fifo);
2081199086Srpaulo	}
2082199086Srpaulo}
2083199086Srpaulo
2084199086Srpauloint
2085199086Srpauloatp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags)
2086199086Srpaulo{
2087199086Srpaulo	struct atp_softc *sc = usb_fifo_softc(fifo);
2088199086Srpaulo	mousemode_t mode;
2089199086Srpaulo	int error = 0;
2090199086Srpaulo
2091199086Srpaulo	mtx_lock(&sc->sc_mutex);
2092199086Srpaulo
2093199086Srpaulo	switch(cmd) {
2094199086Srpaulo	case MOUSE_GETHWINFO:
2095199086Srpaulo		*(mousehw_t *)addr = sc->sc_hw;
2096199086Srpaulo		break;
2097199086Srpaulo	case MOUSE_GETMODE:
2098199086Srpaulo		*(mousemode_t *)addr = sc->sc_mode;
2099199086Srpaulo		break;
2100199086Srpaulo	case MOUSE_SETMODE:
2101199086Srpaulo		mode = *(mousemode_t *)addr;
2102199086Srpaulo
2103199086Srpaulo		if (mode.level == -1)
2104199086Srpaulo			/* Don't change the current setting */
2105199086Srpaulo			;
2106199086Srpaulo		else if ((mode.level < 0) || (mode.level > 1)) {
2107199086Srpaulo			error = EINVAL;
2108199086Srpaulo			goto done;
2109199086Srpaulo		}
2110199086Srpaulo		sc->sc_mode.level = mode.level;
2111199086Srpaulo		sc->sc_pollrate   = mode.rate;
2112199086Srpaulo		sc->sc_hw.buttons = 3;
2113199086Srpaulo
2114199086Srpaulo		if (sc->sc_mode.level == 0) {
2115199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
2116199086Srpaulo			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
2117199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
2118199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
2119199086Srpaulo		} else if (sc->sc_mode.level == 1) {
2120199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
2121199086Srpaulo			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
2122199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
2123199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
2124199086Srpaulo		}
2125199086Srpaulo		atp_reset_buf(sc);
2126199086Srpaulo		break;
2127199086Srpaulo	case MOUSE_GETLEVEL:
2128199086Srpaulo		*(int *)addr = sc->sc_mode.level;
2129199086Srpaulo		break;
2130199086Srpaulo	case MOUSE_SETLEVEL:
2131199086Srpaulo		if (*(int *)addr < 0 || *(int *)addr > 1) {
2132199086Srpaulo			error = EINVAL;
2133199086Srpaulo			goto done;
2134199086Srpaulo		}
2135199086Srpaulo		sc->sc_mode.level = *(int *)addr;
2136199086Srpaulo		sc->sc_hw.buttons = 3;
2137199086Srpaulo
2138199086Srpaulo		if (sc->sc_mode.level == 0) {
2139199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_MSC;
2140199086Srpaulo			sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE;
2141199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK;
2142199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC;
2143199086Srpaulo		} else if (sc->sc_mode.level == 1) {
2144199086Srpaulo			sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE;
2145199086Srpaulo			sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE;
2146199086Srpaulo			sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK;
2147199086Srpaulo			sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC;
2148199086Srpaulo		}
2149199086Srpaulo		atp_reset_buf(sc);
2150199086Srpaulo		break;
2151199086Srpaulo	case MOUSE_GETSTATUS: {
2152199086Srpaulo		mousestatus_t *status = (mousestatus_t *)addr;
2153199086Srpaulo
2154199086Srpaulo		*status = sc->sc_status;
2155199086Srpaulo		sc->sc_status.obutton = sc->sc_status.button;
2156199086Srpaulo		sc->sc_status.button  = 0;
2157199086Srpaulo		sc->sc_status.dx = 0;
2158199086Srpaulo		sc->sc_status.dy = 0;
2159199086Srpaulo		sc->sc_status.dz = 0;
2160199086Srpaulo
2161199086Srpaulo		if (status->dx || status->dy || status->dz)
2162199086Srpaulo			status->flags |= MOUSE_POSCHANGED;
2163199086Srpaulo		if (status->button != status->obutton)
2164199086Srpaulo			status->flags |= MOUSE_BUTTONSCHANGED;
2165199086Srpaulo		break;
2166199086Srpaulo	}
2167199086Srpaulo	default:
2168199086Srpaulo		error = ENOTTY;
2169199086Srpaulo	}
2170199086Srpaulo
2171199086Srpaulodone:
2172199086Srpaulo	mtx_unlock(&sc->sc_mutex);
2173199086Srpaulo	return (error);
2174199086Srpaulo}
2175199086Srpaulo
2176199086Srpaulostatic int
2177199086Srpauloatp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS)
2178199086Srpaulo{
2179199086Srpaulo	int error;
2180199086Srpaulo	u_int tmp;
2181199086Srpaulo	u_int prev_mickeys_scale_factor;
2182199086Srpaulo
2183199086Srpaulo	prev_mickeys_scale_factor = atp_mickeys_scale_factor;
2184199086Srpaulo
2185199086Srpaulo	tmp = atp_mickeys_scale_factor;
2186199086Srpaulo	error = sysctl_handle_int(oidp, &tmp, 0, req);
2187199086Srpaulo	if (error != 0 || req->newptr == NULL)
2188199086Srpaulo		return (error);
2189199086Srpaulo
2190199086Srpaulo	if (tmp == prev_mickeys_scale_factor)
2191199086Srpaulo		return (0);     /* no change */
2192199086Srpaulo
2193199086Srpaulo	atp_mickeys_scale_factor = tmp;
2194199086Srpaulo	DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n",
2195199086Srpaulo	    ATP_DRIVER_NAME, tmp);
2196199086Srpaulo
2197199086Srpaulo	/* Update dependent thresholds. */
2198199086Srpaulo	if (atp_small_movement_threshold == (prev_mickeys_scale_factor >> 3))
2199199086Srpaulo		atp_small_movement_threshold = atp_mickeys_scale_factor >> 3;
2200199086Srpaulo	if (atp_max_delta_mickeys == ((3 * prev_mickeys_scale_factor) >> 1))
2201199086Srpaulo		atp_max_delta_mickeys = ((3 * atp_mickeys_scale_factor) >>1);
2202199086Srpaulo	if (atp_slide_min_movement == (prev_mickeys_scale_factor >> 3))
2203199086Srpaulo		atp_slide_min_movement = atp_mickeys_scale_factor >> 3;
2204199086Srpaulo
2205199086Srpaulo	return (0);
2206199086Srpaulo}
2207199086Srpaulo
2208199086Srpaulostatic device_method_t atp_methods[] = {
2209199086Srpaulo	/* Device interface */
2210199086Srpaulo	DEVMETHOD(device_probe,  atp_probe),
2211199086Srpaulo	DEVMETHOD(device_attach, atp_attach),
2212199086Srpaulo	DEVMETHOD(device_detach, atp_detach),
2213199086Srpaulo	{ 0, 0 }
2214199086Srpaulo};
2215199086Srpaulo
2216199086Srpaulostatic driver_t atp_driver = {
2217233774Shselasky	.name = ATP_DRIVER_NAME,
2218233774Shselasky	.methods = atp_methods,
2219233774Shselasky	.size = sizeof(struct atp_softc)
2220199086Srpaulo};
2221199086Srpaulo
2222199086Srpaulostatic devclass_t atp_devclass;
2223199086Srpaulo
2224199086SrpauloDRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0);
2225199086SrpauloMODULE_DEPEND(atp, usb, 1, 1, 1);
2226212122SthompsaMODULE_VERSION(atp, 1);
2227