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$"); 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 */ 181276701ShselaskySYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, CTLFLAG_RWTUN, 182199086Srpaulo &atp_debug, ATP_LLEVEL_ERROR, "ATP debug level"); 183207077Sthompsa#endif /* USB_DEBUG */ 184199086Srpaulo 185199086Srpaulostatic u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT; 186276701ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RWTUN, 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; 190276701ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RWTUN, 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); 197276701ShselaskySYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale_factor, CTLTYPE_UINT | CTLFLAG_RWTUN, 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; 202276701ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RWTUN, 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; 207276701ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, tap_minimum, CTLFLAG_RWTUN, 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; 216276701ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RWTUN, 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; 225276701ShselaskySYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RWTUN, 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 512262478Shselasky/* TODO: STRUCT_USB_HOST_ID */ 513262478Shselaskystatic const struct usb_device_id wsp_devs[] = { 514262417Shselasky /* MacbookAir1.1 */ 515262417Shselasky ATP_DEV(APPLE, WELLSPRING_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING1)), 516262417Shselasky ATP_DEV(APPLE, WELLSPRING_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING1)), 517262417Shselasky ATP_DEV(APPLE, WELLSPRING_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING1)), 518262417Shselasky 519262417Shselasky /* MacbookProPenryn, aka wellspring2 */ 520262417Shselasky ATP_DEV(APPLE, WELLSPRING2_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING2)), 521262417Shselasky ATP_DEV(APPLE, WELLSPRING2_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING2)), 522262417Shselasky ATP_DEV(APPLE, WELLSPRING2_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING2)), 523262417Shselasky 524262417Shselasky /* Macbook5,1 (unibody), aka wellspring3 */ 525262417Shselasky ATP_DEV(APPLE, WELLSPRING3_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING3)), 526262417Shselasky ATP_DEV(APPLE, WELLSPRING3_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING3)), 527262417Shselasky ATP_DEV(APPLE, WELLSPRING3_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING3)), 528262417Shselasky 529262417Shselasky /* MacbookAir3,2 (unibody), aka wellspring4 */ 530262417Shselasky ATP_DEV(APPLE, WELLSPRING4_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4)), 531262417Shselasky ATP_DEV(APPLE, WELLSPRING4_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4)), 532262417Shselasky ATP_DEV(APPLE, WELLSPRING4_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4)), 533262417Shselasky 534262417Shselasky /* MacbookAir3,1 (unibody), aka wellspring4 */ 535262417Shselasky ATP_DEV(APPLE, WELLSPRING4A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), 536262417Shselasky ATP_DEV(APPLE, WELLSPRING4A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), 537262417Shselasky ATP_DEV(APPLE, WELLSPRING4A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), 538262417Shselasky 539262417Shselasky /* Macbook8 (unibody, March 2011) */ 540262417Shselasky ATP_DEV(APPLE, WELLSPRING5_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5)), 541262417Shselasky ATP_DEV(APPLE, WELLSPRING5_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5)), 542262417Shselasky ATP_DEV(APPLE, WELLSPRING5_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5)), 543262417Shselasky 544262417Shselasky /* MacbookAir4,1 (unibody, July 2011) */ 545262417Shselasky ATP_DEV(APPLE, WELLSPRING6A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), 546262417Shselasky ATP_DEV(APPLE, WELLSPRING6A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), 547262417Shselasky ATP_DEV(APPLE, WELLSPRING6A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), 548262417Shselasky 549262417Shselasky /* MacbookAir4,2 (unibody, July 2011) */ 550262417Shselasky ATP_DEV(APPLE, WELLSPRING6_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6)), 551262417Shselasky ATP_DEV(APPLE, WELLSPRING6_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6)), 552262417Shselasky ATP_DEV(APPLE, WELLSPRING6_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6)), 553262417Shselasky 554262417Shselasky /* Macbook8,2 (unibody) */ 555262417Shselasky ATP_DEV(APPLE, WELLSPRING5A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), 556262417Shselasky ATP_DEV(APPLE, WELLSPRING5A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), 557262417Shselasky ATP_DEV(APPLE, WELLSPRING5A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), 558262417Shselasky 559262417Shselasky /* MacbookPro10,1 (unibody, June 2012) */ 560262417Shselasky /* MacbookPro11,? (unibody, June 2013) */ 561262417Shselasky ATP_DEV(APPLE, WELLSPRING7_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7)), 562262417Shselasky ATP_DEV(APPLE, WELLSPRING7_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7)), 563262417Shselasky ATP_DEV(APPLE, WELLSPRING7_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7)), 564262417Shselasky 565262417Shselasky /* MacbookPro10,2 (unibody, October 2012) */ 566262417Shselasky ATP_DEV(APPLE, WELLSPRING7A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), 567262417Shselasky ATP_DEV(APPLE, WELLSPRING7A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), 568262417Shselasky ATP_DEV(APPLE, WELLSPRING7A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), 569262417Shselasky 570262417Shselasky /* MacbookAir6,2 (unibody, June 2013) */ 571262417Shselasky ATP_DEV(APPLE, WELLSPRING8_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING8)), 572262417Shselasky ATP_DEV(APPLE, WELLSPRING8_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING8)), 573262417Shselasky ATP_DEV(APPLE, WELLSPRING8_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING8)), 574199086Srpaulo}; 575199086Srpaulo 576199086Srpaulotypedef enum atp_stroke_type { 577199086Srpaulo ATP_STROKE_TOUCH, 578199086Srpaulo ATP_STROKE_SLIDE, 579199086Srpaulo} atp_stroke_type; 580199086Srpaulo 581262417Shselaskytypedef enum atp_axis { 582262417Shselasky X = 0, 583262417Shselasky Y = 1, 584262417Shselasky NUM_AXES 585262417Shselasky} atp_axis; 586199086Srpaulo 587262417Shselasky#define ATP_FIFO_BUF_SIZE 8 /* bytes */ 588262417Shselasky#define ATP_FIFO_QUEUE_MAXLEN 50 /* units */ 589262417Shselasky 590262417Shselaskyenum { 591262417Shselasky ATP_INTR_DT, 592262417Shselasky ATP_RESET, 593262417Shselasky ATP_N_TRANSFER, 594262417Shselasky}; 595262417Shselasky 596262417Shselaskytypedef struct fg_stroke_component { 597199086Srpaulo /* Fields encapsulating the pressure-span. */ 598199086Srpaulo u_int loc; /* location (scaled) */ 599199086Srpaulo u_int cum_pressure; /* cumulative compression */ 600199086Srpaulo u_int max_cum_pressure; /* max cumulative compression */ 601199086Srpaulo boolean_t matched; /*to track components as they match against pspans.*/ 602199086Srpaulo 603199086Srpaulo int delta_mickeys; /* change in location (un-smoothened movement)*/ 604262417Shselasky} fg_stroke_component_t; 605199086Srpaulo 606199086Srpaulo/* 607199086Srpaulo * The following structure captures a finger contact with the 608199086Srpaulo * touchpad. A stroke comprises two p-span components and some state. 609199086Srpaulo */ 610199086Srpaulotypedef struct atp_stroke { 611262417Shselasky TAILQ_ENTRY(atp_stroke) entry; 612199086Srpaulo 613262417Shselasky atp_stroke_type type; 614262417Shselasky uint32_t flags; /* the state of this stroke */ 615262417Shselasky#define ATSF_ZOMBIE 0x1 616262417Shselasky boolean_t matched; /* to track match against fingers.*/ 617199086Srpaulo 618262417Shselasky struct timeval ctime; /* create time; for coincident siblings. */ 619199086Srpaulo 620262417Shselasky /* 621262417Shselasky * Unit: interrupts; we maintain this value in 622262417Shselasky * addition to 'ctime' in order to avoid the 623262417Shselasky * expensive call to microtime() at every 624262417Shselasky * interrupt. 625262417Shselasky */ 626262417Shselasky uint32_t age; 627199086Srpaulo 628262417Shselasky /* Location */ 629262417Shselasky int x; 630262417Shselasky int y; 631199086Srpaulo 632262417Shselasky /* Fields containing information about movement. */ 633262417Shselasky int instantaneous_dx; /* curr. change in X location (un-smoothened) */ 634262417Shselasky int instantaneous_dy; /* curr. change in Y location (un-smoothened) */ 635262417Shselasky int pending_dx; /* cum. of pending short movements */ 636262417Shselasky int pending_dy; /* cum. of pending short movements */ 637262417Shselasky int movement_dx; /* interpreted smoothened movement */ 638262417Shselasky int movement_dy; /* interpreted smoothened movement */ 639262417Shselasky int cum_movement_x; /* cum. horizontal movement */ 640262417Shselasky int cum_movement_y; /* cum. vertical movement */ 641262417Shselasky 642262417Shselasky /* 643262417Shselasky * The following member is relevant only for fountain-geyser trackpads. 644262417Shselasky * For these, there is the need to track pressure-spans and cumulative 645262417Shselasky * pressures for stroke components. 646262417Shselasky */ 647262417Shselasky fg_stroke_component_t components[NUM_AXES]; 648262417Shselasky} atp_stroke_t; 649262417Shselasky 650262417Shselaskystruct atp_softc; /* forward declaration */ 651262417Shselaskytypedef void (*sensor_data_interpreter_t)(struct atp_softc *sc, u_int len); 652262417Shselasky 653199086Srpaulostruct atp_softc { 654262417Shselasky device_t sc_dev; 655262417Shselasky struct usb_device *sc_usb_device; 656262417Shselasky struct mtx sc_mutex; /* for synchronization */ 657262417Shselasky struct usb_fifo_sc sc_fifo; 658199086Srpaulo 659262417Shselasky#define MODE_LENGTH 8 660262417Shselasky char sc_mode_bytes[MODE_LENGTH]; /* device mode */ 661199086Srpaulo 662262417Shselasky trackpad_family_t sc_family; 663262417Shselasky const void *sc_params; /* device configuration */ 664262417Shselasky sensor_data_interpreter_t sensor_data_interpreter; 665199086Srpaulo 666262417Shselasky mousehw_t sc_hw; 667262417Shselasky mousemode_t sc_mode; 668262417Shselasky mousestatus_t sc_status; 669199086Srpaulo 670262417Shselasky u_int sc_state; 671262417Shselasky#define ATP_ENABLED 0x01 672262417Shselasky#define ATP_ZOMBIES_EXIST 0x02 673262417Shselasky#define ATP_DOUBLE_TAP_DRAG 0x04 674262417Shselasky#define ATP_VALID 0x08 675199086Srpaulo 676262417Shselasky struct usb_xfer *sc_xfer[ATP_N_TRANSFER]; 677199086Srpaulo 678262417Shselasky u_int sc_pollrate; 679262417Shselasky int sc_fflags; 680199086Srpaulo 681262417Shselasky atp_stroke_t sc_strokes_data[ATP_MAX_STROKES]; 682262417Shselasky TAILQ_HEAD(,atp_stroke) sc_stroke_free; 683262417Shselasky TAILQ_HEAD(,atp_stroke) sc_stroke_used; 684262417Shselasky u_int sc_n_strokes; 685262417Shselasky 686262417Shselasky struct callout sc_callout; 687262417Shselasky 688262417Shselasky /* 689262417Shselasky * button status. Set to non-zero if the mouse-button is physically 690262417Shselasky * pressed. This state variable is exposed through softc to allow 691262417Shselasky * reap_sibling_zombies to avoid registering taps while the trackpad 692262417Shselasky * button is pressed. 693262417Shselasky */ 694262417Shselasky uint8_t sc_ibtn; 695262417Shselasky 696262417Shselasky /* 697262417Shselasky * Time when touch zombies were last reaped; useful for detecting 698262417Shselasky * double-touch-n-drag. 699262417Shselasky */ 700262417Shselasky struct timeval sc_touch_reap_time; 701262417Shselasky 702262417Shselasky u_int sc_idlecount; 703262417Shselasky 704262417Shselasky /* Regarding the data transferred from t-pad in USB INTR packets. */ 705262417Shselasky u_int sc_expected_sensor_data_len; 706262417Shselasky uint8_t sc_sensor_data[ATP_SENSOR_DATA_BUF_MAX] __aligned(4); 707262417Shselasky 708262417Shselasky int sc_cur_x[FG_MAX_XSENSORS]; /* current sensor readings */ 709262417Shselasky int sc_cur_y[FG_MAX_YSENSORS]; 710262417Shselasky int sc_base_x[FG_MAX_XSENSORS]; /* base sensor readings */ 711262417Shselasky int sc_base_y[FG_MAX_YSENSORS]; 712262417Shselasky int sc_pressure_x[FG_MAX_XSENSORS]; /* computed pressures */ 713262417Shselasky int sc_pressure_y[FG_MAX_YSENSORS]; 714262417Shselasky fg_pspan sc_pspans_x[FG_MAX_PSPANS_PER_AXIS]; 715262417Shselasky fg_pspan sc_pspans_y[FG_MAX_PSPANS_PER_AXIS]; 716199086Srpaulo}; 717199086Srpaulo 718199086Srpaulo/* 719262417Shselasky * The last byte of the fountain-geyser sensor data contains status bits; the 720199086Srpaulo * following values define the meanings of these bits. 721262417Shselasky * (only Geyser 3/4) 722199086Srpaulo */ 723262417Shselaskyenum geyser34_status_bits { 724262417Shselasky FG_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */ 725262417Shselasky FG_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/ 726199086Srpaulo}; 727199086Srpaulo 728199086Srpaulotypedef enum interface_mode { 729262417Shselasky RAW_SENSOR_MODE = (uint8_t)0x01, 730199086Srpaulo HID_MODE = (uint8_t)0x08 731199086Srpaulo} interface_mode; 732199086Srpaulo 733262417Shselasky 734199086Srpaulo/* 735199086Srpaulo * function prototypes 736199086Srpaulo */ 737199086Srpaulostatic usb_fifo_cmd_t atp_start_read; 738199086Srpaulostatic usb_fifo_cmd_t atp_stop_read; 739199086Srpaulostatic usb_fifo_open_t atp_open; 740199086Srpaulostatic usb_fifo_close_t atp_close; 741199086Srpaulostatic usb_fifo_ioctl_t atp_ioctl; 742199086Srpaulo 743199086Srpaulostatic struct usb_fifo_methods atp_fifo_methods = { 744199086Srpaulo .f_open = &atp_open, 745199086Srpaulo .f_close = &atp_close, 746199086Srpaulo .f_ioctl = &atp_ioctl, 747199086Srpaulo .f_start_read = &atp_start_read, 748199086Srpaulo .f_stop_read = &atp_stop_read, 749199086Srpaulo .basename[0] = ATP_DRIVER_NAME, 750199086Srpaulo}; 751199086Srpaulo 752199086Srpaulo/* device initialization and shutdown */ 753262417Shselaskystatic usb_error_t atp_set_device_mode(struct atp_softc *, interface_mode); 754262417Shselaskystatic void atp_reset_callback(struct usb_xfer *, usb_error_t); 755262417Shselaskystatic int atp_enable(struct atp_softc *); 756262417Shselaskystatic void atp_disable(struct atp_softc *); 757199086Srpaulo 758199086Srpaulo/* sensor interpretation */ 759262417Shselaskystatic void fg_interpret_sensor_data(struct atp_softc *, u_int); 760262417Shselaskystatic void fg_extract_sensor_data(const int8_t *, u_int, atp_axis, 761262417Shselasky int *, enum fountain_geyser_trackpad_type); 762262417Shselaskystatic void fg_get_pressures(int *, const int *, const int *, int); 763262417Shselaskystatic void fg_detect_pspans(int *, u_int, u_int, fg_pspan *, u_int *); 764262417Shselaskystatic void wsp_interpret_sensor_data(struct atp_softc *, u_int); 765199086Srpaulo 766199086Srpaulo/* movement detection */ 767262417Shselaskystatic boolean_t fg_match_stroke_component(fg_stroke_component_t *, 768262417Shselasky const fg_pspan *, atp_stroke_type); 769262417Shselaskystatic void fg_match_strokes_against_pspans(struct atp_softc *, 770262417Shselasky atp_axis, fg_pspan *, u_int, u_int); 771262417Shselaskystatic boolean_t wsp_match_strokes_against_fingers(struct atp_softc *, 772262417Shselasky wsp_finger_t *, u_int); 773262417Shselaskystatic boolean_t fg_update_strokes(struct atp_softc *, fg_pspan *, u_int, 774262417Shselasky fg_pspan *, u_int); 775262417Shselaskystatic boolean_t wsp_update_strokes(struct atp_softc *, 776262417Shselasky wsp_finger_t [WSP_MAX_FINGERS], u_int); 777262417Shselaskystatic void fg_add_stroke(struct atp_softc *, const fg_pspan *, const fg_pspan *); 778262417Shselaskystatic void fg_add_new_strokes(struct atp_softc *, fg_pspan *, 779262417Shselasky u_int, fg_pspan *, u_int); 780262417Shselaskystatic void wsp_add_stroke(struct atp_softc *, const wsp_finger_t *); 781262417Shselaskystatic void atp_advance_stroke_state(struct atp_softc *, 782262417Shselasky atp_stroke_t *, boolean_t *); 783262417Shselaskystatic boolean_t atp_stroke_has_small_movement(const atp_stroke_t *); 784262417Shselaskystatic void atp_update_pending_mickeys(atp_stroke_t *); 785262417Shselaskystatic boolean_t atp_compute_stroke_movement(atp_stroke_t *); 786262417Shselaskystatic void atp_terminate_stroke(struct atp_softc *, atp_stroke_t *); 787199086Srpaulo 788199086Srpaulo/* tap detection */ 789262417Shselaskystatic boolean_t atp_is_horizontal_scroll(const atp_stroke_t *); 790262417Shselaskystatic boolean_t atp_is_vertical_scroll(const atp_stroke_t *); 791262417Shselaskystatic void atp_reap_sibling_zombies(void *); 792262417Shselaskystatic void atp_convert_to_slide(struct atp_softc *, atp_stroke_t *); 793199086Srpaulo 794199086Srpaulo/* updating fifo */ 795262417Shselaskystatic void atp_reset_buf(struct atp_softc *); 796262417Shselaskystatic void atp_add_to_queue(struct atp_softc *, int, int, int, uint32_t); 797199086Srpaulo 798262417Shselasky/* Device methods. */ 799262417Shselaskystatic device_probe_t atp_probe; 800262417Shselaskystatic device_attach_t atp_attach; 801262417Shselaskystatic device_detach_t atp_detach; 802262417Shselaskystatic usb_callback_t atp_intr; 803262417Shselasky 804262417Shselaskystatic const struct usb_config atp_xfer_config[ATP_N_TRANSFER] = { 805262417Shselasky [ATP_INTR_DT] = { 806262417Shselasky .type = UE_INTERRUPT, 807262417Shselasky .endpoint = UE_ADDR_ANY, 808262417Shselasky .direction = UE_DIR_IN, 809262417Shselasky .flags = { 810262417Shselasky .pipe_bof = 1, /* block pipe on failure */ 811262417Shselasky .short_xfer_ok = 1, 812262417Shselasky }, 813262417Shselasky .bufsize = ATP_SENSOR_DATA_BUF_MAX, 814262417Shselasky .callback = &atp_intr, 815262417Shselasky }, 816262417Shselasky [ATP_RESET] = { 817262417Shselasky .type = UE_CONTROL, 818262417Shselasky .endpoint = 0, /* Control pipe */ 819262417Shselasky .direction = UE_DIR_ANY, 820262417Shselasky .bufsize = sizeof(struct usb_device_request) + MODE_LENGTH, 821262417Shselasky .callback = &atp_reset_callback, 822262417Shselasky .interval = 0, /* no pre-delay */ 823262417Shselasky }, 824262417Shselasky}; 825262417Shselasky 826262417Shselaskystatic atp_stroke_t * 827262417Shselaskyatp_alloc_stroke(struct atp_softc *sc) 828199086Srpaulo{ 829262417Shselasky atp_stroke_t *pstroke; 830199086Srpaulo 831262417Shselasky pstroke = TAILQ_FIRST(&sc->sc_stroke_free); 832262417Shselasky if (pstroke == NULL) 833262417Shselasky goto done; 834199086Srpaulo 835262417Shselasky TAILQ_REMOVE(&sc->sc_stroke_free, pstroke, entry); 836262417Shselasky memset(pstroke, 0, sizeof(*pstroke)); 837262417Shselasky TAILQ_INSERT_TAIL(&sc->sc_stroke_used, pstroke, entry); 838262417Shselasky 839262417Shselasky sc->sc_n_strokes++; 840262417Shselaskydone: 841262417Shselasky return (pstroke); 842199086Srpaulo} 843199086Srpaulo 844262417Shselaskystatic void 845262417Shselaskyatp_free_stroke(struct atp_softc *sc, atp_stroke_t *pstroke) 846199086Srpaulo{ 847262417Shselasky if (pstroke == NULL) 848262417Shselasky return; 849199086Srpaulo 850262417Shselasky sc->sc_n_strokes--; 851199086Srpaulo 852262417Shselasky TAILQ_REMOVE(&sc->sc_stroke_used, pstroke, entry); 853262417Shselasky TAILQ_INSERT_TAIL(&sc->sc_stroke_free, pstroke, entry); 854262417Shselasky} 855199086Srpaulo 856262417Shselaskystatic void 857262417Shselaskyatp_init_stroke_pool(struct atp_softc *sc) 858262417Shselasky{ 859262417Shselasky u_int x; 860199086Srpaulo 861262417Shselasky TAILQ_INIT(&sc->sc_stroke_free); 862262417Shselasky TAILQ_INIT(&sc->sc_stroke_used); 863262417Shselasky 864262417Shselasky sc->sc_n_strokes = 0; 865262417Shselasky 866262417Shselasky memset(&sc->sc_strokes_data, 0, sizeof(sc->sc_strokes_data)); 867262417Shselasky 868262417Shselasky for (x = 0; x != ATP_MAX_STROKES; x++) { 869262417Shselasky TAILQ_INSERT_TAIL(&sc->sc_stroke_free, &sc->sc_strokes_data[x], 870262417Shselasky entry); 871262417Shselasky } 872199086Srpaulo} 873199086Srpaulo 874262417Shselaskystatic usb_error_t 875262417Shselaskyatp_set_device_mode(struct atp_softc *sc, interface_mode newMode) 876262417Shselasky{ 877262417Shselasky uint8_t mode_value; 878262417Shselasky usb_error_t err; 879262417Shselasky 880262417Shselasky if ((newMode != RAW_SENSOR_MODE) && (newMode != HID_MODE)) 881262417Shselasky return (USB_ERR_INVAL); 882262417Shselasky 883262417Shselasky if ((newMode == RAW_SENSOR_MODE) && 884262417Shselasky (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER)) 885262417Shselasky mode_value = (uint8_t)0x04; 886262417Shselasky else 887262417Shselasky mode_value = newMode; 888262417Shselasky 889262417Shselasky err = usbd_req_get_report(sc->sc_usb_device, NULL /* mutex */, 890262417Shselasky sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, 891262417Shselasky 0x03 /* type */, 0x00 /* id */); 892262417Shselasky if (err != USB_ERR_NORMAL_COMPLETION) { 893262417Shselasky DPRINTF("Failed to read device mode (%d)\n", err); 894262417Shselasky return (err); 895262417Shselasky } 896262417Shselasky 897262417Shselasky if (sc->sc_mode_bytes[0] == mode_value) 898262417Shselasky return (err); 899262417Shselasky 900262417Shselasky /* 901262417Shselasky * XXX Need to wait at least 250ms for hardware to get 902262417Shselasky * ready. The device mode handling appears to be handled 903262417Shselasky * asynchronously and we should not issue these commands too 904262417Shselasky * quickly. 905262417Shselasky */ 906262417Shselasky pause("WHW", hz / 4); 907262417Shselasky 908262417Shselasky sc->sc_mode_bytes[0] = mode_value; 909262417Shselasky return (usbd_req_set_report(sc->sc_usb_device, NULL /* mutex */, 910262417Shselasky sc->sc_mode_bytes, sizeof(sc->sc_mode_bytes), 0 /* interface idx */, 911262417Shselasky 0x03 /* type */, 0x00 /* id */)); 912262417Shselasky} 913262417Shselasky 914262417Shselaskystatic void 915199680Sthompsaatp_reset_callback(struct usb_xfer *xfer, usb_error_t error) 916199680Sthompsa{ 917199680Sthompsa usb_device_request_t req; 918199680Sthompsa struct usb_page_cache *pc; 919199680Sthompsa struct atp_softc *sc = usbd_xfer_softc(xfer); 920199680Sthompsa 921262417Shselasky uint8_t mode_value; 922262417Shselasky if (sc->sc_family == TRACKPAD_FAMILY_FOUNTAIN_GEYSER) 923262417Shselasky mode_value = 0x04; 924262417Shselasky else 925262417Shselasky mode_value = RAW_SENSOR_MODE; 926262417Shselasky 927199680Sthompsa switch (USB_GET_STATE(xfer)) { 928199680Sthompsa case USB_ST_SETUP: 929262417Shselasky sc->sc_mode_bytes[0] = mode_value; 930199680Sthompsa req.bmRequestType = UT_WRITE_CLASS_INTERFACE; 931199680Sthompsa req.bRequest = UR_SET_REPORT; 932199680Sthompsa USETW2(req.wValue, 933199680Sthompsa (uint8_t)0x03 /* type */, (uint8_t)0x00 /* id */); 934199680Sthompsa USETW(req.wIndex, 0); 935199680Sthompsa USETW(req.wLength, MODE_LENGTH); 936199680Sthompsa 937199680Sthompsa pc = usbd_xfer_get_frame(xfer, 0); 938199680Sthompsa usbd_copy_in(pc, 0, &req, sizeof(req)); 939199680Sthompsa pc = usbd_xfer_get_frame(xfer, 1); 940199680Sthompsa usbd_copy_in(pc, 0, sc->sc_mode_bytes, MODE_LENGTH); 941199680Sthompsa 942199680Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); 943199680Sthompsa usbd_xfer_set_frame_len(xfer, 1, MODE_LENGTH); 944199680Sthompsa usbd_xfer_set_frames(xfer, 2); 945199680Sthompsa usbd_transfer_submit(xfer); 946199680Sthompsa break; 947199680Sthompsa 948199680Sthompsa case USB_ST_TRANSFERRED: 949199680Sthompsa default: 950199680Sthompsa break; 951199680Sthompsa } 952199680Sthompsa} 953199680Sthompsa 954199086Srpaulostatic int 955199086Srpauloatp_enable(struct atp_softc *sc) 956199086Srpaulo{ 957262417Shselasky if (sc->sc_state & ATP_ENABLED) 958262417Shselasky return (0); 959199086Srpaulo 960199086Srpaulo /* reset status */ 961199086Srpaulo memset(&sc->sc_status, 0, sizeof(sc->sc_status)); 962262417Shselasky 963262417Shselasky atp_init_stroke_pool(sc); 964262417Shselasky 965199086Srpaulo sc->sc_state |= ATP_ENABLED; 966199086Srpaulo 967199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "enabled atp\n"); 968199086Srpaulo return (0); 969199086Srpaulo} 970199086Srpaulo 971199086Srpaulostatic void 972199086Srpauloatp_disable(struct atp_softc *sc) 973199086Srpaulo{ 974199151Snwhitehorn sc->sc_state &= ~(ATP_ENABLED | ATP_VALID); 975199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "disabled atp\n"); 976199086Srpaulo} 977199086Srpaulo 978262417Shselaskystatic void 979262417Shselaskyfg_interpret_sensor_data(struct atp_softc *sc, u_int data_len) 980199086Srpaulo{ 981262417Shselasky u_int n_xpspans = 0; 982262417Shselasky u_int n_ypspans = 0; 983262417Shselasky uint8_t status_bits; 984199086Srpaulo 985262417Shselasky const struct fg_dev_params *params = 986262417Shselasky (const struct fg_dev_params *)sc->sc_params; 987199086Srpaulo 988262417Shselasky fg_extract_sensor_data(sc->sc_sensor_data, params->n_xsensors, X, 989262417Shselasky sc->sc_cur_x, params->prot); 990262417Shselasky fg_extract_sensor_data(sc->sc_sensor_data, params->n_ysensors, Y, 991262417Shselasky sc->sc_cur_y, params->prot); 992199086Srpaulo 993262417Shselasky /* 994262417Shselasky * If this is the initial update (from an untouched 995262417Shselasky * pad), we should set the base values for the sensor 996262417Shselasky * data; deltas with respect to these base values can 997262417Shselasky * be used as pressure readings subsequently. 998262417Shselasky */ 999262417Shselasky status_bits = sc->sc_sensor_data[params->data_len - 1]; 1000262417Shselasky if (((params->prot == FG_TRACKPAD_TYPE_GEYSER3) || 1001262417Shselasky (params->prot == FG_TRACKPAD_TYPE_GEYSER4)) && 1002262417Shselasky ((sc->sc_state & ATP_VALID) == 0)) { 1003262417Shselasky if (status_bits & FG_STATUS_BASE_UPDATE) { 1004262417Shselasky memcpy(sc->sc_base_x, sc->sc_cur_x, 1005262417Shselasky params->n_xsensors * sizeof(*sc->sc_base_x)); 1006262417Shselasky memcpy(sc->sc_base_y, sc->sc_cur_y, 1007262417Shselasky params->n_ysensors * sizeof(*sc->sc_base_y)); 1008262417Shselasky sc->sc_state |= ATP_VALID; 1009262417Shselasky return; 1010199086Srpaulo } 1011199086Srpaulo } 1012199086Srpaulo 1013262417Shselasky /* Get pressure readings and detect p-spans for both axes. */ 1014262417Shselasky fg_get_pressures(sc->sc_pressure_x, sc->sc_cur_x, sc->sc_base_x, 1015262417Shselasky params->n_xsensors); 1016262417Shselasky fg_detect_pspans(sc->sc_pressure_x, params->n_xsensors, 1017262417Shselasky FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_x, &n_xpspans); 1018262417Shselasky fg_get_pressures(sc->sc_pressure_y, sc->sc_cur_y, sc->sc_base_y, 1019262417Shselasky params->n_ysensors); 1020262417Shselasky fg_detect_pspans(sc->sc_pressure_y, params->n_ysensors, 1021262417Shselasky FG_MAX_PSPANS_PER_AXIS, sc->sc_pspans_y, &n_ypspans); 1022199086Srpaulo 1023262417Shselasky /* Update strokes with new pspans to detect movements. */ 1024262417Shselasky if (fg_update_strokes(sc, sc->sc_pspans_x, n_xpspans, sc->sc_pspans_y, n_ypspans)) 1025262417Shselasky sc->sc_status.flags |= MOUSE_POSCHANGED; 1026199086Srpaulo 1027262417Shselasky sc->sc_ibtn = (status_bits & FG_STATUS_BUTTON) ? MOUSE_BUTTON1DOWN : 0; 1028262417Shselasky sc->sc_status.button = sc->sc_ibtn; 1029199086Srpaulo 1030262417Shselasky /* 1031262417Shselasky * The Fountain/Geyser device continues to trigger interrupts 1032262417Shselasky * at a fast rate even after touchpad activity has 1033262417Shselasky * stopped. Upon detecting that the device has remained idle 1034262417Shselasky * beyond a threshold, we reinitialize it to silence the 1035262417Shselasky * interrupts. 1036262417Shselasky */ 1037262417Shselasky if ((sc->sc_status.flags == 0) && (sc->sc_n_strokes == 0)) { 1038262417Shselasky sc->sc_idlecount++; 1039262417Shselasky if (sc->sc_idlecount >= ATP_IDLENESS_THRESHOLD) { 1040262417Shselasky /* 1041262417Shselasky * Use the last frame before we go idle for 1042262417Shselasky * calibration on pads which do not send 1043262417Shselasky * calibration frames. 1044262417Shselasky */ 1045262417Shselasky const struct fg_dev_params *params = 1046262417Shselasky (const struct fg_dev_params *)sc->sc_params; 1047199086Srpaulo 1048262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "idle\n"); 1049199086Srpaulo 1050262417Shselasky if (params->prot < FG_TRACKPAD_TYPE_GEYSER3) { 1051262417Shselasky memcpy(sc->sc_base_x, sc->sc_cur_x, 1052262417Shselasky params->n_xsensors * sizeof(*(sc->sc_base_x))); 1053262417Shselasky memcpy(sc->sc_base_y, sc->sc_cur_y, 1054262417Shselasky params->n_ysensors * sizeof(*(sc->sc_base_y))); 1055262417Shselasky } 1056199086Srpaulo 1057262417Shselasky sc->sc_idlecount = 0; 1058262417Shselasky usbd_transfer_start(sc->sc_xfer[ATP_RESET]); 1059199086Srpaulo } 1060262417Shselasky } else { 1061262417Shselasky sc->sc_idlecount = 0; 1062199086Srpaulo } 1063199086Srpaulo} 1064199086Srpaulo 1065199086Srpaulo/* 1066199086Srpaulo * Interpret the data from the X and Y pressure sensors. This function 1067199086Srpaulo * is called separately for the X and Y sensor arrays. The data in the 1068199086Srpaulo * USB packet is laid out in the following manner: 1069199086Srpaulo * 1070199086Srpaulo * sensor_data: 1071199086Srpaulo * --,--,Y1,Y2,--,Y3,Y4,--,Y5,...,Y10, ... X1,X2,--,X3,X4 1072199086Srpaulo * indices: 0 1 2 3 4 5 6 7 8 ... 15 ... 20 21 22 23 24 1073199086Srpaulo * 1074199086Srpaulo * '--' (in the above) indicates that the value is unimportant. 1075199086Srpaulo * 1076199086Srpaulo * Information about the above layout was obtained from the 1077199086Srpaulo * implementation of the AppleTouch driver in Linux. 1078199086Srpaulo * 1079199086Srpaulo * parameters: 1080199086Srpaulo * sensor_data 1081199086Srpaulo * raw sensor data from the USB packet. 1082199086Srpaulo * num 1083199086Srpaulo * The number of elements in the array 'arr'. 1084199151Snwhitehorn * axis 1085199151Snwhitehorn * Axis of data to fetch 1086199086Srpaulo * arr 1087199086Srpaulo * The array to be initialized with the readings. 1088199151Snwhitehorn * prot 1089199151Snwhitehorn * The protocol to use to interpret the data 1090199086Srpaulo */ 1091262417Shselaskystatic void 1092262417Shselaskyfg_extract_sensor_data(const int8_t *sensor_data, u_int num, atp_axis axis, 1093262417Shselasky int *arr, enum fountain_geyser_trackpad_type prot) 1094199086Srpaulo{ 1095199086Srpaulo u_int i; 1096199086Srpaulo u_int di; /* index into sensor data */ 1097199086Srpaulo 1098199151Snwhitehorn switch (prot) { 1099262417Shselasky case FG_TRACKPAD_TYPE_GEYSER1: 1100199151Snwhitehorn /* 1101199151Snwhitehorn * For Geyser 1, the sensors are laid out in pairs 1102199151Snwhitehorn * every 5 bytes. 1103199151Snwhitehorn */ 1104199151Snwhitehorn for (i = 0, di = (axis == Y) ? 1 : 2; i < 8; di += 5, i++) { 1105199151Snwhitehorn arr[i] = sensor_data[di]; 1106199151Snwhitehorn arr[i+8] = sensor_data[di+2]; 1107262417Shselasky if ((axis == X) && (num > 16)) 1108199151Snwhitehorn arr[i+16] = sensor_data[di+40]; 1109199151Snwhitehorn } 1110199151Snwhitehorn 1111199151Snwhitehorn break; 1112262417Shselasky case FG_TRACKPAD_TYPE_GEYSER2: 1113262417Shselasky for (i = 0, di = (axis == Y) ? 1 : 19; i < num; /* empty */ ) { 1114262417Shselasky arr[i++] = sensor_data[di++]; 1115262417Shselasky arr[i++] = sensor_data[di++]; 1116262417Shselasky di++; 1117262417Shselasky } 1118262417Shselasky break; 1119262417Shselasky case FG_TRACKPAD_TYPE_GEYSER3: 1120262417Shselasky case FG_TRACKPAD_TYPE_GEYSER4: 1121199151Snwhitehorn for (i = 0, di = (axis == Y) ? 2 : 20; i < num; /* empty */ ) { 1122199151Snwhitehorn arr[i++] = sensor_data[di++]; 1123199151Snwhitehorn arr[i++] = sensor_data[di++]; 1124199151Snwhitehorn di++; 1125199151Snwhitehorn } 1126199151Snwhitehorn break; 1127262417Shselasky default: 1128262417Shselasky break; 1129199086Srpaulo } 1130199086Srpaulo} 1131199086Srpaulo 1132262417Shselaskystatic void 1133262417Shselaskyfg_get_pressures(int *p, const int *cur, const int *base, int n) 1134199086Srpaulo{ 1135199086Srpaulo int i; 1136199086Srpaulo 1137199086Srpaulo for (i = 0; i < n; i++) { 1138199086Srpaulo p[i] = cur[i] - base[i]; 1139199086Srpaulo if (p[i] > 127) 1140199086Srpaulo p[i] -= 256; 1141199086Srpaulo if (p[i] < -127) 1142199086Srpaulo p[i] += 256; 1143199086Srpaulo if (p[i] < 0) 1144199086Srpaulo p[i] = 0; 1145199086Srpaulo 1146199086Srpaulo /* 1147199086Srpaulo * Shave off pressures below the noise-pressure 1148199086Srpaulo * threshold; this will reduce the contribution from 1149199086Srpaulo * lower pressure readings. 1150199086Srpaulo */ 1151262417Shselasky if ((u_int)p[i] <= FG_SENSOR_NOISE_THRESHOLD) 1152199086Srpaulo p[i] = 0; /* filter away noise */ 1153199086Srpaulo else 1154262417Shselasky p[i] -= FG_SENSOR_NOISE_THRESHOLD; 1155199086Srpaulo } 1156199086Srpaulo} 1157199086Srpaulo 1158199086Srpaulostatic void 1159262417Shselaskyfg_detect_pspans(int *p, u_int num_sensors, 1160262417Shselasky u_int max_spans, /* max # of pspans permitted */ 1161262417Shselasky fg_pspan *spans, /* finger spans */ 1162262417Shselasky u_int *nspans_p) /* num spans detected */ 1163199086Srpaulo{ 1164199086Srpaulo u_int i; 1165199086Srpaulo int maxp; /* max pressure seen within a span */ 1166199086Srpaulo u_int num_spans = 0; 1167199086Srpaulo 1168262417Shselasky enum fg_pspan_state { 1169199086Srpaulo ATP_PSPAN_INACTIVE, 1170199086Srpaulo ATP_PSPAN_INCREASING, 1171199086Srpaulo ATP_PSPAN_DECREASING, 1172199086Srpaulo } state; /* state of the pressure span */ 1173199086Srpaulo 1174199086Srpaulo /* 1175199086Srpaulo * The following is a simple state machine to track 1176199086Srpaulo * the phase of the pressure span. 1177199086Srpaulo */ 1178262417Shselasky memset(spans, 0, max_spans * sizeof(fg_pspan)); 1179199086Srpaulo maxp = 0; 1180199086Srpaulo state = ATP_PSPAN_INACTIVE; 1181199086Srpaulo for (i = 0; i < num_sensors; i++) { 1182199086Srpaulo if (num_spans >= max_spans) 1183199086Srpaulo break; 1184199086Srpaulo 1185199086Srpaulo if (p[i] == 0) { 1186199086Srpaulo if (state == ATP_PSPAN_INACTIVE) { 1187199086Srpaulo /* 1188199086Srpaulo * There is no pressure information for this 1189199086Srpaulo * sensor, and we aren't tracking a finger. 1190199086Srpaulo */ 1191199086Srpaulo continue; 1192199086Srpaulo } else { 1193199086Srpaulo state = ATP_PSPAN_INACTIVE; 1194199086Srpaulo maxp = 0; 1195199086Srpaulo num_spans++; 1196199086Srpaulo } 1197199086Srpaulo } else { 1198199086Srpaulo switch (state) { 1199199086Srpaulo case ATP_PSPAN_INACTIVE: 1200199086Srpaulo state = ATP_PSPAN_INCREASING; 1201199086Srpaulo maxp = p[i]; 1202199086Srpaulo break; 1203199086Srpaulo 1204199086Srpaulo case ATP_PSPAN_INCREASING: 1205199086Srpaulo if (p[i] > maxp) 1206199086Srpaulo maxp = p[i]; 1207199086Srpaulo else if (p[i] <= (maxp >> 1)) 1208199086Srpaulo state = ATP_PSPAN_DECREASING; 1209199086Srpaulo break; 1210199086Srpaulo 1211199086Srpaulo case ATP_PSPAN_DECREASING: 1212199086Srpaulo if (p[i] > p[i - 1]) { 1213199086Srpaulo /* 1214199086Srpaulo * This is the beginning of 1215199086Srpaulo * another span; change state 1216199086Srpaulo * to give the appearance that 1217199086Srpaulo * we're starting from an 1218199086Srpaulo * inactive span, and then 1219199086Srpaulo * re-process this reading in 1220199086Srpaulo * the next iteration. 1221199086Srpaulo */ 1222199086Srpaulo num_spans++; 1223199086Srpaulo state = ATP_PSPAN_INACTIVE; 1224199086Srpaulo maxp = 0; 1225199086Srpaulo i--; 1226199086Srpaulo continue; 1227199086Srpaulo } 1228199086Srpaulo break; 1229199086Srpaulo } 1230199086Srpaulo 1231199086Srpaulo /* Update the finger span with this reading. */ 1232199086Srpaulo spans[num_spans].width++; 1233199086Srpaulo spans[num_spans].cum += p[i]; 1234199086Srpaulo spans[num_spans].cog += p[i] * (i + 1); 1235199086Srpaulo } 1236199086Srpaulo } 1237199086Srpaulo if (state != ATP_PSPAN_INACTIVE) 1238199086Srpaulo num_spans++; /* close the last finger span */ 1239199086Srpaulo 1240199086Srpaulo /* post-process the spans */ 1241199086Srpaulo for (i = 0; i < num_spans; i++) { 1242199086Srpaulo /* filter away unwanted pressure spans */ 1243262417Shselasky if ((spans[i].cum < FG_PSPAN_MIN_CUM_PRESSURE) || 1244262417Shselasky (spans[i].width > FG_PSPAN_MAX_WIDTH)) { 1245199086Srpaulo if ((i + 1) < num_spans) { 1246199086Srpaulo memcpy(&spans[i], &spans[i + 1], 1247262417Shselasky (num_spans - i - 1) * sizeof(fg_pspan)); 1248199086Srpaulo i--; 1249199086Srpaulo } 1250199086Srpaulo num_spans--; 1251199086Srpaulo continue; 1252199086Srpaulo } 1253199086Srpaulo 1254199086Srpaulo /* compute this span's representative location */ 1255262417Shselasky spans[i].loc = spans[i].cog * FG_SCALE_FACTOR / 1256199086Srpaulo spans[i].cum; 1257199086Srpaulo 1258262417Shselasky spans[i].matched = false; /* not yet matched against a stroke */ 1259199086Srpaulo } 1260199086Srpaulo 1261199086Srpaulo *nspans_p = num_spans; 1262199086Srpaulo} 1263199086Srpaulo 1264262417Shselaskystatic void 1265262417Shselaskywsp_interpret_sensor_data(struct atp_softc *sc, u_int data_len) 1266262417Shselasky{ 1267262417Shselasky const struct wsp_dev_params *params = sc->sc_params; 1268262417Shselasky wsp_finger_t fingers[WSP_MAX_FINGERS]; 1269262417Shselasky struct wsp_finger_sensor_data *source_fingerp; 1270262417Shselasky u_int n_source_fingers; 1271262417Shselasky u_int n_fingers; 1272262417Shselasky u_int i; 1273262417Shselasky 1274262417Shselasky /* validate sensor data length */ 1275262417Shselasky if ((data_len < params->finger_data_offset) || 1276262417Shselasky ((data_len - params->finger_data_offset) % 1277262417Shselasky WSP_SIZEOF_FINGER_SENSOR_DATA) != 0) 1278262417Shselasky return; 1279262417Shselasky 1280262417Shselasky /* compute number of source fingers */ 1281262417Shselasky n_source_fingers = (data_len - params->finger_data_offset) / 1282262417Shselasky WSP_SIZEOF_FINGER_SENSOR_DATA; 1283262417Shselasky 1284262417Shselasky if (n_source_fingers > WSP_MAX_FINGERS) 1285262417Shselasky n_source_fingers = WSP_MAX_FINGERS; 1286262417Shselasky 1287262417Shselasky /* iterate over the source data collecting useful fingers */ 1288262417Shselasky n_fingers = 0; 1289262417Shselasky source_fingerp = (struct wsp_finger_sensor_data *)(sc->sc_sensor_data + 1290262417Shselasky params->finger_data_offset); 1291262417Shselasky 1292262417Shselasky for (i = 0; i < n_source_fingers; i++, source_fingerp++) { 1293262417Shselasky /* swap endianness, if any */ 1294262417Shselasky if (le16toh(0x1234) != 0x1234) { 1295262417Shselasky source_fingerp->origin = le16toh((uint16_t)source_fingerp->origin); 1296262417Shselasky source_fingerp->abs_x = le16toh((uint16_t)source_fingerp->abs_x); 1297262417Shselasky source_fingerp->abs_y = le16toh((uint16_t)source_fingerp->abs_y); 1298262417Shselasky source_fingerp->rel_x = le16toh((uint16_t)source_fingerp->rel_x); 1299262417Shselasky source_fingerp->rel_y = le16toh((uint16_t)source_fingerp->rel_y); 1300262417Shselasky source_fingerp->tool_major = le16toh((uint16_t)source_fingerp->tool_major); 1301262417Shselasky source_fingerp->tool_minor = le16toh((uint16_t)source_fingerp->tool_minor); 1302262417Shselasky source_fingerp->orientation = le16toh((uint16_t)source_fingerp->orientation); 1303262417Shselasky source_fingerp->touch_major = le16toh((uint16_t)source_fingerp->touch_major); 1304262417Shselasky source_fingerp->touch_minor = le16toh((uint16_t)source_fingerp->touch_minor); 1305262417Shselasky source_fingerp->multi = le16toh((uint16_t)source_fingerp->multi); 1306262417Shselasky } 1307262417Shselasky 1308262417Shselasky /* check for minium threshold */ 1309262417Shselasky if (source_fingerp->touch_major == 0) 1310262417Shselasky continue; 1311262417Shselasky 1312262417Shselasky fingers[n_fingers].matched = false; 1313262417Shselasky fingers[n_fingers].x = source_fingerp->abs_x; 1314262417Shselasky fingers[n_fingers].y = -source_fingerp->abs_y; 1315262417Shselasky 1316262417Shselasky n_fingers++; 1317262417Shselasky } 1318262417Shselasky 1319262417Shselasky if ((sc->sc_n_strokes == 0) && (n_fingers == 0)) 1320262417Shselasky return; 1321262417Shselasky 1322262417Shselasky if (wsp_update_strokes(sc, fingers, n_fingers)) 1323262417Shselasky sc->sc_status.flags |= MOUSE_POSCHANGED; 1324262417Shselasky 1325262417Shselasky switch(params->tp_type) { 1326262417Shselasky case WSP_TRACKPAD_TYPE2: 1327262417Shselasky sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE2_BUTTON_DATA_OFFSET]; 1328262417Shselasky break; 1329262417Shselasky case WSP_TRACKPAD_TYPE3: 1330262417Shselasky sc->sc_ibtn = sc->sc_sensor_data[WSP_TYPE3_BUTTON_DATA_OFFSET]; 1331262417Shselasky break; 1332262417Shselasky default: 1333262417Shselasky break; 1334262417Shselasky } 1335262417Shselasky sc->sc_status.button = sc->sc_ibtn ? MOUSE_BUTTON1DOWN : 0; 1336262417Shselasky} 1337262417Shselasky 1338199086Srpaulo/* 1339199086Srpaulo * Match a pressure-span against a stroke-component. If there is a 1340262417Shselasky * match, update the component's state and return true. 1341199086Srpaulo */ 1342199086Srpaulostatic boolean_t 1343262417Shselaskyfg_match_stroke_component(fg_stroke_component_t *component, 1344262417Shselasky const fg_pspan *pspan, atp_stroke_type stroke_type) 1345199086Srpaulo{ 1346200241Srpaulo int delta_mickeys; 1347200241Srpaulo u_int min_pressure; 1348199086Srpaulo 1349200241Srpaulo delta_mickeys = pspan->loc - component->loc; 1350200241Srpaulo 1351262417Shselasky if (abs(delta_mickeys) > (int)FG_MAX_DELTA_MICKEYS) 1352262417Shselasky return (false); /* the finger span is too far out; no match */ 1353199086Srpaulo 1354262417Shselasky component->loc = pspan->loc; 1355200241Srpaulo 1356200241Srpaulo /* 1357200241Srpaulo * A sudden and significant increase in a pspan's cumulative 1358200241Srpaulo * pressure indicates the incidence of a new finger 1359200241Srpaulo * contact. This usually revises the pspan's 1360200241Srpaulo * centre-of-gravity, and hence the location of any/all 1361200241Srpaulo * matching stroke component(s). But such a change should 1362200241Srpaulo * *not* be interpreted as a movement. 1363200241Srpaulo */ 1364262417Shselasky if (pspan->cum > ((3 * component->cum_pressure) >> 1)) 1365200241Srpaulo delta_mickeys = 0; 1366200241Srpaulo 1367199086Srpaulo component->cum_pressure = pspan->cum; 1368199086Srpaulo if (pspan->cum > component->max_cum_pressure) 1369199086Srpaulo component->max_cum_pressure = pspan->cum; 1370199086Srpaulo 1371199086Srpaulo /* 1372200241Srpaulo * Disregard the component's movement if its cumulative 1373200241Srpaulo * pressure drops below a fraction of the maximum; this 1374200241Srpaulo * fraction is determined based on the stroke's type. 1375199086Srpaulo */ 1376200241Srpaulo if (stroke_type == ATP_STROKE_TOUCH) 1377200241Srpaulo min_pressure = (3 * component->max_cum_pressure) >> 2; 1378200241Srpaulo else 1379200241Srpaulo min_pressure = component->max_cum_pressure >> 2; 1380200241Srpaulo if (component->cum_pressure < min_pressure) 1381199086Srpaulo delta_mickeys = 0; 1382199086Srpaulo 1383199086Srpaulo component->delta_mickeys = delta_mickeys; 1384262417Shselasky return (true); 1385199086Srpaulo} 1386199086Srpaulo 1387199086Srpaulostatic void 1388262417Shselaskyfg_match_strokes_against_pspans(struct atp_softc *sc, atp_axis axis, 1389262417Shselasky fg_pspan *pspans, u_int n_pspans, u_int repeat_count) 1390199086Srpaulo{ 1391262417Shselasky atp_stroke_t *strokep; 1392199086Srpaulo u_int repeat_index = 0; 1393262417Shselasky u_int i; 1394199086Srpaulo 1395199086Srpaulo /* Determine the index of the multi-span. */ 1396199086Srpaulo if (repeat_count) { 1397199086Srpaulo for (i = 0; i < n_pspans; i++) { 1398262417Shselasky if (pspans[i].cum > pspans[repeat_index].cum) 1399199086Srpaulo repeat_index = i; 1400199086Srpaulo } 1401199086Srpaulo } 1402199086Srpaulo 1403262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1404262417Shselasky if (strokep->components[axis].matched) 1405199086Srpaulo continue; /* skip matched components */ 1406199086Srpaulo 1407262417Shselasky for (i = 0; i < n_pspans; i++) { 1408262417Shselasky if (pspans[i].matched) 1409199086Srpaulo continue; /* skip matched pspans */ 1410199086Srpaulo 1411262417Shselasky if (fg_match_stroke_component( 1412262417Shselasky &strokep->components[axis], &pspans[i], 1413262417Shselasky strokep->type)) { 1414262417Shselasky 1415199086Srpaulo /* There is a match. */ 1416262417Shselasky strokep->components[axis].matched = true; 1417199086Srpaulo 1418199086Srpaulo /* Take care to repeat at the multi-span. */ 1419262417Shselasky if ((repeat_count > 0) && (i == repeat_index)) 1420199086Srpaulo repeat_count--; 1421199086Srpaulo else 1422262417Shselasky pspans[i].matched = true; 1423199086Srpaulo 1424262417Shselasky break; /* skip to the next strokep */ 1425199086Srpaulo } 1426199086Srpaulo } /* loop over pspans */ 1427199086Srpaulo } /* loop over strokes */ 1428199086Srpaulo} 1429199086Srpaulo 1430262417Shselaskystatic boolean_t 1431262417Shselaskywsp_match_strokes_against_fingers(struct atp_softc *sc, 1432262417Shselasky wsp_finger_t *fingers, u_int n_fingers) 1433262417Shselasky{ 1434262417Shselasky boolean_t movement = false; 1435262417Shselasky atp_stroke_t *strokep; 1436262417Shselasky u_int i; 1437262417Shselasky 1438262417Shselasky /* reset the matched status for all strokes */ 1439262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) 1440262417Shselasky strokep->matched = false; 1441262417Shselasky 1442262417Shselasky for (i = 0; i != n_fingers; i++) { 1443262417Shselasky u_int least_distance_sq = WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ; 1444262417Shselasky atp_stroke_t *strokep_best = NULL; 1445262417Shselasky 1446262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1447262417Shselasky int instantaneous_dx; 1448262417Shselasky int instantaneous_dy; 1449262417Shselasky u_int d_squared; 1450262417Shselasky 1451262417Shselasky if (strokep->matched) 1452262417Shselasky continue; 1453262417Shselasky 1454262417Shselasky instantaneous_dx = fingers[i].x - strokep->x; 1455262417Shselasky instantaneous_dy = fingers[i].y - strokep->y; 1456262417Shselasky 1457262417Shselasky /* skip strokes which are far away */ 1458262417Shselasky d_squared = 1459262417Shselasky (instantaneous_dx * instantaneous_dx) + 1460262417Shselasky (instantaneous_dy * instantaneous_dy); 1461262417Shselasky 1462262417Shselasky if (d_squared < least_distance_sq) { 1463262417Shselasky least_distance_sq = d_squared; 1464262417Shselasky strokep_best = strokep; 1465262417Shselasky } 1466262417Shselasky } 1467262417Shselasky 1468262417Shselasky strokep = strokep_best; 1469262417Shselasky 1470262417Shselasky if (strokep != NULL) { 1471262417Shselasky fingers[i].matched = true; 1472262417Shselasky 1473262417Shselasky strokep->matched = true; 1474262417Shselasky strokep->instantaneous_dx = fingers[i].x - strokep->x; 1475262417Shselasky strokep->instantaneous_dy = fingers[i].y - strokep->y; 1476262417Shselasky strokep->x = fingers[i].x; 1477262417Shselasky strokep->y = fingers[i].y; 1478262417Shselasky 1479262417Shselasky atp_advance_stroke_state(sc, strokep, &movement); 1480262417Shselasky } 1481262417Shselasky } 1482262417Shselasky return (movement); 1483262417Shselasky} 1484262417Shselasky 1485199086Srpaulo/* 1486199086Srpaulo * Update strokes by matching against current pressure-spans. 1487262417Shselasky * Return true if any movement is detected. 1488199086Srpaulo */ 1489199086Srpaulostatic boolean_t 1490262417Shselaskyfg_update_strokes(struct atp_softc *sc, fg_pspan *pspans_x, 1491262417Shselasky u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans) 1492199086Srpaulo{ 1493262417Shselasky atp_stroke_t *strokep; 1494262417Shselasky atp_stroke_t *strokep_next; 1495262417Shselasky boolean_t movement = false; 1496262417Shselasky u_int repeat_count = 0; 1497262417Shselasky u_int i; 1498262417Shselasky u_int j; 1499199086Srpaulo 1500199086Srpaulo /* Reset X and Y components of all strokes as unmatched. */ 1501262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1502262417Shselasky strokep->components[X].matched = false; 1503262417Shselasky strokep->components[Y].matched = false; 1504199086Srpaulo } 1505199086Srpaulo 1506199086Srpaulo /* 1507199086Srpaulo * Usually, the X and Y pspans come in pairs (the common case 1508199086Srpaulo * being a single pair). It is possible, however, that 1509199086Srpaulo * multiple contacts resolve to a single pspan along an 1510199086Srpaulo * axis, as illustrated in the following: 1511199086Srpaulo * 1512199086Srpaulo * F = finger-contact 1513199086Srpaulo * 1514199086Srpaulo * pspan pspan 1515199086Srpaulo * +-----------------------+ 1516199086Srpaulo * | . . | 1517199086Srpaulo * | . . | 1518199086Srpaulo * | . . | 1519199086Srpaulo * | . . | 1520199086Srpaulo * pspan |.........F......F | 1521199086Srpaulo * | | 1522199086Srpaulo * | | 1523199086Srpaulo * | | 1524199086Srpaulo * +-----------------------+ 1525199086Srpaulo * 1526199086Srpaulo * 1527199086Srpaulo * The above case can be detected by a difference in the 1528199086Srpaulo * number of X and Y pspans. When this happens, X and Y pspans 1529199086Srpaulo * aren't easy to pair or match against strokes. 1530199086Srpaulo * 1531199086Srpaulo * When X and Y pspans differ in number, the axis with the 1532199086Srpaulo * smaller number of pspans is regarded as having a repeating 1533199086Srpaulo * pspan (or a multi-pspan)--in the above illustration, the 1534199086Srpaulo * Y-axis has a repeating pspan. Our approach is to try to 1535199086Srpaulo * match the multi-pspan repeatedly against strokes. The 1536199086Srpaulo * difference between the number of X and Y pspans gives us a 1537199086Srpaulo * crude repeat_count for matching multi-pspans--i.e. the 1538199086Srpaulo * multi-pspan along the Y axis (above) has a repeat_count of 1. 1539199086Srpaulo */ 1540199086Srpaulo repeat_count = abs(n_xpspans - n_ypspans); 1541199086Srpaulo 1542262417Shselasky fg_match_strokes_against_pspans(sc, X, pspans_x, n_xpspans, 1543199086Srpaulo (((repeat_count != 0) && ((n_xpspans < n_ypspans))) ? 1544199086Srpaulo repeat_count : 0)); 1545262417Shselasky fg_match_strokes_against_pspans(sc, Y, pspans_y, n_ypspans, 1546199086Srpaulo (((repeat_count != 0) && (n_ypspans < n_xpspans)) ? 1547199086Srpaulo repeat_count : 0)); 1548199086Srpaulo 1549199086Srpaulo /* Update the state of strokes based on the above pspan matches. */ 1550262417Shselasky TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { 1551262417Shselasky 1552262417Shselasky if (strokep->components[X].matched && 1553262417Shselasky strokep->components[Y].matched) { 1554262417Shselasky strokep->matched = true; 1555262417Shselasky strokep->instantaneous_dx = 1556262417Shselasky strokep->components[X].delta_mickeys; 1557262417Shselasky strokep->instantaneous_dy = 1558262417Shselasky strokep->components[Y].delta_mickeys; 1559262417Shselasky atp_advance_stroke_state(sc, strokep, &movement); 1560199086Srpaulo } else { 1561199086Srpaulo /* 1562199086Srpaulo * At least one component of this stroke 1563199086Srpaulo * didn't match against current pspans; 1564199086Srpaulo * terminate it. 1565199086Srpaulo */ 1566262417Shselasky atp_terminate_stroke(sc, strokep); 1567199086Srpaulo } 1568199086Srpaulo } 1569199086Srpaulo 1570199086Srpaulo /* Add new strokes for pairs of unmatched pspans */ 1571199086Srpaulo for (i = 0; i < n_xpspans; i++) { 1572262417Shselasky if (pspans_x[i].matched == false) break; 1573199086Srpaulo } 1574199086Srpaulo for (j = 0; j < n_ypspans; j++) { 1575262417Shselasky if (pspans_y[j].matched == false) break; 1576199086Srpaulo } 1577199086Srpaulo if ((i < n_xpspans) && (j < n_ypspans)) { 1578207077Sthompsa#ifdef USB_DEBUG 1579199086Srpaulo if (atp_debug >= ATP_LLEVEL_INFO) { 1580199086Srpaulo printf("unmatched pspans:"); 1581199086Srpaulo for (; i < n_xpspans; i++) { 1582199086Srpaulo if (pspans_x[i].matched) 1583199086Srpaulo continue; 1584199086Srpaulo printf(" X:[loc:%u,cum:%u]", 1585199086Srpaulo pspans_x[i].loc, pspans_x[i].cum); 1586199086Srpaulo } 1587199086Srpaulo for (; j < n_ypspans; j++) { 1588199086Srpaulo if (pspans_y[j].matched) 1589199086Srpaulo continue; 1590199086Srpaulo printf(" Y:[loc:%u,cum:%u]", 1591199086Srpaulo pspans_y[j].loc, pspans_y[j].cum); 1592199086Srpaulo } 1593199086Srpaulo printf("\n"); 1594199086Srpaulo } 1595207077Sthompsa#endif /* USB_DEBUG */ 1596199086Srpaulo if ((n_xpspans == 1) && (n_ypspans == 1)) 1597199086Srpaulo /* The common case of a single pair of new pspans. */ 1598262417Shselasky fg_add_stroke(sc, &pspans_x[0], &pspans_y[0]); 1599199086Srpaulo else 1600262417Shselasky fg_add_new_strokes(sc, pspans_x, n_xpspans, 1601199086Srpaulo pspans_y, n_ypspans); 1602199086Srpaulo } 1603199086Srpaulo 1604207077Sthompsa#ifdef USB_DEBUG 1605199086Srpaulo if (atp_debug >= ATP_LLEVEL_INFO) { 1606262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 1607262417Shselasky printf(" %s%clc:%u,dm:%d,cum:%d,max:%d,%c" 1608262417Shselasky ",%clc:%u,dm:%d,cum:%d,max:%d,%c", 1609262417Shselasky (strokep->flags & ATSF_ZOMBIE) ? "zomb:" : "", 1610262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<', 1611262417Shselasky strokep->components[X].loc, 1612262417Shselasky strokep->components[X].delta_mickeys, 1613262417Shselasky strokep->components[X].cum_pressure, 1614262417Shselasky strokep->components[X].max_cum_pressure, 1615262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>', 1616262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? '[' : '<', 1617262417Shselasky strokep->components[Y].loc, 1618262417Shselasky strokep->components[Y].delta_mickeys, 1619262417Shselasky strokep->components[Y].cum_pressure, 1620262417Shselasky strokep->components[Y].max_cum_pressure, 1621262417Shselasky (strokep->type == ATP_STROKE_TOUCH) ? ']' : '>'); 1622199086Srpaulo } 1623262417Shselasky if (TAILQ_FIRST(&sc->sc_stroke_used) != NULL) 1624199086Srpaulo printf("\n"); 1625199086Srpaulo } 1626207077Sthompsa#endif /* USB_DEBUG */ 1627262417Shselasky return (movement); 1628262417Shselasky} 1629199086Srpaulo 1630262417Shselasky/* 1631262417Shselasky * Update strokes by matching against current pressure-spans. 1632262417Shselasky * Return true if any movement is detected. 1633262417Shselasky */ 1634262417Shselaskystatic boolean_t 1635262417Shselaskywsp_update_strokes(struct atp_softc *sc, wsp_finger_t *fingers, u_int n_fingers) 1636262417Shselasky{ 1637262417Shselasky boolean_t movement = false; 1638262417Shselasky atp_stroke_t *strokep_next; 1639262417Shselasky atp_stroke_t *strokep; 1640262417Shselasky u_int i; 1641262417Shselasky 1642262417Shselasky if (sc->sc_n_strokes > 0) { 1643262417Shselasky movement = wsp_match_strokes_against_fingers( 1644262417Shselasky sc, fingers, n_fingers); 1645262417Shselasky 1646262417Shselasky /* handle zombie strokes */ 1647262417Shselasky TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { 1648262417Shselasky if (strokep->matched) 1649262417Shselasky continue; 1650262417Shselasky atp_terminate_stroke(sc, strokep); 1651262417Shselasky } 1652262417Shselasky } 1653262417Shselasky 1654262417Shselasky /* initialize unmatched fingers as strokes */ 1655262417Shselasky for (i = 0; i != n_fingers; i++) { 1656262417Shselasky if (fingers[i].matched) 1657262417Shselasky continue; 1658262417Shselasky 1659262417Shselasky wsp_add_stroke(sc, fingers + i); 1660262417Shselasky } 1661199086Srpaulo return (movement); 1662199086Srpaulo} 1663199086Srpaulo 1664199086Srpaulo/* Initialize a stroke using a pressure-span. */ 1665262417Shselaskystatic void 1666262417Shselaskyfg_add_stroke(struct atp_softc *sc, const fg_pspan *pspan_x, 1667262417Shselasky const fg_pspan *pspan_y) 1668199086Srpaulo{ 1669262417Shselasky atp_stroke_t *strokep; 1670199086Srpaulo 1671262417Shselasky strokep = atp_alloc_stroke(sc); 1672262417Shselasky if (strokep == NULL) 1673199086Srpaulo return; 1674199086Srpaulo 1675199086Srpaulo /* 1676199086Srpaulo * Strokes begin as potential touches. If a stroke survives 1677199086Srpaulo * longer than a threshold, or if it records significant 1678199086Srpaulo * cumulative movement, then it is considered a 'slide'. 1679199086Srpaulo */ 1680262417Shselasky strokep->type = ATP_STROKE_TOUCH; 1681262417Shselasky strokep->matched = false; 1682262417Shselasky microtime(&strokep->ctime); 1683262417Shselasky strokep->age = 1; /* number of interrupts */ 1684262417Shselasky strokep->x = pspan_x->loc; 1685262417Shselasky strokep->y = pspan_y->loc; 1686199086Srpaulo 1687262417Shselasky strokep->components[X].loc = pspan_x->loc; 1688262417Shselasky strokep->components[X].cum_pressure = pspan_x->cum; 1689262417Shselasky strokep->components[X].max_cum_pressure = pspan_x->cum; 1690262417Shselasky strokep->components[X].matched = true; 1691199086Srpaulo 1692262417Shselasky strokep->components[Y].loc = pspan_y->loc; 1693262417Shselasky strokep->components[Y].cum_pressure = pspan_y->cum; 1694262417Shselasky strokep->components[Y].max_cum_pressure = pspan_y->cum; 1695262417Shselasky strokep->components[Y].matched = true; 1696199086Srpaulo 1697199086Srpaulo if (sc->sc_n_strokes > 1) { 1698199086Srpaulo /* Reset double-tap-n-drag if we have more than one strokes. */ 1699199086Srpaulo sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 1700199086Srpaulo } 1701199086Srpaulo 1702199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "[%u,%u], time: %u,%ld\n", 1703262417Shselasky strokep->components[X].loc, 1704262417Shselasky strokep->components[Y].loc, 1705262417Shselasky (u_int)strokep->ctime.tv_sec, 1706262417Shselasky (unsigned long int)strokep->ctime.tv_usec); 1707199086Srpaulo} 1708199086Srpaulo 1709199086Srpaulostatic void 1710262417Shselaskyfg_add_new_strokes(struct atp_softc *sc, fg_pspan *pspans_x, 1711262417Shselasky u_int n_xpspans, fg_pspan *pspans_y, u_int n_ypspans) 1712199086Srpaulo{ 1713262417Shselasky fg_pspan spans[2][FG_MAX_PSPANS_PER_AXIS]; 1714233774Shselasky u_int nspans[2]; 1715233774Shselasky u_int i; 1716233774Shselasky u_int j; 1717199086Srpaulo 1718199086Srpaulo /* Copy unmatched pspans into the local arrays. */ 1719199086Srpaulo for (i = 0, nspans[X] = 0; i < n_xpspans; i++) { 1720262417Shselasky if (pspans_x[i].matched == false) { 1721199086Srpaulo spans[X][nspans[X]] = pspans_x[i]; 1722199086Srpaulo nspans[X]++; 1723199086Srpaulo } 1724199086Srpaulo } 1725199086Srpaulo for (j = 0, nspans[Y] = 0; j < n_ypspans; j++) { 1726262417Shselasky if (pspans_y[j].matched == false) { 1727199086Srpaulo spans[Y][nspans[Y]] = pspans_y[j]; 1728199086Srpaulo nspans[Y]++; 1729199086Srpaulo } 1730199086Srpaulo } 1731199086Srpaulo 1732199086Srpaulo if (nspans[X] == nspans[Y]) { 1733199086Srpaulo /* Create new strokes from pairs of unmatched pspans */ 1734199086Srpaulo for (i = 0, j = 0; (i < nspans[X]) && (j < nspans[Y]); i++, j++) 1735262417Shselasky fg_add_stroke(sc, &spans[X][i], &spans[Y][j]); 1736199086Srpaulo } else { 1737199086Srpaulo u_int cum = 0; 1738199086Srpaulo atp_axis repeat_axis; /* axis with multi-pspans */ 1739199086Srpaulo u_int repeat_count; /* repeat count for the multi-pspan*/ 1740199086Srpaulo u_int repeat_index = 0; /* index of the multi-span */ 1741199086Srpaulo 1742199086Srpaulo repeat_axis = (nspans[X] > nspans[Y]) ? Y : X; 1743199086Srpaulo repeat_count = abs(nspans[X] - nspans[Y]); 1744199086Srpaulo for (i = 0; i < nspans[repeat_axis]; i++) { 1745199086Srpaulo if (spans[repeat_axis][i].cum > cum) { 1746199086Srpaulo repeat_index = i; 1747199086Srpaulo cum = spans[repeat_axis][i].cum; 1748199086Srpaulo } 1749199086Srpaulo } 1750199086Srpaulo 1751199086Srpaulo /* Create new strokes from pairs of unmatched pspans */ 1752199086Srpaulo i = 0, j = 0; 1753199086Srpaulo for (; (i < nspans[X]) && (j < nspans[Y]); i++, j++) { 1754262417Shselasky fg_add_stroke(sc, &spans[X][i], &spans[Y][j]); 1755199086Srpaulo 1756199086Srpaulo /* Take care to repeat at the multi-pspan. */ 1757199086Srpaulo if (repeat_count > 0) { 1758199086Srpaulo if ((repeat_axis == X) && 1759199086Srpaulo (repeat_index == i)) { 1760199086Srpaulo i--; /* counter loop increment */ 1761199086Srpaulo repeat_count--; 1762199086Srpaulo } else if ((repeat_axis == Y) && 1763199086Srpaulo (repeat_index == j)) { 1764199086Srpaulo j--; /* counter loop increment */ 1765199086Srpaulo repeat_count--; 1766199086Srpaulo } 1767199086Srpaulo } 1768199086Srpaulo } 1769199086Srpaulo } 1770199086Srpaulo} 1771199086Srpaulo 1772262417Shselasky/* Initialize a stroke from an unmatched finger. */ 1773262417Shselaskystatic void 1774262417Shselaskywsp_add_stroke(struct atp_softc *sc, const wsp_finger_t *fingerp) 1775199086Srpaulo{ 1776262417Shselasky atp_stroke_t *strokep; 1777199086Srpaulo 1778262417Shselasky strokep = atp_alloc_stroke(sc); 1779262417Shselasky if (strokep == NULL) 1780200241Srpaulo return; 1781200241Srpaulo 1782262417Shselasky /* 1783262417Shselasky * Strokes begin as potential touches. If a stroke survives 1784262417Shselasky * longer than a threshold, or if it records significant 1785262417Shselasky * cumulative movement, then it is considered a 'slide'. 1786262417Shselasky */ 1787262417Shselasky strokep->type = ATP_STROKE_TOUCH; 1788262417Shselasky strokep->matched = true; 1789262417Shselasky microtime(&strokep->ctime); 1790262417Shselasky strokep->age = 1; /* number of interrupts */ 1791262417Shselasky strokep->x = fingerp->x; 1792262417Shselasky strokep->y = fingerp->y; 1793199086Srpaulo 1794262417Shselasky /* Reset double-tap-n-drag if we have more than one strokes. */ 1795262417Shselasky if (sc->sc_n_strokes > 1) 1796262417Shselasky sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 1797200241Srpaulo 1798262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "[%d,%d]\n", strokep->x, strokep->y); 1799199086Srpaulo} 1800199086Srpaulo 1801262417Shselaskystatic void 1802262417Shselaskyatp_advance_stroke_state(struct atp_softc *sc, atp_stroke_t *strokep, 1803262417Shselasky boolean_t *movementp) 1804200241Srpaulo{ 1805262417Shselasky /* Revitalize stroke if it had previously been marked as a zombie. */ 1806262417Shselasky if (strokep->flags & ATSF_ZOMBIE) 1807262417Shselasky strokep->flags &= ~ATSF_ZOMBIE; 1808200241Srpaulo 1809262417Shselasky strokep->age++; 1810262417Shselasky if (strokep->age <= atp_stroke_maturity_threshold) { 1811262417Shselasky /* Avoid noise from immature strokes. */ 1812262417Shselasky strokep->instantaneous_dx = 0; 1813262417Shselasky strokep->instantaneous_dy = 0; 1814200241Srpaulo } 1815200241Srpaulo 1816262417Shselasky if (atp_compute_stroke_movement(strokep)) 1817262417Shselasky *movementp = true; 1818199086Srpaulo 1819262417Shselasky if (strokep->type != ATP_STROKE_TOUCH) 1820199086Srpaulo return; 1821199086Srpaulo 1822262417Shselasky /* Convert touch strokes to slides upon detecting movement or age. */ 1823262417Shselasky if ((abs(strokep->cum_movement_x) > atp_slide_min_movement) || 1824262417Shselasky (abs(strokep->cum_movement_y) > atp_slide_min_movement)) 1825262417Shselasky atp_convert_to_slide(sc, strokep); 1826262417Shselasky else { 1827262417Shselasky /* Compute the stroke's age. */ 1828262417Shselasky struct timeval tdiff; 1829262417Shselasky getmicrotime(&tdiff); 1830262417Shselasky if (timevalcmp(&tdiff, &strokep->ctime, >)) { 1831262417Shselasky timevalsub(&tdiff, &strokep->ctime); 1832199086Srpaulo 1833262417Shselasky if ((tdiff.tv_sec > (atp_touch_timeout / 1000000)) || 1834262417Shselasky ((tdiff.tv_sec == (atp_touch_timeout / 1000000)) && 1835262417Shselasky (tdiff.tv_usec >= (atp_touch_timeout % 1000000)))) 1836262417Shselasky atp_convert_to_slide(sc, strokep); 1837199086Srpaulo } 1838199086Srpaulo } 1839199086Srpaulo} 1840199086Srpaulo 1841262417Shselaskystatic boolean_t 1842262417Shselaskyatp_stroke_has_small_movement(const atp_stroke_t *strokep) 1843199086Srpaulo{ 1844262417Shselasky return (((u_int)abs(strokep->instantaneous_dx) <= 1845262417Shselasky atp_small_movement_threshold) && 1846262417Shselasky ((u_int)abs(strokep->instantaneous_dy) <= 1847262417Shselasky atp_small_movement_threshold)); 1848199086Srpaulo} 1849199086Srpaulo 1850199086Srpaulo/* 1851262417Shselasky * Accumulate instantaneous changes into the stroke's 'pending' bucket; if 1852199086Srpaulo * the aggregate exceeds the small_movement_threshold, then retain 1853262417Shselasky * instantaneous changes for later. 1854199086Srpaulo */ 1855199086Srpaulostatic void 1856262417Shselaskyatp_update_pending_mickeys(atp_stroke_t *strokep) 1857199086Srpaulo{ 1858262417Shselasky /* accumulate instantaneous movement */ 1859262417Shselasky strokep->pending_dx += strokep->instantaneous_dx; 1860262417Shselasky strokep->pending_dy += strokep->instantaneous_dy; 1861199086Srpaulo 1862262417Shselasky#define UPDATE_INSTANTANEOUS_AND_PENDING(I, P) \ 1863262417Shselasky if (abs((P)) <= atp_small_movement_threshold) \ 1864262417Shselasky (I) = 0; /* clobber small movement */ \ 1865262417Shselasky else { \ 1866262417Shselasky if ((I) > 0) { \ 1867262417Shselasky /* \ 1868262417Shselasky * Round up instantaneous movement to the nearest \ 1869262417Shselasky * ceiling. This helps preserve small mickey \ 1870262417Shselasky * movements from being lost in following scaling \ 1871262417Shselasky * operation. \ 1872262417Shselasky */ \ 1873262417Shselasky (I) = (((I) + (atp_mickeys_scale_factor - 1)) / \ 1874262417Shselasky atp_mickeys_scale_factor) * \ 1875262417Shselasky atp_mickeys_scale_factor; \ 1876262417Shselasky \ 1877262417Shselasky /* \ 1878262417Shselasky * Deduct the rounded mickeys from pending mickeys. \ 1879262417Shselasky * Note: we multiply by 2 to offset the previous \ 1880262417Shselasky * accumulation of instantaneous movement into \ 1881262417Shselasky * pending. \ 1882262417Shselasky */ \ 1883262417Shselasky (P) -= ((I) << 1); \ 1884262417Shselasky \ 1885262417Shselasky /* truncate pending to 0 if it becomes negative. */ \ 1886262417Shselasky (P) = imax((P), 0); \ 1887262417Shselasky } else { \ 1888262417Shselasky /* \ 1889262417Shselasky * Round down instantaneous movement to the nearest \ 1890262417Shselasky * ceiling. This helps preserve small mickey \ 1891262417Shselasky * movements from being lost in following scaling \ 1892262417Shselasky * operation. \ 1893262417Shselasky */ \ 1894262417Shselasky (I) = (((I) - (atp_mickeys_scale_factor - 1)) / \ 1895262417Shselasky atp_mickeys_scale_factor) * \ 1896262417Shselasky atp_mickeys_scale_factor; \ 1897262417Shselasky \ 1898262417Shselasky /* \ 1899262417Shselasky * Deduct the rounded mickeys from pending mickeys. \ 1900262417Shselasky * Note: we multiply by 2 to offset the previous \ 1901262417Shselasky * accumulation of instantaneous movement into \ 1902262417Shselasky * pending. \ 1903262417Shselasky */ \ 1904262417Shselasky (P) -= ((I) << 1); \ 1905262417Shselasky \ 1906262417Shselasky /* truncate pending to 0 if it becomes positive. */ \ 1907262417Shselasky (P) = imin((P), 0); \ 1908262417Shselasky } \ 1909199086Srpaulo } 1910199086Srpaulo 1911262417Shselasky UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dx, 1912262417Shselasky strokep->pending_dx); 1913262417Shselasky UPDATE_INSTANTANEOUS_AND_PENDING(strokep->instantaneous_dy, 1914262417Shselasky strokep->pending_dy); 1915199086Srpaulo} 1916199086Srpaulo 1917199086Srpaulo/* 1918199086Srpaulo * Compute a smoothened value for the stroke's movement from 1919262417Shselasky * instantaneous changes in the X and Y components. 1920199086Srpaulo */ 1921199086Srpaulostatic boolean_t 1922262417Shselaskyatp_compute_stroke_movement(atp_stroke_t *strokep) 1923199086Srpaulo{ 1924199086Srpaulo /* 1925199086Srpaulo * Short movements are added first to the 'pending' bucket, 1926199086Srpaulo * and then acted upon only when their aggregate exceeds a 1927199086Srpaulo * threshold. This has the effect of filtering away movement 1928199086Srpaulo * noise. 1929199086Srpaulo */ 1930262417Shselasky if (atp_stroke_has_small_movement(strokep)) 1931262417Shselasky atp_update_pending_mickeys(strokep); 1932262417Shselasky else { /* large movement */ 1933199086Srpaulo /* clear away any pending mickeys if there are large movements*/ 1934262417Shselasky strokep->pending_dx = 0; 1935262417Shselasky strokep->pending_dy = 0; 1936199086Srpaulo } 1937199086Srpaulo 1938262417Shselasky /* scale movement */ 1939262417Shselasky strokep->movement_dx = (strokep->instantaneous_dx) / 1940262417Shselasky (int)atp_mickeys_scale_factor; 1941262417Shselasky strokep->movement_dy = (strokep->instantaneous_dy) / 1942262417Shselasky (int)atp_mickeys_scale_factor; 1943199086Srpaulo 1944262417Shselasky if ((abs(strokep->instantaneous_dx) >= ATP_FAST_MOVEMENT_TRESHOLD) || 1945262417Shselasky (abs(strokep->instantaneous_dy) >= ATP_FAST_MOVEMENT_TRESHOLD)) { 1946262417Shselasky strokep->movement_dx <<= 1; 1947262417Shselasky strokep->movement_dy <<= 1; 1948199086Srpaulo } 1949199086Srpaulo 1950262417Shselasky strokep->cum_movement_x += strokep->movement_dx; 1951262417Shselasky strokep->cum_movement_y += strokep->movement_dy; 1952262417Shselasky 1953262417Shselasky return ((strokep->movement_dx != 0) || (strokep->movement_dy != 0)); 1954199086Srpaulo} 1955199086Srpaulo 1956262417Shselasky/* 1957262417Shselasky * Terminate a stroke. Aside from immature strokes, a slide or touch is 1958262417Shselasky * retained as a zombies so as to reap all their termination siblings 1959262417Shselasky * together; this helps establish the number of fingers involved at the 1960262417Shselasky * end of a multi-touch gesture. 1961262417Shselasky */ 1962262417Shselaskystatic void 1963262417Shselaskyatp_terminate_stroke(struct atp_softc *sc, atp_stroke_t *strokep) 1964199086Srpaulo{ 1965262417Shselasky if (strokep->flags & ATSF_ZOMBIE) 1966262417Shselasky return; 1967199086Srpaulo 1968262417Shselasky /* Drop immature strokes rightaway. */ 1969262417Shselasky if (strokep->age <= atp_stroke_maturity_threshold) { 1970262417Shselasky atp_free_stroke(sc, strokep); 1971262417Shselasky return; 1972262417Shselasky } 1973199086Srpaulo 1974262417Shselasky strokep->flags |= ATSF_ZOMBIE; 1975262417Shselasky sc->sc_state |= ATP_ZOMBIES_EXIST; 1976262417Shselasky 1977262417Shselasky callout_reset(&sc->sc_callout, ATP_ZOMBIE_STROKE_REAP_INTERVAL, 1978262417Shselasky atp_reap_sibling_zombies, sc); 1979262417Shselasky 1980262417Shselasky /* 1981262417Shselasky * Reset the double-click-n-drag at the termination of any 1982262417Shselasky * slide stroke. 1983262417Shselasky */ 1984262417Shselasky if (strokep->type == ATP_STROKE_SLIDE) 1985262417Shselasky sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 1986199086Srpaulo} 1987199086Srpaulo 1988262417Shselaskystatic boolean_t 1989262417Shselaskyatp_is_horizontal_scroll(const atp_stroke_t *strokep) 1990262417Shselasky{ 1991262417Shselasky if (abs(strokep->cum_movement_x) < atp_slide_min_movement) 1992262417Shselasky return (false); 1993262417Shselasky if (strokep->cum_movement_y == 0) 1994262417Shselasky return (true); 1995262417Shselasky return (abs(strokep->cum_movement_x / strokep->cum_movement_y) >= 4); 1996262417Shselasky} 1997262417Shselasky 1998262417Shselaskystatic boolean_t 1999262417Shselaskyatp_is_vertical_scroll(const atp_stroke_t *strokep) 2000262417Shselasky{ 2001262417Shselasky if (abs(strokep->cum_movement_y) < atp_slide_min_movement) 2002262417Shselasky return (false); 2003262417Shselasky if (strokep->cum_movement_x == 0) 2004262417Shselasky return (true); 2005262417Shselasky return (abs(strokep->cum_movement_y / strokep->cum_movement_x) >= 4); 2006262417Shselasky} 2007262417Shselasky 2008199086Srpaulostatic void 2009262417Shselaskyatp_reap_sibling_zombies(void *arg) 2010199086Srpaulo{ 2011262417Shselasky struct atp_softc *sc = (struct atp_softc *)arg; 2012262417Shselasky u_int8_t n_touches_reaped = 0; 2013262417Shselasky u_int8_t n_slides_reaped = 0; 2014262417Shselasky u_int8_t n_horizontal_scrolls = 0; 2015262417Shselasky u_int8_t n_vertical_scrolls = 0; 2016262417Shselasky int horizontal_scroll = 0; 2017262417Shselasky int vertical_scroll = 0; 2018262417Shselasky atp_stroke_t *strokep; 2019262417Shselasky atp_stroke_t *strokep_next; 2020199086Srpaulo 2021262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "\n"); 2022199086Srpaulo 2023262417Shselasky TAILQ_FOREACH_SAFE(strokep, &sc->sc_stroke_used, entry, strokep_next) { 2024262417Shselasky if ((strokep->flags & ATSF_ZOMBIE) == 0) 2025199086Srpaulo continue; 2026199086Srpaulo 2027262417Shselasky if (strokep->type == ATP_STROKE_TOUCH) { 2028262417Shselasky n_touches_reaped++; 2029199086Srpaulo } else { 2030262417Shselasky n_slides_reaped++; 2031199086Srpaulo 2032262417Shselasky if (atp_is_horizontal_scroll(strokep)) { 2033262417Shselasky n_horizontal_scrolls++; 2034262417Shselasky horizontal_scroll += strokep->cum_movement_x; 2035262417Shselasky } else if (atp_is_vertical_scroll(strokep)) { 2036262417Shselasky n_vertical_scrolls++; 2037262417Shselasky vertical_scroll += strokep->cum_movement_y; 2038262417Shselasky } 2039199086Srpaulo } 2040199086Srpaulo 2041262417Shselasky atp_free_stroke(sc, strokep); 2042262417Shselasky } 2043199086Srpaulo 2044262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "reaped %u zombies\n", 2045262417Shselasky n_touches_reaped + n_slides_reaped); 2046262417Shselasky sc->sc_state &= ~ATP_ZOMBIES_EXIST; 2047199086Srpaulo 2048262417Shselasky /* No further processing necessary if physical button is depressed. */ 2049262417Shselasky if (sc->sc_ibtn != 0) 2050262417Shselasky return; 2051199086Srpaulo 2052262417Shselasky if ((n_touches_reaped == 0) && (n_slides_reaped == 0)) 2053262417Shselasky return; 2054199086Srpaulo 2055262417Shselasky /* Add a pair of virtual button events (button-down and button-up) if 2056262417Shselasky * the physical button isn't pressed. */ 2057262417Shselasky if (n_touches_reaped != 0) { 2058262417Shselasky if (n_touches_reaped < atp_tap_minimum) 2059262417Shselasky return; 2060199086Srpaulo 2061262417Shselasky switch (n_touches_reaped) { 2062262417Shselasky case 1: 2063262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON1DOWN); 2064262417Shselasky microtime(&sc->sc_touch_reap_time); /* remember this time */ 2065262417Shselasky break; 2066262417Shselasky case 2: 2067262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON3DOWN); 2068262417Shselasky break; 2069262417Shselasky case 3: 2070262417Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON2DOWN); 2071262417Shselasky break; 2072262417Shselasky default: 2073262417Shselasky /* we handle taps of only up to 3 fingers */ 2074262439Shselasky return; 2075199086Srpaulo } 2076262417Shselasky atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ 2077262417Shselasky 2078262439Shselasky } else if ((n_slides_reaped == 2) && (n_horizontal_scrolls == 2)) { 2079262439Shselasky if (horizontal_scroll < 0) 2080262439Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON4DOWN); 2081262439Shselasky else 2082262439Shselasky atp_add_to_queue(sc, 0, 0, 0, MOUSE_BUTTON5DOWN); 2083262439Shselasky atp_add_to_queue(sc, 0, 0, 0, 0); /* button release */ 2084199086Srpaulo } 2085262417Shselasky} 2086199086Srpaulo 2087262417Shselasky/* Switch a given touch stroke to being a slide. */ 2088262417Shselaskystatic void 2089262417Shselaskyatp_convert_to_slide(struct atp_softc *sc, atp_stroke_t *strokep) 2090262417Shselasky{ 2091262417Shselasky strokep->type = ATP_STROKE_SLIDE; 2092262417Shselasky 2093262417Shselasky /* Are we at the beginning of a double-click-n-drag? */ 2094262417Shselasky if ((sc->sc_n_strokes == 1) && 2095262417Shselasky ((sc->sc_state & ATP_ZOMBIES_EXIST) == 0) && 2096262417Shselasky timevalcmp(&strokep->ctime, &sc->sc_touch_reap_time, >)) { 2097262417Shselasky struct timeval delta; 2098262417Shselasky struct timeval window = { 2099262417Shselasky atp_double_tap_threshold / 1000000, 2100262417Shselasky atp_double_tap_threshold % 1000000 2101262417Shselasky }; 2102262417Shselasky 2103262417Shselasky delta = strokep->ctime; 2104262417Shselasky timevalsub(&delta, &sc->sc_touch_reap_time); 2105262417Shselasky if (timevalcmp(&delta, &window, <=)) 2106262417Shselasky sc->sc_state |= ATP_DOUBLE_TAP_DRAG; 2107262417Shselasky } 2108199086Srpaulo} 2109199086Srpaulo 2110262417Shselaskystatic void 2111262417Shselaskyatp_reset_buf(struct atp_softc *sc) 2112262417Shselasky{ 2113262417Shselasky /* reset read queue */ 2114262417Shselasky usb_fifo_reset(sc->sc_fifo.fp[USB_FIFO_RX]); 2115262417Shselasky} 2116199086Srpaulo 2117262417Shselaskystatic void 2118262417Shselaskyatp_add_to_queue(struct atp_softc *sc, int dx, int dy, int dz, 2119262417Shselasky uint32_t buttons_in) 2120262417Shselasky{ 2121262417Shselasky uint32_t buttons_out; 2122262417Shselasky uint8_t buf[8]; 2123199086Srpaulo 2124262417Shselasky dx = imin(dx, 254); dx = imax(dx, -256); 2125262417Shselasky dy = imin(dy, 254); dy = imax(dy, -256); 2126262417Shselasky dz = imin(dz, 126); dz = imax(dz, -128); 2127199086Srpaulo 2128262417Shselasky buttons_out = MOUSE_MSC_BUTTONS; 2129262417Shselasky if (buttons_in & MOUSE_BUTTON1DOWN) 2130262417Shselasky buttons_out &= ~MOUSE_MSC_BUTTON1UP; 2131262417Shselasky else if (buttons_in & MOUSE_BUTTON2DOWN) 2132262417Shselasky buttons_out &= ~MOUSE_MSC_BUTTON2UP; 2133262417Shselasky else if (buttons_in & MOUSE_BUTTON3DOWN) 2134262417Shselasky buttons_out &= ~MOUSE_MSC_BUTTON3UP; 2135262417Shselasky 2136262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "dx=%d, dy=%d, buttons=%x\n", 2137262417Shselasky dx, dy, buttons_out); 2138262417Shselasky 2139262417Shselasky /* Encode the mouse data in standard format; refer to mouse(4) */ 2140262417Shselasky buf[0] = sc->sc_mode.syncmask[1]; 2141262417Shselasky buf[0] |= buttons_out; 2142262417Shselasky buf[1] = dx >> 1; 2143262417Shselasky buf[2] = dy >> 1; 2144262417Shselasky buf[3] = dx - (dx >> 1); 2145262417Shselasky buf[4] = dy - (dy >> 1); 2146262417Shselasky /* Encode extra bytes for level 1 */ 2147262417Shselasky if (sc->sc_mode.level == 1) { 2148262417Shselasky buf[5] = dz >> 1; 2149262417Shselasky buf[6] = dz - (dz >> 1); 2150262417Shselasky buf[7] = (((~buttons_in) >> 3) & MOUSE_SYS_EXTBUTTONS); 2151262417Shselasky } 2152262417Shselasky 2153262417Shselasky usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, 2154262417Shselasky sc->sc_mode.packetsize, 1); 2155262417Shselasky} 2156262417Shselasky 2157199086Srpaulostatic int 2158199086Srpauloatp_probe(device_t self) 2159199086Srpaulo{ 2160199086Srpaulo struct usb_attach_arg *uaa = device_get_ivars(self); 2161199086Srpaulo 2162199086Srpaulo if (uaa->usb_mode != USB_MODE_HOST) 2163199086Srpaulo return (ENXIO); 2164199086Srpaulo 2165262417Shselasky if (uaa->info.bInterfaceClass != UICLASS_HID) 2166199086Srpaulo return (ENXIO); 2167262417Shselasky /* 2168262417Shselasky * Note: for some reason, the check 2169262417Shselasky * (uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) doesn't hold true 2170262417Shselasky * for wellspring trackpads, so we've removed it from the common path. 2171262417Shselasky */ 2172199086Srpaulo 2173262417Shselasky if ((usbd_lookup_id_by_uaa(fg_devs, sizeof(fg_devs), uaa)) == 0) 2174262417Shselasky return ((uaa->info.bInterfaceProtocol == UIPROTO_MOUSE) ? 2175262417Shselasky 0 : ENXIO); 2176262417Shselasky 2177262417Shselasky if ((usbd_lookup_id_by_uaa(wsp_devs, sizeof(wsp_devs), uaa)) == 0) 2178262417Shselasky if (uaa->info.bIfaceIndex == WELLSPRING_INTERFACE_INDEX) 2179262417Shselasky return (0); 2180262417Shselasky 2181262417Shselasky return (ENXIO); 2182199086Srpaulo} 2183199086Srpaulo 2184199086Srpaulostatic int 2185199086Srpauloatp_attach(device_t dev) 2186199086Srpaulo{ 2187262417Shselasky struct atp_softc *sc = device_get_softc(dev); 2188199086Srpaulo struct usb_attach_arg *uaa = device_get_ivars(dev); 2189199086Srpaulo usb_error_t err; 2190262417Shselasky void *descriptor_ptr = NULL; 2191262417Shselasky uint16_t descriptor_len; 2192262417Shselasky unsigned long di; 2193199086Srpaulo 2194199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "sc=%p\n", sc); 2195199086Srpaulo 2196199086Srpaulo sc->sc_dev = dev; 2197199086Srpaulo sc->sc_usb_device = uaa->device; 2198199086Srpaulo 2199262417Shselasky /* Get HID descriptor */ 2200262417Shselasky if (usbd_req_get_hid_desc(uaa->device, NULL, &descriptor_ptr, 2201262417Shselasky &descriptor_len, M_TEMP, uaa->info.bIfaceIndex) != 2202262417Shselasky USB_ERR_NORMAL_COMPLETION) 2203262417Shselasky return (ENXIO); 2204262417Shselasky 2205262417Shselasky /* Get HID report descriptor length */ 2206262417Shselasky sc->sc_expected_sensor_data_len = hid_report_size(descriptor_ptr, 2207262417Shselasky descriptor_len, hid_input, NULL); 2208262417Shselasky free(descriptor_ptr, M_TEMP); 2209262417Shselasky 2210262417Shselasky if ((sc->sc_expected_sensor_data_len <= 0) || 2211262417Shselasky (sc->sc_expected_sensor_data_len > ATP_SENSOR_DATA_BUF_MAX)) { 2212262417Shselasky DPRINTF("atp_attach: datalength invalid or too large: %d\n", 2213262417Shselasky sc->sc_expected_sensor_data_len); 2214262417Shselasky return (ENXIO); 2215262417Shselasky } 2216262417Shselasky 2217199086Srpaulo /* 2218199086Srpaulo * By default the touchpad behaves like an HID device, sending 2219199086Srpaulo * packets with reportID = 2. Such reports contain only 2220199086Srpaulo * limited information--they encode movement deltas and button 2221199086Srpaulo * events,--but do not include data from the pressure 2222199086Srpaulo * sensors. The device input mode can be switched from HID 2223199086Srpaulo * reports to raw sensor data using vendor-specific USB 2224262417Shselasky * control commands. 2225199086Srpaulo */ 2226262417Shselasky if ((err = atp_set_device_mode(sc, RAW_SENSOR_MODE)) != 0) { 2227199086Srpaulo DPRINTF("failed to set mode to 'RAW_SENSOR' (%d)\n", err); 2228199086Srpaulo return (ENXIO); 2229199086Srpaulo } 2230199086Srpaulo 2231199086Srpaulo mtx_init(&sc->sc_mutex, "atpmtx", NULL, MTX_DEF | MTX_RECURSE); 2232199086Srpaulo 2233262417Shselasky di = USB_GET_DRIVER_INFO(uaa); 2234262417Shselasky 2235262417Shselasky sc->sc_family = DECODE_FAMILY_FROM_DRIVER_INFO(di); 2236262417Shselasky 2237262417Shselasky switch(sc->sc_family) { 2238262417Shselasky case TRACKPAD_FAMILY_FOUNTAIN_GEYSER: 2239262417Shselasky sc->sc_params = 2240262417Shselasky &fg_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; 2241262417Shselasky sc->sensor_data_interpreter = fg_interpret_sensor_data; 2242262417Shselasky break; 2243262417Shselasky case TRACKPAD_FAMILY_WELLSPRING: 2244262417Shselasky sc->sc_params = 2245262417Shselasky &wsp_dev_params[DECODE_PRODUCT_FROM_DRIVER_INFO(di)]; 2246262417Shselasky sc->sensor_data_interpreter = wsp_interpret_sensor_data; 2247262417Shselasky break; 2248262417Shselasky default: 2249262417Shselasky goto detach; 2250262417Shselasky } 2251262417Shselasky 2252199086Srpaulo err = usbd_transfer_setup(uaa->device, 2253262417Shselasky &uaa->info.bIfaceIndex, sc->sc_xfer, atp_xfer_config, 2254199086Srpaulo ATP_N_TRANSFER, sc, &sc->sc_mutex); 2255199086Srpaulo if (err) { 2256199086Srpaulo DPRINTF("error=%s\n", usbd_errstr(err)); 2257199086Srpaulo goto detach; 2258199086Srpaulo } 2259199086Srpaulo 2260199086Srpaulo if (usb_fifo_attach(sc->sc_usb_device, sc, &sc->sc_mutex, 2261262417Shselasky &atp_fifo_methods, &sc->sc_fifo, 2262262417Shselasky device_get_unit(dev), -1, uaa->info.bIfaceIndex, 2263262417Shselasky UID_ROOT, GID_OPERATOR, 0644)) { 2264199086Srpaulo goto detach; 2265199086Srpaulo } 2266199086Srpaulo 2267199086Srpaulo device_set_usb_desc(dev); 2268199086Srpaulo 2269199086Srpaulo sc->sc_hw.buttons = 3; 2270199086Srpaulo sc->sc_hw.iftype = MOUSE_IF_USB; 2271199086Srpaulo sc->sc_hw.type = MOUSE_PAD; 2272199086Srpaulo sc->sc_hw.model = MOUSE_MODEL_GENERIC; 2273199086Srpaulo sc->sc_hw.hwid = 0; 2274199086Srpaulo sc->sc_mode.protocol = MOUSE_PROTO_MSC; 2275199086Srpaulo sc->sc_mode.rate = -1; 2276199086Srpaulo sc->sc_mode.resolution = MOUSE_RES_UNKNOWN; 2277199086Srpaulo sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 2278199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 2279199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 2280262417Shselasky sc->sc_mode.accelfactor = 0; 2281262417Shselasky sc->sc_mode.level = 0; 2282199086Srpaulo 2283199086Srpaulo sc->sc_state = 0; 2284262417Shselasky sc->sc_ibtn = 0; 2285199086Srpaulo 2286262417Shselasky callout_init_mtx(&sc->sc_callout, &sc->sc_mutex, 0); 2287199086Srpaulo 2288199086Srpaulo return (0); 2289199086Srpaulo 2290199086Srpaulodetach: 2291199086Srpaulo atp_detach(dev); 2292199086Srpaulo return (ENOMEM); 2293199086Srpaulo} 2294199086Srpaulo 2295199086Srpaulostatic int 2296199086Srpauloatp_detach(device_t dev) 2297199086Srpaulo{ 2298199086Srpaulo struct atp_softc *sc; 2299199086Srpaulo 2300199086Srpaulo sc = device_get_softc(dev); 2301262417Shselasky atp_set_device_mode(sc, HID_MODE); 2302262417Shselasky 2303262417Shselasky mtx_lock(&sc->sc_mutex); 2304262417Shselasky callout_drain(&sc->sc_callout); 2305262417Shselasky if (sc->sc_state & ATP_ENABLED) 2306199086Srpaulo atp_disable(sc); 2307262417Shselasky mtx_unlock(&sc->sc_mutex); 2308199086Srpaulo 2309199086Srpaulo usb_fifo_detach(&sc->sc_fifo); 2310199086Srpaulo 2311199086Srpaulo usbd_transfer_unsetup(sc->sc_xfer, ATP_N_TRANSFER); 2312199086Srpaulo 2313199086Srpaulo mtx_destroy(&sc->sc_mutex); 2314199086Srpaulo 2315199086Srpaulo return (0); 2316199086Srpaulo} 2317199086Srpaulo 2318199086Srpaulostatic void 2319199086Srpauloatp_intr(struct usb_xfer *xfer, usb_error_t error) 2320199086Srpaulo{ 2321199086Srpaulo struct atp_softc *sc = usbd_xfer_softc(xfer); 2322199086Srpaulo struct usb_page_cache *pc; 2323262417Shselasky int len; 2324199086Srpaulo 2325199086Srpaulo usbd_xfer_status(xfer, &len, NULL, NULL, NULL); 2326199086Srpaulo 2327199086Srpaulo switch (USB_GET_STATE(xfer)) { 2328199086Srpaulo case USB_ST_TRANSFERRED: 2329199086Srpaulo pc = usbd_xfer_get_frame(xfer, 0); 2330262417Shselasky usbd_copy_out(pc, 0, sc->sc_sensor_data, len); 2331262417Shselasky if (len < sc->sc_expected_sensor_data_len) { 2332262417Shselasky /* make sure we don't process old data */ 2333262417Shselasky memset(sc->sc_sensor_data + len, 0, 2334262417Shselasky sc->sc_expected_sensor_data_len - len); 2335199086Srpaulo } 2336199086Srpaulo 2337262417Shselasky sc->sc_status.flags &= ~(MOUSE_STDBUTTONSCHANGED | 2338262417Shselasky MOUSE_POSCHANGED); 2339262417Shselasky sc->sc_status.obutton = sc->sc_status.button; 2340199086Srpaulo 2341262417Shselasky (sc->sensor_data_interpreter)(sc, len); 2342199086Srpaulo 2343199086Srpaulo if (sc->sc_status.button != 0) { 2344199086Srpaulo /* Reset DOUBLE_TAP_N_DRAG if the button is pressed. */ 2345199086Srpaulo sc->sc_state &= ~ATP_DOUBLE_TAP_DRAG; 2346199086Srpaulo } else if (sc->sc_state & ATP_DOUBLE_TAP_DRAG) { 2347199086Srpaulo /* Assume a button-press with DOUBLE_TAP_N_DRAG. */ 2348199086Srpaulo sc->sc_status.button = MOUSE_BUTTON1DOWN; 2349199086Srpaulo } 2350199086Srpaulo 2351199086Srpaulo sc->sc_status.flags |= 2352262417Shselasky sc->sc_status.button ^ sc->sc_status.obutton; 2353199086Srpaulo if (sc->sc_status.flags & MOUSE_STDBUTTONSCHANGED) { 2354262417Shselasky DPRINTFN(ATP_LLEVEL_INFO, "button %s\n", 2355262417Shselasky ((sc->sc_status.button & MOUSE_BUTTON1DOWN) ? 2356262417Shselasky "pressed" : "released")); 2357199086Srpaulo } 2358199086Srpaulo 2359262417Shselasky if (sc->sc_status.flags & (MOUSE_POSCHANGED | 2360262417Shselasky MOUSE_STDBUTTONSCHANGED)) { 2361199086Srpaulo 2362262417Shselasky atp_stroke_t *strokep; 2363262417Shselasky u_int8_t n_movements = 0; 2364262417Shselasky int dx = 0; 2365262417Shselasky int dy = 0; 2366262439Shselasky int dz = 0; 2367199086Srpaulo 2368262417Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 2369262439Shselasky if (strokep->flags & ATSF_ZOMBIE) 2370262439Shselasky continue; 2371262439Shselasky 2372262417Shselasky dx += strokep->movement_dx; 2373262417Shselasky dy += strokep->movement_dy; 2374262417Shselasky if (strokep->movement_dx || 2375262417Shselasky strokep->movement_dy) 2376199086Srpaulo n_movements++; 2377199086Srpaulo } 2378199086Srpaulo 2379262417Shselasky /* average movement if multiple strokes record motion.*/ 2380262417Shselasky if (n_movements > 1) { 2381262417Shselasky dx /= (int)n_movements; 2382262417Shselasky dy /= (int)n_movements; 2383262417Shselasky } 2384262417Shselasky 2385262439Shselasky /* detect multi-finger vertical scrolls */ 2386262439Shselasky if (n_movements >= 2) { 2387262439Shselasky boolean_t all_vertical_scrolls = true; 2388262439Shselasky TAILQ_FOREACH(strokep, &sc->sc_stroke_used, entry) { 2389262439Shselasky if (strokep->flags & ATSF_ZOMBIE) 2390262439Shselasky continue; 2391262439Shselasky 2392262439Shselasky if (!atp_is_vertical_scroll(strokep)) 2393262439Shselasky all_vertical_scrolls = false; 2394262439Shselasky } 2395262439Shselasky if (all_vertical_scrolls) { 2396262439Shselasky dz = dy; 2397262439Shselasky dy = dx = 0; 2398262439Shselasky } 2399262439Shselasky } 2400262439Shselasky 2401199086Srpaulo sc->sc_status.dx += dx; 2402199086Srpaulo sc->sc_status.dy += dy; 2403262439Shselasky sc->sc_status.dz += dz; 2404262439Shselasky atp_add_to_queue(sc, dx, -dy, -dz, sc->sc_status.button); 2405199086Srpaulo } 2406199086Srpaulo 2407199086Srpaulo case USB_ST_SETUP: 2408199086Srpaulo tr_setup: 2409199086Srpaulo /* check if we can put more data into the FIFO */ 2410262417Shselasky if (usb_fifo_put_bytes_max(sc->sc_fifo.fp[USB_FIFO_RX]) != 0) { 2411199086Srpaulo usbd_xfer_set_frame_len(xfer, 0, 2412262417Shselasky sc->sc_expected_sensor_data_len); 2413199086Srpaulo usbd_transfer_submit(xfer); 2414199086Srpaulo } 2415199086Srpaulo break; 2416199086Srpaulo 2417199086Srpaulo default: /* Error */ 2418199086Srpaulo if (error != USB_ERR_CANCELLED) { 2419199086Srpaulo /* try clear stall first */ 2420199086Srpaulo usbd_xfer_set_stall(xfer); 2421199086Srpaulo goto tr_setup; 2422199086Srpaulo } 2423199086Srpaulo break; 2424199086Srpaulo } 2425199086Srpaulo} 2426199086Srpaulo 2427199086Srpaulostatic void 2428199086Srpauloatp_start_read(struct usb_fifo *fifo) 2429199086Srpaulo{ 2430199086Srpaulo struct atp_softc *sc = usb_fifo_softc(fifo); 2431199086Srpaulo int rate; 2432199086Srpaulo 2433199086Srpaulo /* Check if we should override the default polling interval */ 2434199086Srpaulo rate = sc->sc_pollrate; 2435199086Srpaulo /* Range check rate */ 2436199086Srpaulo if (rate > 1000) 2437199086Srpaulo rate = 1000; 2438199086Srpaulo /* Check for set rate */ 2439199086Srpaulo if ((rate > 0) && (sc->sc_xfer[ATP_INTR_DT] != NULL)) { 2440199086Srpaulo /* Stop current transfer, if any */ 2441199086Srpaulo usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]); 2442199086Srpaulo /* Set new interval */ 2443199086Srpaulo usbd_xfer_set_interval(sc->sc_xfer[ATP_INTR_DT], 1000 / rate); 2444199086Srpaulo /* Only set pollrate once */ 2445199086Srpaulo sc->sc_pollrate = 0; 2446199086Srpaulo } 2447199086Srpaulo 2448199086Srpaulo usbd_transfer_start(sc->sc_xfer[ATP_INTR_DT]); 2449199086Srpaulo} 2450199086Srpaulo 2451199086Srpaulostatic void 2452199086Srpauloatp_stop_read(struct usb_fifo *fifo) 2453199086Srpaulo{ 2454199086Srpaulo struct atp_softc *sc = usb_fifo_softc(fifo); 2455199086Srpaulo usbd_transfer_stop(sc->sc_xfer[ATP_INTR_DT]); 2456199086Srpaulo} 2457199086Srpaulo 2458199086Srpaulostatic int 2459199086Srpauloatp_open(struct usb_fifo *fifo, int fflags) 2460199086Srpaulo{ 2461262417Shselasky struct atp_softc *sc = usb_fifo_softc(fifo); 2462199086Srpaulo 2463262417Shselasky /* check for duplicate open, should not happen */ 2464262417Shselasky if (sc->sc_fflags & fflags) 2465262417Shselasky return (EBUSY); 2466262417Shselasky 2467262417Shselasky /* check for first open */ 2468262417Shselasky if (sc->sc_fflags == 0) { 2469199086Srpaulo int rc; 2470262417Shselasky if ((rc = atp_enable(sc)) != 0) 2471262417Shselasky return (rc); 2472262417Shselasky } 2473199086Srpaulo 2474262417Shselasky if (fflags & FREAD) { 2475199086Srpaulo if (usb_fifo_alloc_buffer(fifo, 2476262417Shselasky ATP_FIFO_BUF_SIZE, ATP_FIFO_QUEUE_MAXLEN)) { 2477199086Srpaulo return (ENOMEM); 2478199086Srpaulo } 2479199086Srpaulo } 2480199086Srpaulo 2481262417Shselasky sc->sc_fflags |= (fflags & (FREAD | FWRITE)); 2482199086Srpaulo return (0); 2483199086Srpaulo} 2484199086Srpaulo 2485199086Srpaulostatic void 2486199086Srpauloatp_close(struct usb_fifo *fifo, int fflags) 2487199086Srpaulo{ 2488262417Shselasky struct atp_softc *sc = usb_fifo_softc(fifo); 2489262417Shselasky if (fflags & FREAD) 2490262417Shselasky usb_fifo_free_buffer(fifo); 2491199086Srpaulo 2492262417Shselasky sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); 2493262417Shselasky if (sc->sc_fflags == 0) { 2494199086Srpaulo atp_disable(sc); 2495199086Srpaulo } 2496199086Srpaulo} 2497199086Srpaulo 2498262417Shselaskystatic int 2499199086Srpauloatp_ioctl(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags) 2500199086Srpaulo{ 2501199086Srpaulo struct atp_softc *sc = usb_fifo_softc(fifo); 2502199086Srpaulo mousemode_t mode; 2503199086Srpaulo int error = 0; 2504199086Srpaulo 2505199086Srpaulo mtx_lock(&sc->sc_mutex); 2506199086Srpaulo 2507199086Srpaulo switch(cmd) { 2508199086Srpaulo case MOUSE_GETHWINFO: 2509199086Srpaulo *(mousehw_t *)addr = sc->sc_hw; 2510199086Srpaulo break; 2511199086Srpaulo case MOUSE_GETMODE: 2512199086Srpaulo *(mousemode_t *)addr = sc->sc_mode; 2513199086Srpaulo break; 2514199086Srpaulo case MOUSE_SETMODE: 2515199086Srpaulo mode = *(mousemode_t *)addr; 2516199086Srpaulo 2517199086Srpaulo if (mode.level == -1) 2518199086Srpaulo /* Don't change the current setting */ 2519199086Srpaulo ; 2520199086Srpaulo else if ((mode.level < 0) || (mode.level > 1)) { 2521199086Srpaulo error = EINVAL; 2522262417Shselasky break; 2523199086Srpaulo } 2524199086Srpaulo sc->sc_mode.level = mode.level; 2525199086Srpaulo sc->sc_pollrate = mode.rate; 2526199086Srpaulo sc->sc_hw.buttons = 3; 2527199086Srpaulo 2528199086Srpaulo if (sc->sc_mode.level == 0) { 2529262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_MSC; 2530262417Shselasky sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 2531199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 2532199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 2533199086Srpaulo } else if (sc->sc_mode.level == 1) { 2534262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 2535262417Shselasky sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 2536199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 2537199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 2538199086Srpaulo } 2539199086Srpaulo atp_reset_buf(sc); 2540199086Srpaulo break; 2541199086Srpaulo case MOUSE_GETLEVEL: 2542199086Srpaulo *(int *)addr = sc->sc_mode.level; 2543199086Srpaulo break; 2544199086Srpaulo case MOUSE_SETLEVEL: 2545262417Shselasky if ((*(int *)addr < 0) || (*(int *)addr > 1)) { 2546199086Srpaulo error = EINVAL; 2547262417Shselasky break; 2548199086Srpaulo } 2549199086Srpaulo sc->sc_mode.level = *(int *)addr; 2550199086Srpaulo sc->sc_hw.buttons = 3; 2551199086Srpaulo 2552199086Srpaulo if (sc->sc_mode.level == 0) { 2553262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_MSC; 2554262417Shselasky sc->sc_mode.packetsize = MOUSE_MSC_PACKETSIZE; 2555199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_MSC_SYNCMASK; 2556199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_MSC_SYNC; 2557199086Srpaulo } else if (sc->sc_mode.level == 1) { 2558262417Shselasky sc->sc_mode.protocol = MOUSE_PROTO_SYSMOUSE; 2559262417Shselasky sc->sc_mode.packetsize = MOUSE_SYS_PACKETSIZE; 2560199086Srpaulo sc->sc_mode.syncmask[0] = MOUSE_SYS_SYNCMASK; 2561199086Srpaulo sc->sc_mode.syncmask[1] = MOUSE_SYS_SYNC; 2562199086Srpaulo } 2563199086Srpaulo atp_reset_buf(sc); 2564199086Srpaulo break; 2565199086Srpaulo case MOUSE_GETSTATUS: { 2566199086Srpaulo mousestatus_t *status = (mousestatus_t *)addr; 2567199086Srpaulo 2568199086Srpaulo *status = sc->sc_status; 2569199086Srpaulo sc->sc_status.obutton = sc->sc_status.button; 2570199086Srpaulo sc->sc_status.button = 0; 2571262417Shselasky sc->sc_status.dx = 0; 2572262417Shselasky sc->sc_status.dy = 0; 2573262417Shselasky sc->sc_status.dz = 0; 2574199086Srpaulo 2575199086Srpaulo if (status->dx || status->dy || status->dz) 2576199086Srpaulo status->flags |= MOUSE_POSCHANGED; 2577199086Srpaulo if (status->button != status->obutton) 2578199086Srpaulo status->flags |= MOUSE_BUTTONSCHANGED; 2579199086Srpaulo break; 2580199086Srpaulo } 2581262417Shselasky 2582199086Srpaulo default: 2583199086Srpaulo error = ENOTTY; 2584262417Shselasky break; 2585199086Srpaulo } 2586199086Srpaulo 2587199086Srpaulo mtx_unlock(&sc->sc_mutex); 2588199086Srpaulo return (error); 2589199086Srpaulo} 2590199086Srpaulo 2591199086Srpaulostatic int 2592199086Srpauloatp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS) 2593199086Srpaulo{ 2594199086Srpaulo int error; 2595199086Srpaulo u_int tmp; 2596199086Srpaulo 2597199086Srpaulo tmp = atp_mickeys_scale_factor; 2598199086Srpaulo error = sysctl_handle_int(oidp, &tmp, 0, req); 2599199086Srpaulo if (error != 0 || req->newptr == NULL) 2600199086Srpaulo return (error); 2601199086Srpaulo 2602262417Shselasky if (tmp == atp_mickeys_scale_factor) 2603199086Srpaulo return (0); /* no change */ 2604262417Shselasky if ((tmp == 0) || (tmp > (10 * ATP_SCALE_FACTOR))) 2605262417Shselasky return (EINVAL); 2606199086Srpaulo 2607199086Srpaulo atp_mickeys_scale_factor = tmp; 2608199086Srpaulo DPRINTFN(ATP_LLEVEL_INFO, "%s: resetting mickeys_scale_factor to %u\n", 2609199086Srpaulo ATP_DRIVER_NAME, tmp); 2610199086Srpaulo 2611199086Srpaulo return (0); 2612199086Srpaulo} 2613199086Srpaulo 2614262417Shselaskystatic devclass_t atp_devclass; 2615262417Shselasky 2616199086Srpaulostatic device_method_t atp_methods[] = { 2617199086Srpaulo DEVMETHOD(device_probe, atp_probe), 2618199086Srpaulo DEVMETHOD(device_attach, atp_attach), 2619199086Srpaulo DEVMETHOD(device_detach, atp_detach), 2620262417Shselasky 2621262417Shselasky DEVMETHOD_END 2622199086Srpaulo}; 2623199086Srpaulo 2624199086Srpaulostatic driver_t atp_driver = { 2625262417Shselasky .name = ATP_DRIVER_NAME, 2626233774Shselasky .methods = atp_methods, 2627262417Shselasky .size = sizeof(struct atp_softc) 2628199086Srpaulo}; 2629199086Srpaulo 2630199086SrpauloDRIVER_MODULE(atp, uhub, atp_driver, atp_devclass, NULL, 0); 2631199086SrpauloMODULE_DEPEND(atp, usb, 1, 1, 1); 2632212122SthompsaMODULE_VERSION(atp, 1); 2633292080SimpUSB_PNP_HOST_INFO(fg_devs); 2634292080SimpUSB_PNP_HOST_INFO(wsp_devs); 2635