mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-07-23 17:47:28 +08:00
359 lines
12 KiB
Diff
359 lines
12 KiB
Diff
From 89d8ef52b73399098877813502dc8320f7f07a69 Mon Sep 17 00:00:00 2001
|
|
From: Janne Grunau <j@jannau.net>
|
|
Date: Thu, 16 Dec 2021 01:17:48 +0100
|
|
Subject: [PATCH 123/171] 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>
|
|
---
|
|
drivers/hid/Kconfig | 4 +-
|
|
drivers/hid/hid-magicmouse.c | 259 ++++++++++++++++++++++++++++++++++-
|
|
2 files changed, 260 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
|
|
index 77c3c00c877c..abaf704d5a81 100644
|
|
--- a/drivers/hid/Kconfig
|
|
+++ b/drivers/hid/Kconfig
|
|
@@ -676,11 +676,13 @@ config LOGIWHEELS_FF
|
|
config HID_MAGICMOUSE
|
|
tristate "Apple Magic Mouse/Trackpad multi-touch support"
|
|
depends on HID
|
|
+ default SPI_HID_APPLE
|
|
help
|
|
Support for the Apple Magic Mouse/Trackpad multi-touch.
|
|
|
|
Say Y here if you want support for the multi-touch features of the
|
|
- Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
|
|
+ Apple Wireless "Magic" Mouse, the Apple Wireless "Magic" Trackpad and
|
|
+ fource touch Trackpads in Macbooks starting from 2015.
|
|
|
|
config HID_MALTRON
|
|
tristate "Maltron L90 keyboard"
|
|
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
|
|
index 734e97234890..65c4b6307ce9 100644
|
|
--- a/drivers/hid/hid-magicmouse.c
|
|
+++ b/drivers/hid/hid-magicmouse.c
|
|
@@ -113,6 +113,18 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
|
|
#define TRACKPAD2_RES_Y \
|
|
((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
|
|
|
|
+#define J314_TP_DIMENSION_X (float)13000
|
|
+#define J314_TP_MIN_X -5900
|
|
+#define J314_TP_MAX_X 6500
|
|
+#define J314_TP_RES_X \
|
|
+ ((J314_TP_MAX_X - J314_TP_MIN_X) / (J314_TP_DIMENSION_X / 100))
|
|
+#define J314_TP_DIMENSION_Y (float)8100
|
|
+#define J314_TP_MIN_Y -200
|
|
+#define J314_TP_MAX_Y 7400
|
|
+#define J314_TP_RES_Y \
|
|
+ ((J314_TP_MAX_Y - J314_TP_MIN_Y) / (J314_TP_DIMENSION_Y / 100))
|
|
+
|
|
+#define J314_TP_MAX_FINGER_ORIENTATION 16384
|
|
|
|
struct magicmouse_input_ops {
|
|
int (*raw_event)(struct hid_device *hdev,
|
|
@@ -519,6 +531,157 @@ static int magicmouse_raw_event_usb(struct hid_device *hdev,
|
|
return 1;
|
|
}
|
|
|
|
+/**
|
|
+ * struct tp_finger - single trackpad finger structure, le16-aligned
|
|
+ *
|
|
+ * @unknown1: unknown
|
|
+ * @unknown2: unknown
|
|
+ * @abs_x: absolute x coordinate
|
|
+ * @abs_y: absolute y coordinate
|
|
+ * @rel_x: relative x coordinate
|
|
+ * @rel_y: relative y coordinate
|
|
+ * @tool_major: tool area, major axis
|
|
+ * @tool_minor: tool area, minor axis
|
|
+ * @orientation: 16384 when point, else 15 bit angle
|
|
+ * @touch_major: touch area, major axis
|
|
+ * @touch_minor: touch area, minor axis
|
|
+ * @unused: zeros
|
|
+ * @pressure: pressure on forcetouch touchpad
|
|
+ * @multi: one finger: varies, more fingers: constant
|
|
+ * @crc16: on last finger: crc over the whole message struct
|
|
+ * (i.e. message header + this struct) minus the last
|
|
+ * @crc16 field; unknown on all other fingers.
|
|
+ */
|
|
+struct tp_finger {
|
|
+ __le16 unknown1;
|
|
+ __le16 unknown2;
|
|
+ __le16 abs_x;
|
|
+ __le16 abs_y;
|
|
+ __le16 rel_x;
|
|
+ __le16 rel_y;
|
|
+ __le16 tool_major;
|
|
+ __le16 tool_minor;
|
|
+ __le16 orientation;
|
|
+ __le16 touch_major;
|
|
+ __le16 touch_minor;
|
|
+ __le16 unused[2];
|
|
+ __le16 pressure;
|
|
+ __le16 multi;
|
|
+} __attribute__((packed, aligned(2)));
|
|
+
|
|
+/**
|
|
+ * struct trackpad report
|
|
+ *
|
|
+ * @report_id: reportid
|
|
+ * @buttons: HID Usage Buttons 3 1-bit reports
|
|
+ * @num_fingers: the number of fingers being reported in @fingers
|
|
+ * @clicked: same as @buttons
|
|
+ */
|
|
+struct tp_header {
|
|
+ // HID mouse report
|
|
+ u8 report_id;
|
|
+ u8 buttons;
|
|
+ u8 rel_x;
|
|
+ u8 rel_y;
|
|
+ u8 padding[4];
|
|
+ // HID vendor part, up to 1751 bytes
|
|
+ u8 unknown[22];
|
|
+ u8 num_fingers;
|
|
+ u8 clicked;
|
|
+ u8 unknown3[14];
|
|
+};
|
|
+
|
|
+static inline int le16_to_int(__le16 x)
|
|
+{
|
|
+ return (signed short)le16_to_cpu(x);
|
|
+}
|
|
+
|
|
+static void report_finger_data(struct input_dev *input, int slot,
|
|
+ const struct input_mt_pos *pos,
|
|
+ const struct tp_finger *f)
|
|
+{
|
|
+ input_mt_slot(input, slot);
|
|
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
|
|
+
|
|
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR,
|
|
+ le16_to_int(f->touch_major) << 1);
|
|
+ input_report_abs(input, ABS_MT_TOUCH_MINOR,
|
|
+ le16_to_int(f->touch_minor) << 1);
|
|
+ input_report_abs(input, ABS_MT_WIDTH_MAJOR,
|
|
+ le16_to_int(f->tool_major) << 1);
|
|
+ input_report_abs(input, ABS_MT_WIDTH_MINOR,
|
|
+ le16_to_int(f->tool_minor) << 1);
|
|
+ input_report_abs(input, ABS_MT_ORIENTATION,
|
|
+ J314_TP_MAX_FINGER_ORIENTATION - le16_to_int(f->orientation));
|
|
+ input_report_abs(input, ABS_MT_PRESSURE, le16_to_int(f->pressure));
|
|
+ input_report_abs(input, ABS_MT_POSITION_X, pos->x);
|
|
+ input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
|
|
+}
|
|
+
|
|
+static int magicmouse_raw_event_spi(struct hid_device *hdev,
|
|
+ struct hid_report *report, u8 *data, int size)
|
|
+{
|
|
+ struct magicmouse_sc *msc = hid_get_drvdata(hdev);
|
|
+ struct input_dev *input = msc->input;
|
|
+ struct tp_header *tp_hdr;
|
|
+ struct tp_finger *f;
|
|
+ int i, n;
|
|
+ u32 npoints;
|
|
+ const size_t hdr_sz = sizeof(struct tp_header);
|
|
+ const size_t touch_sz = sizeof(struct tp_finger);
|
|
+ u8 map_contacs[MAX_CONTACTS];
|
|
+
|
|
+ // hid_warn(hdev, "%s\n", __func__);
|
|
+ // print_hex_dump_debug("appleft ev: ", DUMP_PREFIX_OFFSET, 16, 1, data,
|
|
+ // size, false);
|
|
+
|
|
+ if (data[0] != TRACKPAD2_USB_REPORT_ID)
|
|
+ return 0;
|
|
+
|
|
+ /* Expect 46 bytes of prefix, and N * 30 bytes of touch data. */
|
|
+ if (size < hdr_sz || ((size - hdr_sz) % touch_sz) != 0)
|
|
+ return 0;
|
|
+
|
|
+ tp_hdr = (struct tp_header *)data;
|
|
+
|
|
+ npoints = (size - hdr_sz) / touch_sz;
|
|
+ if (npoints < tp_hdr->num_fingers || npoints > MAX_CONTACTS) {
|
|
+ hid_warn(hdev,
|
|
+ "unexpected number of touches (%u) for "
|
|
+ "report\n",
|
|
+ npoints);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ n = 0;
|
|
+ for (i = 0; i < tp_hdr->num_fingers; i++) {
|
|
+ f = (struct tp_finger *)(data + hdr_sz + i * touch_sz);
|
|
+ if (le16_to_int(f->touch_major) == 0)
|
|
+ continue;
|
|
+
|
|
+ hid_dbg(hdev, "ev x:%04hx y:%04hx\n", le16_to_int(f->abs_x),
|
|
+ le16_to_int(f->abs_y));
|
|
+ msc->pos[n].x = le16_to_int(f->abs_x);
|
|
+ msc->pos[n].y = -le16_to_int(f->abs_y);
|
|
+ map_contacs[n] = i;
|
|
+ n++;
|
|
+ }
|
|
+
|
|
+ input_mt_assign_slots(input, msc->tracking_ids, msc->pos, n, 0);
|
|
+
|
|
+ for (i = 0; i < n; i++) {
|
|
+ int idx = map_contacs[i];
|
|
+ f = (struct tp_finger *)(data + hdr_sz + idx * touch_sz);
|
|
+ report_finger_data(input, msc->tracking_ids[i], &msc->pos[i], f);
|
|
+ }
|
|
+
|
|
+ input_mt_sync_frame(input);
|
|
+ input_report_key(input, BTN_MOUSE, data[1] & 1);
|
|
+
|
|
+ input_sync(input);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
static int magicmouse_event(struct hid_device *hdev, struct hid_field *field,
|
|
struct hid_usage *usage, __s32 value)
|
|
{
|
|
@@ -698,6 +861,79 @@ static int magicmouse_setup_input_usb(struct input_dev *input,
|
|
return 0;
|
|
}
|
|
|
|
+static int magicmouse_setup_input_spi(struct input_dev *input,
|
|
+ struct hid_device *hdev)
|
|
+{
|
|
+ int error;
|
|
+ int mt_flags = 0;
|
|
+
|
|
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
|
+ __clear_bit(BTN_0, input->keybit);
|
|
+ __clear_bit(BTN_RIGHT, input->keybit);
|
|
+ __clear_bit(BTN_MIDDLE, input->keybit);
|
|
+ __clear_bit(EV_REL, input->evbit);
|
|
+ __clear_bit(REL_X, input->relbit);
|
|
+ __clear_bit(REL_Y, input->relbit);
|
|
+
|
|
+ mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK;
|
|
+
|
|
+ /* finger touch area */
|
|
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 5000, 0, 0);
|
|
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 5000, 0, 0);
|
|
+
|
|
+ /* finger approach area */
|
|
+ input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 5000, 0, 0);
|
|
+ input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 5000, 0, 0);
|
|
+
|
|
+ /* Note: Touch Y position from the device is inverted relative
|
|
+ * to how pointer motion is reported (and relative to how USB
|
|
+ * HID recommends the coordinates work). This driver keeps
|
|
+ * the origin at the same position, and just uses the additive
|
|
+ * inverse of the reported Y.
|
|
+ */
|
|
+
|
|
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 6000, 0, 0);
|
|
+
|
|
+ /*
|
|
+ * This makes libinput recognize this as a PressurePad and
|
|
+ * stop trying to use pressure for touch size. Pressure unit
|
|
+ * seems to be ~grams on these touchpads.
|
|
+ */
|
|
+ input_abs_set_res(input, ABS_MT_PRESSURE, 1);
|
|
+
|
|
+ /* finger orientation */
|
|
+ input_set_abs_params(input, ABS_MT_ORIENTATION, -J314_TP_MAX_FINGER_ORIENTATION,
|
|
+ J314_TP_MAX_FINGER_ORIENTATION, 0, 0);
|
|
+
|
|
+ /* finger position */
|
|
+ input_set_abs_params(input, ABS_MT_POSITION_X, J314_TP_MIN_X, J314_TP_MAX_X,
|
|
+ 0, 0);
|
|
+ /* Y axis is inverted */
|
|
+ input_set_abs_params(input, ABS_MT_POSITION_Y, -J314_TP_MAX_Y, -J314_TP_MIN_Y,
|
|
+ 0, 0);
|
|
+
|
|
+ /* X/Y resolution */
|
|
+ input_abs_set_res(input, ABS_MT_POSITION_X, J314_TP_RES_X);
|
|
+ input_abs_set_res(input, ABS_MT_POSITION_Y, J314_TP_RES_Y);
|
|
+
|
|
+ input_set_events_per_packet(input, 60);
|
|
+
|
|
+ /* touchpad button */
|
|
+ input_set_capability(input, EV_KEY, BTN_MOUSE);
|
|
+
|
|
+ /*
|
|
+ * hid-input may mark device as using autorepeat, but the trackpad does
|
|
+ * not actually want it.
|
|
+ */
|
|
+ __clear_bit(EV_REP, input->evbit);
|
|
+
|
|
+ error = input_mt_init_slots(input, MAX_CONTACTS, mt_flags);
|
|
+ if (error)
|
|
+ return error;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int magicmouse_input_mapping(struct hid_device *hdev,
|
|
struct hid_input *hi, struct hid_field *field,
|
|
struct hid_usage *usage, unsigned long **bit, int *max)
|
|
@@ -753,6 +989,9 @@ static int magicmouse_enable_multitouch(struct hid_device *hdev)
|
|
feature_size = sizeof(feature_mt_trackpad2_usb);
|
|
feature = feature_mt_trackpad2_usb;
|
|
}
|
|
+ } else if (hdev->vendor == SPI_VENDOR_ID_APPLE) {
|
|
+ feature_size = sizeof(feature_mt_trackpad2_usb);
|
|
+ feature = feature_mt_trackpad2_usb;
|
|
} else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
|
|
feature_size = sizeof(feature_mt_mouse2);
|
|
feature = feature_mt_mouse2;
|
|
@@ -827,14 +1066,26 @@ static int magicmouse_probe(struct hid_device *hdev,
|
|
struct hid_report *report;
|
|
int ret;
|
|
|
|
+ if (id->bus == BUS_SPI && id->vendor == SPI_VENDOR_ID_APPLE &&
|
|
+ hdev->type != HID_TYPE_SPI_MOUSE)
|
|
+ return -ENODEV;
|
|
+
|
|
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
|
|
if (msc == NULL) {
|
|
hid_err(hdev, "can't alloc magicmouse descriptor\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
- msc->input_ops.raw_event = magicmouse_raw_event_usb;
|
|
- msc->input_ops.setup_input = magicmouse_setup_input_usb;
|
|
+ // internal trackpad use a data format use input ops to avoid
|
|
+ // conflicts with the report ID.
|
|
+ if (id->vendor == SPI_VENDOR_ID_APPLE) {
|
|
+ msc->input_ops.raw_event = magicmouse_raw_event_spi;
|
|
+ msc->input_ops.setup_input = magicmouse_setup_input_spi;
|
|
+
|
|
+ } else {
|
|
+ msc->input_ops.raw_event = magicmouse_raw_event_usb;
|
|
+ msc->input_ops.setup_input = magicmouse_setup_input_usb;
|
|
+ }
|
|
|
|
msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
|
|
msc->hdev = hdev;
|
|
@@ -884,6 +1135,8 @@ static int magicmouse_probe(struct hid_device *hdev,
|
|
else /* USB_VENDOR_ID_APPLE */
|
|
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
|
TRACKPAD2_USB_REPORT_ID, 0);
|
|
+ } else if (id->vendor == SPI_VENDOR_ID_APPLE) {
|
|
+ report = hid_register_report(hdev, HID_INPUT_REPORT, 2, 0);
|
|
} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
|
|
report = hid_register_report(hdev, HID_INPUT_REPORT,
|
|
TRACKPAD_REPORT_ID, 0);
|
|
@@ -978,6 +1231,8 @@ static const struct hid_device_id magic_mice[] = {
|
|
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
|
|
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
|
|
USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
|
|
+ { HID_SPI_DEVICE(SPI_VENDOR_ID_APPLE, HID_ANY_ID),
|
|
+ .driver_data = 0 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(hid, magic_mice);
|
|
--
|
|
2.34.1
|
|
|