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