Module Name: src
Committed By: snj
Date: Thu Oct 15 20:24:30 UTC 2015
Modified Files:
src/share/man/man4 [netbsd-7]: btmagic.4
src/sys/dev/bluetooth [netbsd-7]: btmagic.c
Log Message:
Pull up following revision(s) (requested by bouyer in ticket #869):
share/man/man4/btmagic.4: revisions 1.3-1.6
sys/dev/bluetooth/btmagic.c: revisions 1.12, 1.14
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
--
Mention magic trackpad support
--
Grammar, whitespace; make AUTHORS section from HISTORY since it mostly cites
people.
--
Add basic support for tap detection, emulating a left-click.
--
New sentence, new line.
To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.2.26.1 src/share/man/man4/btmagic.4
cvs rdiff -u -r1.11 -r1.11.2.1 src/sys/dev/bluetooth/btmagic.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/share/man/man4/btmagic.4
diff -u src/share/man/man4/btmagic.4:1.2 src/share/man/man4/btmagic.4:1.2.26.1
--- src/share/man/man4/btmagic.4:1.2 Mon Oct 11 15:15:57 2010
+++ src/share/man/man4/btmagic.4 Thu Oct 15 20:24:30 2015
@@ -1,4 +1,4 @@
-.\" $NetBSD: btmagic.4,v 1.2 2010/10/11 15:15:57 plunky Exp $
+.\" $NetBSD: btmagic.4,v 1.2.26.1 2015/10/15 20:24:30 snj Exp $
.\"
.\" Copyright (c) 2010 The NetBSD Foundation, Inc.
.\" All rights reserved.
@@ -27,12 +27,12 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd October 11, 2010
+.Dd July 4, 2015
.Dt BTMAGIC 4
.Os
.Sh NAME
.Nm btmagic
-.Nd Apple Magic Mouse
+.Nd Apple Magic Mouse and Apple Magic Trackpad
.Sh SYNOPSIS
.Cd "btmagic* at bthub?"
.Cd "wsmouse* at btmagic?"
@@ -42,6 +42,8 @@ The
driver provides support for the
.Tn Bluetooth
.Dq Magic Mouse
+and
+.Dq Magic Trackpad
from
.Tn Apple, Inc .
As remote devices cannot be discovered by autoconfig, configuring
@@ -49,10 +51,10 @@ a mouse is normally carried out with the
.Xr btdevctl 8
program.
.Pp
-The Magic Mouse uses the standard
+The Magic Mouse and Magic Trackpad use the standard
.Tn USB
-Human Interface Device protocol to communicate, but does not provide a
-proper HID Descriptor, and requires specific initializations to enable
+Human Interface Device protocol to communicate, but do not provide a
+proper HID Descriptor, and require specific initializations to enable
the proprietary touch reports.
.Pp
The Magic Mouse provides basic mouse functionality with two buttons,
@@ -69,6 +71,25 @@ along with the pressure needed to discer
distance necessary to trigger scrolling and the additional downscale
factor applied to scroll movements.
.Pp
+The Magic Trackpad provides multi touch functionality and one button.
+The
+.Nm
+driver emulates 3 buttons by splitting the area at the bottom of the
+device in 3 equal zones and detects finger presence in one of these zones
+when the button is pressed.
+In addition, a tap in any area of the trackpad is interpreted as a left click.
+The timeout for tap detection defaults to 100ms
+and is adjustable with
+.Xr sysctl 8 .
+.Pp
+Pointer movement is reported for single-touch
+movements over the device, and scroll is reported for multi-touch movements.
+.Pp
+The trackpad has a base resolution of 1300dpi, which the driver scales
+by default to a less sensitive 650dpi, but this is adjustable with
+.Xr sysctl 8
+along with the additional downscale factor applied to scroll movements.
+.Pp
.Nm
interfaces to the system as usual through the
.Xr wsmouse 4
@@ -77,7 +98,7 @@ driver, and the following properties are
.It vendor-id
Must be 0x05ac.
.It product-id
-Must be 0x030d.
+Must be 0x030d or 0x030e.
.It local-bdaddr
Local device address.
.It remote-bdaddr
@@ -106,7 +127,8 @@ during the pairing process.
.Xr wsmouse 4 ,
.Xr btdevctl 8 ,
.Xr sysctl 8
-.Sh HISTORY
+.Sh AUTHORS
+.An -nosplit
The
.Nm
driver was written by
@@ -115,3 +137,10 @@ with reference to the
.Tn Linux
driver written by
.An Michael Poole .
+.An Manuel Bouyer
+added Magic Trackpad support, with reference to the
+.Tn Linux
+driver written by
+.An Michael Poole
+and
+.An Chase Douglas .
Index: src/sys/dev/bluetooth/btmagic.c
diff -u src/sys/dev/bluetooth/btmagic.c:1.11 src/sys/dev/bluetooth/btmagic.c:1.11.2.1
--- src/sys/dev/bluetooth/btmagic.c:1.11 Tue Aug 5 07:55:31 2014
+++ src/sys/dev/bluetooth/btmagic.c Thu Oct 15 20:24:30 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: btmagic.c,v 1.11 2014/08/05 07:55:31 rtr Exp $ */
+/* $NetBSD: btmagic.c,v 1.11.2.1 2015/10/15 20:24:30 snj 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.11.2.1 2015/10/15 20:24:30 snj Exp $");
#include <sys/param.h>
#include <sys/conf.h>
@@ -163,12 +163,19 @@ struct btmagic_softc {
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;
+ /* button emulation with tap */
+ int sc_tapmb_id; /* which ID selects the button */
+ struct timeval sc_taptime;
+ int sc_taptimeout;
+ callout_t sc_tapcallout;
};
/* sc_flags */
@@ -189,6 +196,8 @@ static int btmagic_listen(struct btmagi
static int btmagic_connect(struct btmagic_softc *);
static int btmagic_sysctl_resolution(SYSCTLFN_PROTO);
static int btmagic_sysctl_scale(SYSCTLFN_PROTO);
+static int btmagic_tap(struct btmagic_softc *, int);
+static int btmagic_sysctl_taptimeout(SYSCTLFN_PROTO);
CFATTACH_DECL_NEW(btmagic, sizeof(struct btmagic_softc),
btmagic_match, btmagic_attach, btmagic_detach, NULL);
@@ -216,7 +225,17 @@ static void btmagic_complete(void *, in
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);
+static void btmagic_tapcallout(void *);
+
+/* 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 +278,8 @@ btmagic_match(device_t self, cfdata_t cf
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;
@@ -279,8 +299,12 @@ btmagic_attach(device_t parent, device_t
*/
sc->sc_dev = self;
sc->sc_state = BTMAGIC_CLOSED;
+ sc->sc_mb_id = -1;
+ sc->sc_tapmb_id = -1;
callout_init(&sc->sc_timeout, 0);
callout_setfunc(&sc->sc_timeout, btmagic_timeout, sc);
+ callout_init(&sc->sc_tapcallout, 0);
+ callout_setfunc(&sc->sc_tapcallout, btmagic_tapcallout, sc);
sockopt_init(&sc->sc_mode, BTPROTO_L2CAP, SO_L2CAP_LM, 0);
/*
@@ -320,6 +344,7 @@ btmagic_attach(device_t parent, device_t
sc->sc_firm = 6;
sc->sc_dist = 130;
sc->sc_scale = 20;
+ sc->sc_taptimeout = 100;
sysctl_createv(&sc->sc_log, 0, NULL, &node,
0,
@@ -366,6 +391,14 @@ btmagic_attach(device_t parent, device_t
(void *)sc, 0,
CTL_HW, node->sysctl_num,
CTL_CREATE, CTL_EOL);
+ sysctl_createv(&sc->sc_log, 0, NULL, NULL,
+ CTLFLAG_READWRITE,
+ CTLTYPE_INT, "taptimeout",
+ "timeout for tap detection in milliseconds",
+ btmagic_sysctl_taptimeout, 0,
+ (void *)sc, 0,
+ CTL_HW, node->sysctl_num,
+ CTL_CREATE, CTL_EOL);
}
/*
@@ -425,6 +458,8 @@ btmagic_detach(device_t self, int flags)
sc->sc_ctl = NULL;
}
+ callout_halt(&sc->sc_tapcallout, bt_lock);
+ callout_destroy(&sc->sc_tapcallout);
callout_halt(&sc->sc_timeout, bt_lock);
callout_destroy(&sc->sc_timeout);
@@ -605,6 +640,31 @@ btmagic_sysctl_scale(SYSCTLFN_ARGS)
return 0;
}
+/* validate tap timeout */
+static int
+btmagic_sysctl_taptimeout(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node;
+ struct btmagic_softc *sc;
+ int t, error;
+
+ node = *rnode;
+ sc = node.sysctl_data;
+
+ t = sc->sc_taptimeout;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (t < max(1000 / hz, 1) || t > 999)
+ return EINVAL;
+
+ sc->sc_taptimeout = t;
+ DPRINTF(sc, "taptimeout = %u", t);
+ return 0;
+}
+
/*****************************************************************************
*
* wsmouse(4) accessops
@@ -1047,15 +1107,18 @@ btmagic_input(void *arg, struct mbuf *m)
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 +1131,7 @@ btmagic_input(void *arg, struct mbuf *m)
}
break;
- case 0x47: /* Battery strength (feature) */
+ case BATT_STRENGHT_REPORT_ID: /* Battery strength (feature) */
if (len != 3)
break;
@@ -1076,7 +1139,7 @@ btmagic_input(void *arg, struct mbuf *m)
data[2]);
break;
- case 0x61: /* Surface detection (input) */
+ case SURFACE_REPORT_ID: /* Surface detection (input) */
if (len != 3)
break;
@@ -1246,7 +1309,7 @@ static const struct {
#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 +1353,12 @@ btmagic_input_magic(struct btmagic_softc
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 +1372,7 @@ btmagic_input_magic(struct btmagic_softc
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 +1386,14 @@ btmagic_input_magic(struct btmagic_softc
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 +1422,262 @@ btmagic_input_magic(struct btmagic_softc
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) {
+ /*
+ * this id selects the button
+ * ignore for move/scroll
+ */
+ continue;
+ }
+
+ tx = ax - sc->sc_ax[id];
+ ty = ay - sc->sc_ay[id];
+
+ if (ISSET(sc->sc_smask, __BIT(id))) {
+ struct timeval now_tv;
+ getmicrotime(&now_tv);
+ if (sc->sc_nfingers == 1 && mb == 0 &&
+ timercmp(&sc->sc_taptime, &now_tv, >)) {
+ /* still detecting a tap */
+ continue;
+ }
+
+ if (sc->sc_nfingers == 1 || mb != 0) {
+ /* single finger moving */
+ dx += btmagic_scale(tx, &sc->sc_rx,
+ sc->sc_resolution);
+ dy += btmagic_scale(ty, &sc->sc_ry,
+ sc->sc_resolution);
+ } else {
+ /* scrolling fingers */
+ dz += btmagic_scale(ty, &sc->sc_rz,
+ sc->sc_resolution / sc->sc_scale);
+ dw += btmagic_scale(tx, &sc->sc_rw,
+ sc->sc_resolution / sc->sc_scale);
+ }
+ } else if (ay > MAGICT_B_YMAX) { /* new finger */
+ sc->sc_rx = 0;
+ sc->sc_ry = 0;
+ sc->sc_rz = 0;
+ sc->sc_rw = 0;
+ KASSERT(!ISSET(sc->sc_smask, __BIT(id)));
+ SET(sc->sc_smask, __BIT(id));
+ sc->sc_nfingers++;
+ if (sc->sc_tapmb_id == -1 &&
+ mb == 0 && sc->sc_mb == 0) {
+ sc->sc_tapmb_id = id;
+ getmicrotime(&sc->sc_taptime);
+ sc->sc_taptime.tv_usec +=
+ sc->sc_taptimeout * 1000;
+ if (sc->sc_taptime.tv_usec > 1000000) {
+ sc->sc_taptime.tv_usec -=
+ 1000000;
+ sc->sc_taptime.tv_sec++;
+ }
+ }
+
+ }
+
+ break;
+ default:
+ if (ISSET(sc->sc_smask, __BIT(id))) {
+ CLR(sc->sc_smask, __BIT(id));
+ sc->sc_nfingers--;
+ KASSERT(sc->sc_nfingers >= 0);
+ if (id == sc->sc_tapmb_id) {
+ mb = btmagic_tap(sc, id);
+ }
+ }
+ break;
+ }
+
+ sc->sc_ax[id] = ax;
+ sc->sc_ay[id] = ay;
+ }
+
+ if (dx != 0 || dy != 0 || dz != 0 || dw != 0 || mb != sc->sc_mb) {
+ sc->sc_mb = mb;
+
+ s = spltty();
+ wsmouse_input(sc->sc_wsmouse, mb,
+ dx, dy, -dz, dw, WSMOUSE_INPUT_DELTA);
+ splx(s);
+ }
+}
+
+static int
+btmagic_tap(struct btmagic_softc *sc, int id)
+{
+ struct timeval now_tv;
+
+ sc->sc_tapmb_id = -1;
+ getmicrotime(&now_tv);
+ if (timercmp(&sc->sc_taptime, &now_tv, >)) {
+ /* got a tap */
+ callout_schedule(
+ &sc->sc_tapcallout,
+ mstohz(sc->sc_taptimeout));
+ return __BIT(0);
+ }
+ return 0;
+}
+
+static void
+btmagic_tapcallout(void *arg)
+{
+ struct btmagic_softc *sc = arg;
+ int s;
+
+ mutex_enter(bt_lock);
+ callout_ack(&sc->sc_tapcallout);
+ if ((sc->sc_mb & __BIT(0)) != 0) {
+ sc->sc_mb &= ~__BIT(0);
+ s = spltty();
+ wsmouse_input(sc->sc_wsmouse, sc->sc_mb,
+ 0, 0, 0, 0, WSMOUSE_INPUT_DELTA);
+ splx(s);
+ }
+ mutex_exit(bt_lock);
+}