Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/usb Add uatp(4), a driver for USB Apple trackpads.



details:   https://anonhg.NetBSD.org/src/rev/fd94032bc85f
branches:  trunk
changeset: 780703:fd94032bc85f
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Sat Aug 04 04:34:54 2012 +0000

description:
Add uatp(4), a driver for USB Apple trackpads.

This is a work-in-progress driver for USB trackpads found in Apple
laptops since 2005, theoretically covering more models than pbms(4)
and supporting more features.  However, the motion smoothing and
acceleration formulae are still pretty sketchy, and I have tested
this only on one model of MacBook from 2006.  Feedback welcome.

Should the smooting, acceleration, tapping, &c., be done in userland?
Probably, but we don't have the necessary interface for that to work
well yet -- wsmouse isn't enough as is.

diffstat:

 sys/dev/usb/files.usb |     7 +-
 sys/dev/usb/uatp.c    |  2692 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 2698 insertions(+), 1 deletions(-)

diffs (truncated from 2717 to 300 lines):

diff -r 086e96c6877f -r fd94032bc85f sys/dev/usb/files.usb
--- a/sys/dev/usb/files.usb     Sat Aug 04 04:06:00 2012 +0000
+++ b/sys/dev/usb/files.usb     Sat Aug 04 04:34:54 2012 +0000
@@ -1,4 +1,4 @@
-#      $NetBSD: files.usb,v 1.123 2012/05/30 14:30:35 nonaka Exp $
+#      $NetBSD: files.usb,v 1.124 2012/08/04 04:34:54 riastradh Exp $
 #
 # Config file and device description for machine-independent USB code.
 # Included by ports that need it.  Ports that use it must provide
@@ -91,6 +91,11 @@
 attach ums at uhidbus
 file   dev/usb/ums.c                   ums
 
+# USB Apple trackpad
+device uatp: hid, wsmousedev
+attach uatp at uhidbus
+file   dev/usb/uatp.c                  uatp
+
 # Touchscreens
 device uts: hid, wsmousedev, tpcalib
 attach uts at uhidbus
diff -r 086e96c6877f -r fd94032bc85f sys/dev/usb/uatp.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/dev/usb/uatp.c        Sat Aug 04 04:34:54 2012 +0000
@@ -0,0 +1,2692 @@
+/*     $NetBSD: uatp.c,v 1.1 2012/08/04 04:34:55 riastradh Exp $       */
+
+/*-
+ * Copyright (c) 2011, 2012 Taylor R. Campbell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * uatp(4) - USB Apple Trackpad
+ *
+ * The uatp driver talks the protocol of the USB trackpads found in
+ * Apple laptops since 2005, including PowerBooks, iBooks, MacBooks,
+ * and MacBook Pros.  Some of these also present generic USB HID mice
+ * on another USB report id, which the ums(4) driver can handle, but
+ * Apple's protocol gives more detailed sensor data that lets us detect
+ * multiple fingers to emulate multi-button mice and scroll wheels.
+ */
+
+/*
+ * Protocol
+ *
+ * The device has a set of horizontal sensors, each being a column at a
+ * particular position on the x axis that tells you whether there is
+ * pressure anywhere on that column, and vertical sensors, each being a
+ * row at a particular position on the y axis that tells you whether
+ * there is pressure anywhere on that row.
+ *
+ * Whenever the device senses anything, it emits a readout of all of
+ * the sensors, in some model-dependent order.  (For the order, see
+ * read_sample_1 and read_sample_2.)  Each sensor datum is an unsigned
+ * eight-bit quantity representing some measure of pressure.  (Of
+ * course, it really measures capacitance, not pressure, but we'll call
+ * it `pressure' here.)
+ */
+
+/*
+ * Interpretation
+ *
+ * To interpret the finger's position on the trackpad, the driver
+ * computes a weighted average over all possible positions, weighted by
+ * the pressure at that position.  The weighted average is computed in
+ * the dimensions of the screen, rather than the trackpad, in order to
+ * admit a finer resolution of positions than the trackpad grid.
+ *
+ * To update the finger's position smoothly on the trackpad, the driver
+ * computes a weighted average of the old raw position, the old
+ * smoothed position, and the new smoothed position.  The weights are
+ * given by the old_raw_weight, old_smoothed_weight, and new_raw_weight
+ * sysctl knobs.
+ *
+ * Finally, to move the cursor, the driver takes the difference between
+ * the old and new positions and accelerates it according to some
+ * heuristic knobs that need to be reworked.
+ *
+ * Finally, there are some bells & whistles to detect tapping and to
+ * emulate a three-button mouse by leaving two or three fingers on the
+ * trackpad while pressing the button.
+ */
+
+/*
+ * Future work
+ *
+ * With the raw sensor data available, we could implement fancier bells
+ * & whistles too, such as pinch-to-zoom.  However, wsmouse supports
+ * only four-dimensional mice with buttons, and we already use two
+ * dimensions for mousing and two dimensions for scrolling, so there's
+ * no straightforward way to report zooming and other gestures to the
+ * operating system.  Probably a better way to do this would be just to
+ * attach uhid(4) instead of uatp(4) and to read the raw sensors data
+ * yourself -- but that requires hairy mode switching for recent models
+ * (see geyser34_enable_raw_mode).
+ *
+ * XXX Rework the acceleration knobs.
+ * XXX Implement edge scrolling.
+ * XXX Fix sysctl setup; preserve knobs across suspend/resume.
+ *     (uatp0 detaches and reattaches across suspend/resume, so as
+ *     written, the sysctl tree is torn down and rebuilt, losing any
+ *     state the user may have set.)
+ * XXX Refactor motion state so I can understand it again.
+ *     Should make a struct uatp_motion for all that state.
+ * XXX Add hooks for ignoring trackpad input while typing.
+ */
+
+/*
+ * Classifying devices
+ *
+ * I have only one MacBook to test this driver, but the driver should
+ * be applicable to almost every Apple laptop made since the beginning
+ * of 2005, so the driver reports lots of debugging output to help to
+ * classify devices.  Boot with `boot -v' (verbose) and check the
+ * output of `dmesg | grep uatp' to answer the following questions:
+ *
+ * - What devices (vendor, product, class, subclass, proto, USB HID
+ *   report dump) fail to attach when you think they should work?
+ *     (vendor not apple, class not hid, proto not mouse)
+ *
+ * - What devices have an unknown product id?
+ *     `unknown vendor/product id'
+ *
+ * - What devices have the wrong screen-to-trackpad ratios?
+ *     `... x sensors, scaled by ... for ... points on screen'
+ *     `... y sensors, scaled by ... for ... points on screen'
+ *   You can tweak hw.uatp0.x_ratio and hw.uatp0.y_ratio to adjust
+ *   this, up to a maximum of 384 for each value.
+ *
+ * - What devices have the wrong input size?
+ *     `expected input size ... but got ... for Apple trackpad'
+ *
+ * - What devices give wrong-sized packets?
+ *     `discarding ...-byte input'
+ *
+ * - What devices split packets in chunks?
+ *     `partial packet: ... bytes'
+ *
+ * - What devices develop large sensor readouts?
+ *     `large sensor readout: ...'
+ *
+ * - What devices have the wrong number of sensors?  Are there parts of
+ *   your trackpad that the system doesn't seem to notice?  You can
+ *   tweak hw.uatp0.x_sensors and hw.uatp0.y_sensors, up to a maximum
+ *   of 32 for each value.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: uatp.c,v 1.1 2012/08/04 04:34:55 riastradh Exp $");
+
+#include <sys/atomic.h>
+#include <sys/device.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/workqueue.h>
+
+/* WTF?  */
+extern int hz;
+
+/* Order is important here...sigh...  */
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/uhidev.h>
+#include <dev/usb/hid.h>
+#include <dev/usb/usbhid.h>
+
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsmousevar.h>
+
+#if 1
+#  define UATP_DEBUG   1
+#else
+#  include "opt_uatp_debug.h"
+#endif
+
+#define CHECK(condition, fail) do {                                    \
+       if (! (condition)) {                                            \
+               aprint_error_dev(uatp_dev(sc), "%s: check failed: %s\n",\
+                       __func__, #condition);                          \
+               fail;                                                   \
+       }                                                               \
+} while (0)
+
+#define UATP_DEBUG_ATTACH      (1 << 0)
+#define UATP_DEBUG_MISC                (1 << 1)
+#define UATP_DEBUG_WSMOUSE     (1 << 2)
+#define UATP_DEBUG_IOCTL       (1 << 3)
+#define UATP_DEBUG_RESET       (1 << 4)
+#define UATP_DEBUG_INTR                (1 << 5)
+#define UATP_DEBUG_PARSE       (1 << 6)
+#define UATP_DEBUG_TAP         (1 << 7)
+#define UATP_DEBUG_EMUL_BUTTON (1 << 8)
+#define UATP_DEBUG_ACCUMULATE  (1 << 9)
+#define UATP_DEBUG_STATUS      (1 << 10)
+#define UATP_DEBUG_SPURINTR    (1 << 11)
+#define UATP_DEBUG_MOVE                (1 << 12)
+#define UATP_DEBUG_ACCEL       (1 << 13)
+#define UATP_DEBUG_TRACK_DIST  (1 << 14)
+#define UATP_DEBUG_PALM                (1 << 15)
+
+#if UATP_DEBUG
+#  define DPRINTF(sc, flags, format) do {                              \
+       if ((flags) & (sc)->sc_debug_flags) {                           \
+               printf("%s: %s: ", device_xname(uatp_dev(sc)), __func__); \
+               printf format;                                          \
+       }                                                               \
+} while (0)
+#else
+#  define DPRINTF(sc, flags, format) do {} while (0)
+#endif
+
+/* Maximum number of bytes in an incoming packet of sensor data.  */
+#define UATP_MAX_INPUT_SIZE    81
+
+/* Maximum number of sensors in each dimension.  */
+#define UATP_MAX_X_SENSORS     32
+#define UATP_MAX_Y_SENSORS     32
+#define UATP_MAX_SENSORS       32
+#define UATP_SENSORS           (UATP_MAX_X_SENSORS + UATP_MAX_Y_SENSORS)
+
+/* Maximum accumulated sensor value.  */
+#define UATP_MAX_ACC           0xff
+
+/* Maximum screen dimension to sensor dimension ratios.  */
+#define UATP_MAX_X_RATIO       0x180
+#define UATP_MAX_Y_RATIO       0x180
+#define UATP_MAX_RATIO         0x180
+
+/* Maximum weight for positions in motion calculation.  */
+#define UATP_MAX_WEIGHT                0x7f
+
+/* Maximum possible trackpad position in a single dimension.  */
+#define UATP_MAX_POSITION      (UATP_MAX_SENSORS * UATP_MAX_RATIO)
+
+/* Bounds on acceleration.  */
+#define UATP_MAX_MOTION_MULTIPLIER     16
+
+/* Status bits transmitted in the last byte of an input packet.  */
+#define UATP_STATUS_BUTTON     (1 << 0)        /* Button pressed */
+#define UATP_STATUS_BASE       (1 << 2)        /* Base sensor data */
+#define UATP_STATUS_POST_RESET (1 << 4)        /* Post-reset */
+
+/* Forward declarations */
+
+struct uatp_softc;             /* Device driver state.  */
+struct uatp_descriptor;                /* Descriptor for a particular model.  */
+struct uatp_parameters;                /* Parameters common to a set of models.  */
+struct uatp_knobs;             /* User-settable configuration knobs.  */
+enum uatp_tap_state {
+       TAP_STATE_INITIAL,
+       TAP_STATE_TAPPING,
+       TAP_STATE_TAPPED,
+       TAP_STATE_DOUBLE_TAPPING,
+       TAP_STATE_DRAGGING_DOWN,
+       TAP_STATE_DRAGGING_UP,
+       TAP_STATE_TAPPING_IN_DRAG,
+};
+
+static const struct uatp_descriptor *find_uatp_descriptor
+    (const struct uhidev_attach_arg *);
+static device_t uatp_dev(const struct uatp_softc *);
+static uint8_t *uatp_x_sample(struct uatp_softc *);
+static uint8_t *uatp_y_sample(struct uatp_softc *);
+static int *uatp_x_acc(struct uatp_softc *);
+static int *uatp_y_acc(struct uatp_softc *);
+static void uatp_clear_position(struct uatp_softc *);
+static unsigned int uatp_x_sensors(const struct uatp_softc *);
+static unsigned int uatp_y_sensors(const struct uatp_softc *);
+static unsigned int uatp_x_ratio(const struct uatp_softc *);
+static unsigned int uatp_y_ratio(const struct uatp_softc *);
+static unsigned int uatp_old_raw_weight(const struct uatp_softc *);



Home | Main Index | Thread Index | Old Index