git: ef8397c28e98 - main - bcm5974(4): add Magic Trackpad 2 (USB only) support

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Tue, 25 Apr 2023 09:21:57 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=ef8397c28e981e0115d435aabcf57cee7ea5f5ee

commit ef8397c28e981e0115d435aabcf57cee7ea5f5ee
Author:     Val Packett <val@packett.cool>
AuthorDate: 2023-04-25 09:20:53 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2023-04-25 09:20:53 +0000

    bcm5974(4): add Magic Trackpad 2 (USB only) support
    
    The MT2 uses a compact report format, but otherwise is similar in many
    ways to the internal trackpads, it even uses the same mode switching
    commands.
    
    Reviewed by:    wulf
    MFC after:      1 month
    Differential revision:  https://reviews.freebsd.org/D34437
---
 sys/dev/hid/bcm5974.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++--
 sys/dev/usb/usbdevs   |  1 +
 2 files changed, 84 insertions(+), 3 deletions(-)

diff --git a/sys/dev/hid/bcm5974.c b/sys/dev/hid/bcm5974.c
index a7f2a076bc94..dc0edc8dd27d 100644
--- a/sys/dev/hid/bcm5974.c
+++ b/sys/dev/hid/bcm5974.c
@@ -114,11 +114,13 @@ enum tp_type {
 	TYPE2,			/* button integrated in trackpad */
 	TYPE3,			/* additional header fields since June 2013 */
 	TYPE4,                  /* additional header field for pressure data */
+	TYPE_MT2U,			/* Magic Trackpad 2 USB */
 	TYPE_CNT
 };
 
 /* list of device capability bits */
 #define	HAS_INTEGRATED_BUTTON	1
+#define	USES_COMPACT_REPORT	2
 
 struct tp_type_params {
 	uint8_t	caps;		/* device capability bitmask */
@@ -150,6 +152,36 @@ struct tp_type_params {
 		.offset = 23 * 2,
 		.delta = 2,
 	},
+	[TYPE_MT2U] = {
+		.caps = HAS_INTEGRATED_BUTTON | USES_COMPACT_REPORT,
+		.button = 1,
+		.offset = 12,
+		.delta = 0,
+	},
+};
+
+/* trackpad finger structure - compact version for external "Magic" devices */
+struct tp_finger_compact {
+	uint32_t coords; /* not struct directly due to endian conversion */
+	uint8_t touch_major;
+	uint8_t touch_minor;
+	uint8_t size;
+	uint8_t pressure;
+	unsigned int orientation: 3;
+	unsigned int _unknown1: 1;
+	unsigned int id: 4;
+} __packed;
+
+_Static_assert((sizeof(struct tp_finger_compact) == 9), "tp_finger struct size must be 9");
+
+union tp_finger_compact_coords {
+	uint32_t num;
+	struct {
+		signed int x: 13;
+		signed int y: 13;
+		signed int _unknown: 4;
+		signed int state: 2;
+	} __packed;
 };
 
 /* trackpad finger structure - little endian */
@@ -191,6 +223,7 @@ enum {
 	BCM5974_FLAG_WELLSPRING7A,
 	BCM5974_FLAG_WELLSPRING8,
 	BCM5974_FLAG_WELLSPRING9,
+	BCM5974_FLAG_MAGIC_TRACKPAD2_USB,
 	BCM5974_FLAG_MAX,
 };
 
@@ -342,6 +375,15 @@ static const struct bcm5974_dev_params bcm5974_dev_params[BCM5974_FLAG_MAX] = {
 		.o = { SN_ORIENT,
 		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
 	},
+	[BCM5974_FLAG_MAGIC_TRACKPAD2_USB] = {
+		.tp = tp + TYPE_MT2U,
+		.p = { SN_PRESSURE, 0, 256, 256 },
+		.w = { SN_WIDTH, 0, 2048, 0 },
+		.x = { SN_COORD, -3678, 3934, 48 },
+		.y = { SN_COORD, -2478, 2587, 44 },
+		.o = { SN_ORIENT,
+		    -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION, 0 },
+	},
 };
 
 #define	BCM5974_DEV(v,p,i)	{					\
@@ -415,6 +457,9 @@ static const struct hid_device_id bcm5974_devs[] = {
 	BCM5974_DEV(APPLE, WELLSPRING9_ANSI, BCM5974_FLAG_WELLSPRING9),
 	BCM5974_DEV(APPLE, WELLSPRING9_ISO, BCM5974_FLAG_WELLSPRING9),
 	BCM5974_DEV(APPLE, WELLSPRING9_JIS, BCM5974_FLAG_WELLSPRING9),
+
+	/* External "Magic" devices */
+	BCM5974_DEV(APPLE, MAGIC_TRACKPAD2, BCM5974_FLAG_MAGIC_TRACKPAD2_USB),
 };
 
 struct bcm5974_softc {
@@ -550,6 +595,7 @@ bcm5974_set_device_mode(struct bcm5974_softc *sc, bool on)
 	case TYPE3:	/* Type 3 does not require a mode switch */
 		break;
 	case TYPE4:
+	case TYPE_MT2U:
 		err = bcm5974_set_device_mode_hid(sc, on);
 		break;
 	default:
@@ -636,8 +682,10 @@ bcm5974_attach(device_t dev)
 	BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MAJOR, sc->sc_params->w);
 	BCM5974_ABS(sc->sc_evdev, ABS_MT_TOUCH_MINOR, sc->sc_params->w);
 	/* finger approach area */
-	BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w);
-	BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w);
+	if ((sc->sc_params->tp->caps & USES_COMPACT_REPORT) == 0) {
+		BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MAJOR, sc->sc_params->w);
+		BCM5974_ABS(sc->sc_evdev, ABS_MT_WIDTH_MINOR, sc->sc_params->w);
+	}
 	/* finger orientation */
 	BCM5974_ABS(sc->sc_evdev, ABS_MT_ORIENTATION, sc->sc_params->o);
 	/* button properties */
@@ -649,7 +697,8 @@ bcm5974_attach(device_t dev)
 	    0, MAX_FINGERS - 1, 0, 0, 0);
 	evdev_support_abs(sc->sc_evdev, ABS_MT_TRACKING_ID,
 	    -1, MAX_FINGERS - 1, 0, 0, 0);
-	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK);
+	if ((sc->sc_params->tp->caps & USES_COMPACT_REPORT) == 0)
+		evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_TRACK);
 	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_AUTOREL);
 	/* Synaptics compatibility events */
 	evdev_set_flag(sc->sc_evdev, EVDEV_FLAG_MT_STCOMPAT);
@@ -694,12 +743,17 @@ bcm5974_intr(void *context, void *data, hid_size_t len)
 	const struct bcm5974_dev_params *params = sc->sc_params;
 	union evdev_mt_slot slot_data;
 	struct tp_finger *f;
+	struct tp_finger_compact *fc;
+	union tp_finger_compact_coords coords;
 	int ntouch;			/* the finger number in touch */
 	int ibt;			/* button status */
 	int i;
 	int slot;
 	uint8_t fsize = sizeof(struct tp_finger) + params->tp->delta;
 
+	if ((params->tp->caps & USES_COMPACT_REPORT) != 0)
+		fsize = sizeof(struct tp_finger_compact) + params->tp->delta;
+
 	if ((len < params->tp->offset + fsize) ||
 	    ((len - params->tp->offset) % fsize) != 0) {
 		DPRINTFN(BCM5974_LLEVEL_INFO, "Invalid length: %d, %x, %x\n",
@@ -711,6 +765,32 @@ bcm5974_intr(void *context, void *data, hid_size_t len)
 	ntouch = (len - params->tp->offset) / fsize;
 
 	for (i = 0, slot = 0; i != ntouch; i++) {
+		if ((params->tp->caps & USES_COMPACT_REPORT) != 0) {
+			fc = (struct tp_finger_compact *)(((uint8_t *)data) +
+			     params->tp->offset + params->tp->delta + i * fsize);
+			coords.num = le32toh(fc->coords);
+			DPRINTFN(BCM5974_LLEVEL_INFO,
+			    "[%d]ibt=%d, taps=%d, x=%5d, y=%5d, state=%4d, "
+			    "tchmaj=%4d, tchmin=%4d, size=%4d, pressure=%4d, "
+			    "ot=%4x, id=%4x\n",
+			    i, ibt, ntouch, coords.x, coords.y,
+			    fc->state, fc->touch_major, fc->touch_minor, fc->size,
+			    fc->pressure, fc->orientation, fc->id);
+			if (fc->touch_major == 0)
+				continue;
+			slot_data = (union evdev_mt_slot) {
+				.id = fc->id,
+				.x = coords.x,
+				.y = params->y.min + params->y.max - coords.y,
+				.p = fc->pressure,
+				.maj = fc->touch_major << 2,
+				.min = fc->touch_minor << 2,
+				.ori = -fc->orientation,
+			};
+			evdev_mt_push_slot(sc->sc_evdev, slot, &slot_data);
+			slot++;
+			continue;
+		}
 		f = (struct tp_finger *)(((uint8_t *)data) +
 		    params->tp->offset + params->tp->delta + i * fsize);
 		DPRINTFN(BCM5974_LLEVEL_INFO,
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index 54f0a4ace3a2..3af37f74a8b9 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -1193,6 +1193,7 @@ product APPLE WELLSPRING8_JIS	0x0292	Apple Internal Keyboard/Trackpad
 product APPLE WELLSPRING9_ANSI	0x0272	Apple Internal Keyboard/Trackpad
 product APPLE WELLSPRING9_ISO	0x0273	Apple Internal Keyboard/Trackpad
 product APPLE WELLSPRING9_JIS	0x0274	Apple Internal Keyboard/Trackpad
+product APPLE MAGIC_TRACKPAD2	0x0265	Apple Magic Trackpad 2
 product APPLE MOUSE		0x0301	Mouse M4848
 product APPLE OPTMOUSE		0x0302	Optical mouse
 product APPLE MIGHTYMOUSE	0x0304	Mighty Mouse