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