Skip to content

Commit 39855f0

Browse files
committed
HID: magicmouse: add support for Macbook trackpads
The trackpads in Macbooks beginning in 2015 are HID devices connected over SPI. On Intel Macbooks they are currently supported by applespi.c. This chang adds support for the trackpads on Apple Silicon Macbooks starting in late 2020. They use a new HID over SPI transport driver. The touch report format differs from USB/BT Magic Trackpads. It is the same format as the type 4 format supported by bcm5974.c. Signed-off-by: Janne Grunau <j@jannau.net>
1 parent 5c40f74 commit 39855f0

2 files changed

Lines changed: 260 additions & 3 deletions

File tree

drivers/hid/Kconfig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,11 +689,13 @@ config LOGIWHEELS_FF
689689

690690
config HID_MAGICMOUSE
691691
tristate "Apple Magic Mouse/Trackpad multi-touch support"
692+
default SPI_HID_APPLE
692693
help
693694
Support for the Apple Magic Mouse/Trackpad multi-touch.
694695

695696
Say Y here if you want support for the multi-touch features of the
696-
Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
697+
Apple Wireless "Magic" Mouse, the Apple Wireless "Magic" Trackpad and
698+
force touch Trackpads in Macbooks starting from 2015.
697699

698700
config HID_MALTRON
699701
tristate "Maltron L90 keyboard"

drivers/hid/hid-magicmouse.c

Lines changed: 257 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
114114
#define TRACKPAD2_RES_Y \
115115
((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
116116

117+
#define J314_TP_DIMENSION_X (float)13000
118+
#define J314_TP_MIN_X -5900
119+
#define J314_TP_MAX_X 6500
120+
#define J314_TP_RES_X \
121+
((J314_TP_MAX_X - J314_TP_MIN_X) / (J314_TP_DIMENSION_X / 100))
122+
#define J314_TP_DIMENSION_Y (float)8100
123+
#define J314_TP_MIN_Y -200
124+
#define J314_TP_MAX_Y 7400
125+
#define J314_TP_RES_Y \
126+
((J314_TP_MAX_Y - J314_TP_MIN_Y) / (J314_TP_DIMENSION_Y / 100))
127+
128+
#define J314_TP_MAX_FINGER_ORIENTATION 16384
117129

118130
struct magicmouse_input_ops {
119131
int (*raw_event)(struct hid_device *hdev,
@@ -534,6 +546,157 @@ static int magicmouse_raw_event_usb(struct hid_device *hdev,
534546
return 1;
535547
}
536548

549+
/**
550+
* struct tp_finger - single trackpad finger structure, le16-aligned
551+
*
552+
* @unknown1: unknown
553+
* @unknown2: unknown
554+
* @abs_x: absolute x coordinate
555+
* @abs_y: absolute y coordinate
556+
* @rel_x: relative x coordinate
557+
* @rel_y: relative y coordinate
558+
* @tool_major: tool area, major axis
559+
* @tool_minor: tool area, minor axis
560+
* @orientation: 16384 when point, else 15 bit angle
561+
* @touch_major: touch area, major axis
562+
* @touch_minor: touch area, minor axis
563+
* @unused: zeros
564+
* @pressure: pressure on forcetouch touchpad
565+
* @multi: one finger: varies, more fingers: constant
566+
* @crc16: on last finger: crc over the whole message struct
567+
* (i.e. message header + this struct) minus the last
568+
* @crc16 field; unknown on all other fingers.
569+
*/
570+
struct tp_finger {
571+
__le16 unknown1;
572+
__le16 unknown2;
573+
__le16 abs_x;
574+
__le16 abs_y;
575+
__le16 rel_x;
576+
__le16 rel_y;
577+
__le16 tool_major;
578+
__le16 tool_minor;
579+
__le16 orientation;
580+
__le16 touch_major;
581+
__le16 touch_minor;
582+
__le16 unused[2];
583+
__le16 pressure;
584+
__le16 multi;
585+
} __attribute__((packed, aligned(2)));
586+
587+
/**
588+
* struct trackpad report
589+
*
590+
* @report_id: reportid
591+
* @buttons: HID Usage Buttons 3 1-bit reports
592+
* @num_fingers: the number of fingers being reported in @fingers
593+
* @clicked: same as @buttons
594+
*/
595+
struct tp_header {
596+
// HID mouse report
597+
u8 report_id;
598+
u8 buttons;
599+
u8 rel_x;
600+
u8 rel_y;
601+
u8 padding[4];
602+
// HID vendor part, up to 1751 bytes
603+
u8 unknown[22];
604+
u8 num_fingers;
605+
u8 clicked;
606+
u8 unknown3[14];
607+
};
608+
609+
static inline int le16_to_int(__le16 x)
610+
{
611+
return (signed short)le16_to_cpu(x);
612+
}
613+
614+
static void report_finger_data(struct input_dev *input, int slot,
615+
const struct input_mt_pos *pos,
616+
const struct tp_finger *f)
617+
{
618+
input_mt_slot(input, slot);
619+
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
620+
621+
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
622+
le16_to_int(f->touch_major) << 1);
623+
input_report_abs(input, ABS_MT_TOUCH_MINOR,
624+
le16_to_int(f->touch_minor) << 1);
625+
input_report_abs(input, ABS_MT_WIDTH_MAJOR,
626+
le16_to_int(f->tool_major) << 1);
627+
input_report_abs(input, ABS_MT_WIDTH_MINOR,
628+
le16_to_int(f->tool_minor) << 1);
629+
input_report_abs(input, ABS_MT_ORIENTATION,
630+
J314_TP_MAX_FINGER_ORIENTATION - le16_to_int(f->orientation));
631+
input_report_abs(input, ABS_MT_PRESSURE, le16_to_int(f->pressure));
632+
input_report_abs(input, ABS_MT_POSITION_X, pos->x);
633+
input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
634+
}
635+
636+
static int magicmouse_raw_event_spi(struct hid_device *hdev,
637+
struct hid_report *report, u8 *data, int size)
638+
{
639+
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
640+
struct input_dev *input = msc->input;
641+
struct tp_header *tp_hdr;
642+
struct tp_finger *f;
643+
int i, n;
644+
u32 npoints;
645+
const size_t hdr_sz = sizeof(struct tp_header);
646+
const size_t touch_sz = sizeof(struct tp_finger);
647+
u8 map_contacs[MAX_CONTACTS];
648+
649+
// hid_warn(hdev, "%s\n", __func__);
650+
// print_hex_dump_debug("appleft ev: ", DUMP_PREFIX_OFFSET, 16, 1, data,
651+
// size, false);
652+
653+
if (data[0] != TRACKPAD2_USB_REPORT_ID)
654+
return 0;
655+
656+
/* Expect 46 bytes of prefix, and N * 30 bytes of touch data. */
657+
if (size < hdr_sz || ((size - hdr_sz) % touch_sz) != 0)
658+
return 0;
659+
660+
tp_hdr = (struct tp_header *)data;
661+
662+
npoints = (size - hdr_sz) / touch_sz;
663+
if (npoints < tp_hdr->num_fingers || npoints > MAX_CONTACTS) {
664+
hid_warn(hdev,
665+
"unexpected number of touches (%u) for "
666+
"report\n",
667+
npoints);
668+
return 0;
669+
}
670+
671+
n = 0;
672+
for (i = 0; i < tp_hdr->num_fingers; i++) {
673+
f = (struct tp_finger *)(data + hdr_sz + i * touch_sz);
674+
if (le16_to_int(f->touch_major) == 0)
675+
continue;
676+
677+
hid_dbg(hdev, "ev x:%04x y:%04x\n", le16_to_int(f->abs_x),
678+
le16_to_int(f->abs_y));
679+
msc->pos[n].x = le16_to_int(f->abs_x);
680+
msc->pos[n].y = -le16_to_int(f->abs_y);
681+
map_contacs[n] = i;
682+
n++;
683+
}
684+
685+
input_mt_assign_slots(input, msc->tracking_ids, msc->pos, n, 0);
686+
687+
for (i = 0; i < n; i++) {
688+
int idx = map_contacs[i];
689+
f = (struct tp_finger *)(data + hdr_sz + idx * touch_sz);
690+
report_finger_data(input, msc->tracking_ids[i], &msc->pos[i], f);
691+
}
692+
693+
input_mt_sync_frame(input);
694+
input_report_key(input, BTN_MOUSE, data[1] & 1);
695+
696+
input_sync(input);
697+
return 1;
698+
}
699+
537700
static int magicmouse_event(struct hid_device *hdev, struct hid_field *field,
538701
struct hid_usage *usage, __s32 value)
539702
{
@@ -721,6 +884,79 @@ static int magicmouse_setup_input_usb(struct input_dev *input,
721884
return 0;
722885
}
723886

887+
static int magicmouse_setup_input_spi(struct input_dev *input,
888+
struct hid_device *hdev)
889+
{
890+
int error;
891+
int mt_flags = 0;
892+
893+
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
894+
__clear_bit(BTN_0, input->keybit);
895+
__clear_bit(BTN_RIGHT, input->keybit);
896+
__clear_bit(BTN_MIDDLE, input->keybit);
897+
__clear_bit(EV_REL, input->evbit);
898+
__clear_bit(REL_X, input->relbit);
899+
__clear_bit(REL_Y, input->relbit);
900+
901+
mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK;
902+
903+
/* finger touch area */
904+
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 5000, 0, 0);
905+
input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 5000, 0, 0);
906+
907+
/* finger approach area */
908+
input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 5000, 0, 0);
909+
input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 5000, 0, 0);
910+
911+
/* Note: Touch Y position from the device is inverted relative
912+
* to how pointer motion is reported (and relative to how USB
913+
* HID recommends the coordinates work). This driver keeps
914+
* the origin at the same position, and just uses the additive
915+
* inverse of the reported Y.
916+
*/
917+
918+
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 6000, 0, 0);
919+
920+
/*
921+
* This makes libinput recognize this as a PressurePad and
922+
* stop trying to use pressure for touch size. Pressure unit
923+
* seems to be ~grams on these touchpads.
924+
*/
925+
input_abs_set_res(input, ABS_MT_PRESSURE, 1);
926+
927+
/* finger orientation */
928+
input_set_abs_params(input, ABS_MT_ORIENTATION, -J314_TP_MAX_FINGER_ORIENTATION,
929+
J314_TP_MAX_FINGER_ORIENTATION, 0, 0);
930+
931+
/* finger position */
932+
input_set_abs_params(input, ABS_MT_POSITION_X, J314_TP_MIN_X, J314_TP_MAX_X,
933+
0, 0);
934+
/* Y axis is inverted */
935+
input_set_abs_params(input, ABS_MT_POSITION_Y, -J314_TP_MAX_Y, -J314_TP_MIN_Y,
936+
0, 0);
937+
938+
/* X/Y resolution */
939+
input_abs_set_res(input, ABS_MT_POSITION_X, J314_TP_RES_X);
940+
input_abs_set_res(input, ABS_MT_POSITION_Y, J314_TP_RES_Y);
941+
942+
input_set_events_per_packet(input, 60);
943+
944+
/* touchpad button */
945+
input_set_capability(input, EV_KEY, BTN_MOUSE);
946+
947+
/*
948+
* hid-input may mark device as using autorepeat, but the trackpad does
949+
* not actually want it.
950+
*/
951+
__clear_bit(EV_REP, input->evbit);
952+
953+
error = input_mt_init_slots(input, MAX_CONTACTS, mt_flags);
954+
if (error)
955+
return error;
956+
957+
return 0;
958+
}
959+
724960
static int magicmouse_input_mapping(struct hid_device *hdev,
725961
struct hid_input *hi, struct hid_field *field,
726962
struct hid_usage *usage, unsigned long **bit, int *max)
@@ -779,6 +1015,9 @@ static int magicmouse_enable_multitouch(struct hid_device *hdev)
7791015
feature_size = sizeof(feature_mt_trackpad2_usb);
7801016
feature = feature_mt_trackpad2_usb;
7811017
}
1018+
} else if (hdev->vendor == SPI_VENDOR_ID_APPLE) {
1019+
feature_size = sizeof(feature_mt_trackpad2_usb);
1020+
feature = feature_mt_trackpad2_usb;
7821021
} else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
7831022
feature_size = sizeof(feature_mt_mouse2);
7841023
feature = feature_mt_mouse2;
@@ -854,14 +1093,26 @@ static int magicmouse_probe(struct hid_device *hdev,
8541093
struct hid_report *report;
8551094
int ret;
8561095

1096+
if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE &&
1097+
hdev->type != HID_TYPE_SPI_MOUSE)
1098+
return -ENODEV;
1099+
8571100
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
8581101
if (msc == NULL) {
8591102
hid_err(hdev, "can't alloc magicmouse descriptor\n");
8601103
return -ENOMEM;
8611104
}
8621105

863-
msc->input_ops.raw_event = magicmouse_raw_event_usb;
864-
msc->input_ops.setup_input = magicmouse_setup_input_usb;
1106+
// internal trackpad use a data format use input ops to avoid
1107+
// conflicts with the report ID.
1108+
if (id->vendor == SPI_VENDOR_ID_APPLE) {
1109+
msc->input_ops.raw_event = magicmouse_raw_event_spi;
1110+
msc->input_ops.setup_input = magicmouse_setup_input_spi;
1111+
1112+
} else {
1113+
msc->input_ops.raw_event = magicmouse_raw_event_usb;
1114+
msc->input_ops.setup_input = magicmouse_setup_input_usb;
1115+
}
8651116

8661117
msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
8671118
msc->hdev = hdev;
@@ -914,6 +1165,8 @@ static int magicmouse_probe(struct hid_device *hdev,
9141165
else /* USB_VENDOR_ID_APPLE */
9151166
report = hid_register_report(hdev, HID_INPUT_REPORT,
9161167
TRACKPAD2_USB_REPORT_ID, 0);
1168+
} else if (id->vendor == SPI_VENDOR_ID_APPLE) {
1169+
report = hid_register_report(hdev, HID_INPUT_REPORT, 2, 0);
9171170
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
9181171
report = hid_register_report(hdev, HID_INPUT_REPORT,
9191172
TRACKPAD_REPORT_ID, 0);
@@ -1013,6 +1266,8 @@ static const struct hid_device_id magic_mice[] = {
10131266
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), .driver_data = 0 },
10141267
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
10151268
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), .driver_data = 0 },
1269+
{ HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID),
1270+
.driver_data = 0 },
10161271
{ }
10171272
};
10181273
MODULE_DEVICE_TABLE(hid, magic_mice);

0 commit comments

Comments
 (0)