From owner-svn-src-all@FreeBSD.ORG Wed Mar 12 08:04:09 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 1AF6C752; Wed, 12 Mar 2014 08:04:09 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 0557C84E; Wed, 12 Mar 2014 08:04:09 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s2C848Lf059073; Wed, 12 Mar 2014 08:04:08 GMT (envelope-from hselasky@svn.freebsd.org) Received: (from hselasky@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s2C848td059072; Wed, 12 Mar 2014 08:04:08 GMT (envelope-from hselasky@svn.freebsd.org) Message-Id: <201403120804.s2C848td059072@svn.freebsd.org> From: Hans Petter Selasky Date: Wed, 12 Mar 2014 08:04:08 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org Subject: svn commit: r263067 - stable/8/sys/dev/usb/input X-SVN-Group: stable-8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 12 Mar 2014 08:04:09 -0000 Author: hselasky Date: Wed Mar 12 08:04:08 2014 New Revision: 263067 URL: http://svnweb.freebsd.org/changeset/base/263067 Log: MFC r262417, r262439, r262454, r262455 and r262478: - Several updates and improvements to ATP driver. - Start effort merging WSP and ATP driver. Modified: stable/8/sys/dev/usb/input/atp.c Directory Properties: stable/8/sys/ (props changed) stable/8/sys/dev/ (props changed) stable/8/sys/dev/usb/ (props changed) Modified: stable/8/sys/dev/usb/input/atp.c ============================================================================== --- stable/8/sys/dev/usb/input/atp.c Wed Mar 12 08:00:20 2014 (r263066) +++ stable/8/sys/dev/usb/input/atp.c Wed Mar 12 08:04:08 2014 (r263067) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009 Rohit Grover + * Copyright (c) 2014 Rohit Grover * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,29 +24,64 @@ * SUCH DAMAGE. */ +/* + * Some tables, structures, definitions and constant values for the + * touchpad protocol has been copied from Linux's + * "drivers/input/mouse/bcm5974.c" which has the following copyright + * holders under GPLv2. All device specific code in this driver has + * been written from scratch. The decoding algorithm is based on + * output from FreeBSD's usbdump. + * + * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se) + * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com) + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net) + * Copyright (C) 2005 Stelian Pop (stelian@popies.net) + * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de) + * Copyright (C) 2005 Peter Osterlund (petero2@telia.com) + * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch) + * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch) + */ + +/* + * Author's note: 'atp' supports two distinct families of Apple trackpad + * products: the older Fountain/Geyser and the latest Wellspring trackpads. + * The first version made its appearance with FreeBSD 8 and worked only with + * the Fountain/Geyser hardware. A fork of this driver for Wellspring was + * contributed by Huang Wen Hui. This driver unifies the Wellspring effort + * and also improves upon the original work. + * + * I'm grateful to Stephan Scheunig, Angela Naegele, and Nokia IT-support + * for helping me with access to hardware. Thanks also go to Nokia for + * giving me an opportunity to do this work. + */ + #include __FBSDID("$FreeBSD$"); +#include +#include #include +#include #include #include -#include +#include #include #include #include -#include +#include +#include #include #include #include #include #include -#include -#include #include #include #include #include + #include "usbdevs.h" #define USB_DEBUG_VAR atp_debug @@ -54,6 +89,14 @@ __FBSDID("$FreeBSD$"); #include +#ifndef true +#define true 1 +#endif + +#ifndef false +#define false 0 +#endif + #define ATP_DRIVER_NAME "atp" /* @@ -61,17 +104,35 @@ __FBSDID("$FreeBSD$"); * `options' statements in the kernel configuration file. */ -/* The multiplier used to translate sensor reported positions to mickeys. */ +/* The divisor used to translate sensor reported positions to mickeys. */ #ifndef ATP_SCALE_FACTOR -#define ATP_SCALE_FACTOR 48 +#define ATP_SCALE_FACTOR 16 +#endif + +/* Threshold for small movement noise (in mickeys) */ +#ifndef ATP_SMALL_MOVEMENT_THRESHOLD +#define ATP_SMALL_MOVEMENT_THRESHOLD 30 +#endif + +/* Threshold of instantaneous deltas beyond which movement is considered fast.*/ +#ifndef ATP_FAST_MOVEMENT_TRESHOLD +#define ATP_FAST_MOVEMENT_TRESHOLD 150 #endif /* - * This is the age (in microseconds) beyond which a touch is - * considered to be a slide; and therefore a tap event isn't registered. + * This is the age in microseconds beyond which a touch is considered + * to be a slide; and therefore a tap event isn't registered. */ #ifndef ATP_TOUCH_TIMEOUT -#define ATP_TOUCH_TIMEOUT 125000 +#define ATP_TOUCH_TIMEOUT 125000 +#endif + +#ifndef ATP_IDLENESS_THRESHOLD +#define ATP_IDLENESS_THRESHOLD 10 +#endif + +#ifndef FG_SENSOR_NOISE_THRESHOLD +#define FG_SENSOR_NOISE_THRESHOLD 2 #endif /* @@ -82,39 +143,40 @@ __FBSDID("$FreeBSD$"); * tap events preceding the slide for such a gesture. */ #ifndef ATP_DOUBLE_TAP_N_DRAG_THRESHOLD -#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000 +#define ATP_DOUBLE_TAP_N_DRAG_THRESHOLD 200000 #endif /* - * The device provides us only with pressure readings from an array of - * X and Y sensors; for our algorithms, we need to interpret groups - * (typically pairs) of X and Y readings as being related to a single - * finger stroke. We can relate X and Y readings based on their times - * of incidence. The coincidence window should be at least 10000us - * since it is used against values from getmicrotime(), which has a - * precision of around 10ms. - */ -#ifndef ATP_COINCIDENCE_THRESHOLD -#define ATP_COINCIDENCE_THRESHOLD 40000 /* unit: microseconds */ -#if ATP_COINCIDENCE_THRESHOLD > 100000 -#error "ATP_COINCIDENCE_THRESHOLD too large" -#endif -#endif /* #ifndef ATP_COINCIDENCE_THRESHOLD */ + * The wait duration in ticks after losing a touch contact before + * zombied strokes are reaped and turned into button events. + */ +#define ATP_ZOMBIE_STROKE_REAP_INTERVAL (hz / 20) /* 50 ms */ + +/* The multiplier used to translate sensor reported positions to mickeys. */ +#define FG_SCALE_FACTOR 380 /* - * The wait duration (in microseconds) after losing a touch contact - * before zombied strokes are reaped and turned into button events. + * The movement threshold for a stroke; this is the maximum difference + * in position which will be resolved as a continuation of a stroke + * component. */ -#define ATP_ZOMBIE_STROKE_REAP_WINDOW 50000 -#if ATP_ZOMBIE_STROKE_REAP_WINDOW > 100000 -#error "ATP_ZOMBIE_STROKE_REAP_WINDOW too large" +#define FG_MAX_DELTA_MICKEYS ((3 * (FG_SCALE_FACTOR)) >> 1) + +/* Distance-squared threshold for matching a finger with a known stroke */ +#ifndef WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ +#define WSP_MAX_ALLOWED_MATCH_DISTANCE_SQ 1000000 #endif -/* end of driver specific options */ +/* Ignore pressure spans with cumulative press. below this value. */ +#define FG_PSPAN_MIN_CUM_PRESSURE 10 + +/* Maximum allowed width for pressure-spans.*/ +#define FG_PSPAN_MAX_WIDTH 4 +/* end of driver specific options */ /* Tunables */ -SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB atp"); +static SYSCTL_NODE(_hw_usb, OID_AUTO, atp, CTLFLAG_RW, 0, "USB ATP"); #ifdef USB_DEBUG enum atp_log_level { @@ -129,13 +191,14 @@ SYSCTL_INT(_hw_usb_atp, OID_AUTO, debug, #endif /* USB_DEBUG */ static u_int atp_touch_timeout = ATP_TOUCH_TIMEOUT; -SYSCTL_INT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW, &atp_touch_timeout, - 125000, "age threshold (in micros) for a touch"); +SYSCTL_UINT(_hw_usb_atp, OID_AUTO, touch_timeout, CTLFLAG_RW, + &atp_touch_timeout, 125000, "age threshold in microseconds for a touch"); static u_int atp_double_tap_threshold = ATP_DOUBLE_TAP_N_DRAG_THRESHOLD; -SYSCTL_INT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RW, +SYSCTL_UINT(_hw_usb_atp, OID_AUTO, double_tap_threshold, CTLFLAG_RW, &atp_double_tap_threshold, ATP_DOUBLE_TAP_N_DRAG_THRESHOLD, - "maximum time (in micros) between a double-tap"); + "maximum time in microseconds to allow association between a double-tap and " + "drag gesture"); static u_int atp_mickeys_scale_factor = ATP_SCALE_FACTOR; static int atp_sysctl_scale_factor_handler(SYSCTL_HANDLER_ARGS); @@ -143,264 +206,539 @@ SYSCTL_PROC(_hw_usb_atp, OID_AUTO, scale &atp_mickeys_scale_factor, sizeof(atp_mickeys_scale_factor), atp_sysctl_scale_factor_handler, "IU", "movement scale factor"); -static u_int atp_small_movement_threshold = ATP_SCALE_FACTOR >> 3; +static u_int atp_small_movement_threshold = ATP_SMALL_MOVEMENT_THRESHOLD; SYSCTL_UINT(_hw_usb_atp, OID_AUTO, small_movement, CTLFLAG_RW, - &atp_small_movement_threshold, ATP_SCALE_FACTOR >> 3, + &atp_small_movement_threshold, ATP_SMALL_MOVEMENT_THRESHOLD, "the small movement black-hole for filtering noise"); -/* - * The movement threshold for a stroke; this is the maximum difference - * in position which will be resolved as a continuation of a stroke - * component. - */ -static u_int atp_max_delta_mickeys = ((3 * ATP_SCALE_FACTOR) >> 1); -SYSCTL_UINT(_hw_usb_atp, OID_AUTO, max_delta_mickeys, CTLFLAG_RW, - &atp_max_delta_mickeys, ((3 * ATP_SCALE_FACTOR) >> 1), - "max. mickeys-delta which will match against an existing stroke"); + +static u_int atp_tap_minimum = 1; +SYSCTL_UINT(_hw_usb_atp, OID_AUTO, tap_minimum, CTLFLAG_RW, + &atp_tap_minimum, 1, "Minimum number of taps before detection"); + /* * Strokes which accumulate at least this amount of absolute movement * from the aggregate of their components are considered as * slides. Unit: mickeys. */ -static u_int atp_slide_min_movement = (ATP_SCALE_FACTOR >> 3); +static u_int atp_slide_min_movement = 2 * ATP_SMALL_MOVEMENT_THRESHOLD; SYSCTL_UINT(_hw_usb_atp, OID_AUTO, slide_min_movement, CTLFLAG_RW, - &atp_slide_min_movement, (ATP_SCALE_FACTOR >> 3), + &atp_slide_min_movement, 2 * ATP_SMALL_MOVEMENT_THRESHOLD, "strokes with at least this amt. of movement are considered slides"); /* * The minimum age of a stroke for it to be considered mature; this * helps filter movements (noise) from immature strokes. Units: interrupts. */ -static u_int atp_stroke_maturity_threshold = 2; +static u_int atp_stroke_maturity_threshold = 4; SYSCTL_UINT(_hw_usb_atp, OID_AUTO, stroke_maturity_threshold, CTLFLAG_RW, - &atp_stroke_maturity_threshold, 2, + &atp_stroke_maturity_threshold, 4, "the minimum age of a stroke for it to be considered mature"); -/* Accept pressure readings from sensors only if above this value. */ -static u_int atp_sensor_noise_threshold = 2; -SYSCTL_UINT(_hw_usb_atp, OID_AUTO, sensor_noise_threshold, CTLFLAG_RW, - &atp_sensor_noise_threshold, 2, - "accept pressure readings from sensors only if above this value"); +typedef enum atp_trackpad_family { + TRACKPAD_FAMILY_FOUNTAIN_GEYSER, + TRACKPAD_FAMILY_WELLSPRING, + TRACKPAD_FAMILY_MAX /* keep this at the tail end of the enumeration */ +} trackpad_family_t; + +enum fountain_geyser_product { + FOUNTAIN, + GEYSER1, + GEYSER1_17inch, + GEYSER2, + GEYSER3, + GEYSER4, + FOUNTAIN_GEYSER_PRODUCT_MAX /* keep this at the end */ +}; -/* Ignore pressure spans with cumulative press. below this value. */ -static u_int atp_pspan_min_cum_pressure = 10; -SYSCTL_UINT(_hw_usb_atp, OID_AUTO, pspan_min_cum_pressure, CTLFLAG_RW, - &atp_pspan_min_cum_pressure, 10, - "ignore pressure spans with cumulative press. below this value"); +enum wellspring_product { + WELLSPRING1, + WELLSPRING2, + WELLSPRING3, + WELLSPRING4, + WELLSPRING4A, + WELLSPRING5, + WELLSPRING6A, + WELLSPRING6, + WELLSPRING5A, + WELLSPRING7, + WELLSPRING7A, + WELLSPRING8, + WELLSPRING_PRODUCT_MAX /* keep this at the end of the enumeration */ +}; -/* Maximum allowed width for pressure-spans.*/ -static u_int atp_pspan_max_width = 4; -SYSCTL_UINT(_hw_usb_atp, OID_AUTO, pspan_max_width, CTLFLAG_RW, - &atp_pspan_max_width, 4, - "maximum allowed width (in sensors) for pressure-spans"); - -/* We support three payload protocols */ -typedef enum { - ATP_PROT_GEYSER1, - ATP_PROT_GEYSER2, - ATP_PROT_GEYSER3, -} atp_protocol; +/* trackpad header types */ +enum fountain_geyser_trackpad_type { + FG_TRACKPAD_TYPE_GEYSER1, + FG_TRACKPAD_TYPE_GEYSER2, + FG_TRACKPAD_TYPE_GEYSER3, + FG_TRACKPAD_TYPE_GEYSER4, +}; +enum wellspring_trackpad_type { + WSP_TRACKPAD_TYPE1, /* plain trackpad */ + WSP_TRACKPAD_TYPE2, /* button integrated in trackpad */ + WSP_TRACKPAD_TYPE3 /* additional header fields since June 2013 */ +}; -/* Define the various flavours of devices supported by this driver. */ -enum { - ATP_DEV_PARAMS_0, - ATP_DEV_PARAMS_PBOOK, - ATP_DEV_PARAMS_PBOOK_15A, - ATP_DEV_PARAMS_PBOOK_17, - ATP_N_DEV_PARAMS +/* + * Trackpad family and product and family are encoded together in the + * driver_info value associated with a trackpad product. + */ +#define N_PROD_BITS 8 /* Number of bits used to encode product */ +#define ENCODE_DRIVER_INFO(FAMILY, PROD) \ + (((FAMILY) << N_PROD_BITS) | (PROD)) +#define DECODE_FAMILY_FROM_DRIVER_INFO(INFO) ((INFO) >> N_PROD_BITS) +#define DECODE_PRODUCT_FROM_DRIVER_INFO(INFO) \ + ((INFO) & ((1 << N_PROD_BITS) - 1)) + +#define FG_DRIVER_INFO(PRODUCT) \ + ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_FOUNTAIN_GEYSER, PRODUCT) +#define WELLSPRING_DRIVER_INFO(PRODUCT) \ + ENCODE_DRIVER_INFO(TRACKPAD_FAMILY_WELLSPRING, PRODUCT) + +/* + * The following structure captures the state of a pressure span along + * an axis. Each contact with the touchpad results in separate + * pressure spans along the two axes. + */ +typedef struct fg_pspan { + u_int width; /* in units of sensors */ + u_int cum; /* cumulative compression (from all sensors) */ + u_int cog; /* center of gravity */ + u_int loc; /* location (scaled using the mickeys factor) */ + boolean_t matched; /* to track pspans as they match against strokes. */ +} fg_pspan; + +#define FG_MAX_PSPANS_PER_AXIS 3 +#define FG_MAX_STROKES (2 * FG_MAX_PSPANS_PER_AXIS) + +#define WELLSPRING_INTERFACE_INDEX 1 + +/* trackpad finger data offsets, le16-aligned */ +#define WSP_TYPE1_FINGER_DATA_OFFSET (13 * 2) +#define WSP_TYPE2_FINGER_DATA_OFFSET (15 * 2) +#define WSP_TYPE3_FINGER_DATA_OFFSET (19 * 2) + +/* trackpad button data offsets */ +#define WSP_TYPE2_BUTTON_DATA_OFFSET 15 +#define WSP_TYPE3_BUTTON_DATA_OFFSET 23 + +/* list of device capability bits */ +#define HAS_INTEGRATED_BUTTON 1 + +/* trackpad finger structure - little endian */ +struct wsp_finger_sensor_data { + int16_t origin; /* zero when switching track finger */ + int16_t abs_x; /* absolute x coordinate */ + int16_t abs_y; /* absolute y coordinate */ + int16_t rel_x; /* relative x coordinate */ + int16_t rel_y; /* relative y coordinate */ + int16_t tool_major; /* tool area, major axis */ + int16_t tool_minor; /* tool area, minor axis */ + int16_t orientation; /* 16384 when point, else 15 bit angle */ + int16_t touch_major; /* touch area, major axis */ + int16_t touch_minor; /* touch area, minor axis */ + int16_t unused[3]; /* zeros */ + int16_t multi; /* one finger: varies, more fingers: constant */ +} __packed; + +typedef struct wsp_finger { + /* to track fingers as they match against strokes. */ + boolean_t matched; + + /* location (scaled using the mickeys factor) */ + int x; + int y; +} wsp_finger_t; + +#define WSP_MAX_FINGERS 16 +#define WSP_SIZEOF_FINGER_SENSOR_DATA sizeof(struct wsp_finger_sensor_data) +#define WSP_SIZEOF_ALL_FINGER_DATA (WSP_MAX_FINGERS * \ + WSP_SIZEOF_FINGER_SENSOR_DATA) +#define WSP_MAX_FINGER_ORIENTATION 16384 + +#define ATP_SENSOR_DATA_BUF_MAX 1024 +#if (ATP_SENSOR_DATA_BUF_MAX < ((WSP_MAX_FINGERS * 14 * 2) + \ + WSP_TYPE3_FINGER_DATA_OFFSET)) +/* note: 14 * 2 in the above is based on sizeof(struct wsp_finger_sensor_data)*/ +#error "ATP_SENSOR_DATA_BUF_MAX is too small" +#endif + +#define ATP_MAX_STROKES MAX(WSP_MAX_FINGERS, FG_MAX_STROKES) + +#define FG_MAX_XSENSORS 26 +#define FG_MAX_YSENSORS 16 + +/* device-specific configuration */ +struct fg_dev_params { + u_int data_len; /* for sensor data */ + u_int n_xsensors; + u_int n_ysensors; + enum fountain_geyser_trackpad_type prot; }; -struct atp_dev_params { - u_int data_len; /* for sensor data */ - u_int n_xsensors; - u_int n_ysensors; - atp_protocol prot; -} atp_dev_params[ATP_N_DEV_PARAMS] = { - [ATP_DEV_PARAMS_0] = { - .data_len = 64, - .n_xsensors = 20, - .n_ysensors = 10, - .prot = ATP_PROT_GEYSER3 +struct wsp_dev_params { + uint8_t caps; /* device capability bitmask */ + uint8_t tp_type; /* type of trackpad interface */ + uint8_t finger_data_offset; /* offset to trackpad finger data */ +}; + +static const struct fg_dev_params fg_dev_params[FOUNTAIN_GEYSER_PRODUCT_MAX] = { + [FOUNTAIN] = { + .data_len = 81, + .n_xsensors = 16, + .n_ysensors = 16, + .prot = FG_TRACKPAD_TYPE_GEYSER1 }, - [ATP_DEV_PARAMS_PBOOK] = { + [GEYSER1] = { .data_len = 81, .n_xsensors = 16, .n_ysensors = 16, - .prot = ATP_PROT_GEYSER1 + .prot = FG_TRACKPAD_TYPE_GEYSER1 }, - [ATP_DEV_PARAMS_PBOOK_15A] = { + [GEYSER1_17inch] = { + .data_len = 81, + .n_xsensors = 26, + .n_ysensors = 16, + .prot = FG_TRACKPAD_TYPE_GEYSER1 + }, + [GEYSER2] = { .data_len = 64, .n_xsensors = 15, .n_ysensors = 9, - .prot = ATP_PROT_GEYSER2 + .prot = FG_TRACKPAD_TYPE_GEYSER2 }, - [ATP_DEV_PARAMS_PBOOK_17] = { - .data_len = 81, - .n_xsensors = 26, - .n_ysensors = 16, - .prot = ATP_PROT_GEYSER1 + [GEYSER3] = { + .data_len = 64, + .n_xsensors = 20, + .n_ysensors = 10, + .prot = FG_TRACKPAD_TYPE_GEYSER3 }, + [GEYSER4] = { + .data_len = 64, + .n_xsensors = 20, + .n_ysensors = 10, + .prot = FG_TRACKPAD_TYPE_GEYSER4 + } }; -static const STRUCT_USB_HOST_ID atp_devs[] = { +static const STRUCT_USB_HOST_ID fg_devs[] = { + /* PowerBooks Feb 2005, iBooks G4 */ + { USB_VPI(USB_VENDOR_APPLE, 0x020e, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x020f, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0210, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x030a, FG_DRIVER_INFO(FOUNTAIN)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x030b, FG_DRIVER_INFO(GEYSER1)) }, + + /* PowerBooks Oct 2005 */ + { USB_VPI(USB_VENDOR_APPLE, 0x0214, FG_DRIVER_INFO(GEYSER2)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0215, FG_DRIVER_INFO(GEYSER2)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0216, FG_DRIVER_INFO(GEYSER2)) }, + /* Core Duo MacBook & MacBook Pro */ - { USB_VPI(USB_VENDOR_APPLE, 0x0217, ATP_DEV_PARAMS_0) }, - { USB_VPI(USB_VENDOR_APPLE, 0x0218, ATP_DEV_PARAMS_0) }, - { USB_VPI(USB_VENDOR_APPLE, 0x0219, ATP_DEV_PARAMS_0) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0217, FG_DRIVER_INFO(GEYSER3)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0218, FG_DRIVER_INFO(GEYSER3)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0219, FG_DRIVER_INFO(GEYSER3)) }, /* Core2 Duo MacBook & MacBook Pro */ - { USB_VPI(USB_VENDOR_APPLE, 0x021a, ATP_DEV_PARAMS_0) }, - { USB_VPI(USB_VENDOR_APPLE, 0x021b, ATP_DEV_PARAMS_0) }, - { USB_VPI(USB_VENDOR_APPLE, 0x021c, ATP_DEV_PARAMS_0) }, + { USB_VPI(USB_VENDOR_APPLE, 0x021a, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x021b, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x021c, FG_DRIVER_INFO(GEYSER4)) }, /* Core2 Duo MacBook3,1 */ - { USB_VPI(USB_VENDOR_APPLE, 0x0229, ATP_DEV_PARAMS_0) }, - { USB_VPI(USB_VENDOR_APPLE, 0x022a, ATP_DEV_PARAMS_0) }, - { USB_VPI(USB_VENDOR_APPLE, 0x022b, ATP_DEV_PARAMS_0) }, - - /* 12 inch PowerBook and iBook */ - { USB_VPI(USB_VENDOR_APPLE, 0x030a, ATP_DEV_PARAMS_PBOOK) }, - { USB_VPI(USB_VENDOR_APPLE, 0x030b, ATP_DEV_PARAMS_PBOOK) }, - - /* 15 inch PowerBook */ - { USB_VPI(USB_VENDOR_APPLE, 0x020e, ATP_DEV_PARAMS_PBOOK) }, - { USB_VPI(USB_VENDOR_APPLE, 0x020f, ATP_DEV_PARAMS_PBOOK) }, - { USB_VPI(USB_VENDOR_APPLE, 0x0215, ATP_DEV_PARAMS_PBOOK_15A) }, + { USB_VPI(USB_VENDOR_APPLE, 0x0229, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x022a, FG_DRIVER_INFO(GEYSER4)) }, + { USB_VPI(USB_VENDOR_APPLE, 0x022b, FG_DRIVER_INFO(GEYSER4)) }, /* 17 inch PowerBook */ - { USB_VPI(USB_VENDOR_APPLE, 0x020d, ATP_DEV_PARAMS_PBOOK_17) }, + { USB_VPI(USB_VENDOR_APPLE, 0x020d, FG_DRIVER_INFO(GEYSER1_17inch)) }, +}; +static const struct wsp_dev_params wsp_dev_params[WELLSPRING_PRODUCT_MAX] = { + [WELLSPRING1] = { + .caps = 0, + .tp_type = WSP_TRACKPAD_TYPE1, + .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, + }, + [WELLSPRING2] = { + .caps = 0, + .tp_type = WSP_TRACKPAD_TYPE1, + .finger_data_offset = WSP_TYPE1_FINGER_DATA_OFFSET, + }, + [WELLSPRING3] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING4] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING4A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING5] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING6] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING5A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING6A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING7] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING7A] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE2, + .finger_data_offset = WSP_TYPE2_FINGER_DATA_OFFSET, + }, + [WELLSPRING8] = { + .caps = HAS_INTEGRATED_BUTTON, + .tp_type = WSP_TRACKPAD_TYPE3, + .finger_data_offset = WSP_TYPE3_FINGER_DATA_OFFSET, + }, }; -/* - * The following structure captures the state of a pressure span along - * an axis. Each contact with the touchpad results in separate - * pressure spans along the two axes. - */ -typedef struct atp_pspan { - u_int width; /* in units of sensors */ - u_int cum; /* cumulative compression (from all sensors) */ - u_int cog; /* center of gravity */ - u_int loc; /* location (scaled using the mickeys factor) */ - boolean_t matched; /* to track pspans as they match against strokes. */ -} atp_pspan; +#define ATP_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + +/* TODO: STRUCT_USB_HOST_ID */ +static const struct usb_device_id wsp_devs[] = { + /* MacbookAir1.1 */ + ATP_DEV(APPLE, WELLSPRING_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING1)), + ATP_DEV(APPLE, WELLSPRING_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING1)), + ATP_DEV(APPLE, WELLSPRING_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING1)), + + /* MacbookProPenryn, aka wellspring2 */ + ATP_DEV(APPLE, WELLSPRING2_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING2)), + ATP_DEV(APPLE, WELLSPRING2_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING2)), + ATP_DEV(APPLE, WELLSPRING2_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING2)), + + /* Macbook5,1 (unibody), aka wellspring3 */ + ATP_DEV(APPLE, WELLSPRING3_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING3)), + ATP_DEV(APPLE, WELLSPRING3_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING3)), + ATP_DEV(APPLE, WELLSPRING3_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING3)), + + /* MacbookAir3,2 (unibody), aka wellspring4 */ + ATP_DEV(APPLE, WELLSPRING4_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4)), + ATP_DEV(APPLE, WELLSPRING4_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4)), + ATP_DEV(APPLE, WELLSPRING4_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4)), + + /* MacbookAir3,1 (unibody), aka wellspring4 */ + ATP_DEV(APPLE, WELLSPRING4A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), + ATP_DEV(APPLE, WELLSPRING4A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), + ATP_DEV(APPLE, WELLSPRING4A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING4A)), + + /* Macbook8 (unibody, March 2011) */ + ATP_DEV(APPLE, WELLSPRING5_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5)), + ATP_DEV(APPLE, WELLSPRING5_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5)), + ATP_DEV(APPLE, WELLSPRING5_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5)), + + /* MacbookAir4,1 (unibody, July 2011) */ + ATP_DEV(APPLE, WELLSPRING6A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), + ATP_DEV(APPLE, WELLSPRING6A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), + ATP_DEV(APPLE, WELLSPRING6A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6A)), + + /* MacbookAir4,2 (unibody, July 2011) */ + ATP_DEV(APPLE, WELLSPRING6_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING6)), + ATP_DEV(APPLE, WELLSPRING6_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING6)), + ATP_DEV(APPLE, WELLSPRING6_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING6)), + + /* Macbook8,2 (unibody) */ + ATP_DEV(APPLE, WELLSPRING5A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), + ATP_DEV(APPLE, WELLSPRING5A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), + ATP_DEV(APPLE, WELLSPRING5A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING5A)), + + /* MacbookPro10,1 (unibody, June 2012) */ + /* MacbookPro11,? (unibody, June 2013) */ + ATP_DEV(APPLE, WELLSPRING7_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7)), + ATP_DEV(APPLE, WELLSPRING7_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7)), + ATP_DEV(APPLE, WELLSPRING7_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7)), + + /* MacbookPro10,2 (unibody, October 2012) */ + ATP_DEV(APPLE, WELLSPRING7A_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), + ATP_DEV(APPLE, WELLSPRING7A_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), + ATP_DEV(APPLE, WELLSPRING7A_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING7A)), + + /* MacbookAir6,2 (unibody, June 2013) */ + ATP_DEV(APPLE, WELLSPRING8_ANSI, WELLSPRING_DRIVER_INFO(WELLSPRING8)), + ATP_DEV(APPLE, WELLSPRING8_ISO, WELLSPRING_DRIVER_INFO(WELLSPRING8)), + ATP_DEV(APPLE, WELLSPRING8_JIS, WELLSPRING_DRIVER_INFO(WELLSPRING8)), +}; typedef enum atp_stroke_type { ATP_STROKE_TOUCH, ATP_STROKE_SLIDE, } atp_stroke_type; -#define ATP_MAX_PSPANS_PER_AXIS 3 +typedef enum atp_axis { + X = 0, + Y = 1, + NUM_AXES +} atp_axis; -typedef struct atp_stroke_component { +#define ATP_FIFO_BUF_SIZE 8 /* bytes */ +#define ATP_FIFO_QUEUE_MAXLEN 50 /* units */ + +enum { + ATP_INTR_DT, + ATP_RESET, + ATP_N_TRANSFER, +}; + +typedef struct fg_stroke_component { /* Fields encapsulating the pressure-span. */ u_int loc; /* location (scaled) */ u_int cum_pressure; /* cumulative compression */ u_int max_cum_pressure; /* max cumulative compression */ boolean_t matched; /*to track components as they match against pspans.*/ - /* Fields containing information about movement. */ int delta_mickeys; /* change in location (un-smoothened movement)*/ - int pending; /* cum. of pending short movements */ - int movement; /* current smoothened movement */ -} atp_stroke_component; - -typedef enum atp_axis { - X = 0, - Y = 1 -} atp_axis; - -#define ATP_MAX_STROKES (2 * ATP_MAX_PSPANS_PER_AXIS) +} fg_stroke_component_t; /* * The following structure captures a finger contact with the * touchpad. A stroke comprises two p-span components and some state. */ typedef struct atp_stroke { - atp_stroke_type type; - struct timeval ctime; /* create time; for coincident siblings. */ - u_int age; /* - * Unit: interrupts; we maintain - * this value in addition to - * 'ctime' in order to avoid the - * expensive call to microtime() - * at every interrupt. - */ - - atp_stroke_component components[2]; - u_int velocity_squared; /* - * Average magnitude (squared) - * of recent velocity. - */ - u_int cum_movement; /* cum. absolute movement so far */ - - uint32_t flags; /* the state of this stroke */ -#define ATSF_ZOMBIE 0x1 -} atp_stroke; + TAILQ_ENTRY(atp_stroke) entry; -#define ATP_FIFO_BUF_SIZE 8 /* bytes */ -#define ATP_FIFO_QUEUE_MAXLEN 50 /* units */ + atp_stroke_type type; + uint32_t flags; /* the state of this stroke */ +#define ATSF_ZOMBIE 0x1 + boolean_t matched; /* to track match against fingers.*/ -enum { - ATP_INTR_DT, - ATP_RESET, - ATP_N_TRANSFER, -}; + struct timeval ctime; /* create time; for coincident siblings. */ + + /* + * Unit: interrupts; we maintain this value in + * addition to 'ctime' in order to avoid the + * expensive call to microtime() at every + * interrupt. + */ + uint32_t age; + + /* Location */ + int x; + int y; + + /* Fields containing information about movement. */ + int instantaneous_dx; /* curr. change in X location (un-smoothened) */ + int instantaneous_dy; /* curr. change in Y location (un-smoothened) */ + int pending_dx; /* cum. of pending short movements */ + int pending_dy; /* cum. of pending short movements */ + int movement_dx; /* interpreted smoothened movement */ + int movement_dy; /* interpreted smoothened movement */ + int cum_movement_x; /* cum. horizontal movement */ + int cum_movement_y; /* cum. vertical movement */ + + /* + * The following member is relevant only for fountain-geyser trackpads. + * For these, there is the need to track pressure-spans and cumulative + * pressures for stroke components. + */ + fg_stroke_component_t components[NUM_AXES]; +} atp_stroke_t; + +struct atp_softc; /* forward declaration */ +typedef void (*sensor_data_interpreter_t)(struct atp_softc *sc, u_int len); struct atp_softc { - device_t sc_dev; - struct usb_device *sc_usb_device; -#define MODE_LENGTH 8 - char sc_mode_bytes[MODE_LENGTH]; /* device mode */ - struct mtx sc_mutex; /* for synchronization */ - struct usb_xfer *sc_xfer[ATP_N_TRANSFER]; - struct usb_fifo_sc sc_fifo; - - struct atp_dev_params *sc_params; - - mousehw_t sc_hw; - mousemode_t sc_mode; - u_int sc_pollrate; - mousestatus_t sc_status; - u_int sc_state; -#define ATP_ENABLED 0x01 -#define ATP_ZOMBIES_EXIST 0x02 -#define ATP_DOUBLE_TAP_DRAG 0x04 -#define ATP_VALID 0x08 - - u_int sc_left_margin; - u_int sc_right_margin; - - atp_stroke sc_strokes[ATP_MAX_STROKES]; - u_int sc_n_strokes; - - int8_t *sensor_data; /* from interrupt packet */ - int *base_x; /* base sensor readings */ - int *base_y; - int *cur_x; /* current sensor readings */ - int *cur_y; - int *pressure_x; /* computed pressures */ - int *pressure_y; + device_t sc_dev; + struct usb_device *sc_usb_device; + struct mtx sc_mutex; /* for synchronization */ + struct usb_fifo_sc sc_fifo; + +#define MODE_LENGTH 8 + char sc_mode_bytes[MODE_LENGTH]; /* device mode */ + + trackpad_family_t sc_family; + const void *sc_params; /* device configuration */ + sensor_data_interpreter_t sensor_data_interpreter; + + mousehw_t sc_hw; + mousemode_t sc_mode; + mousestatus_t sc_status; + + u_int sc_state; +#define ATP_ENABLED 0x01 +#define ATP_ZOMBIES_EXIST 0x02 +#define ATP_DOUBLE_TAP_DRAG 0x04 +#define ATP_VALID 0x08 + + struct usb_xfer *sc_xfer[ATP_N_TRANSFER]; + + u_int sc_pollrate; + int sc_fflags; + + atp_stroke_t sc_strokes_data[ATP_MAX_STROKES]; + TAILQ_HEAD(,atp_stroke) sc_stroke_free; + TAILQ_HEAD(,atp_stroke) sc_stroke_used; + u_int sc_n_strokes; - u_int sc_idlecount; /* preceding idle interrupts */ -#define ATP_IDLENESS_THRESHOLD 10 + struct callout sc_callout; - struct timeval sc_reap_time; - struct timeval sc_reap_ctime; /*ctime of siblings to be reaped*/ + /* + * button status. Set to non-zero if the mouse-button is physically + * pressed. This state variable is exposed through softc to allow + * reap_sibling_zombies to avoid registering taps while the trackpad + * button is pressed. + */ + uint8_t sc_ibtn; + + /* + * Time when touch zombies were last reaped; useful for detecting + * double-touch-n-drag. + */ + struct timeval sc_touch_reap_time; + + u_int sc_idlecount; + + /* Regarding the data transferred from t-pad in USB INTR packets. */ + u_int sc_expected_sensor_data_len; + uint8_t sc_sensor_data[ATP_SENSOR_DATA_BUF_MAX] __aligned(4); + + int sc_cur_x[FG_MAX_XSENSORS]; /* current sensor readings */ + int sc_cur_y[FG_MAX_YSENSORS]; + int sc_base_x[FG_MAX_XSENSORS]; /* base sensor readings */ + int sc_base_y[FG_MAX_YSENSORS]; + int sc_pressure_x[FG_MAX_XSENSORS]; /* computed pressures */ + int sc_pressure_y[FG_MAX_YSENSORS]; + fg_pspan sc_pspans_x[FG_MAX_PSPANS_PER_AXIS]; + fg_pspan sc_pspans_y[FG_MAX_PSPANS_PER_AXIS]; }; /* - * The last byte of the sensor data contains status bits; the + * The last byte of the fountain-geyser sensor data contains status bits; the * following values define the meanings of these bits. + * (only Geyser 3/4) */ -enum atp_status_bits { - ATP_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */ - ATP_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/ +enum geyser34_status_bits { + FG_STATUS_BUTTON = (uint8_t)0x01, /* The button was pressed */ + FG_STATUS_BASE_UPDATE = (uint8_t)0x04, /* Data from an untouched pad.*/ }; typedef enum interface_mode { - RAW_SENSOR_MODE = (uint8_t)0x04, + RAW_SENSOR_MODE = (uint8_t)0x01, HID_MODE = (uint8_t)0x08 } interface_mode; + /* * function prototypes */ @@ -420,100 +758,183 @@ static struct usb_fifo_methods atp_fifo_ }; /* device initialization and shutdown */ -static usb_error_t atp_req_get_report(struct usb_device *udev, void *data); -static int atp_set_device_mode(device_t dev, interface_mode mode); -static void atp_reset_callback(struct usb_xfer *, usb_error_t); -static int atp_enable(struct atp_softc *sc); -static void atp_disable(struct atp_softc *sc); -static int atp_softc_populate(struct atp_softc *); -static void atp_softc_unpopulate(struct atp_softc *); +static usb_error_t atp_set_device_mode(struct atp_softc *, interface_mode); +static void atp_reset_callback(struct usb_xfer *, usb_error_t); +static int atp_enable(struct atp_softc *); +static void atp_disable(struct atp_softc *); /* sensor interpretation */ -static __inline void atp_interpret_sensor_data(const int8_t *, u_int, atp_axis, - int *, atp_protocol); -static __inline void atp_get_pressures(int *, const int *, const int *, int); -static void atp_detect_pspans(int *, u_int, u_int, atp_pspan *, - u_int *); +static void fg_interpret_sensor_data(struct atp_softc *, u_int); +static void fg_extract_sensor_data(const int8_t *, u_int, atp_axis, + int *, enum fountain_geyser_trackpad_type); +static void fg_get_pressures(int *, const int *, const int *, int); +static void fg_detect_pspans(int *, u_int, u_int, fg_pspan *, u_int *); +static void wsp_interpret_sensor_data(struct atp_softc *, u_int); /* movement detection */ -static boolean_t atp_match_stroke_component(atp_stroke_component *, - const atp_pspan *, atp_stroke_type); -static void atp_match_strokes_against_pspans(struct atp_softc *, - atp_axis, atp_pspan *, u_int, u_int); -static boolean_t atp_update_strokes(struct atp_softc *, - atp_pspan *, u_int, atp_pspan *, u_int); -static __inline void atp_add_stroke(struct atp_softc *, const atp_pspan *, - const atp_pspan *); -static void atp_add_new_strokes(struct atp_softc *, atp_pspan *, - u_int, atp_pspan *, u_int); -static void atp_advance_stroke_state(struct atp_softc *, - atp_stroke *, boolean_t *); -static void atp_terminate_stroke(struct atp_softc *, u_int); -static __inline boolean_t atp_stroke_has_small_movement(const atp_stroke *); -static __inline void atp_update_pending_mickeys(atp_stroke_component *); -static void atp_compute_smoothening_scale_ratio(atp_stroke *, int *, - int *); -static boolean_t atp_compute_stroke_movement(atp_stroke *); +static boolean_t fg_match_stroke_component(fg_stroke_component_t *, + const fg_pspan *, atp_stroke_type); +static void fg_match_strokes_against_pspans(struct atp_softc *, + atp_axis, fg_pspan *, u_int, u_int); +static boolean_t wsp_match_strokes_against_fingers(struct atp_softc *, + wsp_finger_t *, u_int); +static boolean_t fg_update_strokes(struct atp_softc *, fg_pspan *, u_int, + fg_pspan *, u_int); +static boolean_t wsp_update_strokes(struct atp_softc *, + wsp_finger_t [WSP_MAX_FINGERS], u_int); +static void fg_add_stroke(struct atp_softc *, const fg_pspan *, const fg_pspan *); +static void fg_add_new_strokes(struct atp_softc *, fg_pspan *, + u_int, fg_pspan *, u_int); +static void wsp_add_stroke(struct atp_softc *, const wsp_finger_t *); +static void atp_advance_stroke_state(struct atp_softc *, + atp_stroke_t *, boolean_t *); +static boolean_t atp_stroke_has_small_movement(const atp_stroke_t *); +static void atp_update_pending_mickeys(atp_stroke_t *); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***