HID: magicmouse: Report battery level over USB

When connected over USB, the Apple Magic Mouse 2 and the Apple Magic
Trackpad 2 register multiple interfaces, one of them is used to report
the battery level.

However, unlike when connected over Bluetooth, the battery level is not
reported automatically and it is required to fetch it manually.

Fix the battery report descriptor and add a timer to fetch the battery
level.

Signed-off-by: José Expósito <jose.exposito89@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
This commit is contained in:
José Expósito 2021-11-18 17:52:08 +01:00 committed by Jiri Kosina
parent 5af06603c4
commit 0b91b4e4da

View file

@ -57,6 +57,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
#define MOUSE_REPORT_ID 0x29 #define MOUSE_REPORT_ID 0x29
#define MOUSE2_REPORT_ID 0x12 #define MOUSE2_REPORT_ID 0x12
#define DOUBLE_REPORT_ID 0xf7 #define DOUBLE_REPORT_ID 0xf7
#define USB_BATTERY_TIMEOUT_MS 60000
/* These definitions are not precise, but they're close enough. (Bits /* These definitions are not precise, but they're close enough. (Bits
* 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
* to be some kind of bit mask -- 0x20 may be a near-field reading, * to be some kind of bit mask -- 0x20 may be a near-field reading,
@ -140,6 +142,7 @@ struct magicmouse_sc {
struct hid_device *hdev; struct hid_device *hdev;
struct delayed_work work; struct delayed_work work;
struct timer_list battery_timer;
}; };
static int magicmouse_firm_touch(struct magicmouse_sc *msc) static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@ -735,6 +738,44 @@ static void magicmouse_enable_mt_work(struct work_struct *work)
hid_err(msc->hdev, "unable to request touch data (%d)\n", ret); hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
} }
static int magicmouse_fetch_battery(struct hid_device *hdev)
{
#ifdef CONFIG_HID_BATTERY_STRENGTH
struct hid_report_enum *report_enum;
struct hid_report *report;
if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE ||
(hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 &&
hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2))
return -1;
report_enum = &hdev->report_enum[hdev->battery_report_type];
report = report_enum->report_id_hash[hdev->battery_report_id];
if (!report || report->maxfield < 1)
return -1;
if (hdev->battery_capacity == hdev->battery_max)
return -1;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
return 0;
#else
return -1;
#endif
}
static void magicmouse_battery_timer_tick(struct timer_list *t)
{
struct magicmouse_sc *msc = from_timer(msc, t, battery_timer);
struct hid_device *hdev = msc->hdev;
if (magicmouse_fetch_battery(hdev) == 0) {
mod_timer(&msc->battery_timer,
jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
}
}
static int magicmouse_probe(struct hid_device *hdev, static int magicmouse_probe(struct hid_device *hdev,
const struct hid_device_id *id) const struct hid_device_id *id)
{ {
@ -742,11 +783,6 @@ static int magicmouse_probe(struct hid_device *hdev,
struct hid_report *report; struct hid_report *report;
int ret; int ret;
if (id->vendor == USB_VENDOR_ID_APPLE &&
id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
hdev->type != HID_TYPE_USBMOUSE)
return -ENODEV;
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
if (msc == NULL) { if (msc == NULL) {
hid_err(hdev, "can't alloc magicmouse descriptor\n"); hid_err(hdev, "can't alloc magicmouse descriptor\n");
@ -772,6 +808,16 @@ static int magicmouse_probe(struct hid_device *hdev,
return ret; return ret;
} }
timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0);
mod_timer(&msc->battery_timer,
jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
magicmouse_fetch_battery(hdev);
if (id->vendor == USB_VENDOR_ID_APPLE &&
(id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
(id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && hdev->type != HID_TYPE_USBMOUSE)))
return 0;
if (!msc->input) { if (!msc->input) {
hid_err(hdev, "magicmouse input not registered\n"); hid_err(hdev, "magicmouse input not registered\n");
ret = -ENOMEM; ret = -ENOMEM;
@ -832,17 +878,52 @@ static void magicmouse_remove(struct hid_device *hdev)
{ {
struct magicmouse_sc *msc = hid_get_drvdata(hdev); struct magicmouse_sc *msc = hid_get_drvdata(hdev);
if (msc) if (msc) {
cancel_delayed_work_sync(&msc->work); cancel_delayed_work_sync(&msc->work);
del_timer_sync(&msc->battery_timer);
}
hid_hw_stop(hdev); hid_hw_stop(hdev);
} }
static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
/*
* Change the usage from:
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
* 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3
* To:
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
* 0x09, 0x02, // Usage (Mouse) 2
*/
if (hdev->vendor == USB_VENDOR_ID_APPLE &&
(hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
*rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
hid_info(hdev,
"fixing up magicmouse battery report descriptor\n");
*rsize = *rsize - 1;
rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
if (!rdesc)
return NULL;
rdesc[0] = 0x05;
rdesc[1] = 0x01;
rdesc[2] = 0x09;
rdesc[3] = 0x02;
}
return rdesc;
}
static const struct hid_device_id magic_mice[] = { static const struct hid_device_id magic_mice[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 }, USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
@ -858,6 +939,7 @@ static struct hid_driver magicmouse_driver = {
.id_table = magic_mice, .id_table = magic_mice,
.probe = magicmouse_probe, .probe = magicmouse_probe,
.remove = magicmouse_remove, .remove = magicmouse_remove,
.report_fixup = magicmouse_report_fixup,
.raw_event = magicmouse_raw_event, .raw_event = magicmouse_raw_event,
.event = magicmouse_event, .event = magicmouse_event,
.input_mapping = magicmouse_input_mapping, .input_mapping = magicmouse_input_mapping,