atp.c revision 262417
1199086Srpaulo/*- 2262417Shselasky * Copyright (c) 2014 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 27262417Shselasky/* 28262417Shselasky * Some tables, structures, definitions and constant values for the 29262417Shselasky * touchpad protocol has been copied from Linux's 30262417Shselasky * "drivers/input/mouse/bcm5974.c" which has the following copyright 31262417Shselasky * holders under GPLv2. All device specific code in this driver has 32262417Shselasky * been written from scratch. The decoding algorithm is based on 33262417Shselasky * output from FreeBSD's usbdump. 34262417Shselasky * 35262417Shselasky * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) 36262417Shselasky * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) 37262417Shselasky * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) 38262417Shselasky * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) 39262417Shselasky * Copyright (C) 2005 Stelian Pop (stelian@popies.net) 40262417Shselasky * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) 41262417Shselasky * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) 42262417Shselasky * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) 43262417Shselasky * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) 44262417Shselasky */ 45262417Shselasky 46262417Shselasky/* 47262417Shselasky * Author's note: 'atp' supports two distinct families of Apple trackpad 48262417Shselasky * products: the older Fountain/Geyser and the latest Wellspring trackpads. 49262417Shselasky * The first version made its appearance with FreeBSD 8 and worked only with 50262417Shselasky * the Fountain/Geyser hardware. A fork of this driver for Wellspring was 51262417Shselasky * contributed by Huang Wen Hui. This driver unifies the Wellspring effort 52262417Shselasky * and also improves upon the original work. 53262417Shselasky * 54262417Shselasky * I'm grateful to Stephan Scheunig, Angela Naegele, and Nokia IT-support 55262417Shselasky * for helping me with access to hardware. Thanks also go to Nokia for 56262417Shselasky * giving me an opportunity to do this work. 57262417Shselasky */ 58262417Shselasky 59199086Srpaulo#include <sys/cdefs.h> 60199086Srpaulo__FBSDID("$FreeBSD: head/sys/dev/usb/input/atp.c 262417 2014-02-23 23:36:32Z hselasky $"); 61199086Srpaulo 62262417Shselasky#include <sys/stdint.h> 63262417Shselasky#include <sys/stddef.h> 64199086Srpaulo#include <sys/param.h> 65262417Shselasky#include <sys/types.h> 66199086Srpaulo#include <sys/systm.h> 67199086Srpaulo#include <sys/kernel.h> 68262417Shselasky#include <sys/bus.h> 69199086Srpaulo#include <sys/module.h> 70199086Srpaulo#include <sys/lock.h> 71199086Srpaulo#include <sys/mutex.h> 72262417Shselasky#include <sys/sysctl.h> 73262417Shselasky#include <sys/malloc.h> 74199086Srpaulo#include <sys/conf.h> 75199086Srpaulo#include <sys/fcntl.h> 76199086Srpaulo#include <sys/file.h> 77199086Srpaulo#include <sys/selinfo.h> 78199086Srpaulo#include <sys/poll.h> 79199086Srpaulo 80199086Srpaulo#include <dev/usb/usb.h> 81199086Srpaulo#include <dev/usb/usbdi.h> 82199086Srpaulo#include <dev/usb/usbdi_util.h> 83199086Srpaulo#include <dev/usb/usbhid.h> 84262417Shselasky 85199086Srpaulo#include "usbdevs.h" 86199086Srpaulo 87199086Srpaulo#define USB_DEBUG_VAR atp_debug 88199086Srpaulo#include <dev/usb/usb_debug.h> 89199086Srpaulo 90199086Srpaulo#include <sys/mouse.h> 91199086Srpaulo 92199086Srpaulo#define ATP_DRIVER_NAME "atp" 93199086Srpaulo 94199086Srpaulo/* 95199086Srpaulo * Driver specific options: the following options may be set by 96199086Srpaulo * `options' statements in the kernel configuration file. 97199086Srpaulo */ 98199086Srpaulo 99262417Shselasky/* The divisor used to translate sensor reported positions to mickeys. */ 100199086Srpaulo#ifndef ATP_SCALE_FACTOR 101262417Shselasky#define ATP_SCALE_FACTOR 16 102199086Srpaulo#endif 103199086Srpaulo 104262417Shselasky/* Threshold for small movement noise (in mickeys) */ 105262417Shselasky#ifndef ATP_SMALL_MOVEMENT_THRESHOLD 106262417Shselasky#define ATP_SMALL_MOVEMENT_THRESHOLD 30 107262417Shselasky#endif 108262417Shselasky 109262417Shselasky/* Threshold of instantaneous deltas beyond which movement is considered fast.*/ 110262417Shselasky#ifndef ATP_FAST_MOVEMENT_TRESHOLD 111262417Shselasky#define ATP_FAST_MOVEMENT_TRESHOLD 150 112262417Shselasky#endif 113262417Shselasky 114199086Srpaulo/* 115262417Shselasky * This is the age in microseconds beyond which a touch is considered 116262417Shselasky * to be a slide; and therefore a tap event isn't registered. 117199086Srpaulo */ 118199086Srpaulo#ifndef ATP_TOUCH_TIMEOUT 119262417Shselasky#define ATP_TOUCH_TIMEOUT 125000 120199086Srpaulo#endif 121199086Srpaulo 122262417Shselasky#ifndef ATP_IDLENESS_THRESHOLD 123262417Shselasky#define ATP_IDLENESS_THRESHOLD 10 124262417Shselasky#endif 125262417Shselasky 126262417Shselasky#ifndef FG_SENSOR_NOISE_THRESHOLD 127262417Shselasky#define FG_SENSOR_NOISE_THRESHOLD 2 128262417Shselasky#endif 129262417Shselasky 130199086Srpaulo/* 131199086Srpaulo * A double-tap followed by a single-finger slide is treated as a 132199086Srpaulo * special gesture. The driver responds to this gesture by assuming a 133199086Srpaulo * virtual button-press for the lifetime of the slide. The following 134199086Srpaulo * threshold is the maximum time gap (in microseconds) between the two 135199086Srpaulo * tap events preceding the slide for such a gesture. 136199086Srpaulo */ 137199086Srpaulo#ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 138262417Shselasky#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000 139199086Srpaulo#endif 140199086Srpaulo 141199086Srpaulo/* 142262417Shselasky * The wait duration in ticks after losing a touch contact before 143262417Shselasky * zombied strokes are reaped and turned into button events. 144199086Srpaulo */ 145262417Shselasky#define ATP_ZOMBIE_STROKE_REAP_INTERVAL (hz / 20) /* 50 ms */ 146199086Srpaulo 147262417Shselasky/* The multiplier used to translate sensor reported positions to mickeys. */ 148262417Shselasky#define FG_SCALE_FACTOR 380 149262417Shselasky 150199086Srpaulo/* 151262417Shselasky * The movement threshold for a stroke; this is the maximum difference 152262417Shselasky * in position which will be resolved as a continuation of a stroke 153262417Shselasky * component. 154199086Srpaulo */ 155262417Shselasky#define FG_MAX_DELTA_MICKEYS ((3 * (FG_SCALE_FACTOR)) >> 1) 156262417Shselasky 157262417Shselasky/* Distance-squared threshold for matching a finger with a known stroke */ 158262417Shselasky#ifndef WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ 159262417Shselasky#define WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ 1000000 160199086Srpaulo#endif 161199086Srpaulo 162262417Shselasky/* Ignore pressure spans with cumulative press. below this value. */ 163262417Shselasky#define FG_PSPAN_MIN_CUM_PRESSURE 10 164262417Shselasky 165262417Shselasky/* Maximum allowed width for pressure-spans.*/ 166262417Shselasky#define FG_PSPAN_MAX_WIDTH 4 167262417Shselasky 168199086Srpaulo/* end of driver specific options */ 169199086Srpaulo 170199086Srpaulo/* Tunables */ 171262417Shselaskystatic SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB ATP"); 172199086Srpaulo 173207077Sthompsa#ifdef USB_DEBUG 174199086Srpauloenum atp_log_level { 175199086Srpaulo ATP_LLEVEL_DISABLED = 0, 176199086Srpaulo ATP_LLEVEL_ERROR, 177199086Srpaulo ATP_LLEVEL_DEBUG, /* for troubleshooting */ 178199086Srpaulo ATP_LLEVEL_INFO, /* for diagnostics */ 179199086Srpaulo}; 180199086Srpaulostatic int atp_debug = ATP_LLEVEL_ERROR; /* the default is to only log errors */ 181199086SrpauloSYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RW, 182199086Srpaulo &atp_debug, ATP_LLEVEL_ERROR, "ATP debug level"); 183207077Sthompsa#endif /* USB_DEBUG */ 184199086Srpaulo 185199086Srpaulostatic u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT; 186217323SmdfSYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW, 187262417Shselasky &atp_touch_timeout, 125000, "age threshold in microseconds for a touch"); 188199086Srpaulo 189199086Srpaulostatic u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD; 190217323SmdfSYSCTL_UINT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RW, 191199086Srpaulo &atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD, 192262417Shselasky "maximum time in microseconds to allow association between a double-tap and " 193262417Shselasky "drag gesture"); 194199086Srpaulo 195199086Srpaulostatic u_int atp_mickeys_scale_factor = ATP_SCALE_FACTOR; 196199086Srpaulostatic int atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS); 197199086SrpauloSYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RW, 198199086Srpaulo &atp_mickeys_scale_factor, sizeof(atp_mickeys_scale_factor), 199199086Srpaulo atp_sysctl_scale_factor_handler, "IU", "movement scale factor"); 200199086Srpaulo 201262417Shselaskystatic u_int atp_small_movement_threshold = ATP_SMALL_MOVEMENT_THRESHOLD; 202199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RW, 203262417Shselasky &atp_small_movement_threshold, ATP_SMALL_MOVEMENT_THRESHOLD, 204199086Srpaulo "the small movement black-hole for filtering noise"); 205262417Shselasky 206262417Shselaskystatic u_int atp_tap_minimum = 1; 207262417ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, tap_minimum, CTLFLAG_RW, 208262417Shselasky &atp_tap_minimum, 1, "Minimum number of taps before detection"); 209262417Shselasky 210199086Srpaulo/* 211199086Srpaulo * Strokes which accumulate at least this amount of absolute movement 212199086Srpaulo * from the aggregate of their components are considered as 213199086Srpaulo * slides. Unit: mickeys. 214199086Srpaulo */ 215262417Shselaskystatic u_int atp_slide_min_movement = 2 * ATP_SMALL_MOVEMENT_THRESHOLD; 216199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RW, 217262417Shselasky &atp_slide_min_movement, 2 * ATP_SMALL_MOVEMENT_THRESHOLD, 218199086Srpaulo "strokes with at least this amt. of movement are considered slides"); 219199086Srpaulo 220199086Srpaulo/* 221199086Srpaulo * The minimum age of a stroke for it to be considered mature; this 222199086Srpaulo * helps filter movements (noise) from immature strokes. Units: interrupts. 223199086Srpaulo */ 224262417Shselaskystatic u_int atp_stroke_maturity_threshold = 4; 225199086SrpauloSYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RW, 226262417Shselasky &atp_stroke_maturity_threshold, 4, 227199086Srpaulo "the minimum age of a stroke for it to be considered mature"); 228199086Srpaulo 229262417Shselaskytypedef enum atp_trackpad_family { 230262417Shselasky TRACKPAD_FAMILY_FOUNTAIN_GEYSER, 231262417Shselasky TRACKPAD_FAMILY_WELLSPRING, 232262417Shselasky TRACKPAD_FAMILY_MAX /* keep this at the tail end of the enumeration */ 233262417Shselasky} trackpad_family_t; 234199086Srpaulo 235262417Shselaskyenum fountain_geyser_product { 236262417Shselasky FOUNTAIN, 237262417Shselasky GEYSER1, 238262417Shselasky GEYSER1_17inch, 239262417Shselasky GEYSER2, 240262417Shselasky GEYSER3, 241262417Shselasky GEYSER4, 242262417Shselasky FOUNTAIN_GEYSER_PRODUCT_MAX /* keep this at the end */ 243262417Shselasky}; 244199086Srpaulo 245262417Shselaskyenum wellspring_product { 246262417Shselasky WELLSPRING1, 247262417Shselasky WELLSPRING2, 248262417Shselasky WELLSPRING3, 249262417Shselasky WELLSPRING4, 250262417Shselasky WELLSPRING4A, 251262417Shselasky WELLSPRING5, 252262417Shselasky WELLSPRING6A, 253262417Shselasky WELLSPRING6, 254262417Shselasky WELLSPRING5A, 255262417Shselasky WELLSPRING7, 256262417Shselasky WELLSPRING7A, 257262417Shselasky WELLSPRING8, 258262417Shselasky WELLSPRING_PRODUCT_MAX /* keep this at the end of the enumeration */ 259262417Shselasky}; 260199086Srpaulo 261262417Shselasky/* trackpad header types */ 262262417Shselaskyenum fountain_geyser_trackpad_type { 263262417Shselasky FG_TRACKPAD_TYPE_GEYSER1, 264262417Shselasky FG_TRACKPAD_TYPE_GEYSER2, 265262417Shselasky FG_TRACKPAD_TYPE_GEYSER3, 266262417Shselasky FG_TRACKPAD_TYPE_GEYSER4, 267262417Shselasky}; 268262417Shselaskyenum wellspring_trackpad_type { 269262417Shselasky WSP_TRACKPAD_TYPE1, /* plain trackpad */ 270262417Shselasky WSP_TRACKPAD_TYPE2, /* button integrated in trackpad */ 271262417Shselasky WSP_TRACKPAD_TYPE3 /* additional header fields since June 2013 */ 272262417Shselasky}; 273199086Srpaulo 274262417Shselasky/* 275262417Shselasky * Trackpad family and product and family are encoded together in the 276262417Shselasky * driver_info value associated with a trackpad product. 277262417Shselasky */ 278262417Shselasky#define N_PROD_BITS 8 /* Number of bits used to encode product */ 279262417Shselasky#define ENCODE_DRIVER_INFO(FAMILY, PROD) \ 280262417Shselasky (((FAMILY) << N_PROD_BITS) | (PROD)) 281262417Shselasky#define DECODE_FAMILY_FROM_DRIVER_INFO(INFO) ((INFO) >> N_PROD_BITS) 282262417Shselasky#define DECODE_PRODUCT_FROM_DRIVER_INFO(INFO) \ 283262417Shselasky ((INFO) & ((1 << N_PROD_BITS) - 1)) 284262417Shselasky 285262417Shselasky#define FG_DRIVER_INFO(PRODUCT) \ 286262417Shselasky ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_FOUNTAIN_GEYSER, PRODUCT) 287262417Shselasky#define WELLSPRING_DRIVER_INFO(PRODUCT) \ 288262417Shselasky ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_WELLSPRING, PRODUCT) 289262417Shselasky 290262417Shselasky/* 291262417Shselasky * The following structure captures the state of a pressure span along 292262417Shselasky * an axis. Each contact with the touchpad results in separate 293262417Shselasky * pressure spans along the two axes. 294262417Shselasky */ 295262417Shselaskytypedef struct fg_pspan { 296262417Shselasky u_int width; /* in units of sensors */ 297262417Shselasky u_int cum; /* cumulative compression (from all sensors) */ 298262417Shselasky u_int cog; /* center of gravity */ 299262417Shselasky u_int loc; /* location (scaled using the mickeys factor) */ 300262417Shselasky boolean_t matched; /* to track pspans as they match against strokes. */ 301262417Shselasky} fg_pspan; 302262417Shselasky 303262417Shselasky#define FG_MAX_PSPANS_PER_AXIS 3 304262417Shselasky#define FG_MAX_STROKES (2 * FG_MAX_PSPANS_PER_AXIS) 305262417Shselasky 306262417Shselasky#define WELLSPRING_INTERFACE_INDEX 1 307262417Shselasky 308262417Shselasky/* trackpad finger data offsets, le16-aligned */ 309262417Shselasky#define WSP_TYPE1_FINGER_DATA_OFFSET (13 * 2) 310262417Shselasky#define WSP_TYPE2_FINGER_DATA_OFFSET (15 * 2) 311262417Shselasky#define WSP_TYPE3_FINGER_DATA_OFFSET (19 * 2) 312262417Shselasky 313262417Shselasky/* trackpad button data offsets */ 314262417Shselasky#define WSP_TYPE2_BUTTON_DATA_OFFSET 15 315262417Shselasky#define WSP_TYPE3_BUTTON_DATA_OFFSET 23 316262417Shselasky 317262417Shselasky/* list of device capability bits */ 318262417Shselasky#define HAS_INTEGRATED_BUTTON 1 319262417Shselasky 320262417Shselasky/* trackpad finger structure - little endian */ 321262417Shselaskystruct wsp_finger_sensor_data { 322262417Shselasky int16_t origin; /* zero when switching track finger */ 323262417Shselasky int16_t abs_x; /* absolute x coordinate */ 324262417Shselasky int16_t abs_y; /* absolute y coordinate */ 325262417Shselasky int16_t rel_x; /* relative x coordinate */ 326262417Shselasky int16_t rel_y; /* relative y coordinate */ 327262417Shselasky int16_t tool_major; /* tool area, major axis */ 328262417Shselasky int16_t tool_minor; /* tool area, minor axis */ 329262417Shselasky int16_t orientation; /* 16384 when point, else 15 bit angle */ 330262417Shselasky int16_t touch_major; /* touch area, major axis */ 331262417Shselasky int16_t touch_minor; /* touch area, minor axis */ 332262417Shselasky int16_t unused[3]; /* zeros */ 333262417Shselasky int16_t multi; /* one finger: varies, more fingers: constant */ 334262417Shselasky} __packed; 335262417Shselasky 336262417Shselaskytypedef struct wsp_finger { 337262417Shselasky /* to track fingers as they match against strokes. */ 338262417Shselasky boolean_t matched; 339262417Shselasky 340262417Shselasky /* location (scaled using the mickeys factor) */ 341262417Shselasky int x; 342262417Shselasky int y; 343262417Shselasky} wsp_finger_t; 344262417Shselasky 345262417Shselasky#define WSP_MAX_FINGERS 16 346262417Shselasky#define WSP_SIZEOF_FINGER_SENSOR_DATA sizeof(struct wsp_finger_sensor_data) 347262417Shselasky#define WSP_SIZEOF_ALL_FINGER_DATA (WSP_MAX_FINGERS * \ 348262417Shselasky WSP_SIZEOF_FINGER_SENSOR_DATA) 349262417Shselasky#define WSP_MAX_FINGER_ORIENTATION 16384 350262417Shselasky 351262417Shselasky#define ATP_SENSOR_DATA_BUF_MAX 1024 352262417Shselasky#if (ATP_SENSOR_DATA_BUF_MAX < ((WSP_MAX_FINGERS * 14 * 2) + \ 353262417Shselasky WSP_TYPE3_FINGER_DATA_OFFSET)) 354262417Shselasky/* note: 14 * 2 in the above is based on sizeof(struct wsp_finger_sensor_data)*/ 355262417Shselasky#error "ATP_SENSOR_DATA_BUF_MAX is too small" 356262417Shselasky#endif 357262417Shselasky 358262417Shselasky#define ATP_MAX_STROKES MAX(WSP_MAX_FINGERS, FG_MAX_STROKES) 359262417Shselasky 360262417Shselasky#define FG_MAX_XSENSORS 26 361262417Shselasky#define FG_MAX_YSENSORS 16 362262417Shselasky 363262417Shselasky/* device-specific configuration */ 364262417Shselaskystruct fg_dev_params { 365262417Shselasky u_int data_len; /* for sensor data */ 366262417Shselasky u_int n_xsensors; 367262417Shselasky u_int n_ysensors; 368262417Shselasky enum fountain_geyser_trackpad_type prot; 369199086Srpaulo}; 370262417Shselaskystruct wsp_dev_params { 371262417Shselasky uint8_t caps; /* device capability bitmask */ 372262417Shselasky uint8_t tp_type; /* type of trackpad interface */ 373262417Shselasky uint8_t finger_data_offset; /* offset to trackpad finger data */ 374262417Shselasky}; 375262417Shselasky 376262417Shselaskystatic const struct fg_dev_params fg_dev_params[FOUNTAIN_GEYSER_PRODUCT_MAX] = { 377262417Shselasky [FOUNTAIN] = { 378262417Shselasky .data_len = 81, 379262417Shselasky .n_xsensors = 16, 380262417Shselasky .n_ysensors = 16, 381262417Shselasky .prot = FG_TRACKPAD_TYPE_GEYSER1 382199086Srpaulo }, 383262417Shselasky [GEYSER1] = { 384199151Snwhitehorn .data_len = 81, 385199151Snwhitehorn .n_xsensors = 16, 386199151Snwhitehorn .n_ysensors = 16, 387262417Shselasky .prot = FG_TRACKPAD_TYPE_GEYSER1 388199151Snwhitehorn }, 389262417Shselasky [GEYSER1_17inch] = { 390262417Shselasky .data_len = 81, 391262417Shselasky .n_xsensors = 26, 392262417Shselasky .n_ysensors = 16, 393262417Shselasky .prot = FG_TRACKPAD_TYPE_GEYSER1 394262417Shselasky }, 395262417Shselasky [GEYSER2] = { 396199151Snwhitehorn .data_len = 64, 397199151Snwhitehorn .n_xsensors = 15, 398199151Snwhitehorn .n_ysensors = 9, 399262417Shselasky .prot = FG_TRACKPAD_TYPE_GEYSER2 400199151Snwhitehorn }, 401262417Shselasky [GEYSER3] = { 402262417Shselasky .data_len = 64, 403262417Shselasky .n_xsensors = 20, 404262417Shselasky .n_ysensors = 10, 405262417Shselasky .prot = FG_TRACKPAD_TYPE_GEYSER3 406199151Snwhitehorn }, 407262417Shselasky [GEYSER4] = { 408262417Shselasky .data_len = 64, 409262417Shselasky .n_xsensors = 20, 410262417Shselasky .n_ysensors = 10, 411262417Shselasky .prot = FG_TRACKPAD_TYPE_GEYSER4 412262417Shselasky } 413199086Srpaulo}; 414199086Srpaulo 415262417Shselaskystatic const STRUCT_USB_HOST_ID fg_devs[] = { 416262417Shselasky /* PowerBooks Feb 2005, iBooks G4 */ 417262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x020e, FG_DRIVER_INFO(FOUNTAIN)) }, 418262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x020f, FG_DRIVER_INFO(FOUNTAIN)) }, 419262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0210, FG_DRIVER_INFO(FOUNTAIN)) }, 420262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x030a, FG_DRIVER_INFO(FOUNTAIN)) }, 421262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x030b, FG_DRIVER_INFO(GEYSER1)) }, 422262417Shselasky 423262417Shselasky /* PowerBooks Oct 2005 */ 424262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0214, FG_DRIVER_INFO(GEYSER2)) }, 425262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0215, FG_DRIVER_INFO(GEYSER2)) }, 426262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0216, FG_DRIVER_INFO(GEYSER2)) }, 427262417Shselasky 428199086Srpaulo /* Core Duo MacBook & MacBook Pro */ 429262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0217, FG_DRIVER_INFO(GEYSER3)) }, 430262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0218, FG_DRIVER_INFO(GEYSER3)) }, 431262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0219, FG_DRIVER_INFO(GEYSER3)) }, 432199086Srpaulo 433199086Srpaulo /* Core2 Duo MacBook & MacBook Pro */ 434262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x021a, FG_DRIVER_INFO(GEYSER4)) }, 435262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x021b, FG_DRIVER_INFO(GEYSER4)) }, 436262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x021c, FG_DRIVER_INFO(GEYSER4)) }, 437199086Srpaulo 438199086Srpaulo /* Core2 Duo MacBook3,1 */ 439262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x0229, FG_DRIVER_INFO(GEYSER4)) }, 440262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x022a, FG_DRIVER_INFO(GEYSER4)) }, 441262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x022b, FG_DRIVER_INFO(GEYSER4)) }, 442199151Snwhitehorn 443262417Shselasky /* 17 inch PowerBook */ 444262417Shselasky { USB_VPI(USB_VENDOR_APPLE, 0x020d, FG_DRIVER_INFO(GEYSER1_17inch)) }, 445262417Shselasky}; 446199151Snwhitehorn 447262417Shselaskystatic const struct wsp_dev_params wsp_dev_params[WELLSPRING_PRODUCT_MAX] = { 448262417Shselasky [WELLSPRING1] = { 449262417Shselasky .caps = 0, 450262417Shselasky .tp_type = WSP_TRACKPAD_TYPE1, 451262417Shselasky .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, 452262417Shselasky }, 453262417Shselasky [WELLSPRING2] = { 454262417Shselasky .caps = 0, 455262417Shselasky .tp_type = WSP_TRACKPAD_TYPE1, 456262417Shselasky .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, 457262417Shselasky }, 458262417Shselasky [WELLSPRING3] = { 459262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 460262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 461262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 462262417Shselasky }, 463262417Shselasky [WELLSPRING4] = { 464262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 465262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 466262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 467262417Shselasky }, 468262417Shselasky [WELLSPRING4A] = { 469262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 470262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 471262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 472262417Shselasky }, 473262417Shselasky [WELLSPRING5] = { 474262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 475262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 476262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 477262417Shselasky }, 478262417Shselasky [WELLSPRING6] = { 479262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 480262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 481262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 482262417Shselasky }, 483262417Shselasky [WELLSPRING5A] = { 484262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 485262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 486262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 487262417Shselasky }, 488262417Shselasky [WELLSPRING6A] = { 489262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 490262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 491262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 492262417Shselasky }, 493262417Shselasky [WELLSPRING7] = { 494262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 495262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 496262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 497262417Shselasky }, 498262417Shselasky [WELLSPRING7A] = { 499262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 500262417Shselasky .tp_type = WSP_TRACKPAD_TYPE2, 501262417Shselasky .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, 502262417Shselasky }, 503262417Shselasky [WELLSPRING8] = { 504262417Shselasky .caps = HAS_INTEGRATED_BUTTON, 505262417Shselasky .tp_type = WSP_TRACKPAD_TYPE3, 506262417Shselasky .finger_data_offset = WSP_TYPE3_FINGER_DATA_OFFSET, 507262417Shselasky }, 508262417Shselasky}; 509199151Snwhitehorn 510262417Shselasky#define ATP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } 511199151Snwhitehorn 512262417Shselaskystatic const STRUCT_USB_HOST_ID wsp_devs[] = { 513262417Shselasky /* MacbookAir1.1 */ 514262417Shselasky ATP_DEV(APPLE, WELLSPRING_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING1)), 515262417Shselasky ATP_DEV(APPLE, WELLSPRING_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING1)), 516262417Shselasky ATP_DEV(APPLE, WELLSPRING_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING1)), 517262417Shselasky 518262417Shselasky /* MacbookProPenryn, aka wellspring2 */ 519262417Shselasky ATP_DEV(APPLE, WELLSPRING2_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING2)), 520262417Shselasky ATP_DEV(APPLE, WELLSPRING2_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING2)), 521262417Shselasky ATP_DEV(APPLE, WELLSPRING2_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING2)), 522262417Shselasky 523262417Shselasky /* Macbook5,1 (unibody), aka wellspring3 */ 524262417Shselasky ATP_DEV(APPLE, WELLSPRING3_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING3)), 525262417Shselasky ATP_DEV(APPLE, WELLSPRING3_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING3)), 526262417Shselasky ATP_DEV(APPLE, WELLSPRING3_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING3)), 527262417Shselasky 528262417Shselasky /* MacbookAir3,2 (unibody), aka wellspring4 */ 529262417Shselasky ATP_DEV(APPLE, WELLSPRING4_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4)), 530262417Shselasky ATP_DEV(APPLE, WELLSPRING4_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4)), 531262417Shselasky ATP_DEV(APPLE, WELLSPRING4_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4)), 532262417Shselasky 533262417Shselasky /* MacbookAir3,1 (unibody), aka wellspring4 */ 534262417Shselasky ATP_DEV(APPLE, WELLSPRING4A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), 535262417Shselasky ATP_DEV(APPLE, WELLSPRING4A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), 536262417Shselasky ATP_DEV(APPLE, WELLSPRING4A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), 537262417Shselasky 538262417Shselasky /* Macbook8 (unibody, March 2011) */ 539262417Shselasky ATP_DEV(APPLE, WELLSPRING5_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5)), 540262417Shselasky ATP_DEV(APPLE, WELLSPRING5_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5)), 541262417Shselasky ATP_DEV(APPLE, WELLSPRING5_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5)), 542262417Shselasky 543262417Shselasky /* MacbookAir4,1 (unibody, July 2011) */ 544262417Shselasky ATP_DEV(APPLE, WELLSPRING6A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), 545262417Shselasky ATP_DEV(APPLE, WELLSPRING6A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), 546262417Shselasky ATP_DEV(APPLE, WELLSPRING6A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), 547262417Shselasky 548262417Shselasky /* MacbookAir4,2 (unibody, July 2011) */ 549262417Shselasky ATP_DEV(APPLE, WELLSPRING6_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6)), 550262417Shselasky ATP_DEV(APPLE, WELLSPRING6_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6)), 551262417Shselasky ATP_DEV(APPLE, WELLSPRING6_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6)), 552262417Shselasky 553262417Shselasky /* Macbook8,2 (unibody) */ 554262417Shselasky ATP_DEV(APPLE, WELLSPRING5A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), 555262417Shselasky ATP_DEV(APPLE, WELLSPRING5A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), 556262417Shselasky ATP_DEV(APPLE, WELLSPRING5A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), 557262417Shselasky 558262417Shselasky /* MacbookPro10,1 (unibody, June 2012) */ 559262417Shselasky /* MacbookPro11,? (unibody, June 2013) */ 560262417Shselasky ATP_DEV(APPLE, WELLSPRING7_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7)), 561262417Shselasky ATP_DEV(APPLE, WELLSPRING7_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7)), 562262417Shselasky ATP_DEV(APPLE, WELLSPRING7_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7)), 563262417Shselasky 564262417Shselasky /* MacbookPro10,2 (unibody, October 2012) */ 565262417Shselasky ATP_DEV(APPLE, WELLSPRING7A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), 566262417Shselasky ATP_DEV(APPLE, WELLSPRING7A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), 567262417Shselasky ATP_DEV(APPLE, WELLSPRING7A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), 568262417Shselasky 569262417Shselasky /* MacbookAir6,2 (unibody, June 2013) */ 570262417Shselasky ATP_DEV(APPLE, WELLSPRING8_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING8)), 571262417Shselasky ATP_DEV(APPLE, WELLSPRING8_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING8)), 572262417Shselasky ATP_DEV(APPLE, WELLSPRING8_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING8)), 573199086Srpaulo}; 574199086Srpaulo 575199086Srpaulotypedef enum atp_stroke_type { 576199086Srpaulo ATP_STROKE_TOUCH, 577199086Srpaulo ATP_STROKE_SLIDE, 578199086Srpaulo} atp_stroke_type; 579199086Srpaulo 580262417Shselaskytypedef enum atp_axis { 581262417Shselasky X = 0, 582262417Shselasky Y = 1, 583262417Shselasky NUM_AXES 584262417Shselasky} atp_axis; 585199086Srpaulo 586262417Shselasky#define ATP_FIFO_BUF_SIZE 8 /* bytes */ 587262417Shselasky#define ATP_FIFO_QUEUE_MAXLEN 50 /* units */ 588262417Shselasky 589262417Shselaskyenum { 590262417Shselasky ATP_INTR_DT, 591262417Shselasky ATP_RESET, 592262417Shselasky ATP_N_TRANSFER, 593262417Shselasky}; 594262417Shselasky 595262417Shselaskytypedef struct fg_stroke_component { 596199086Srpaulo /* Fields encapsulating the pressure-span. */ 597199086Srpaulo u_int loc; /* location (scaled) */ 598199086Srpaulo u_int cum_pressure; /* cumulative compression */ 599199086Srpaulo u_int max_cum_pressure; /* max cumulative compression */ 600199086Srpaulo boolean_t matched; /*to track components as they match against pspans.*/ 601199086Srpaulo 602199086Srpaulo int delta_mickeys; /* change in location (un-smoothened movement)*/ 603262417Shselasky} fg_stroke_component_t; 604199086Srpaulo 605199086Srpaulo/* 606199086Srpaulo * The following structure captures a finger contact with the 607199086Srpaulo * touchpad. A stroke comprises two p-span components and some state. 608199086Srpaulo */ 609199086Srpaulotypedef struct atp_stroke { 610262417Shselasky TAILQ_ENTRY(atp_stroke) entry; 611199086Srpaulo 612262417Shselasky atp_stroke_type type; 613262417Shselasky uint32_t flags; /* the state of this stroke */ 614262417Shselasky#define ATSF_ZOMBIE 0x1 615262417Shselasky boolean_t matched; /* to track match against fingers.*/ 616199086Srpaulo 617262417Shselasky struct timeval ctime; /* create time; for coincident siblings. */ 618199086Srpaulo 619262417Shselasky /* 620262417Shselasky * Unit: interrupts; we maintain this value in 621262417Shselasky * addition to 'ctime' in order to avoid the 622262417Shselasky * expensive call to microtime() at every 623262417Shselasky * interrupt. 624262417Shselasky */ 625262417Shselasky uint32_t age; 626199086Srpaulo 627262417Shselasky /* Location */ 628262417Shselasky int x; 629262417Shselasky int y; 630199086Srpaulo 631262417Shselasky /* Fields containing information about movement. */ 632262417Shselasky int instantaneous_dx; /* curr. change in X location (un-smoothened) */ 633262417Shselasky int instantaneous_dy; /* curr. change in Y location (un-smoothened) */ 634262417Shselasky int pending_dx; /* cum. of pending short movements */ 635262417Shselasky int pending_dy; /* cum. of pending short movements */ 636262417Shselasky int movement_dx; /* interpreted smoothened movement */ 637262417Shselasky int movement_dy; /* interpreted smoothened movement */ 638262417Shselasky int cum_movement_x; /* cum. horizontal movement */ 639262417Shselasky int cum_movement_y; /* cum. vertical movement */ 640262417Shselasky 641262417Shselasky /* 642262417Shselasky * The following member is relevant only for fountain-geyser trackpads. 643262417Shselasky * For these, there is the need to track pressure-spans and cumulative 644262417Shselasky * pressures for stroke components. 645262417Shselasky */ 646262417Shselasky fg_stroke_component_t components[NUM_AXES]; 647262417Shselasky} atp_stroke_t; 648262417Shselasky 649262417Shselaskystruct atp_softc; /* forward declaration */ 650262417Shselaskytypedef void (*sensor_data_interpreter_t)(struct atp_softc *sc, u_int len); 651262417Shselasky 652199086Srpaulostruct atp_softc { 653262417Shselasky device_t sc_dev; 654262417Shselasky struct usb_device *sc_usb_device; 655262417Shselasky struct mtx sc_mutex; /* for synchronization */ 656262417Shselasky struct usb_fifo_sc sc_fifo; 657199086Srpaulo 658262417Shselasky#define MODE_LENGTH 8 659262417Shselasky char sc_mode_bytes[MODE_LENGTH]; /* device mode */ 660199086Srpaulo 661262417Shselasky trackpad_family_t sc_family; 662262417Shselasky const void *sc_params; /* device configuration */ 663262417Shselasky sensor_data_interpreter_t sensor_data_interpreter; 664199086Srpaulo 665262417Shselasky mousehw_t sc_hw; 666262417Shselasky mousemode_t sc_mode; 667262417Shselasky mousestatus_t sc_status; 668199086Srpaulo 669262417Shselasky u_int sc_state; 670262417Shselasky#define ATP_ENABLED 0x01 671262417Shselasky#define ATP_ZOMBIES_EXIST 0x02 672262417Shselasky#define ATP_DOUBLE_TAP_DRAG 0x04 673262417Shselasky#define ATP_VALID 0x08 674199086Srpaulo 675262417Shselasky struct usb_xfer *sc_xfer[ATP_N_TRANSFER]; 676199086Srpaulo 677262417Shselasky u_int sc_pollrate; 678262417Shselasky int sc_fflags; 679199086Srpaulo 680262417Shselasky atp_stroke_t sc_strokes_data[ATP_MAX_STROKES]; 681262417Shselasky TAILQ_HEAD(,atp_stroke) sc_stroke_free; 682262417Shselasky TAILQ_HEAD(,atp_stroke) sc_stroke_used; 683262417Shselasky u_int sc_n_strokes; 684262417Shselasky 685262417Shselasky struct callout sc_callout; 686262417Shselasky 687262417Shselasky /* 688262417Shselasky * button status. Set to non-zero if the mouse-button is physically 689262417Shselasky * pressed. This state variable is exposed through softc to allow 690262417Shselasky * reap_sibling_zombies to avoid registering taps while the trackpad 691262417Shselasky * button is pressed. 692262417Shselasky */ 693262417Shselasky uint8_t sc_ibtn; 694262417Shselasky 695262417Shselasky /* 696262417Shselasky * Time when touch zombies were last reaped; useful for detecting 697262417Shselasky * double-touch-n-drag. 698262417Shselasky */ 699262417Shselasky struct timeval sc_touch_reap_time; 700262417Shselasky 701262417Shselasky u_int sc_idlecount; 702262417Shselasky 703262417Shselasky /* Regarding the data transferred from t-pad in USB INTR packets. */ 704262417Shselasky u_int sc_expected_sensor_data_len; 705262417Shselasky uint8_t sc_sensor_data[ATP_SENSOR_DATA_BUF_MAX] __aligned(4); 706262417Shselasky 707262417Shselasky int sc_cur_x[FG_MAX_XSENSORS]; /* current sensor readings */ 708262417Shselasky int sc_cur_y[FG_MAX_YSENSORS]; 709262417Shselasky int sc_base_x[FG_MAX_XSENSORS]; /* base sensor readings */ 710262417Shselasky int sc_base_y[FG_MAX_YSENSORS]; 711262417Shselasky int sc_pressure_x[FG_MAX_XSENSORS]; /* computed pressures */ 712262417Shselasky int sc_pressure_y[FG_MAX_YSENSORS]; 713262417Shselasky fg_pspan sc_pspans_x[FG_MAX_PSPANS_PER_AXIS]; 714262417Shselasky fg_pspan sc_pspans_y[FG_MAX_PSPANS_PER_AXIS]; 715199086Srpaulo}; 716199086Srpaulo 717199086Srpaulo/* 718262417Shselasky * The last byte of the fountain-geyser sensor data contains status bits; the 719199086Srpaulo * following values define the meanings of these bits. 720262417Shselasky * (only Geyser 3/4) 721199086Srpaulo */ 722262417Shselaskyenum geyser34_status_bits { 723262417Shselasky FG_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */ 724262417Shselasky FG_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/ 725199086Srpaulo}; 726199086Srpaulo 727199086Srpaulotypedef enum interface_mode { 728262417Shselasky RAW_SENSOR_MODE = (uint8_t)0x01, 729199086Srpaulo HID_MODE = (uint8_t)0x08 730199086Srpaulo} interface_mode; 731199086Srpaulo 732262417Shselasky 733199086Srpaulo/* 734199086Srpaulo * function prototypes 735199086Srpaulo */ 736199086Srpaulostatic usb_fifo_cmd_t atp_start_read; 737199086Srpaulostatic usb_fifo_cmd_t atp_stop_read; 738199086Srpaulostatic usb_fifo_open_t atp_open; 739199086Srpaulostatic usb_fifo_close_t atp_close; 740199086Srpaulostatic usb_fifo_ioctl_t atp_ioctl; 741199086Srpaulo 742199086Srpaulostatic struct usb_fifo_methods atp_fifo_methods = { 743199086Srpaulo .f_open = &atp_open, 744199086Srpaulo .f_close = &atp_close, 745199086Srpaulo .f_ioctl = &atp_ioctl, 746199086Srpaulo .f_start_read = &atp_start_read, 747199086Srpaulo .f_stop_read = &atp_stop_read, 748199086Srpaulo .basename[0] = ATP_DRIVER_NAME, 749199086Srpaulo}; 750199086Srpaulo 751199086Srpaulo/* device initialization and shutdown */ 752262417Shselaskystatic usb_error_t atp_set_device_mode(struct atp_softc *, interface_mode); 753262417Shselaskystatic void atp_reset_callback(struct usb_xfer *, usb_error_t); 754262417Shselaskystatic int atp_enable(struct atp_softc *); 755262417Shselaskystatic void atp_disable(struct atp_softc *); 756199086Srpaulo 757199086Srpaulo/* sensor interpretation */ 758262417Shselaskystatic void fg_interpret_sensor_data(struct atp_softc *, u_int); 759262417Shselaskystatic void fg_extract_sensor_data(const int8_t *, u_int, atp_axis, 760262417Shselasky int *, enum fountain_geyser_trackpad_type); 761262417Shselaskystatic void fg_get_pressures(int *, const int *, const int *, int); 762262417Shselaskystatic void fg_detect_pspans(int *, u_int, u_int, fg_pspan *, u_int *); 763262417Shselaskystatic void wsp_interpret_sensor_data(struct atp_softc *, u_int); 764199086Srpaulo 765199086Srpaulo/* movement detection */ 766262417Shselaskystatic boolean_t fg_match_stroke_component(fg_stroke_component_t *, 767262417Shselasky const fg_pspan *, atp_stroke_type); 768262417Shselaskystatic void fg_match_strokes_against_pspans(struct atp_softc *, 769262417Shselasky atp_axis, fg_pspan *, u_int, u_int); 770262417Shselaskystatic boolean_t wsp_match_strokes_against_fingers(struct atp_softc *, 771262417Shselasky wsp_finger_t *, u_int); 772262417Shselaskystatic boolean_t fg_update_strokes(struct atp_softc *, fg_pspan *, u_int, 773262417Shselasky fg_pspan *, u_int); 774262417Shselaskystatic boolean_t wsp_update_strokes(struct atp_softc *, 775262417Shselasky wsp_finger_t [WSP_MAX_FINGERS], u_int); 776262417Shselaskystatic void fg_add_stroke(struct atp_softc *, const fg_pspan *, const fg_pspan *); 777262417Shselaskystatic void fg_add_new_strokes(struct atp_softc *, fg_pspan *, 778262417Shselasky u_int, fg_pspan *, u_int); 779262417Shselaskystatic void wsp_add_stroke(struct atp_softc *, const wsp_finger_t *); 780262417Shselaskystatic void atp_advance_stroke_state(struct atp_softc *, 781262417Shselasky atp_stroke_t *, boolean_t *); 782262417Shselaskystatic boolean_t atp_stroke_has_small_movement(const atp_stroke_t *); 783262417Shselaskystatic void atp_update_pending_mickeys(atp_stroke_t *); 784262417Shselaskystatic boolean_t atp_compute_stroke_movement(atp_stroke_t *); 785262417Shselaskystatic void atp_terminate_stroke(struct atp_softc *, atp_stroke_t *); 786199086Srpaulo 787199086Srpaulo/* tap detection */ 788262417Shselaskystatic boolean_t atp_is_horizontal_scroll(const atp_stroke_t *); 789262417Shselaskystatic boolean_t atp_is_vertical_scroll(const atp_stroke_t *); 790262417Shselaskystatic void atp_reap_sibling_zombies(void *); 791262417Shselaskystatic void atp_convert_to_slide(struct atp_softc *, atp_stroke_t *); 792199086Srpaulo 793199086Srpaulo/* updating fifo */ 794262417Shselaskystatic void atp_reset_buf(struct atp_softc *); 795262417Shselaskystatic void atp_add_to_queue(struct atp_softc *, int, int, int, uint32_t); 796199086Srpaulo 797262417Shselaskystatic const sensor_data_interpreter_t atp_sensor_data_interpreters[TRACKPAD_FAMILY_MAX] = { 798262417Shselasky [TRACKPAD_FAMILY_FOUNTAIN_GEYSER] = fg_interpret_sensor_data, 799262417Shselasky [TRACKPAD_FAMILY_WELLSPRING] = wsp_interpret_sensor_data, 800262417Shselasky}; 801199086Srpaulo 802262417Shselasky/* Device methods. */ 803262417Shselaskystatic device_probe_t atp_probe; 804262417Shselaskystatic device_attach_t atp_attach; 805262417Shselaskystatic device_detach_t atp_detach; 806262417Shselaskystatic usb_callback_t atp_intr; 807262417Shselasky 808262417Shselaskystatic const struct usb_config atp_xfer_config[ATP_N_TRANSFER] = { 809262417Shselasky [ATP_INTR_DT] = { 810262417Shselasky .type = UE_INTERRUPT, 811262417Shselasky .endpoint = UE_ADDR_ANY, 812262417Shselasky .direction = UE_DIR_IN, 813262417Shselasky .flags = { 814262417Shselasky .pipe_bof = 1, /* block pipe on failure */ 815262417Shselasky .short_xfer_ok = 1, 816262417Shselasky }, 817262417Shselasky .bufsize = ATP_SENSOR_DATA_BUF_MAX, 818262417Shselasky .callback = &atp_intr, 819262417Shselasky }, 820262417Shselasky [ATP_RESET] = { 821262417Shselasky .type = UE_CONTROL, 822262417Shselasky .endpoint = 0, /* Control pipe */ 823262417Shselasky .direction = UE_DIR_ANY, 824262417Shselasky .bufsize = sizeof(struct usb_device_request) + MODE_LENGTH, 825262417Shselasky .callback = &atp_reset_callback, 826262417Shselasky .interval = 0, /* no pre-delay */ 827262417Shselasky }, 828262417Shselasky}; 829262417Shselasky 830262417Shselaskystatic atp_stroke_t * 831262417Shselaskyatp_alloc_stroke(struct atp_softc *sc) 832199086Srpaulo{ 833262417Shselasky atp_stroke_t *pstroke; 834199086Srpaulo 835262417Shselasky pstroke = TAILQ_FIRST(&sc->sc_stroke_free); 836262417Shselasky if (pstroke == NULL) 837262417Shselasky goto done; 838199086Srpaulo 839262417Shselasky TAILQ_REMOVE(&sc->sc_stroke_free, pstroke, entry); 840262417Shselasky memset(pstroke, 0, sizeof(*pstroke)); 841262417Shselasky TAILQ_INSERT_TAIL(&sc->sc_stroke_used, pstroke, entry); 842262417Shselasky 843262417Shselasky sc->sc_n_strokes++; 844262417Shselaskydone: 845262417Shselasky return (pstroke); 846199086Srpaulo} 847199086Srpaulo 848262417Shselaskystatic void 849262417Shselaskyatp_free_stroke(struct atp_softc *sc, atp_stroke_t *pstroke) 850199086Srpaulo{ 851262417Shselasky if (pstroke == NULL) 852262417Shselasky return; 853199086Srpaulo 854262417Shselasky sc->sc_n_strokes--; 855199086Srpaulo 856262417Shselasky TAILQ_REMOVE(&sc->sc_stroke_used, pstroke, entry); 857262417Shselasky TAILQ_INSERT_TAIL(&sc->sc_stroke_free, pstroke, entry); 858262417Shselasky} 859199086Srpaulo 860262417Shselaskystatic void 861262417Shselaskyatp_init_stroke_pool(struct atp_softc *sc) 862262417Shselasky{ 863262417Shselasky u_int x; 864199086Srpaulo 865262417Shselasky TAILQ_INIT(&sc->sc_stroke_free); 866262417Shselasky TAILQ_INIT(&sc->sc_stroke_used); 867262417Shselasky 868262417Shselasky sc->sc_n_strokes = 0; 869262417Shselasky 870262417Shselasky memset(&sc->sc_strokes_data, 0, sizeof(sc->sc_strokes_data)); 871262417Shselasky 872262417Shselasky for (x = 0; x != ATP_MAX_STROKES; x++) { 873262417Shselasky TAILQ_INSERT_TAIL(&sc->sc_stroke_free, &sc->sc_strokes_data[x], 874262417Shselasky entry); 875262417Shselasky } 876199086Srpaulo} 877199086Srpaulo 878262417Shselaskystatic usb_error_t 879262417Shselaskyatp_set_device_mode(struct atp_softc *sc, interface_mode newMode) 880262417Shselasky{ 881262417Shselasky uint8_t mode_value; 882262417Shselasky usb_error_t err; 883262417Shselasky 884262417Shselasky if ((newMode != RAW_SENSOR_MODE) && (newMode != HID_MODE)) 885262417Shselasky return (USB_ERR_INVAL); 886262417Shselasky 887262417Shselasky if ((newMode == RAW_SENSOR_MODE) && 888262417Shselasky (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER)) 889262417Shselasky mode_value = (uint8_t)0x04; 890262417Shselasky else 891262417Shselasky mode_value = newMode; 892262417Shselasky 893262417Shselasky err = usbd_req_get_report(sc->sc_usb_device, NULL /* mutex */, 894262417Shselasky sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, 895262417Shselasky 0x03 /* type */, 0x00 /* id */); 896262417Shselasky if (err != USB_ERR_NORMAL_COMPLETION) { 897262417Shselasky DPRINTF("Failed to read device mode (%d)\n", err); 898262417Shselasky return (err); 899262417Shselasky } 900262417Shselasky 901262417Shselasky if (sc->sc_mode_bytes[0] == mode_value) 902262417Shselasky return (err); 903262417Shselasky 904262417Shselasky /* 905262417Shselasky * XXX Need to wait at least 250ms for hardware to get 906262417Shselasky * ready. The device mode handling appears to be handled 907262417Shselasky * asynchronously and we should not issue these commands too 908262417Shselasky * quickly. 909262417Shselasky */ 910262417Shselasky pause("WHW", hz / 4); 911262417Shselasky 912262417Shselasky sc->sc_mode_bytes[0] = mode_value; 913262417Shselasky return (usbd_req_set_report(sc->sc_usb_device, NULL /* mutex */, 914262417Shselasky sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, 915262417Shselasky 0x03 /* type */, 0x00 /* id */)); 916262417Shselasky} 917262417Shselasky 918262417Shselaskystatic void 919199680Sthompsaatp_reset_callback(struct usb_xfer *xfer, usb_error_t error) 920199680Sthompsa{ 921199680Sthompsa usb_device_request_t req; 922199680Sthompsa struct usb_page_cache *pc; 923199680Sthompsa struct atp_softc *sc = usbd_xfer_softc(xfer); 924199680Sthompsa 925262417Shselasky uint8_t mode_value; 926262417Shselasky if (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER) 927262417Shselasky mode_value = 0x04; 928262417Shselasky else 929262417Shselasky mode_value = RAW_SENSOR_MODE; 930262417Shselasky 931199680Sthompsa switch (USB_GET_STATE(xfer)) { 932199680Sthompsa case USB_ST_SETUP: 933262417Shselasky sc->sc_mode_bytes[0] = mode_value; 934199680Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 935199680Sthompsa req.bRequest = UR_SET_REPORT; 936199680Sthompsa USETW2(req.wValue, 937199680Sthompsa (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */); 938199680Sthompsa USETW(req.wIndex, 0); 939199680Sthompsa USETW(req.wLength, MODE_LENGTH); 940199680Sthompsa 941199680Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 942199680Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 943199680Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 944199680Sthompsa usbd_copy_in(pc, 0, sc->sc_mode_bytes, MODE_LENGTH); 945199680Sthompsa 946199680Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 947199680Sthompsa usbd_xfer_set_frame_len(xfer, 1, MODE_LENGTH); 948199680Sthompsa usbd_xfer_set_frames(xfer, 2); 949199680Sthompsa usbd_transfer_submit(xfer); 950199680Sthompsa break; 951199680Sthompsa 952199680Sthompsa case USB_ST_TRANSFERRED: 953199680Sthompsa default: 954199680Sthompsa break; 955199680Sthompsa } 956199680Sthompsa} 957199680Sthompsa 958199086Srpaulostatic int 959199086Srpauloatp_enable(struct atp_softc *sc) 960199086Srpaulo{ 961262417Shselasky if (sc->sc_state & ATP_ENABLED) 962262417Shselasky return (0); 963199086Srpaulo 964199086Srpaulo /* reset status */ 965199086Srpaulo memset(&sc->sc_status, 0, sizeof(sc->sc_status)); 966262417Shselasky 967262417Shselasky atp_init_stroke_pool(sc); 968262417Shselasky 969199086Srpaulo sc->sc_state |= ATP_ENABLED; 970199086Srpaulo 971199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n"); 972199086Srpaulo return (0); 973199086Srpaulo} 974199086Srpaulo 975199086Srpaulostatic void 976199086Srpauloatp_disable(struct atp_softc *sc) 977199086Srpaulo{ 978199151Snwhitehorn sc->sc_state &= ~(ATP_ENABLED | ATP_VALID); 979199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n"); 980199086Srpaulo} 981199086Srpaulo 982262417Shselaskystatic void 983262417Shselaskyfg_interpret_sensor_data(struct atp_softc *sc, u_int data_len) 984199086Srpaulo{ 985262417Shselasky u_int n_xpspans = 0; 986262417Shselasky u_int n_ypspans = 0; 987262417Shselasky uint8_t status_bits; 988199086Srpaulo 989262417Shselasky const struct fg_dev_params *params = 990262417Shselasky (const struct fg_dev_params *)sc->sc_params; 991199086Srpaulo 992262417Shselasky fg_extract_sensor_data(sc->sc_sensor_data, params->n_xsensors, X, 993262417Shselasky sc->sc_cur_x, params->prot); 994262417Shselasky fg_extract_sensor_data(sc->sc_sensor_data, params->n_ysensors, Y, 995262417Shselasky sc->sc_cur_y, params->prot); 996199086Srpaulo 997262417Shselasky /* 998262417Shselasky * If this is the initial update (from an untouched 999262417Shselasky * pad), we should set the base values for the sensor 1000262417Shselasky * data; deltas with respect to these base values can 1001262417Shselasky * be used as pressure readings subsequently. 1002262417Shselasky */ 1003262417Shselasky status_bits = sc->sc_sensor_data[params->data_len - 1]; 1004262417Shselasky if (((params->prot == FG_TRACKPAD_TYPE_GEYSER3) || 1005262417Shselasky (params->prot == FG_TRACKPAD_TYPE_GEYSER4)) && 1006262417Shselasky ((sc->sc_state & ATP_VALID) == 0)) { 1007262417Shselasky if (status_bits & FG_STATUS_BASE_UPDATE) { 1008262417Shselasky memcpy(sc->sc_base_x, sc->sc_cur_x, 1009262417Shselasky params->n_xsensors * sizeof(*sc->sc_base_x)); 1010262417Shselasky memcpy(sc->sc_base_y, sc->sc_cur_y, 1011262417Shselasky params->n_ysensors * sizeof(*sc->sc_base_y)); 1012262417Shselasky sc->sc_state |= ATP_VALID; 1013262417Shselasky return; 1014199086Srpaulo } 1015199086Srpaulo } 1016199086Srpaulo 1017262417Shselasky /* Get pressure readings and detect p-spans for both axes. */ 1018262417Shselasky fg_get_pressures(sc->sc_pressure_x, sc->sc_cur_x, sc->sc_base_x, 1019262417Shselasky params->n_xsensors); 1020262417Shselasky fg_detect_pspans(sc->sc_pressure_x, params->n_xsensors, 1021262417Shselasky FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_x, &n_xpspans); 1022262417Shselasky fg_get_pressures(sc->sc_pressure_y, sc->sc_cur_y, sc->sc_base_y, 1023262417Shselasky params->n_ysensors); 1024262417Shselasky fg_detect_pspans(sc->sc_pressure_y, params->n_ysensors, 1025262417Shselasky FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_y, &n_ypspans); 1026199086Srpaulo 1027262417Shselasky /* Update strokes with new pspans to detect movements. */ 1028262417Shselasky if (fg_update_strokes(sc, sc->sc_pspans_x, n_xpspans, sc->sc_pspans_y, n_ypspans)) 1029262417Shselasky sc->sc_status.flags |= MOUSE_POSCHANGED; 1030199086Srpaulo 1031262417Shselasky sc->sc_ibtn = (status_bits & FG_STATUS_BUTTON) ? MOUSE_BUTTON1DOWN : 0; 1032262417Shselasky sc->sc_status.button = sc->sc_ibtn; 1033199086Srpaulo 1034262417Shselasky /* 1035262417Shselasky * The Fountain/Geyser device continues to trigger interrupts 1036262417Shselasky * at a fast rate even after touchpad activity has 1037262417Shselasky * stopped. Upon detecting that the device has remained idle 1038262417Shselasky * beyond a threshold, we reinitialize it to silence the 1039262417Shselasky * interrupts. 1040262417Shselasky */ 1041262417Shselasky if ((sc->sc_status.flags == 0) && (sc->sc_n_strokes == 0)) { 1042262417Shselasky sc->sc_idlecount++; 1043262417Shselasky if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) { 1044262417Shselasky /* 1045262417Shselasky * Use the last frame before we go idle for 1046262417Shselasky * calibration on pads which do not send 1047262417Shselasky * calibration frames. 1048262417Shselasky */ 1049262417Shselasky const struct fg_dev_params *params = 1050262417Shselasky (const struct fg_dev_params *)sc->sc_params; 1051199086Srpaulo 1052262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "idle\n"); 1053199086Srpaulo 1054262417Shselasky if (params->prot < FG_TRACKPAD_TYPE_GEYSER3) { 1055262417Shselasky memcpy(sc->sc_base_x, sc->sc_cur_x, 1056262417Shselasky params->n_xsensors * sizeof(*(sc->sc_base_x))); 1057262417Shselasky memcpy(sc->sc_base_y, sc->sc_cur_y, 1058262417Shselasky params->n_ysensors * sizeof(*(sc->sc_base_y))); 1059262417Shselasky } 1060199086Srpaulo 1061262417Shselasky sc->sc_idlecount = 0; 1062262417Shselasky usbd_transfer_start(sc->sc_xfer[ATP_RESET]); 1063199086Srpaulo } 1064262417Shselasky } else { 1065262417Shselasky sc->sc_idlecount = 0; 1066199086Srpaulo } 1067199086Srpaulo} 1068199086Srpaulo 1069199086Srpaulo/* 1070199086Srpaulo * Interpret the data from the X and Y pressure sensors. This function 1071199086Srpaulo * is called separately for the X and Y sensor arrays. The data in the 1072199086Srpaulo * USB packet is laid out in the following manner: 1073199086Srpaulo * 1074199086Srpaulo * sensor_data: 1075199086Srpaulo * --,--,Y1,Y2,--,Y3,Y4,--,Y5,...,Y10, ... X1,X2,--,X3,X4 1076199086Srpaulo * indices: 0 1 2 3 4 5 6 7 8 ... 15 ... 20 21 22 23 24 1077199086Srpaulo * 1078199086Srpaulo * '--' (in the above) indicates that the value is unimportant. 1079199086Srpaulo * 1080199086Srpaulo * Information about the above layout was obtained from the 1081199086Srpaulo * implementation of the AppleTouch driver in Linux. 1082199086Srpaulo * 1083199086Srpaulo * parameters: 1084199086Srpaulo * sensor_data 1085199086Srpaulo * raw sensor data from the USB packet. 1086199086Srpaulo * num 1087199086Srpaulo * The number of elements in the array 'arr'. 1088199151Snwhitehorn * axis 1089199151Snwhitehorn * Axis of data to fetch 1090199086Srpaulo * arr 1091199086Srpaulo * The array to be initialized with the readings. 1092199151Snwhitehorn * prot 1093199151Snwhitehorn * The protocol to use to interpret the data 1094199086Srpaulo */ 1095262417Shselaskystatic void 1096262417Shselaskyfg_extract_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis, 1097262417Shselasky int *arr, enum fountain_geyser_trackpad_type prot) 1098199086Srpaulo{ 1099199086Srpaulo u_int i; 1100199086Srpaulo u_int di; /* index into sensor data */ 1101199086Srpaulo 1102199151Snwhitehorn switch (prot) { 1103262417Shselasky case FG_TRACKPAD_TYPE_GEYSER1: 1104199151Snwhitehorn /* 1105199151Snwhitehorn * For Geyser 1, the sensors are laid out in pairs 1106199151Snwhitehorn * every 5 bytes. 1107199151Snwhitehorn */ 1108199151Snwhitehorn for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) { 1109199151Snwhitehorn arr[i] = sensor_data[di]; 1110199151Snwhitehorn arr[i+8] = sensor_data[di+2]; 1111262417Shselasky if ((axis == X) && (num > 16)) 1112199151Snwhitehorn arr[i+16] = sensor_data[di+40]; 1113199151Snwhitehorn } 1114199151Snwhitehorn 1115199151Snwhitehorn break; 1116262417Shselasky case FG_TRACKPAD_TYPE_GEYSER2: 1117262417Shselasky for (i = 0, di = (axis == Y) ? 1 : 19; i < num; /* empty */ ) { 1118262417Shselasky arr[i++] = sensor_data[di++]; 1119262417Shselasky arr[i++] = sensor_data[di++]; 1120262417Shselasky di++; 1121262417Shselasky } 1122262417Shselasky break; 1123262417Shselasky case FG_TRACKPAD_TYPE_GEYSER3: 1124262417Shselasky case FG_TRACKPAD_TYPE_GEYSER4: 1125199151Snwhitehorn for (i = 0, di = (axis == Y) ? 2 : 20; i < num; /* empty */ ) { 1126199151Snwhitehorn arr[i++] = sensor_data[di++]; 1127199151Snwhitehorn arr[i++] = sensor_data[di++]; 1128199151Snwhitehorn di++; 1129199151Snwhitehorn } 1130199151Snwhitehorn break; 1131262417Shselasky default: 1132262417Shselasky break; 1133199086Srpaulo } 1134199086Srpaulo} 1135199086Srpaulo 1136262417Shselaskystatic void 1137262417Shselaskyfg_get_pressures(int *p, const int *cur, const int *base, int n) 1138199086Srpaulo{ 1139199086Srpaulo int i; 1140199086Srpaulo 1141199086Srpaulo for (i = 0; i < n; i++) { 1142199086Srpaulo p[i] = cur[i] - base[i]; 1143199086Srpaulo if (p[i] > 127) 1144199086Srpaulo p[i] -= 256; 1145199086Srpaulo if (p[i] < -127) 1146199086Srpaulo p[i] += 256; 1147199086Srpaulo if (p[i] < 0) 1148199086Srpaulo p[i] = 0; 1149199086Srpaulo 1150199086Srpaulo /* 1151199086Srpaulo * Shave off pressures below the noise-pressure 1152199086Srpaulo * threshold; this will reduce the contribution from 1153199086Srpaulo * lower pressure readings. 1154199086Srpaulo */ 1155262417Shselasky if ((u_int)p[i] <= FG_SENSOR_NOISE_THRESHOLD) 1156199086Srpaulo p[i] = 0; /* filter away noise */ 1157199086Srpaulo else 1158262417Shselasky p[i] -= FG_SENSOR_NOISE_THRESHOLD; 1159199086Srpaulo } 1160199086Srpaulo} 1161199086Srpaulo 1162199086Srpaulostatic void 1163262417Shselaskyfg_detect_pspans(int *p, u_int num_sensors, 1164262417Shselasky u_int max_spans, /* max # of pspans permitted */ 1165262417Shselasky fg_pspan *spans, /* finger spans */ 1166262417Shselasky u_int *nspans_p) /* num spans detected */ 1167199086Srpaulo{ 1168199086Srpaulo u_int i; 1169199086Srpaulo int maxp; /* max pressure seen within a span */ 1170199086Srpaulo u_int num_spans = 0; 1171199086Srpaulo 1172262417Shselasky enum fg_pspan_state { 1173199086Srpaulo ATP_PSPAN_INACTIVE, 1174199086Srpaulo ATP_PSPAN_INCREASING, 1175199086Srpaulo ATP_PSPAN_DECREASING, 1176199086Srpaulo } state; /* state of the pressure span */ 1177199086Srpaulo 1178199086Srpaulo /* 1179199086Srpaulo * The following is a simple state machine to track 1180199086Srpaulo * the phase of the pressure span. 1181199086Srpaulo */ 1182262417Shselasky memset(spans, 0, max_spans * sizeof(fg_pspan)); 1183199086Srpaulo maxp = 0; 1184199086Srpaulo state = ATP_PSPAN_INACTIVE; 1185199086Srpaulo for (i = 0; i < num_sensors; i++) { 1186199086Srpaulo if (num_spans >= max_spans) 1187199086Srpaulo break; 1188199086Srpaulo 1189199086Srpaulo if (p[i] == 0) { 1190199086Srpaulo if (state == ATP_PSPAN_INACTIVE) { 1191199086Srpaulo /* 1192199086Srpaulo * There is no pressure information for this 1193199086Srpaulo * sensor, and we aren't tracking a finger. 1194199086Srpaulo */ 1195199086Srpaulo continue; 1196199086Srpaulo } else { 1197199086Srpaulo state = ATP_PSPAN_INACTIVE; 1198199086Srpaulo maxp = 0; 1199199086Srpaulo num_spans++; 1200199086Srpaulo } 1201199086Srpaulo } else { 1202199086Srpaulo switch (state) { 1203199086Srpaulo case ATP_PSPAN_INACTIVE: 1204199086Srpaulo state = ATP_PSPAN_INCREASING; 1205199086Srpaulo maxp = p[i]; 1206199086Srpaulo break; 1207199086Srpaulo 1208199086Srpaulo case ATP_PSPAN_INCREASING: 1209199086Srpaulo if (p[i] > maxp) 1210199086Srpaulo maxp = p[i]; 1211199086Srpaulo else if (p[i] <= (maxp >> 1)) 1212199086Srpaulo state = ATP_PSPAN_DECREASING; 1213199086Srpaulo break; 1214199086Srpaulo 1215199086Srpaulo case ATP_PSPAN_DECREASING: 1216199086Srpaulo if (p[i] > p[i - 1]) { 1217199086Srpaulo /* 1218199086Srpaulo * This is the beginning of 1219199086Srpaulo * another span; change state 1220199086Srpaulo * to give the appearance that 1221199086Srpaulo * we're starting from an 1222199086Srpaulo * inactive span, and then 1223199086Srpaulo * re-process this reading in 1224199086Srpaulo * the next iteration. 1225199086Srpaulo */ 1226199086Srpaulo num_spans++; 1227199086Srpaulo state = ATP_PSPAN_INACTIVE; 1228199086Srpaulo maxp = 0; 1229199086Srpaulo i--; 1230199086Srpaulo continue; 1231199086Srpaulo } 1232199086Srpaulo break; 1233199086Srpaulo } 1234199086Srpaulo 1235199086Srpaulo /* Update the finger span with this reading. */ 1236199086Srpaulo spans[num_spans].width++; 1237199086Srpaulo spans[num_spans].cum += p[i]; 1238199086Srpaulo spans[num_spans].cog += p[i] * (i + 1); 1239199086Srpaulo } 1240199086Srpaulo } 1241199086Srpaulo if (state != ATP_PSPAN_INACTIVE) 1242199086Srpaulo num_spans++; /* close the last finger span */ 1243199086Srpaulo 1244199086Srpaulo /* post-process the spans */ 1245199086Srpaulo for (i = 0; i < num_spans; i++) { 1246199086Srpaulo /* filter away unwanted pressure spans */ 1247262417Shselasky if ((spans[i].cum < FG_PSPAN_MIN_CUM_PRESSURE) || 1248262417Shselasky (spans[i].width > FG_PSPAN_MAX_WIDTH)) { 1249199086Srpaulo if ((i + 1) < num_spans) { 1250199086Srpaulo memcpy(&spans[i], &spans[i + 1], 1251262417Shselasky (num_spans - i - 1) * sizeof(fg_pspan)); 1252199086Srpaulo i--; 1253199086Srpaulo } 1254199086Srpaulo num_spans--; 1255199086Srpaulo continue; 1256199086Srpaulo } 1257199086Srpaulo 1258199086Srpaulo /* compute this span's representative location */ 1259262417Shselasky spans[i].loc = spans[i].cog * FG_SCALE_FACTOR / 1260199086Srpaulo spans[i].cum; 1261199086Srpaulo 1262262417Shselasky spans[i].matched = false; /* not yet matched against a stroke */ 1263199086Srpaulo } 1264199086Srpaulo 1265199086Srpaulo *nspans_p = num_spans; 1266199086Srpaulo} 1267199086Srpaulo 1268262417Shselaskystatic void 1269262417Shselaskywsp_interpret_sensor_data(struct atp_softc *sc, u_int data_len) 1270262417Shselasky{ 1271262417Shselasky const struct wsp_dev_params *params = sc->sc_params; 1272262417Shselasky wsp_finger_t fingers[WSP_MAX_FINGERS]; 1273262417Shselasky struct wsp_finger_sensor_data *source_fingerp; 1274262417Shselasky u_int n_source_fingers; 1275262417Shselasky u_int n_fingers; 1276262417Shselasky u_int i; 1277262417Shselasky 1278262417Shselasky /* validate sensor data length */ 1279262417Shselasky if ((data_len < params->finger_data_offset) || 1280262417Shselasky ((data_len - params->finger_data_offset) % 1281262417Shselasky WSP_SIZEOF_FINGER_SENSOR_DATA) != 0) 1282262417Shselasky return; 1283262417Shselasky 1284262417Shselasky /* compute number of source fingers */ 1285262417Shselasky n_source_fingers = (data_len - params->finger_data_offset) / 1286262417Shselasky WSP_SIZEOF_FINGER_SENSOR_DATA; 1287262417Shselasky 1288262417Shselasky if (n_source_fingers > WSP_MAX_FINGERS) 1289262417Shselasky n_source_fingers = WSP_MAX_FINGERS; 1290262417Shselasky 1291262417Shselasky /* iterate over the source data collecting useful fingers */ 1292262417Shselasky n_fingers = 0; 1293262417Shselasky source_fingerp = (struct wsp_finger_sensor_data *)(sc->sc_sensor_data + 1294262417Shselasky params->finger_data_offset); 1295262417Shselasky 1296262417Shselasky for (i = 0; i < n_source_fingers; i++, source_fingerp++) { 1297262417Shselasky /* swap endianness, if any */ 1298262417Shselasky if (le16toh(0x1234) != 0x1234) { 1299262417Shselasky source_fingerp->origin = le16toh((uint16_t)source_fingerp->origin); 1300262417Shselasky source_fingerp->abs_x = le16toh((uint16_t)source_fingerp->abs_x); 1301262417Shselasky source_fingerp->abs_y = le16toh((uint16_t)source_fingerp->abs_y); 1302262417Shselasky source_fingerp->rel_x = le16toh((uint16_t)source_fingerp->rel_x); 1303262417Shselasky source_fingerp->rel_y = le16toh((uint16_t)source_fingerp->rel_y); 1304262417Shselasky source_fingerp->tool_major = le16toh((uint16_t)source_fingerp->tool_major); 1305262417Shselasky source_fingerp->tool_minor = le16toh((uint16_t)source_fingerp->tool_minor); 1306262417Shselasky source_fingerp->orientation = le16toh((uint16_t)source_fingerp->orientation); 1307262417Shselasky source_fingerp->touch_major = le16toh((uint16_t)source_fingerp->touch_major); 1308262417Shselasky source_fingerp->touch_minor = le16toh((uint16_t)source_fingerp->touch_minor); 1309262417Shselasky source_fingerp->multi = le16toh((uint16_t)source_fingerp->multi); 1310262417Shselasky } 1311262417Shselasky 1312262417Shselasky /* check for minium threshold */ 1313262417Shselasky if (source_fingerp->touch_major == 0) 1314262417Shselasky continue; 1315262417Shselasky 1316262417Shselasky fingers[n_fingers].matched = false; 1317262417Shselasky fingers[n_fingers].x = source_fingerp->abs_x; 1318262417Shselasky fingers[n_fingers].y = -source_fingerp->abs_y; 1319262417Shselasky 1320262417Shselasky n_fingers++; 1321262417Shselasky } 1322262417Shselasky 1323262417Shselasky if ((sc->sc_n_strokes == 0) && (n_fingers == 0)) 1324262417Shselasky return; 1325262417Shselasky 1326262417Shselasky if (wsp_update_strokes(sc, fingers, n_fingers)) 1327262417Shselasky sc->sc_status.flags |= MOUSE_POSCHANGED; 1328262417Shselasky 1329262417Shselasky switch(params->tp_type) { 1330262417Shselasky case WSP_TRACKPAD_TYPE2: 1331262417Shselasky sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE2_BUTTON_DATA_OFFSET]; 1332262417Shselasky break; 1333262417Shselasky case WSP_TRACKPAD_TYPE3: 1334262417Shselasky sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE3_BUTTON_DATA_OFFSET]; 1335262417Shselasky break; 1336262417Shselasky default: 1337262417Shselasky break; 1338262417Shselasky } 1339262417Shselasky sc->sc_status.button = sc->sc_ibtn ? MOUSE_BUTTON1DOWN : 0; 1340262417Shselasky} 1341262417Shselasky 1342199086Srpaulo/* 1343199086Srpaulo * Match a pressure-span against a stroke-component. If there is a 1344262417Shselasky * match, update the component's state and return true. 1345199086Srpaulo */ 1346199086Srpaulostatic boolean_t 1347262417Shselaskyfg_match_stroke_component(fg_stroke_component_t *component, 1348262417Shselasky const fg_pspan *pspan, atp_stroke_type stroke_type) 1349199086Srpaulo{ 1350200241Srpaulo int delta_mickeys; 1351200241Srpaulo u_int min_pressure; 1352199086Srpaulo 1353200241Srpaulo delta_mickeys = pspan->loc - component->loc; 1354200241Srpaulo 1355262417Shselasky if (abs(delta_mickeys) > (int)FG_MAX_DELTA_MICKEYS) 1356262417Shselasky return (false); /* the finger span is too far out; no match */ 1357199086Srpaulo 1358262417Shselasky component->loc = pspan->loc; 1359200241Srpaulo 1360200241Srpaulo /* 1361200241Srpaulo * A sudden and significant increase in a pspan's cumulative 1362200241Srpaulo * pressure indicates the incidence of a new finger 1363200241Srpaulo * contact. This usually revises the pspan's 1364200241Srpaulo * centre-of-gravity, and hence the location of any/all 1365200241Srpaulo * matching stroke component(s). But such a change should 1366200241Srpaulo * *not* be interpreted as a movement. 1367200241Srpaulo */ 1368262417Shselasky if (pspan->cum > ((3 * component->cum_pressure) >> 1)) 1369200241Srpaulo delta_mickeys = 0; 1370200241Srpaulo 1371199086Srpaulo component->cum_pressure = pspan->cum; 1372199086Srpaulo if (pspan->cum > component->max_cum_pressure) 1373199086Srpaulo component->max_cum_pressure = pspan->cum; 1374199086Srpaulo 1375199086Srpaulo /* 1376200241Srpaulo * Disregard the component's movement if its cumulative 1377200241Srpaulo * pressure drops below a fraction of the maximum; this 1378200241Srpaulo * fraction is determined based on the stroke's type. 1379199086Srpaulo */ 1380200241Srpaulo if (stroke_type == ATP_STROKE_TOUCH) 1381200241Srpaulo min_pressure = (3 * component->max_cum_pressure) >> 2; 1382200241Srpaulo else 1383200241Srpaulo min_pressure = component->max_cum_pressure >> 2; 1384200241Srpaulo if (component->cum_pressure < min_pressure) 1385199086Srpaulo delta_mickeys = 0; 1386199086Srpaulo 1387199086Srpaulo component->delta_mickeys = delta_mickeys; 1388262417Shselasky return (true); 1389199086Srpaulo} 1390199086Srpaulo 1391199086Srpaulostatic void 1392262417Shselaskyfg_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis, 1393262417Shselasky fg_pspan *pspans, u_int n_pspans, u_int repeat_count) 1394199086Srpaulo{ 1395262417Shselasky atp_stroke_t *strokep; 1396199086Srpaulo u_int repeat_index = 0; 1397262417Shselasky u_int i; 1398199086Srpaulo 1399199086Srpaulo /* Determine the index of the multi-span. */ 1400199086Srpaulo if (repeat_count) { 1401199086Srpaulo for (i = 0; i < n_pspans; i++) { 1402262417Shselasky if (pspans[i].cum > pspans[repeat_index].cum) 1403199086Srpaulo repeat_index = i; 1404199086Srpaulo } 1405199086Srpaulo } 1406199086Srpaulo 1407262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1408262417Shselasky if (strokep->components[axis].matched) 1409199086Srpaulo continue; /* skip matched components */ 1410199086Srpaulo 1411262417Shselasky for (i = 0; i < n_pspans; i++) { 1412262417Shselasky if (pspans[i].matched) 1413199086Srpaulo continue; /* skip matched pspans */ 1414199086Srpaulo 1415262417Shselasky if (fg_match_stroke_component( 1416262417Shselasky &strokep->components[axis], &pspans[i], 1417262417Shselasky strokep->type)) { 1418262417Shselasky 1419199086Srpaulo /* There is a match. */ 1420262417Shselasky strokep->components[axis].matched = true; 1421199086Srpaulo 1422199086Srpaulo /* Take care to repeat at the multi-span. */ 1423262417Shselasky if ((repeat_count > 0) && (i == repeat_index)) 1424199086Srpaulo repeat_count--; 1425199086Srpaulo else 1426262417Shselasky pspans[i].matched = true; 1427199086Srpaulo 1428262417Shselasky break; /* skip to the next strokep */ 1429199086Srpaulo } 1430199086Srpaulo } /* loop over pspans */ 1431199086Srpaulo } /* loop over strokes */ 1432199086Srpaulo} 1433199086Srpaulo 1434262417Shselaskystatic boolean_t 1435262417Shselaskywsp_match_strokes_against_fingers(struct atp_softc *sc, 1436262417Shselasky wsp_finger_t *fingers, u_int n_fingers) 1437262417Shselasky{ 1438262417Shselasky boolean_t movement = false; 1439262417Shselasky atp_stroke_t *strokep; 1440262417Shselasky u_int i; 1441262417Shselasky 1442262417Shselasky /* reset the matched status for all strokes */ 1443262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) 1444262417Shselasky strokep->matched = false; 1445262417Shselasky 1446262417Shselasky for (i = 0; i != n_fingers; i++) { 1447262417Shselasky u_int least_distance_sq = WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ; 1448262417Shselasky atp_stroke_t *strokep_best = NULL; 1449262417Shselasky 1450262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1451262417Shselasky int instantaneous_dx; 1452262417Shselasky int instantaneous_dy; 1453262417Shselasky u_int d_squared; 1454262417Shselasky 1455262417Shselasky if (strokep->matched) 1456262417Shselasky continue; 1457262417Shselasky 1458262417Shselasky instantaneous_dx = fingers[i].x - strokep->x; 1459262417Shselasky instantaneous_dy = fingers[i].y - strokep->y; 1460262417Shselasky 1461262417Shselasky /* skip strokes which are far away */ 1462262417Shselasky d_squared = 1463262417Shselasky (instantaneous_dx * instantaneous_dx) + 1464262417Shselasky (instantaneous_dy * instantaneous_dy); 1465262417Shselasky 1466262417Shselasky if (d_squared < least_distance_sq) { 1467262417Shselasky least_distance_sq = d_squared; 1468262417Shselasky strokep_best = strokep; 1469262417Shselasky } 1470262417Shselasky } 1471262417Shselasky 1472262417Shselasky strokep = strokep_best; 1473262417Shselasky 1474262417Shselasky if (strokep != NULL) { 1475262417Shselasky fingers[i].matched = true; 1476262417Shselasky 1477262417Shselasky strokep->matched = true; 1478262417Shselasky strokep->instantaneous_dx = fingers[i].x - strokep->x; 1479262417Shselasky strokep->instantaneous_dy = fingers[i].y - strokep->y; 1480262417Shselasky strokep->x = fingers[i].x; 1481262417Shselasky strokep->y = fingers[i].y; 1482262417Shselasky 1483262417Shselasky atp_advance_stroke_state(sc, strokep, &movement); 1484262417Shselasky } 1485262417Shselasky } 1486262417Shselasky return (movement); 1487262417Shselasky} 1488262417Shselasky 1489199086Srpaulo/* 1490199086Srpaulo * Update strokes by matching against current pressure-spans. 1491262417Shselasky * Return true if any movement is detected. 1492199086Srpaulo */ 1493199086Srpaulostatic boolean_t 1494262417Shselaskyfg_update_strokes(struct atp_softc *sc, fg_pspan *pspans_x, 1495262417Shselasky u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans) 1496199086Srpaulo{ 1497262417Shselasky atp_stroke_t *strokep; 1498262417Shselasky atp_stroke_t *strokep_next; 1499262417Shselasky boolean_t movement = false; 1500262417Shselasky u_int repeat_count = 0; 1501262417Shselasky u_int i; 1502262417Shselasky u_int j; 1503199086Srpaulo 1504199086Srpaulo /* Reset X and Y components of all strokes as unmatched. */ 1505262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1506262417Shselasky strokep->components[X].matched = false; 1507262417Shselasky strokep->components[Y].matched = false; 1508199086Srpaulo } 1509199086Srpaulo 1510199086Srpaulo /* 1511199086Srpaulo * Usually, the X and Y pspans come in pairs (the common case 1512199086Srpaulo * being a single pair). It is possible, however, that 1513199086Srpaulo * multiple contacts resolve to a single pspan along an 1514199086Srpaulo * axis, as illustrated in the following: 1515199086Srpaulo * 1516199086Srpaulo * F = finger-contact 1517199086Srpaulo * 1518199086Srpaulo * pspan pspan 1519199086Srpaulo * +-----------------------+ 1520199086Srpaulo * | . . | 1521199086Srpaulo * | . . | 1522199086Srpaulo * | . . | 1523199086Srpaulo * | . . | 1524199086Srpaulo * pspan |.........F......F | 1525199086Srpaulo * | | 1526199086Srpaulo * | | 1527199086Srpaulo * | | 1528199086Srpaulo * +-----------------------+ 1529199086Srpaulo * 1530199086Srpaulo * 1531199086Srpaulo * The above case can be detected by a difference in the 1532199086Srpaulo * number of X and Y pspans. When this happens, X and Y pspans 1533199086Srpaulo * aren't easy to pair or match against strokes. 1534199086Srpaulo * 1535199086Srpaulo * When X and Y pspans differ in number, the axis with the 1536199086Srpaulo * smaller number of pspans is regarded as having a repeating 1537199086Srpaulo * pspan (or a multi-pspan)--in the above illustration, the 1538199086Srpaulo * Y-axis has a repeating pspan. Our approach is to try to 1539199086Srpaulo * match the multi-pspan repeatedly against strokes. The 1540199086Srpaulo * difference between the number of X and Y pspans gives us a 1541199086Srpaulo * crude repeat_count for matching multi-pspans--i.e. the 1542199086Srpaulo * multi-pspan along the Y axis (above) has a repeat_count of 1. 1543199086Srpaulo */ 1544199086Srpaulo repeat_count = abs(n_xpspans - n_ypspans); 1545199086Srpaulo 1546262417Shselasky fg_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans, 1547199086Srpaulo (((repeat_count != 0) && ((n_xpspans < n_ypspans))) ? 1548199086Srpaulo repeat_count : 0)); 1549262417Shselasky fg_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans, 1550199086Srpaulo (((repeat_count != 0) && (n_ypspans < n_xpspans)) ? 1551199086Srpaulo repeat_count : 0)); 1552199086Srpaulo 1553199086Srpaulo /* Update the state of strokes based on the above pspan matches. */ 1554262417Shselasky TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { 1555262417Shselasky 1556262417Shselasky if (strokep->components[X].matched && 1557262417Shselasky strokep->components[Y].matched) { 1558262417Shselasky strokep->matched = true; 1559262417Shselasky strokep->instantaneous_dx = 1560262417Shselasky strokep->components[X].delta_mickeys; 1561262417Shselasky strokep->instantaneous_dy = 1562262417Shselasky strokep->components[Y].delta_mickeys; 1563262417Shselasky atp_advance_stroke_state(sc, strokep, &movement); 1564199086Srpaulo } else { 1565199086Srpaulo /* 1566199086Srpaulo * At least one component of this stroke 1567199086Srpaulo * didn't match against current pspans; 1568199086Srpaulo * terminate it. 1569199086Srpaulo */ 1570262417Shselasky atp_terminate_stroke(sc, strokep); 1571199086Srpaulo } 1572199086Srpaulo } 1573199086Srpaulo 1574199086Srpaulo /* Add new strokes for pairs of unmatched pspans */ 1575199086Srpaulo for (i = 0; i < n_xpspans; i++) { 1576262417Shselasky if (pspans_x[i].matched == false) break; 1577199086Srpaulo } 1578199086Srpaulo for (j = 0; j < n_ypspans; j++) { 1579262417Shselasky if (pspans_y[j].matched == false) break; 1580199086Srpaulo } 1581199086Srpaulo if ((i < n_xpspans) && (j < n_ypspans)) { 1582207077Sthompsa#ifdef USB_DEBUG 1583199086Srpaulo if (atp_debug >= ATP_LLEVEL_INFO) { 1584199086Srpaulo printf("unmatched pspans:"); 1585199086Srpaulo for (; i < n_xpspans; i++) { 1586199086Srpaulo if (pspans_x[i].matched) 1587199086Srpaulo continue; 1588199086Srpaulo printf(" X:[loc:%u,cum:%u]", 1589199086Srpaulo pspans_x[i].loc, pspans_x[i].cum); 1590199086Srpaulo } 1591199086Srpaulo for (; j < n_ypspans; j++) { 1592199086Srpaulo if (pspans_y[j].matched) 1593199086Srpaulo continue; 1594199086Srpaulo printf(" Y:[loc:%u,cum:%u]", 1595199086Srpaulo pspans_y[j].loc, pspans_y[j].cum); 1596199086Srpaulo } 1597199086Srpaulo printf("\n"); 1598199086Srpaulo } 1599207077Sthompsa#endif /* USB_DEBUG */ 1600199086Srpaulo if ((n_xpspans == 1) && (n_ypspans == 1)) 1601199086Srpaulo /* The common case of a single pair of new pspans. */ 1602262417Shselasky fg_add_stroke(sc, &pspans_x[0], &pspans_y[0]); 1603199086Srpaulo else 1604262417Shselasky fg_add_new_strokes(sc, pspans_x, n_xpspans, 1605199086Srpaulo pspans_y, n_ypspans); 1606199086Srpaulo } 1607199086Srpaulo 1608207077Sthompsa#ifdef USB_DEBUG 1609199086Srpaulo if (atp_debug >= ATP_LLEVEL_INFO) { 1610262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1611262417Shselasky printf(" %s%clc:%u,dm:%d,cum:%d,max:%d,%c" 1612262417Shselasky ",%clc:%u,dm:%d,cum:%d,max:%d,%c", 1613262417Shselasky (strokep->flags & ATSF_ZOMBIE) ? "zomb:" : "", 1614262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<', 1615262417Shselasky strokep->components[X].loc, 1616262417Shselasky strokep->components[X].delta_mickeys, 1617262417Shselasky strokep->components[X].cum_pressure, 1618262417Shselasky strokep->components[X].max_cum_pressure, 1619262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>', 1620262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<', 1621262417Shselasky strokep->components[Y].loc, 1622262417Shselasky strokep->components[Y].delta_mickeys, 1623262417Shselasky strokep->components[Y].cum_pressure, 1624262417Shselasky strokep->components[Y].max_cum_pressure, 1625262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>'); 1626199086Srpaulo } 1627262417Shselasky if (TAILQ_FIRST(&sc->sc_stroke_used) != NULL) 1628199086Srpaulo printf("\n"); 1629199086Srpaulo } 1630207077Sthompsa#endif /* USB_DEBUG */ 1631262417Shselasky return (movement); 1632262417Shselasky} 1633199086Srpaulo 1634262417Shselasky/* 1635262417Shselasky * Update strokes by matching against current pressure-spans. 1636262417Shselasky * Return true if any movement is detected. 1637262417Shselasky */ 1638262417Shselaskystatic boolean_t 1639262417Shselaskywsp_update_strokes(struct atp_softc *sc, wsp_finger_t *fingers, u_int n_fingers) 1640262417Shselasky{ 1641262417Shselasky boolean_t movement = false; 1642262417Shselasky atp_stroke_t *strokep_next; 1643262417Shselasky atp_stroke_t *strokep; 1644262417Shselasky u_int i; 1645262417Shselasky 1646262417Shselasky if (sc->sc_n_strokes > 0) { 1647262417Shselasky movement = wsp_match_strokes_against_fingers( 1648262417Shselasky sc, fingers, n_fingers); 1649262417Shselasky 1650262417Shselasky /* handle zombie strokes */ 1651262417Shselasky TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { 1652262417Shselasky if (strokep->matched) 1653262417Shselasky continue; 1654262417Shselasky atp_terminate_stroke(sc, strokep); 1655262417Shselasky } 1656262417Shselasky } 1657262417Shselasky 1658262417Shselasky /* initialize unmatched fingers as strokes */ 1659262417Shselasky for (i = 0; i != n_fingers; i++) { 1660262417Shselasky if (fingers[i].matched) 1661262417Shselasky continue; 1662262417Shselasky 1663262417Shselasky wsp_add_stroke(sc, fingers + i); 1664262417Shselasky } 1665199086Srpaulo return (movement); 1666199086Srpaulo} 1667199086Srpaulo 1668199086Srpaulo/* Initialize a stroke using a pressure-span. */ 1669262417Shselaskystatic void 1670262417Shselaskyfg_add_stroke(struct atp_softc *sc, const fg_pspan *pspan_x, 1671262417Shselasky const fg_pspan *pspan_y) 1672199086Srpaulo{ 1673262417Shselasky atp_stroke_t *strokep; 1674199086Srpaulo 1675262417Shselasky strokep = atp_alloc_stroke(sc); 1676262417Shselasky if (strokep == NULL) 1677199086Srpaulo return; 1678199086Srpaulo 1679199086Srpaulo /* 1680199086Srpaulo * Strokes begin as potential touches. If a stroke survives 1681199086Srpaulo * longer than a threshold, or if it records significant 1682199086Srpaulo * cumulative movement, then it is considered a 'slide'. 1683199086Srpaulo */ 1684262417Shselasky strokep->type = ATP_STROKE_TOUCH; 1685262417Shselasky strokep->matched = false; 1686262417Shselasky microtime(&strokep->ctime); 1687262417Shselasky strokep->age = 1; /* number of interrupts */ 1688262417Shselasky strokep->x = pspan_x->loc; 1689262417Shselasky strokep->y = pspan_y->loc; 1690199086Srpaulo 1691262417Shselasky strokep->components[X].loc = pspan_x->loc; 1692262417Shselasky strokep->components[X].cum_pressure = pspan_x->cum; 1693262417Shselasky strokep->components[X].max_cum_pressure = pspan_x->cum; 1694262417Shselasky strokep->components[X].matched = true; 1695199086Srpaulo 1696262417Shselasky strokep->components[Y].loc = pspan_y->loc; 1697262417Shselasky strokep->components[Y].cum_pressure = pspan_y->cum; 1698262417Shselasky strokep->components[Y].max_cum_pressure = pspan_y->cum; 1699262417Shselasky strokep->components[Y].matched = true; 1700199086Srpaulo 1701199086Srpaulo if (sc->sc_n_strokes > 1) { 1702199086Srpaulo /* Reset double-tap-n-drag if we have more than one strokes. */ 1703199086Srpaulo sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 1704199086Srpaulo } 1705199086Srpaulo 1706199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "[%u,%u], time: %u,%ld\n", 1707262417Shselasky strokep->components[X].loc, 1708262417Shselasky strokep->components[Y].loc, 1709262417Shselasky (u_int)strokep->ctime.tv_sec, 1710262417Shselasky (unsigned long int)strokep->ctime.tv_usec); 1711199086Srpaulo} 1712199086Srpaulo 1713199086Srpaulostatic void 1714262417Shselaskyfg_add_new_strokes(struct atp_softc *sc, fg_pspan *pspans_x, 1715262417Shselasky u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans) 1716199086Srpaulo{ 1717262417Shselasky fg_pspan spans[2][FG_MAX_PSPANS_PER_AXIS]; 1718233774Shselasky u_int nspans[2]; 1719233774Shselasky u_int i; 1720233774Shselasky u_int j; 1721199086Srpaulo 1722199086Srpaulo /* Copy unmatched pspans into the local arrays. */ 1723199086Srpaulo for (i = 0, nspans[X] = 0; i < n_xpspans; i++) { 1724262417Shselasky if (pspans_x[i].matched == false) { 1725199086Srpaulo spans[X][nspans[X]] = pspans_x[i]; 1726199086Srpaulo nspans[X]++; 1727199086Srpaulo } 1728199086Srpaulo } 1729199086Srpaulo for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) { 1730262417Shselasky if (pspans_y[j].matched == false) { 1731199086Srpaulo spans[Y][nspans[Y]] = pspans_y[j]; 1732199086Srpaulo nspans[Y]++; 1733199086Srpaulo } 1734199086Srpaulo } 1735199086Srpaulo 1736199086Srpaulo if (nspans[X] == nspans[Y]) { 1737199086Srpaulo /* Create new strokes from pairs of unmatched pspans */ 1738199086Srpaulo for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++) 1739262417Shselasky fg_add_stroke(sc, &spans[X][i], &spans[Y][j]); 1740199086Srpaulo } else { 1741199086Srpaulo u_int cum = 0; 1742199086Srpaulo atp_axis repeat_axis; /* axis with multi-pspans */ 1743199086Srpaulo u_int repeat_count; /* repeat count for the multi-pspan*/ 1744199086Srpaulo u_int repeat_index = 0; /* index of the multi-span */ 1745199086Srpaulo 1746199086Srpaulo repeat_axis = (nspans[X] > nspans[Y]) ? Y : X; 1747199086Srpaulo repeat_count = abs(nspans[X] - nspans[Y]); 1748199086Srpaulo for (i = 0; i < nspans[repeat_axis]; i++) { 1749199086Srpaulo if (spans[repeat_axis][i].cum > cum) { 1750199086Srpaulo repeat_index = i; 1751199086Srpaulo cum = spans[repeat_axis][i].cum; 1752199086Srpaulo } 1753199086Srpaulo } 1754199086Srpaulo 1755199086Srpaulo /* Create new strokes from pairs of unmatched pspans */ 1756199086Srpaulo i = 0, j = 0; 1757199086Srpaulo for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) { 1758262417Shselasky fg_add_stroke(sc, &spans[X][i], &spans[Y][j]); 1759199086Srpaulo 1760199086Srpaulo /* Take care to repeat at the multi-pspan. */ 1761199086Srpaulo if (repeat_count > 0) { 1762199086Srpaulo if ((repeat_axis == X) && 1763199086Srpaulo (repeat_index == i)) { 1764199086Srpaulo i--; /* counter loop increment */ 1765199086Srpaulo repeat_count--; 1766199086Srpaulo } else if ((repeat_axis == Y) && 1767199086Srpaulo (repeat_index == j)) { 1768199086Srpaulo j--; /* counter loop increment */ 1769199086Srpaulo repeat_count--; 1770199086Srpaulo } 1771199086Srpaulo } 1772199086Srpaulo } 1773199086Srpaulo } 1774199086Srpaulo} 1775199086Srpaulo 1776262417Shselasky/* Initialize a stroke from an unmatched finger. */ 1777262417Shselaskystatic void 1778262417Shselaskywsp_add_stroke(struct atp_softc *sc, const wsp_finger_t *fingerp) 1779199086Srpaulo{ 1780262417Shselasky atp_stroke_t *strokep; 1781199086Srpaulo 1782262417Shselasky strokep = atp_alloc_stroke(sc); 1783262417Shselasky if (strokep == NULL) 1784200241Srpaulo return; 1785200241Srpaulo 1786262417Shselasky /* 1787262417Shselasky * Strokes begin as potential touches. If a stroke survives 1788262417Shselasky * longer than a threshold, or if it records significant 1789262417Shselasky * cumulative movement, then it is considered a 'slide'. 1790262417Shselasky */ 1791262417Shselasky strokep->type = ATP_STROKE_TOUCH; 1792262417Shselasky strokep->matched = true; 1793262417Shselasky microtime(&strokep->ctime); 1794262417Shselasky strokep->age = 1; /* number of interrupts */ 1795262417Shselasky strokep->x = fingerp->x; 1796262417Shselasky strokep->y = fingerp->y; 1797199086Srpaulo 1798262417Shselasky /* Reset double-tap-n-drag if we have more than one strokes. */ 1799262417Shselasky if (sc->sc_n_strokes > 1) 1800262417Shselasky sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 1801200241Srpaulo 1802262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "[%d,%d]\n", strokep->x, strokep->y); 1803199086Srpaulo} 1804199086Srpaulo 1805262417Shselaskystatic void 1806262417Shselaskyatp_advance_stroke_state(struct atp_softc *sc, atp_stroke_t *strokep, 1807262417Shselasky boolean_t *movementp) 1808200241Srpaulo{ 1809262417Shselasky /* Revitalize stroke if it had previously been marked as a zombie. */ 1810262417Shselasky if (strokep->flags & ATSF_ZOMBIE) 1811262417Shselasky strokep->flags &= ~ATSF_ZOMBIE; 1812200241Srpaulo 1813262417Shselasky strokep->age++; 1814262417Shselasky if (strokep->age <= atp_stroke_maturity_threshold) { 1815262417Shselasky /* Avoid noise from immature strokes. */ 1816262417Shselasky strokep->instantaneous_dx = 0; 1817262417Shselasky strokep->instantaneous_dy = 0; 1818200241Srpaulo } 1819200241Srpaulo 1820262417Shselasky if (atp_compute_stroke_movement(strokep)) 1821262417Shselasky *movementp = true; 1822199086Srpaulo 1823262417Shselasky if (strokep->type != ATP_STROKE_TOUCH) 1824199086Srpaulo return; 1825199086Srpaulo 1826262417Shselasky /* Convert touch strokes to slides upon detecting movement or age. */ 1827262417Shselasky if ((abs(strokep->cum_movement_x) > atp_slide_min_movement) || 1828262417Shselasky (abs(strokep->cum_movement_y) > atp_slide_min_movement)) 1829262417Shselasky atp_convert_to_slide(sc, strokep); 1830262417Shselasky else { 1831262417Shselasky /* Compute the stroke's age. */ 1832262417Shselasky struct timeval tdiff; 1833262417Shselasky getmicrotime(&tdiff); 1834262417Shselasky if (timevalcmp(&tdiff, &strokep->ctime, >)) { 1835262417Shselasky timevalsub(&tdiff, &strokep->ctime); 1836199086Srpaulo 1837262417Shselasky if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) || 1838262417Shselasky ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) && 1839262417Shselasky (tdiff.tv_usec >= (atp_touch_timeout % 1000000)))) 1840262417Shselasky atp_convert_to_slide(sc, strokep); 1841199086Srpaulo } 1842199086Srpaulo } 1843199086Srpaulo} 1844199086Srpaulo 1845262417Shselaskystatic boolean_t 1846262417Shselaskyatp_stroke_has_small_movement(const atp_stroke_t *strokep) 1847199086Srpaulo{ 1848262417Shselasky return (((u_int)abs(strokep->instantaneous_dx) <= 1849262417Shselasky atp_small_movement_threshold) && 1850262417Shselasky ((u_int)abs(strokep->instantaneous_dy) <= 1851262417Shselasky atp_small_movement_threshold)); 1852199086Srpaulo} 1853199086Srpaulo 1854199086Srpaulo/* 1855262417Shselasky * Accumulate instantaneous changes into the stroke's 'pending' bucket; if 1856199086Srpaulo * the aggregate exceeds the small_movement_threshold, then retain 1857262417Shselasky * instantaneous changes for later. 1858199086Srpaulo */ 1859199086Srpaulostatic void 1860262417Shselaskyatp_update_pending_mickeys(atp_stroke_t *strokep) 1861199086Srpaulo{ 1862262417Shselasky /* accumulate instantaneous movement */ 1863262417Shselasky strokep->pending_dx += strokep->instantaneous_dx; 1864262417Shselasky strokep->pending_dy += strokep->instantaneous_dy; 1865199086Srpaulo 1866262417Shselasky#define UPDATE_INSTANTANEOUS_AND_PENDING(I, P) \ 1867262417Shselasky if (abs((P)) <= atp_small_movement_threshold) \ 1868262417Shselasky (I) = 0; /* clobber small movement */ \ 1869262417Shselasky else { \ 1870262417Shselasky if ((I) > 0) { \ 1871262417Shselasky /* \ 1872262417Shselasky * Round up instantaneous movement to the nearest \ 1873262417Shselasky * ceiling. This helps preserve small mickey \ 1874262417Shselasky * movements from being lost in following scaling \ 1875262417Shselasky * operation. \ 1876262417Shselasky */ \ 1877262417Shselasky (I) = (((I) + (atp_mickeys_scale_factor - 1)) / \ 1878262417Shselasky atp_mickeys_scale_factor) * \ 1879262417Shselasky atp_mickeys_scale_factor; \ 1880262417Shselasky \ 1881262417Shselasky /* \ 1882262417Shselasky * Deduct the rounded mickeys from pending mickeys. \ 1883262417Shselasky * Note: we multiply by 2 to offset the previous \ 1884262417Shselasky * accumulation of instantaneous movement into \ 1885262417Shselasky * pending. \ 1886262417Shselasky */ \ 1887262417Shselasky (P) -= ((I) << 1); \ 1888262417Shselasky \ 1889262417Shselasky /* truncate pending to 0 if it becomes negative. */ \ 1890262417Shselasky (P) = imax((P), 0); \ 1891262417Shselasky } else { \ 1892262417Shselasky /* \ 1893262417Shselasky * Round down instantaneous movement to the nearest \ 1894262417Shselasky * ceiling. This helps preserve small mickey \ 1895262417Shselasky * movements from being lost in following scaling \ 1896262417Shselasky * operation. \ 1897262417Shselasky */ \ 1898262417Shselasky (I) = (((I) - (atp_mickeys_scale_factor - 1)) / \ 1899262417Shselasky atp_mickeys_scale_factor) * \ 1900262417Shselasky atp_mickeys_scale_factor; \ 1901262417Shselasky \ 1902262417Shselasky /* \ 1903262417Shselasky * Deduct the rounded mickeys from pending mickeys. \ 1904262417Shselasky * Note: we multiply by 2 to offset the previous \ 1905262417Shselasky * accumulation of instantaneous movement into \ 1906262417Shselasky * pending. \ 1907262417Shselasky */ \ 1908262417Shselasky (P) -= ((I) << 1); \ 1909262417Shselasky \ 1910262417Shselasky /* truncate pending to 0 if it becomes positive. */ \ 1911262417Shselasky (P) = imin((P), 0); \ 1912262417Shselasky } \ 1913199086Srpaulo } 1914199086Srpaulo 1915262417Shselasky UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dx, 1916262417Shselasky strokep->pending_dx); 1917262417Shselasky UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dy, 1918262417Shselasky strokep->pending_dy); 1919199086Srpaulo} 1920199086Srpaulo 1921199086Srpaulo/* 1922199086Srpaulo * Compute a smoothened value for the stroke's movement from 1923262417Shselasky * instantaneous changes in the X and Y components. 1924199086Srpaulo */ 1925199086Srpaulostatic boolean_t 1926262417Shselaskyatp_compute_stroke_movement(atp_stroke_t *strokep) 1927199086Srpaulo{ 1928199086Srpaulo /* 1929199086Srpaulo * Short movements are added first to the 'pending' bucket, 1930199086Srpaulo * and then acted upon only when their aggregate exceeds a 1931199086Srpaulo * threshold. This has the effect of filtering away movement 1932199086Srpaulo * noise. 1933199086Srpaulo */ 1934262417Shselasky if (atp_stroke_has_small_movement(strokep)) 1935262417Shselasky atp_update_pending_mickeys(strokep); 1936262417Shselasky else { /* large movement */ 1937199086Srpaulo /* clear away any pending mickeys if there are large movements*/ 1938262417Shselasky strokep->pending_dx = 0; 1939262417Shselasky strokep->pending_dy = 0; 1940199086Srpaulo } 1941199086Srpaulo 1942262417Shselasky /* scale movement */ 1943262417Shselasky strokep->movement_dx = (strokep->instantaneous_dx) / 1944262417Shselasky (int)atp_mickeys_scale_factor; 1945262417Shselasky strokep->movement_dy = (strokep->instantaneous_dy) / 1946262417Shselasky (int)atp_mickeys_scale_factor; 1947199086Srpaulo 1948262417Shselasky if ((abs(strokep->instantaneous_dx) >= ATP_FAST_MOVEMENT_TRESHOLD) || 1949262417Shselasky (abs(strokep->instantaneous_dy) >= ATP_FAST_MOVEMENT_TRESHOLD)) { 1950262417Shselasky strokep->movement_dx <<= 1; 1951262417Shselasky strokep->movement_dy <<= 1; 1952199086Srpaulo } 1953199086Srpaulo 1954262417Shselasky strokep->cum_movement_x += strokep->movement_dx; 1955262417Shselasky strokep->cum_movement_y += strokep->movement_dy; 1956262417Shselasky 1957262417Shselasky return ((strokep->movement_dx != 0) || (strokep->movement_dy != 0)); 1958199086Srpaulo} 1959199086Srpaulo 1960262417Shselasky/* 1961262417Shselasky * Terminate a stroke. Aside from immature strokes, a slide or touch is 1962262417Shselasky * retained as a zombies so as to reap all their termination siblings 1963262417Shselasky * together; this helps establish the number of fingers involved at the 1964262417Shselasky * end of a multi-touch gesture. 1965262417Shselasky */ 1966262417Shselaskystatic void 1967262417Shselaskyatp_terminate_stroke(struct atp_softc *sc, atp_stroke_t *strokep) 1968199086Srpaulo{ 1969262417Shselasky if (strokep->flags & ATSF_ZOMBIE) 1970262417Shselasky return; 1971199086Srpaulo 1972262417Shselasky /* Drop immature strokes rightaway. */ 1973262417Shselasky if (strokep->age <= atp_stroke_maturity_threshold) { 1974262417Shselasky atp_free_stroke(sc, strokep); 1975262417Shselasky return; 1976262417Shselasky } 1977199086Srpaulo 1978262417Shselasky strokep->flags |= ATSF_ZOMBIE; 1979262417Shselasky sc->sc_state |= ATP_ZOMBIES_EXIST; 1980262417Shselasky 1981262417Shselasky callout_reset(&sc->sc_callout, ATP_ZOMBIE_STROKE_REAP_INTERVAL, 1982262417Shselasky atp_reap_sibling_zombies, sc); 1983262417Shselasky 1984262417Shselasky /* 1985262417Shselasky * Reset the double-click-n-drag at the termination of any 1986262417Shselasky * slide stroke. 1987262417Shselasky */ 1988262417Shselasky if (strokep->type == ATP_STROKE_SLIDE) 1989262417Shselasky sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 1990199086Srpaulo} 1991199086Srpaulo 1992262417Shselaskystatic boolean_t 1993262417Shselaskyatp_is_horizontal_scroll(const atp_stroke_t *strokep) 1994262417Shselasky{ 1995262417Shselasky if (abs(strokep->cum_movement_x) < atp_slide_min_movement) 1996262417Shselasky return (false); 1997262417Shselasky if (strokep->cum_movement_y == 0) 1998262417Shselasky return (true); 1999262417Shselasky return (abs(strokep->cum_movement_x / strokep->cum_movement_y) >= 4); 2000262417Shselasky} 2001262417Shselasky 2002262417Shselaskystatic boolean_t 2003262417Shselaskyatp_is_vertical_scroll(const atp_stroke_t *strokep) 2004262417Shselasky{ 2005262417Shselasky if (abs(strokep->cum_movement_y) < atp_slide_min_movement) 2006262417Shselasky return (false); 2007262417Shselasky if (strokep->cum_movement_x == 0) 2008262417Shselasky return (true); 2009262417Shselasky return (abs(strokep->cum_movement_y / strokep->cum_movement_x) >= 4); 2010262417Shselasky} 2011262417Shselasky 2012199086Srpaulostatic void 2013262417Shselaskyatp_reap_sibling_zombies(void *arg) 2014199086Srpaulo{ 2015262417Shselasky struct atp_softc *sc = (struct atp_softc *)arg; 2016262417Shselasky u_int8_t n_touches_reaped = 0; 2017262417Shselasky u_int8_t n_slides_reaped = 0; 2018262417Shselasky u_int8_t n_horizontal_scrolls = 0; 2019262417Shselasky u_int8_t n_vertical_scrolls = 0; 2020262417Shselasky int horizontal_scroll = 0; 2021262417Shselasky int vertical_scroll = 0; 2022262417Shselasky atp_stroke_t *strokep; 2023262417Shselasky atp_stroke_t *strokep_next; 2024199086Srpaulo 2025262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "\n"); 2026199086Srpaulo 2027262417Shselasky TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { 2028262417Shselasky if ((strokep->flags & ATSF_ZOMBIE) == 0) 2029199086Srpaulo continue; 2030199086Srpaulo 2031262417Shselasky if (strokep->type == ATP_STROKE_TOUCH) { 2032262417Shselasky n_touches_reaped++; 2033199086Srpaulo } else { 2034262417Shselasky n_slides_reaped++; 2035199086Srpaulo 2036262417Shselasky if (atp_is_horizontal_scroll(strokep)) { 2037262417Shselasky n_horizontal_scrolls++; 2038262417Shselasky horizontal_scroll += strokep->cum_movement_x; 2039262417Shselasky } else if (atp_is_vertical_scroll(strokep)) { 2040262417Shselasky n_vertical_scrolls++; 2041262417Shselasky vertical_scroll += strokep->cum_movement_y; 2042262417Shselasky } 2043199086Srpaulo } 2044199086Srpaulo 2045262417Shselasky atp_free_stroke(sc, strokep); 2046262417Shselasky } 2047199086Srpaulo 2048262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", 2049262417Shselasky n_touches_reaped + n_slides_reaped); 2050262417Shselasky sc->sc_state &= ~ATP_ZOMBIES_EXIST; 2051199086Srpaulo 2052262417Shselasky /* No further processing necessary if physical button is depressed. */ 2053262417Shselasky if (sc->sc_ibtn != 0) 2054262417Shselasky return; 2055199086Srpaulo 2056262417Shselasky if ((n_touches_reaped == 0) && (n_slides_reaped == 0)) 2057262417Shselasky return; 2058199086Srpaulo 2059262417Shselasky /* Add a pair of virtual button events (button-down and button-up) if 2060262417Shselasky * the physical button isn't pressed. */ 2061262417Shselasky if (n_touches_reaped != 0) { 2062262417Shselasky if (n_touches_reaped < atp_tap_minimum) 2063262417Shselasky return; 2064199086Srpaulo 2065262417Shselasky switch (n_touches_reaped) { 2066262417Shselasky case 1: 2067262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN); 2068262417Shselasky microtime(&sc->sc_touch_reap_time); /* remember this time */ 2069262417Shselasky break; 2070262417Shselasky case 2: 2071262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN); 2072262417Shselasky break; 2073262417Shselasky case 3: 2074262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN); 2075262417Shselasky break; 2076262417Shselasky default: 2077262417Shselasky /* we handle taps of only up to 3 fingers */ 2078262417Shselasky break; 2079199086Srpaulo } 2080262417Shselasky atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ 2081262417Shselasky 2082262417Shselasky } else if (n_slides_reaped == 2) { 2083262417Shselasky if (n_horizontal_scrolls == 2) { 2084262417Shselasky if (horizontal_scroll < 0) 2085262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON4DOWN); 2086262417Shselasky else 2087262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON5DOWN); 2088262417Shselasky atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ 2089262417Shselasky } 2090199086Srpaulo } 2091262417Shselasky} 2092199086Srpaulo 2093262417Shselasky/* Switch a given touch stroke to being a slide. */ 2094262417Shselaskystatic void 2095262417Shselaskyatp_convert_to_slide(struct atp_softc *sc, atp_stroke_t *strokep) 2096262417Shselasky{ 2097262417Shselasky strokep->type = ATP_STROKE_SLIDE; 2098262417Shselasky 2099262417Shselasky /* Are we at the beginning of a double-click-n-drag? */ 2100262417Shselasky if ((sc->sc_n_strokes == 1) && 2101262417Shselasky ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) && 2102262417Shselasky timevalcmp(&strokep->ctime, &sc->sc_touch_reap_time, >)) { 2103262417Shselasky struct timeval delta; 2104262417Shselasky struct timeval window = { 2105262417Shselasky atp_double_tap_threshold / 1000000, 2106262417Shselasky atp_double_tap_threshold % 1000000 2107262417Shselasky }; 2108262417Shselasky 2109262417Shselasky delta = strokep->ctime; 2110262417Shselasky timevalsub(&delta, &sc->sc_touch_reap_time); 2111262417Shselasky if (timevalcmp(&delta, &window, <=)) 2112262417Shselasky sc->sc_state |= ATP_DOUBLE_TAP_DRAG; 2113262417Shselasky } 2114199086Srpaulo} 2115199086Srpaulo 2116262417Shselaskystatic void 2117262417Shselaskyatp_reset_buf(struct atp_softc *sc) 2118262417Shselasky{ 2119262417Shselasky /* reset read queue */ 2120262417Shselasky usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); 2121262417Shselasky} 2122199086Srpaulo 2123262417Shselaskystatic void 2124262417Shselaskyatp_add_to_queue(struct atp_softc *sc, int dx, int dy, int dz, 2125262417Shselasky uint32_t buttons_in) 2126262417Shselasky{ 2127262417Shselasky uint32_t buttons_out; 2128262417Shselasky uint8_t buf[8]; 2129199086Srpaulo 2130262417Shselasky dx = imin(dx, 254); dx = imax(dx, -256); 2131262417Shselasky dy = imin(dy, 254); dy = imax(dy, -256); 2132262417Shselasky dz = imin(dz, 126); dz = imax(dz, -128); 2133199086Srpaulo 2134262417Shselasky buttons_out = MOUSE_MSC_BUTTONS; 2135262417Shselasky if (buttons_in & MOUSE_BUTTON1DOWN) 2136262417Shselasky buttons_out &= ~MOUSE_MSC_BUTTON1UP; 2137262417Shselasky else if (buttons_in & MOUSE_BUTTON2DOWN) 2138262417Shselasky buttons_out &= ~MOUSE_MSC_BUTTON2UP; 2139262417Shselasky else if (buttons_in & MOUSE_BUTTON3DOWN) 2140262417Shselasky buttons_out &= ~MOUSE_MSC_BUTTON3UP; 2141262417Shselasky 2142262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n", 2143262417Shselasky dx, dy, buttons_out); 2144262417Shselasky 2145262417Shselasky /* Encode the mouse data in standard format; refer to mouse(4) */ 2146262417Shselasky buf[0] = sc->sc_mode.syncmask[1]; 2147262417Shselasky buf[0] |= buttons_out; 2148262417Shselasky buf[1] = dx >> 1; 2149262417Shselasky buf[2] = dy >> 1; 2150262417Shselasky buf[3] = dx - (dx >> 1); 2151262417Shselasky buf[4] = dy - (dy >> 1); 2152262417Shselasky /* Encode extra bytes for level 1 */ 2153262417Shselasky if (sc->sc_mode.level == 1) { 2154262417Shselasky buf[5] = dz >> 1; 2155262417Shselasky buf[6] = dz - (dz >> 1); 2156262417Shselasky buf[7] = (((~buttons_in) >> 3) & MOUSE_SYS_EXTBUTTONS); 2157262417Shselasky } 2158262417Shselasky 2159262417Shselasky usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, 2160262417Shselasky sc->sc_mode.packetsize, 1); 2161262417Shselasky} 2162262417Shselasky 2163199086Srpaulostatic int 2164199086Srpauloatp_probe(device_t self) 2165199086Srpaulo{ 2166199086Srpaulo struct usb_attach_arg *uaa = device_get_ivars(self); 2167199086Srpaulo 2168199086Srpaulo if (uaa->usb_mode != USB_MODE_HOST) 2169199086Srpaulo return (ENXIO); 2170199086Srpaulo 2171262417Shselasky if (uaa->info.bInterfaceClass != UICLASS_HID) 2172199086Srpaulo return (ENXIO); 2173262417Shselasky /* 2174262417Shselasky * Note: for some reason, the check 2175262417Shselasky * (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) doesn't hold true 2176262417Shselasky * for wellspring trackpads, so we've removed it from the common path. 2177262417Shselasky */ 2178199086Srpaulo 2179262417Shselasky if ((usbd_lookup_id_by_uaa(fg_devs, sizeof(fg_devs), uaa)) == 0) 2180262417Shselasky return ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) ? 2181262417Shselasky 0 : ENXIO); 2182262417Shselasky 2183262417Shselasky if ((usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)) == 0) 2184262417Shselasky if (uaa->info.bIfaceIndex == WELLSPRING_INTERFACE_INDEX) 2185262417Shselasky return (0); 2186262417Shselasky 2187262417Shselasky return (ENXIO); 2188199086Srpaulo} 2189199086Srpaulo 2190199086Srpaulostatic int 2191199086Srpauloatp_attach(device_t dev) 2192199086Srpaulo{ 2193262417Shselasky struct atp_softc *sc = device_get_softc(dev); 2194199086Srpaulo struct usb_attach_arg *uaa = device_get_ivars(dev); 2195199086Srpaulo usb_error_t err; 2196262417Shselasky void *descriptor_ptr = NULL; 2197262417Shselasky uint16_t descriptor_len; 2198262417Shselasky unsigned long di; 2199199086Srpaulo 2200199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc); 2201199086Srpaulo 2202199086Srpaulo sc->sc_dev = dev; 2203199086Srpaulo sc->sc_usb_device = uaa->device; 2204199086Srpaulo 2205262417Shselasky /* Get HID descriptor */ 2206262417Shselasky if (usbd_req_get_hid_desc(uaa->device, NULL, &descriptor_ptr, 2207262417Shselasky &descriptor_len, M_TEMP, uaa->info.bIfaceIndex) != 2208262417Shselasky USB_ERR_NORMAL_COMPLETION) 2209262417Shselasky return (ENXIO); 2210262417Shselasky 2211262417Shselasky /* Get HID report descriptor length */ 2212262417Shselasky sc->sc_expected_sensor_data_len = hid_report_size(descriptor_ptr, 2213262417Shselasky descriptor_len, hid_input, NULL); 2214262417Shselasky free(descriptor_ptr, M_TEMP); 2215262417Shselasky 2216262417Shselasky if ((sc->sc_expected_sensor_data_len <= 0) || 2217262417Shselasky (sc->sc_expected_sensor_data_len > ATP_SENSOR_DATA_BUF_MAX)) { 2218262417Shselasky DPRINTF("atp_attach: datalength invalid or too large: %d\n", 2219262417Shselasky sc->sc_expected_sensor_data_len); 2220262417Shselasky return (ENXIO); 2221262417Shselasky } 2222262417Shselasky 2223199086Srpaulo /* 2224199086Srpaulo * By default the touchpad behaves like an HID device, sending 2225199086Srpaulo * packets with reportID = 2. Such reports contain only 2226199086Srpaulo * limited information--they encode movement deltas and button 2227199086Srpaulo * events,--but do not include data from the pressure 2228199086Srpaulo * sensors. The device input mode can be switched from HID 2229199086Srpaulo * reports to raw sensor data using vendor-specific USB 2230262417Shselasky * control commands. 2231199086Srpaulo */ 2232262417Shselasky if ((err = atp_set_device_mode(sc, RAW_SENSOR_MODE)) != 0) { 2233199086Srpaulo DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err); 2234199086Srpaulo return (ENXIO); 2235199086Srpaulo } 2236199086Srpaulo 2237199086Srpaulo mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE); 2238199086Srpaulo 2239262417Shselasky di = USB_GET_DRIVER_INFO(uaa); 2240262417Shselasky 2241262417Shselasky sc->sc_family = DECODE_FAMILY_FROM_DRIVER_INFO(di); 2242262417Shselasky 2243262417Shselasky switch(sc->sc_family) { 2244262417Shselasky case TRACKPAD_FAMILY_FOUNTAIN_GEYSER: 2245262417Shselasky sc->sc_params = 2246262417Shselasky &fg_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; 2247262417Shselasky sc->sensor_data_interpreter = fg_interpret_sensor_data; 2248262417Shselasky break; 2249262417Shselasky case TRACKPAD_FAMILY_WELLSPRING: 2250262417Shselasky sc->sc_params = 2251262417Shselasky &wsp_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; 2252262417Shselasky sc->sensor_data_interpreter = wsp_interpret_sensor_data; 2253262417Shselasky break; 2254262417Shselasky default: 2255262417Shselasky goto detach; 2256262417Shselasky } 2257262417Shselasky 2258199086Srpaulo err = usbd_transfer_setup(uaa->device, 2259262417Shselasky &uaa->info.bIfaceIndex, sc->sc_xfer, atp_xfer_config, 2260199086Srpaulo ATP_N_TRANSFER, sc, &sc->sc_mutex); 2261199086Srpaulo if (err) { 2262199086Srpaulo DPRINTF("error=%s\n", usbd_errstr(err)); 2263199086Srpaulo goto detach; 2264199086Srpaulo } 2265199086Srpaulo 2266199086Srpaulo if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex, 2267262417Shselasky &atp_fifo_methods, &sc->sc_fifo, 2268262417Shselasky device_get_unit(dev), -1, uaa->info.bIfaceIndex, 2269262417Shselasky UID_ROOT, GID_OPERATOR, 0644)) { 2270199086Srpaulo goto detach; 2271199086Srpaulo } 2272199086Srpaulo 2273199086Srpaulo device_set_usb_desc(dev); 2274199086Srpaulo 2275199086Srpaulo sc->sc_hw.buttons = 3; 2276199086Srpaulo sc->sc_hw.iftype = MOUSE_IF_USB; 2277199086Srpaulo sc->sc_hw.type = MOUSE_PAD; 2278199086Srpaulo sc->sc_hw.model = MOUSE_MODEL_GENERIC; 2279199086Srpaulo sc->sc_hw.hwid = 0; 2280199086Srpaulo sc->sc_mode.protocol = MOUSE_PROTO_MSC; 2281199086Srpaulo sc->sc_mode.rate = -1; 2282199086Srpaulo sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; 2283199086Srpaulo sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 2284199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 2285199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 2286262417Shselasky sc->sc_mode.accelfactor = 0; 2287262417Shselasky sc->sc_mode.level = 0; 2288199086Srpaulo 2289199086Srpaulo sc->sc_state = 0; 2290262417Shselasky sc->sc_ibtn = 0; 2291199086Srpaulo 2292262417Shselasky callout_init_mtx(&sc->sc_callout, &sc->sc_mutex, 0); 2293199086Srpaulo 2294199086Srpaulo return (0); 2295199086Srpaulo 2296199086Srpaulodetach: 2297199086Srpaulo atp_detach(dev); 2298199086Srpaulo return (ENOMEM); 2299199086Srpaulo} 2300199086Srpaulo 2301199086Srpaulostatic int 2302199086Srpauloatp_detach(device_t dev) 2303199086Srpaulo{ 2304199086Srpaulo struct atp_softc *sc; 2305199086Srpaulo 2306199086Srpaulo sc = device_get_softc(dev); 2307262417Shselasky atp_set_device_mode(sc, HID_MODE); 2308262417Shselasky 2309262417Shselasky mtx_lock(&sc->sc_mutex); 2310262417Shselasky callout_drain(&sc->sc_callout); 2311262417Shselasky if (sc->sc_state & ATP_ENABLED) 2312199086Srpaulo atp_disable(sc); 2313262417Shselasky mtx_unlock(&sc->sc_mutex); 2314199086Srpaulo 2315199086Srpaulo usb_fifo_detach(&sc->sc_fifo); 2316199086Srpaulo 2317199086Srpaulo usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER); 2318199086Srpaulo 2319199086Srpaulo mtx_destroy(&sc->sc_mutex); 2320199086Srpaulo 2321199086Srpaulo return (0); 2322199086Srpaulo} 2323199086Srpaulo 2324199086Srpaulostatic void 2325199086Srpauloatp_intr(struct usb_xfer *xfer, usb_error_t error) 2326199086Srpaulo{ 2327199086Srpaulo struct atp_softc *sc = usbd_xfer_softc(xfer); 2328199086Srpaulo struct usb_page_cache *pc; 2329262417Shselasky int len; 2330199086Srpaulo 2331199086Srpaulo usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 2332199086Srpaulo 2333199086Srpaulo switch (USB_GET_STATE(xfer)) { 2334199086Srpaulo case USB_ST_TRANSFERRED: 2335199086Srpaulo pc = usbd_xfer_get_frame(xfer, 0); 2336262417Shselasky usbd_copy_out(pc, 0, sc->sc_sensor_data, len); 2337262417Shselasky if (len < sc->sc_expected_sensor_data_len) { 2338262417Shselasky /* make sure we don't process old data */ 2339262417Shselasky memset(sc->sc_sensor_data + len, 0, 2340262417Shselasky sc->sc_expected_sensor_data_len - len); 2341199086Srpaulo } 2342199086Srpaulo 2343262417Shselasky sc->sc_status.flags &= ~(MOUSE_STDBUTTONSCHANGED | 2344262417Shselasky MOUSE_POSCHANGED); 2345262417Shselasky sc->sc_status.obutton = sc->sc_status.button; 2346199086Srpaulo 2347262417Shselasky (sc->sensor_data_interpreter)(sc, len); 2348199086Srpaulo 2349199086Srpaulo if (sc->sc_status.button != 0) { 2350199086Srpaulo /* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */ 2351199086Srpaulo sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 2352199086Srpaulo } else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) { 2353199086Srpaulo /* Assume a button-press with DOUBLE_TAP_N_DRAG. */ 2354199086Srpaulo sc->sc_status.button = MOUSE_BUTTON1DOWN; 2355199086Srpaulo } 2356199086Srpaulo 2357199086Srpaulo sc->sc_status.flags |= 2358262417Shselasky sc->sc_status.button ^ sc->sc_status.obutton; 2359199086Srpaulo if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) { 2360262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "button %s\n", 2361262417Shselasky ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ? 2362262417Shselasky "pressed" : "released")); 2363199086Srpaulo } 2364199086Srpaulo 2365262417Shselasky if (sc->sc_status.flags & (MOUSE_POSCHANGED | 2366262417Shselasky MOUSE_STDBUTTONSCHANGED)) { 2367199086Srpaulo 2368262417Shselasky atp_stroke_t *strokep; 2369262417Shselasky u_int8_t n_movements = 0; 2370262417Shselasky int dx = 0; 2371262417Shselasky int dy = 0; 2372199086Srpaulo 2373262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 2374262417Shselasky dx += strokep->movement_dx; 2375262417Shselasky dy += strokep->movement_dy; 2376262417Shselasky if (strokep->movement_dx || 2377262417Shselasky strokep->movement_dy) 2378199086Srpaulo n_movements++; 2379199086Srpaulo } 2380199086Srpaulo 2381262417Shselasky /* average movement if multiple strokes record motion.*/ 2382262417Shselasky if (n_movements > 1) { 2383262417Shselasky dx /= (int)n_movements; 2384262417Shselasky dy /= (int)n_movements; 2385262417Shselasky } 2386262417Shselasky 2387199086Srpaulo sc->sc_status.dx += dx; 2388199086Srpaulo sc->sc_status.dy += dy; 2389262417Shselasky atp_add_to_queue(sc, dx, -dy, 0, sc->sc_status.button); 2390199086Srpaulo } 2391199086Srpaulo 2392199086Srpaulo case USB_ST_SETUP: 2393199086Srpaulo tr_setup: 2394199086Srpaulo /* check if we can put more data into the FIFO */ 2395262417Shselasky if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { 2396199086Srpaulo usbd_xfer_set_frame_len(xfer, 0, 2397262417Shselasky sc->sc_expected_sensor_data_len); 2398199086Srpaulo usbd_transfer_submit(xfer); 2399199086Srpaulo } 2400199086Srpaulo break; 2401199086Srpaulo 2402199086Srpaulo default: /* Error */ 2403199086Srpaulo if (error != USB_ERR_CANCELLED) { 2404199086Srpaulo /* try clear stall first */ 2405199086Srpaulo usbd_xfer_set_stall(xfer); 2406199086Srpaulo goto tr_setup; 2407199086Srpaulo } 2408199086Srpaulo break; 2409199086Srpaulo } 2410199086Srpaulo} 2411199086Srpaulo 2412199086Srpaulostatic void 2413199086Srpauloatp_start_read(struct usb_fifo *fifo) 2414199086Srpaulo{ 2415199086Srpaulo struct atp_softc *sc = usb_fifo_softc(fifo); 2416199086Srpaulo int rate; 2417199086Srpaulo 2418199086Srpaulo /* Check if we should override the default polling interval */ 2419199086Srpaulo rate = sc->sc_pollrate; 2420199086Srpaulo /* Range check rate */ 2421199086Srpaulo if (rate > 1000) 2422199086Srpaulo rate = 1000; 2423199086Srpaulo /* Check for set rate */ 2424199086Srpaulo if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) { 2425199086Srpaulo /* Stop current transfer, if any */ 2426199086Srpaulo usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]); 2427199086Srpaulo /* Set new interval */ 2428199086Srpaulo usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate); 2429199086Srpaulo /* Only set pollrate once */ 2430199086Srpaulo sc->sc_pollrate = 0; 2431199086Srpaulo } 2432199086Srpaulo 2433199086Srpaulo usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]); 2434199086Srpaulo} 2435199086Srpaulo 2436199086Srpaulostatic void 2437199086Srpauloatp_stop_read(struct usb_fifo *fifo) 2438199086Srpaulo{ 2439199086Srpaulo struct atp_softc *sc = usb_fifo_softc(fifo); 2440199086Srpaulo usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]); 2441199086Srpaulo} 2442199086Srpaulo 2443199086Srpaulostatic int 2444199086Srpauloatp_open(struct usb_fifo *fifo, int fflags) 2445199086Srpaulo{ 2446262417Shselasky struct atp_softc *sc = usb_fifo_softc(fifo); 2447199086Srpaulo 2448262417Shselasky /* check for duplicate open, should not happen */ 2449262417Shselasky if (sc->sc_fflags & fflags) 2450262417Shselasky return (EBUSY); 2451262417Shselasky 2452262417Shselasky /* check for first open */ 2453262417Shselasky if (sc->sc_fflags == 0) { 2454199086Srpaulo int rc; 2455262417Shselasky if ((rc = atp_enable(sc)) != 0) 2456262417Shselasky return (rc); 2457262417Shselasky } 2458199086Srpaulo 2459262417Shselasky if (fflags & FREAD) { 2460199086Srpaulo if (usb_fifo_alloc_buffer(fifo, 2461262417Shselasky ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) { 2462199086Srpaulo return (ENOMEM); 2463199086Srpaulo } 2464199086Srpaulo } 2465199086Srpaulo 2466262417Shselasky sc->sc_fflags |= (fflags & (FREAD | FWRITE)); 2467199086Srpaulo return (0); 2468199086Srpaulo} 2469199086Srpaulo 2470199086Srpaulostatic void 2471199086Srpauloatp_close(struct usb_fifo *fifo, int fflags) 2472199086Srpaulo{ 2473262417Shselasky struct atp_softc *sc = usb_fifo_softc(fifo); 2474262417Shselasky if (fflags & FREAD) 2475262417Shselasky usb_fifo_free_buffer(fifo); 2476199086Srpaulo 2477262417Shselasky sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); 2478262417Shselasky if (sc->sc_fflags == 0) { 2479199086Srpaulo atp_disable(sc); 2480199086Srpaulo } 2481199086Srpaulo} 2482199086Srpaulo 2483262417Shselaskystatic int 2484199086Srpauloatp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 2485199086Srpaulo{ 2486199086Srpaulo struct atp_softc *sc = usb_fifo_softc(fifo); 2487199086Srpaulo mousemode_t mode; 2488199086Srpaulo int error = 0; 2489199086Srpaulo 2490199086Srpaulo mtx_lock(&sc->sc_mutex); 2491199086Srpaulo 2492199086Srpaulo switch(cmd) { 2493199086Srpaulo case MOUSE_GETHWINFO: 2494199086Srpaulo *(mousehw_t *)addr = sc->sc_hw; 2495199086Srpaulo break; 2496199086Srpaulo case MOUSE_GETMODE: 2497199086Srpaulo *(mousemode_t *)addr = sc->sc_mode; 2498199086Srpaulo break; 2499199086Srpaulo case MOUSE_SETMODE: 2500199086Srpaulo mode = *(mousemode_t *)addr; 2501199086Srpaulo 2502199086Srpaulo if (mode.level == -1) 2503199086Srpaulo /* Don't change the current setting */ 2504199086Srpaulo ; 2505199086Srpaulo else if ((mode.level < 0) || (mode.level > 1)) { 2506199086Srpaulo error = EINVAL; 2507262417Shselasky break; 2508199086Srpaulo } 2509199086Srpaulo sc->sc_mode.level = mode.level; 2510199086Srpaulo sc->sc_pollrate = mode.rate; 2511199086Srpaulo sc->sc_hw.buttons = 3; 2512199086Srpaulo 2513199086Srpaulo if (sc->sc_mode.level == 0) { 2514262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_MSC; 2515262417Shselasky sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 2516199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 2517199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 2518199086Srpaulo } else if (sc->sc_mode.level == 1) { 2519262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 2520262417Shselasky sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 2521199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 2522199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 2523199086Srpaulo } 2524199086Srpaulo atp_reset_buf(sc); 2525199086Srpaulo break; 2526199086Srpaulo case MOUSE_GETLEVEL: 2527199086Srpaulo *(int *)addr = sc->sc_mode.level; 2528199086Srpaulo break; 2529199086Srpaulo case MOUSE_SETLEVEL: 2530262417Shselasky if ((*(int *)addr < 0) || (*(int *)addr > 1)) { 2531199086Srpaulo error = EINVAL; 2532262417Shselasky break; 2533199086Srpaulo } 2534199086Srpaulo sc->sc_mode.level = *(int *)addr; 2535199086Srpaulo sc->sc_hw.buttons = 3; 2536199086Srpaulo 2537199086Srpaulo if (sc->sc_mode.level == 0) { 2538262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_MSC; 2539262417Shselasky sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 2540199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 2541199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 2542199086Srpaulo } else if (sc->sc_mode.level == 1) { 2543262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 2544262417Shselasky sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 2545199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 2546199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 2547199086Srpaulo } 2548199086Srpaulo atp_reset_buf(sc); 2549199086Srpaulo break; 2550199086Srpaulo case MOUSE_GETSTATUS: { 2551199086Srpaulo mousestatus_t *status = (mousestatus_t *)addr; 2552199086Srpaulo 2553199086Srpaulo *status = sc->sc_status; 2554199086Srpaulo sc->sc_status.obutton = sc->sc_status.button; 2555199086Srpaulo sc->sc_status.button = 0; 2556262417Shselasky sc->sc_status.dx = 0; 2557262417Shselasky sc->sc_status.dy = 0; 2558262417Shselasky sc->sc_status.dz = 0; 2559199086Srpaulo 2560199086Srpaulo if (status->dx || status->dy || status->dz) 2561199086Srpaulo status->flags |= MOUSE_POSCHANGED; 2562199086Srpaulo if (status->button != status->obutton) 2563199086Srpaulo status->flags |= MOUSE_BUTTONSCHANGED; 2564199086Srpaulo break; 2565199086Srpaulo } 2566262417Shselasky 2567199086Srpaulo default: 2568199086Srpaulo error = ENOTTY; 2569262417Shselasky break; 2570199086Srpaulo } 2571199086Srpaulo 2572199086Srpaulo mtx_unlock(&sc->sc_mutex); 2573199086Srpaulo return (error); 2574199086Srpaulo} 2575199086Srpaulo 2576199086Srpaulostatic int 2577199086Srpauloatp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS) 2578199086Srpaulo{ 2579199086Srpaulo int error; 2580199086Srpaulo u_int tmp; 2581199086Srpaulo 2582199086Srpaulo tmp = atp_mickeys_scale_factor; 2583199086Srpaulo error = sysctl_handle_int(oidp, &tmp, 0, req); 2584199086Srpaulo if (error != 0 || req->newptr == NULL) 2585199086Srpaulo return (error); 2586199086Srpaulo 2587262417Shselasky if (tmp == atp_mickeys_scale_factor) 2588199086Srpaulo return (0); /* no change */ 2589262417Shselasky if ((tmp == 0) || (tmp > (10 * ATP_SCALE_FACTOR))) 2590262417Shselasky return (EINVAL); 2591199086Srpaulo 2592199086Srpaulo atp_mickeys_scale_factor = tmp; 2593199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n", 2594199086Srpaulo ATP_DRIVER_NAME, tmp); 2595199086Srpaulo 2596199086Srpaulo return (0); 2597199086Srpaulo} 2598199086Srpaulo 2599262417Shselaskystatic devclass_t atp_devclass; 2600262417Shselasky 2601199086Srpaulostatic device_method_t atp_methods[] = { 2602199086Srpaulo DEVMETHOD(device_probe, atp_probe), 2603199086Srpaulo DEVMETHOD(device_attach, atp_attach), 2604199086Srpaulo DEVMETHOD(device_detach, atp_detach), 2605262417Shselasky 2606262417Shselasky DEVMETHOD_END 2607199086Srpaulo}; 2608199086Srpaulo 2609199086Srpaulostatic driver_t atp_driver = { 2610262417Shselasky .name = ATP_DRIVER_NAME, 2611233774Shselasky .methods = atp_methods, 2612262417Shselasky .size = sizeof(struct atp_softc) 2613199086Srpaulo}; 2614199086Srpaulo 2615199086SrpauloDRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0); 2616199086SrpauloMODULE_DEPEND(atp, usb, 1, 1, 1); 2617212122SthompsaMODULE_VERSION(atp, 1); 2618