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
This commit is contained in:
parent
6a3c9cdcd1
commit
8692d33ed3
|
@ -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.
|
* Copyright (c) 2010 The NetBSD Foundation, Inc.
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#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/param.h>
|
||||||
#include <sys/conf.h>
|
#include <sys/conf.h>
|
||||||
|
@ -163,11 +163,13 @@ struct btmagic_softc {
|
||||||
int sc_rw;
|
int sc_rw;
|
||||||
|
|
||||||
/* previous touches */
|
/* previous touches */
|
||||||
uint32_t sc_smask; /* scrolling */
|
uint32_t sc_smask; /* active(s) IDs */
|
||||||
int sc_az[16];
|
int sc_nfingers; /* number of active IDs */
|
||||||
int sc_aw[16];
|
int sc_ax[16];
|
||||||
|
int sc_ay[16];
|
||||||
|
|
||||||
/* previous mouse buttons */
|
/* previous mouse buttons */
|
||||||
|
int sc_mb_id; /* which ID selects the button */
|
||||||
uint32_t sc_mb;
|
uint32_t sc_mb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,7 +218,16 @@ static void btmagic_complete(void *, int);
|
||||||
static void btmagic_linkmode(void *, int);
|
static void btmagic_linkmode(void *, int);
|
||||||
static void btmagic_input(void *, struct mbuf *);
|
static void btmagic_input(void *, struct mbuf *);
|
||||||
static void btmagic_input_basic(struct btmagic_softc *, uint8_t *, size_t);
|
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 = {
|
static const struct btproto btmagic_ctl_proto = {
|
||||||
btmagic_connecting,
|
btmagic_connecting,
|
||||||
|
@ -259,7 +270,8 @@ btmagic_match(device_t self, cfdata_t cfdata, void *aux)
|
||||||
if (prop_dictionary_get_uint16(aux, BTDEVvendor, &v)
|
if (prop_dictionary_get_uint16(aux, BTDEVvendor, &v)
|
||||||
&& prop_dictionary_get_uint16(aux, BTDEVproduct, &p)
|
&& prop_dictionary_get_uint16(aux, BTDEVproduct, &p)
|
||||||
&& v == USB_VENDOR_APPLE
|
&& 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 2; /* trump bthidev(4) */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1047,15 +1059,18 @@ btmagic_input(void *arg, struct mbuf *m)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (data[1]) {
|
switch (data[1]) {
|
||||||
case 0x10: /* Basic mouse (input) */
|
case BASIC_REPORT_ID: /* Basic mouse (input) */
|
||||||
btmagic_input_basic(sc, data + 2, len - 2);
|
btmagic_input_basic(sc, data + 2, len - 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x29: /* Magic touch (input) */
|
case TRACKPAD_REPORT_ID: /* Magic trackpad (input) */
|
||||||
btmagic_input_magic(sc, data + 2, len - 2);
|
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;
|
break;
|
||||||
|
|
||||||
case 0x30: /* Battery status (input) */
|
case BATT_STAT_REPORT_ID: /* Battery status (input) */
|
||||||
if (len != 3)
|
if (len != 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1068,7 +1083,7 @@ btmagic_input(void *arg, struct mbuf *m)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x47: /* Battery strength (feature) */
|
case BATT_STRENGHT_REPORT_ID: /* Battery strength (feature) */
|
||||||
if (len != 3)
|
if (len != 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1076,7 +1091,7 @@ btmagic_input(void *arg, struct mbuf *m)
|
||||||
data[2]);
|
data[2]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x61: /* Surface detection (input) */
|
case SURFACE_REPORT_ID: /* Surface detection (input) */
|
||||||
if (len != 3)
|
if (len != 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1246,7 +1261,7 @@ static const struct {
|
||||||
#define BTMAGIC_PHASE_CANCEL 0x0
|
#define BTMAGIC_PHASE_CANCEL 0x0
|
||||||
|
|
||||||
static void
|
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;
|
uint32_t mb;
|
||||||
int dx, dy, dz, dw;
|
int dx, dy, dz, dw;
|
||||||
|
@ -1290,10 +1305,12 @@ btmagic_input_magic(struct btmagic_softc *sc, uint8_t *data, size_t len)
|
||||||
|
|
||||||
switch (hid_get_udata(data, &touch.phase)) {
|
switch (hid_get_udata(data, &touch.phase)) {
|
||||||
case BTMAGIC_PHASE_CONT:
|
case BTMAGIC_PHASE_CONT:
|
||||||
|
#define sc_az sc_ay
|
||||||
|
#define sc_aw sc_ax
|
||||||
tz = az - sc->sc_az[id];
|
tz = az - sc->sc_az[id];
|
||||||
tw = aw - sc->sc_aw[id];
|
tw = aw - sc->sc_aw[id];
|
||||||
|
|
||||||
if (ISSET(sc->sc_smask, id)) {
|
if (ISSET(sc->sc_smask, __BIT(id))) {
|
||||||
/* scrolling finger */
|
/* scrolling finger */
|
||||||
dz += btmagic_scale(tz, &sc->sc_rz,
|
dz += btmagic_scale(tz, &sc->sc_rz,
|
||||||
sc->sc_resolution / sc->sc_scale);
|
sc->sc_resolution / sc->sc_scale);
|
||||||
|
@ -1307,7 +1324,7 @@ btmagic_input_magic(struct btmagic_softc *sc, uint8_t *data, size_t len)
|
||||||
sc->sc_rw = 0;
|
sc->sc_rw = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SET(sc->sc_smask, id);
|
SET(sc->sc_smask, __BIT(id));
|
||||||
} else {
|
} else {
|
||||||
/* not scrolling finger */
|
/* not scrolling finger */
|
||||||
az = sc->sc_az[id];
|
az = sc->sc_az[id];
|
||||||
|
@ -1321,12 +1338,14 @@ btmagic_input_magic(struct btmagic_softc *sc, uint8_t *data, size_t len)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
CLR(sc->sc_smask, id);
|
CLR(sc->sc_smask, __BIT(id));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sc->sc_az[id] = az;
|
sc->sc_az[id] = az;
|
||||||
sc->sc_aw[id] = aw;
|
sc->sc_aw[id] = aw;
|
||||||
|
#undef sc_az
|
||||||
|
#undef sc_aw
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1355,3 +1374,204 @@ btmagic_input_magic(struct btmagic_softc *sc, uint8_t *data, size_t len)
|
||||||
splx(s);
|
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))) {
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (ISSET(sc->sc_smask, __BIT(id))) {
|
||||||
|
CLR(sc->sc_smask, __BIT(id));
|
||||||
|
sc->sc_nfingers--;
|
||||||
|
KASSERT(sc->sc_nfingers >= 0);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue