Source-Changes-HG archive

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

[src/trunk]: src/sys/dev/bluetooth Add support for Apple Magic Trackpad.



details:   https://anonhg.NetBSD.org/src/rev/2e6c67e8ca54
branches:  trunk
changeset: 337211:2e6c67e8ca54
user:      bouyer <bouyer%NetBSD.org@localhost>
date:      Mon Apr 06 17:45:31 2015 +0000

description:
Add support for Apple Magic Trackpad.
3 button emulation by detecting in which area of the bottom of
the device the trackpad's button is pressed.
Pointer move support with 1 finger touch, X/Y scroll with 2-finger touch.
TODO:
- detect tap to emulate button press and drag/n/drop.
- Detect and support zoom, if wsmouse allows to report this

diffstat:

 sys/dev/bluetooth/btmagic.c |  254 +++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 237 insertions(+), 17 deletions(-)

diffs (truncated from 357 to 300 lines):

diff -r 8f2222e82e8c -r 2e6c67e8ca54 sys/dev/bluetooth/btmagic.c
--- a/sys/dev/bluetooth/btmagic.c       Mon Apr 06 17:16:25 2015 +0000
+++ b/sys/dev/bluetooth/btmagic.c       Mon Apr 06 17:45:31 2015 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: btmagic.c,v 1.11 2014/08/05 07:55:31 rtr Exp $ */
+/*     $NetBSD: btmagic.c,v 1.12 2015/04/06 17:45:31 bouyer Exp $      */
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -85,7 +85,7 @@
  *****************************************************************************/
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: btmagic.c,v 1.11 2014/08/05 07:55:31 rtr Exp $");
+__KERNEL_RCSID(0, "$NetBSD: btmagic.c,v 1.12 2015/04/06 17:45:31 bouyer Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -163,11 +163,13 @@
        int                     sc_rw;
 
        /* previous touches */
-       uint32_t                sc_smask;       /* scrolling */
-       int                     sc_az[16];
-       int                     sc_aw[16];
+       uint32_t                sc_smask;       /* active(s) IDs */
+       int                     sc_nfingers;    /* number of active IDs */
+       int                     sc_ax[16];
+       int                     sc_ay[16];
 
        /* previous mouse buttons */
+       int                     sc_mb_id; /* which ID selects the button */
        uint32_t                sc_mb;
 };
 
@@ -216,7 +218,16 @@
 static void  btmagic_linkmode(void *, int);
 static void  btmagic_input(void *, struct mbuf *);
 static void  btmagic_input_basic(struct btmagic_softc *, uint8_t *, size_t);
-static void  btmagic_input_magic(struct btmagic_softc *, uint8_t *, size_t);
+static void  btmagic_input_magicm(struct btmagic_softc *, uint8_t *, size_t);
+static void  btmagic_input_magict(struct btmagic_softc *, uint8_t *, size_t);
+
+/* report types (data[1]) */
+#define BASIC_REPORT_ID                0x10
+#define TRACKPAD_REPORT_ID     0x28
+#define MOUSE_REPORT_ID                0x29
+#define BATT_STAT_REPORT_ID    0x30
+#define BATT_STRENGHT_REPORT_ID        0x47
+#define SURFACE_REPORT_ID      0x61
 
 static const struct btproto btmagic_ctl_proto = {
        btmagic_connecting,
@@ -259,7 +270,8 @@
        if (prop_dictionary_get_uint16(aux, BTDEVvendor, &v)
            && prop_dictionary_get_uint16(aux, BTDEVproduct, &p)
            && v == USB_VENDOR_APPLE
-           && p == USB_PRODUCT_APPLE_MAGICMOUSE)
+           && (p == USB_PRODUCT_APPLE_MAGICMOUSE ||
+               p == USB_PRODUCT_APPLE_MAGICTRACKPAD))
                return 2;       /* trump bthidev(4) */
 
        return 0;
@@ -1047,15 +1059,18 @@
                        break;
 
                switch (data[1]) {
-               case 0x10: /* Basic mouse (input) */
+               case BASIC_REPORT_ID: /* Basic mouse (input) */
                        btmagic_input_basic(sc, data + 2, len - 2);
                        break;
 
-               case 0x29: /* Magic touch (input) */
-                       btmagic_input_magic(sc, data + 2, len - 2);
+               case TRACKPAD_REPORT_ID: /* Magic trackpad (input) */
+                       btmagic_input_magict(sc, data + 2, len - 2);
+                       break;
+               case MOUSE_REPORT_ID: /* Magic touch (input) */
+                       btmagic_input_magicm(sc, data + 2, len - 2);
                        break;
 
-               case 0x30: /* Battery status (input) */
+               case BATT_STAT_REPORT_ID: /* Battery status (input) */
                        if (len != 3)
                                break;
 
@@ -1068,7 +1083,7 @@
                        }
                        break;
 
-               case 0x47: /* Battery strength (feature) */
+               case BATT_STRENGHT_REPORT_ID: /* Battery strength (feature) */
                        if (len != 3)
                                break;
 
@@ -1076,7 +1091,7 @@
                            data[2]);
                        break;
 
-               case 0x61: /* Surface detection (input) */
+               case SURFACE_REPORT_ID: /* Surface detection (input) */
                        if (len != 3)
                                break;
 
@@ -1246,7 +1261,7 @@
 #define BTMAGIC_PHASE_CANCEL   0x0
 
 static void
-btmagic_input_magic(struct btmagic_softc *sc, uint8_t *data, size_t len)
+btmagic_input_magicm(struct btmagic_softc *sc, uint8_t *data, size_t len)
 {
        uint32_t mb;
        int dx, dy, dz, dw;
@@ -1290,10 +1305,12 @@
 
                switch (hid_get_udata(data, &touch.phase)) {
                case BTMAGIC_PHASE_CONT:
+#define sc_az sc_ay
+#define sc_aw sc_ax
                        tz = az - sc->sc_az[id];
                        tw = aw - sc->sc_aw[id];
 
-                       if (ISSET(sc->sc_smask, id)) {
+                       if (ISSET(sc->sc_smask, __BIT(id))) {
                                /* scrolling finger */
                                dz += btmagic_scale(tz, &sc->sc_rz,
                                    sc->sc_resolution / sc->sc_scale);
@@ -1307,7 +1324,7 @@
                                        sc->sc_rw = 0;
                                }
 
-                               SET(sc->sc_smask, id);
+                               SET(sc->sc_smask, __BIT(id));
                        } else {
                                /* not scrolling finger */
                                az = sc->sc_az[id];
@@ -1321,12 +1338,14 @@
                        break;
 
                default:
-                       CLR(sc->sc_smask, id);
+                       CLR(sc->sc_smask, __BIT(id));
                        break;
                }
 
                sc->sc_az[id] = az;
                sc->sc_aw[id] = aw;
+#undef sc_az
+#undef sc_aw
        }
 
        /*
@@ -1355,3 +1374,204 @@
                splx(s);
        }
 }
+
+/*
+ * the Magic touch trackpad report (0x28), according to the Linux driver
+ * written by Michael Poole and Chase Douglas, is variable length starting
+ * with the fixed 24-bit header
+ *
+ *     button 1        1-bit
+ *      unknown                5-bits
+ *     timestamp       18-bits
+ *
+ * followed by (up to 5?) touch reports of 72-bits each
+ *
+ *     abs X           13-bits (signed)
+ *     abs Y           13-bits (signed)
+ *     unknown         6-bits
+ *     axis major      8-bits
+ *     axis minor      8-bits
+ *     pressure        6-bits
+ *     id              4-bits
+ *     angle           6-bits  (from E(0)->N(32)->W(64))
+ *     unknown         4-bits
+ *     phase           4-bits
+ */
+
+static const struct {
+       struct hid_location button;
+       struct hid_location timestamp;
+} magict = {
+       .button = { .pos =  0, .size = 1 },
+       .timestamp = { .pos = 6, .size = 18 },
+};
+
+static const struct {
+       struct hid_location aX;
+       struct hid_location aY;
+       struct hid_location major;
+       struct hid_location minor;
+       struct hid_location pressure;
+       struct hid_location id;
+       struct hid_location angle;
+       struct hid_location unknown;
+       struct hid_location phase;
+} toucht = {
+       .aX = { .pos = 0, .size = 13 },
+       .aY = { .pos = 13, .size = 13 },
+       .major = { .pos = 32, .size = 8 },
+       .minor = { .pos = 40, .size = 8 },
+       .pressure = { .pos = 48, .size = 6 },
+       .id = { .pos = 54, .size = 4 },
+       .angle = { .pos = 58, .size = 6 },
+       .unknown = { .pos = 64, .size = 4 },
+       .phase = { .pos = 68, .size = 4 },
+};
+
+/*
+ * as for btmagic_input_magicm, 
+ * the phase of the touch starts at 0x01 as the finger is first detected
+ * approaching the mouse, increasing to 0x04 while the finger is touching,
+ * then increases towards 0x07 as the finger is lifted, and we get 0x00
+ * when the touch is cancelled. The values below seem to be produced for
+ * every touch, the others less consistently depending on how fast the
+ * approach or departure is.
+ *
+ * In fact we ignore touches unless they are in the steady 0x04 phase.
+ */
+
+/* min and max values reported */
+#define MAGICT_X_MIN   (-2910)
+#define MAGICT_X_MAX   (3170)
+#define MAGICT_Y_MIN   (-2565)
+#define MAGICT_Y_MAX   (2455)
+
+/*
+ * area for detecting the buttons: divide in 3 areas on X, 
+ * below -1900 on y
+ */
+#define MAGICT_B_YMAX  (-1900)
+#define MAGICT_B_XSIZE ((MAGICT_X_MAX - MAGICT_X_MIN) / 3)
+#define MAGICT_B_X1MAX (MAGICT_X_MIN + MAGICT_B_XSIZE)
+#define MAGICT_B_X2MAX (MAGICT_X_MIN + MAGICT_B_XSIZE * 2)
+
+static void
+btmagic_input_magict(struct btmagic_softc *sc, uint8_t *data, size_t len)
+{
+       bool bpress;
+       uint32_t mb;
+       int id, ax, ay, tx, ty;
+       int dx, dy, dz, dw;
+       int s;
+
+       if (((len - 3) % 9) != 0)
+               return;
+
+       bpress = 0;
+       if (hid_get_udata(data, &magict.button))
+               bpress = 1;
+
+       dx = dy = dz = dw = 0;
+       mb = 0;
+
+       len = (len - 3) / 9;
+       for (data += 3; len-- > 0; data += 9) {
+               id = hid_get_udata(data, &toucht.id);
+               ax = hid_get_data(data, &toucht.aX);
+               ay = hid_get_data(data, &toucht.aY);
+
+               DPRINTF(sc,
+                   "btmagic_input_magicm: id %d ax %d ay %d phase %ld %s\n",
+                   id, ax, ay, hid_get_udata(data, &toucht.phase),
+                   bpress ? "button pressed" : "");
+
+               /*
+                * a single touch is interpreted as a mouse move.
+                * If a button is pressed, the touch in the button area
+                * defined above defines the button; a second touch is
+                * interpreted as a mouse move.
+                */
+
+               switch (hid_get_udata(data, &toucht.phase)) {
+               case BTMAGIC_PHASE_CONT:
+                       if (bpress) {
+                               if (sc->sc_mb == 0 && ay < MAGICT_B_YMAX) {
+                                       /*
+                                        * we have a new button press,
+                                        * and this id tells which one
+                                        */
+                                       if (ax < MAGICT_B_X1MAX)
+                                               mb = __BIT(0);
+                                       else if (ax > MAGICT_B_X2MAX)
+                                               mb = __BIT(2);
+                                       else
+                                               mb = __BIT(1);
+                                       sc->sc_mb_id = id;
+                               } else {
+                                       /* keep previous state */
+                                       mb = sc->sc_mb;
+                               }
+                       } else {
+                               /* no button pressed */
+                               mb = 0;
+                               sc->sc_mb_id = -1;
+                       }
+                       if (id == sc->sc_mb_id) {
+                               /*



Home | Main Index | Thread Index | Old Index