Skip to content

Commit 1a0d1e7

Browse files
committed
HID: magicmouse: Handle touch controller resets on SPI devices
On at least some SPI devices (e.g. recent Apple Silicon machines), the Broadcom touch controller is prone to crashing. When this happens, the STM eventually notices and resets it. It then notifies the driver via HID report 0x60, and the driver needs to re-enable MT mode to make things work again. This poses an additional issue: the hidinput core will close the low-level transport while the device is closed, which can cause us to miss a reset notification. To fix this, override the input open/close callbacks and send the MT enable every time the HID device is opened, instead of only once on probe. This should increase general robustness, even if the reset mechanism doesn't work for some reason, so it's worth doing it for USB devices too. MTP devices are exempt since they do not require the MT enable at all. Signed-off-by: Hector Martin <[email protected]>
1 parent f7d8fa7 commit 1a0d1e7

1 file changed

Lines changed: 133 additions & 70 deletions

File tree

drivers/hid/hid-magicmouse.c

Lines changed: 133 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
6060
#define MOUSE2_REPORT_ID 0x12
6161
#define DOUBLE_REPORT_ID 0xf7
6262
#define SPI_REPORT_ID 0x02
63+
#define SPI_RESET_REPORT_ID 0x60
6364
#define MTP_REPORT_ID 0x75
6465
#define USB_BATTERY_TIMEOUT_MS 60000
6566

@@ -170,6 +171,97 @@ struct magicmouse_sc {
170171
struct magicmouse_input_ops input_ops;
171172
};
172173

174+
static int magicmouse_enable_multitouch(struct hid_device *hdev)
175+
{
176+
const u8 *feature;
177+
const u8 feature_mt[] = { 0xD7, 0x01 };
178+
const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 };
179+
const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 };
180+
const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 };
181+
u8 *buf;
182+
int ret;
183+
int feature_size;
184+
185+
if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
186+
if (hdev->vendor == BT_VENDOR_ID_APPLE) {
187+
feature_size = sizeof(feature_mt_trackpad2_bt);
188+
feature = feature_mt_trackpad2_bt;
189+
} else { /* USB_VENDOR_ID_APPLE */
190+
feature_size = sizeof(feature_mt_trackpad2_usb);
191+
feature = feature_mt_trackpad2_usb;
192+
}
193+
} else if (hdev->vendor == SPI_VENDOR_ID_APPLE) {
194+
feature_size = sizeof(feature_mt_trackpad2_usb);
195+
feature = feature_mt_trackpad2_usb;
196+
} else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
197+
feature_size = sizeof(feature_mt_mouse2);
198+
feature = feature_mt_mouse2;
199+
} else {
200+
feature_size = sizeof(feature_mt);
201+
feature = feature_mt;
202+
}
203+
204+
buf = kmemdup(feature, feature_size, GFP_KERNEL);
205+
if (!buf)
206+
return -ENOMEM;
207+
208+
ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size,
209+
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
210+
kfree(buf);
211+
return ret;
212+
}
213+
214+
static void magicmouse_enable_mt_work(struct work_struct *work)
215+
{
216+
struct magicmouse_sc *msc =
217+
container_of(work, struct magicmouse_sc, work.work);
218+
int ret;
219+
220+
ret = magicmouse_enable_multitouch(msc->hdev);
221+
if (ret < 0)
222+
hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
223+
}
224+
225+
static int magicmouse_open(struct input_dev *dev)
226+
{
227+
struct hid_device *hdev = input_get_drvdata(dev);
228+
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
229+
int ret;
230+
231+
ret = hid_hw_open(hdev);
232+
if (ret)
233+
return ret;
234+
235+
/*
236+
* Some devices repond with 'invalid report id' when feature
237+
* report switching it into multitouch mode is sent to it.
238+
*
239+
* This results in -EIO from the _raw low-level transport callback,
240+
* but there seems to be no other way of switching the mode.
241+
* Thus the super-ugly hacky success check below.
242+
*/
243+
ret = magicmouse_enable_multitouch(hdev);
244+
if (ret == -EIO && hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
245+
schedule_delayed_work(&msc->work, msecs_to_jiffies(500));
246+
return 0;
247+
}
248+
if (ret < 0)
249+
hid_err(hdev, "unable to request touch data (%d)\n", ret);
250+
251+
/*
252+
* MT enable is usually not required after the first time, so don't
253+
* consider it fatal.
254+
*/
255+
return 0;
256+
}
257+
258+
static void magicmouse_close(struct input_dev *dev)
259+
{
260+
struct hid_device *hdev = input_get_drvdata(dev);
261+
262+
hid_hw_close(hdev);
263+
}
264+
173265
static int magicmouse_firm_touch(struct magicmouse_sc *msc)
174266
{
175267
int touch = -1;
@@ -691,12 +783,19 @@ static int magicmouse_raw_event_mtp(struct hid_device *hdev,
691783
static int magicmouse_raw_event_spi(struct hid_device *hdev,
692784
struct hid_report *report, u8 *data, int size)
693785
{
786+
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
694787
const size_t hdr_sz = sizeof(struct tp_mouse_report);
695788

696-
if (size < hdr_sz)
789+
if (!size)
697790
return 0;
698791

699-
if (data[0] != TRACKPAD2_USB_REPORT_ID)
792+
if (data[0] == SPI_RESET_REPORT_ID) {
793+
hid_info(hdev, "Touch controller was reset, re-enabling touch mode\n");
794+
schedule_delayed_work(&msc->work, msecs_to_jiffies(10));
795+
return 1;
796+
}
797+
798+
if (data[0] != TRACKPAD2_USB_REPORT_ID || size < hdr_sz)
700799
return 0;
701800

702801
return magicmouse_raw_event_mtp(hdev, report, data + hdr_sz, size - hdr_sz);
@@ -878,10 +977,17 @@ static int magicmouse_setup_input_usb(struct input_dev *input,
878977
*/
879978
__clear_bit(EV_REP, input->evbit);
880979

980+
/*
981+
* This isn't strictly speaking needed for USB, but enabling MT on
982+
* device open is probably more robust than only doing it once on probe
983+
* even if USB devices are not known to suffer from the SPI reset issue.
984+
*/
985+
input->open = magicmouse_open;
986+
input->close = magicmouse_close;
881987
return 0;
882988
}
883989

884-
static int magicmouse_setup_input_spi(struct input_dev *input,
990+
static int magicmouse_setup_input_mtp(struct input_dev *input,
885991
struct hid_device *hdev)
886992
{
887993
int error;
@@ -954,6 +1060,25 @@ static int magicmouse_setup_input_spi(struct input_dev *input,
9541060
return 0;
9551061
}
9561062

1063+
static int magicmouse_setup_input_spi(struct input_dev *input,
1064+
struct hid_device *hdev)
1065+
{
1066+
int ret = magicmouse_setup_input_mtp(input, hdev);
1067+
if (ret)
1068+
return ret;
1069+
1070+
/*
1071+
* Override the default input->open function to send the MT
1072+
* enable every time the device is opened. This ensures it works
1073+
* even if we missed a reset event due to the device being closed.
1074+
* input->close is overridden for symmetry.
1075+
*/
1076+
input->open = magicmouse_open;
1077+
input->close = magicmouse_close;
1078+
1079+
return 0;
1080+
}
1081+
9571082
static int magicmouse_input_mapping(struct hid_device *hdev,
9581083
struct hid_input *hi, struct hid_field *field,
9591084
struct hid_usage *usage, unsigned long **bit, int *max)
@@ -990,57 +1115,6 @@ static int magicmouse_input_configured(struct hid_device *hdev,
9901115
return 0;
9911116
}
9921117

993-
static int magicmouse_enable_multitouch(struct hid_device *hdev)
994-
{
995-
const u8 *feature;
996-
const u8 feature_mt[] = { 0xD7, 0x01 };
997-
const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 };
998-
const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 };
999-
const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 };
1000-
u8 *buf;
1001-
int ret;
1002-
int feature_size;
1003-
1004-
if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
1005-
if (hdev->vendor == BT_VENDOR_ID_APPLE) {
1006-
feature_size = sizeof(feature_mt_trackpad2_bt);
1007-
feature = feature_mt_trackpad2_bt;
1008-
} else { /* USB_VENDOR_ID_APPLE */
1009-
feature_size = sizeof(feature_mt_trackpad2_usb);
1010-
feature = feature_mt_trackpad2_usb;
1011-
}
1012-
} else if (hdev->vendor == SPI_VENDOR_ID_APPLE) {
1013-
feature_size = sizeof(feature_mt_trackpad2_usb);
1014-
feature = feature_mt_trackpad2_usb;
1015-
} else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
1016-
feature_size = sizeof(feature_mt_mouse2);
1017-
feature = feature_mt_mouse2;
1018-
} else {
1019-
feature_size = sizeof(feature_mt);
1020-
feature = feature_mt;
1021-
}
1022-
1023-
buf = kmemdup(feature, feature_size, GFP_KERNEL);
1024-
if (!buf)
1025-
return -ENOMEM;
1026-
1027-
ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size,
1028-
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
1029-
kfree(buf);
1030-
return ret;
1031-
}
1032-
1033-
static void magicmouse_enable_mt_work(struct work_struct *work)
1034-
{
1035-
struct magicmouse_sc *msc =
1036-
container_of(work, struct magicmouse_sc, work.work);
1037-
int ret;
1038-
1039-
ret = magicmouse_enable_multitouch(msc->hdev);
1040-
if (ret < 0)
1041-
hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
1042-
}
1043-
10441118
static int magicmouse_fetch_battery(struct hid_device *hdev)
10451119
{
10461120
#ifdef CONFIG_HID_BATTERY_STRENGTH
@@ -1100,7 +1174,7 @@ static int magicmouse_probe(struct hid_device *hdev,
11001174
// conflicts with the report ID.
11011175
if (id->bus == BUS_HOST) {
11021176
msc->input_ops.raw_event = magicmouse_raw_event_mtp;
1103-
msc->input_ops.setup_input = magicmouse_setup_input_spi;
1177+
msc->input_ops.setup_input = magicmouse_setup_input_mtp;
11041178
} else if (id->bus == BUS_SPI) {
11051179
msc->input_ops.raw_event = magicmouse_raw_event_spi;
11061180
msc->input_ops.setup_input = magicmouse_setup_input_spi;
@@ -1180,21 +1254,10 @@ static int magicmouse_probe(struct hid_device *hdev,
11801254
if (id->bus == BUS_HOST)
11811255
return 0;
11821256

1183-
/*
1184-
* Some devices repond with 'invalid report id' when feature
1185-
* report switching it into multitouch mode is sent to it.
1186-
*
1187-
* This results in -EIO from the _raw low-level transport callback,
1188-
* but there seems to be no other way of switching the mode.
1189-
* Thus the super-ugly hacky success check below.
1190-
*/
1191-
ret = magicmouse_enable_multitouch(hdev);
1192-
if (ret != -EIO && ret < 0) {
1193-
hid_err(hdev, "unable to request touch data (%d)\n", ret);
1194-
goto err_stop_hw;
1195-
}
1196-
if (ret == -EIO && id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
1197-
schedule_delayed_work(&msc->work, msecs_to_jiffies(500));
1257+
/* SPI devices need to watch for reset events to re-send the MT enable */
1258+
if (id->bus == BUS_SPI) {
1259+
report = hid_register_report(hdev, HID_INPUT_REPORT, SPI_RESET_REPORT_ID, 0);
1260+
report->size = 2;
11981261
}
11991262

12001263
return 0;

0 commit comments

Comments
 (0)