Skip to content

Commit 0b91b4e

Browse files
JoseExpositoJiri Kosina
authored andcommitted
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>
1 parent 5af0660 commit 0b91b4e

1 file changed

Lines changed: 88 additions & 6 deletions

File tree

drivers/hid/hid-magicmouse.c

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
5757
#define MOUSE_REPORT_ID 0x29
5858
#define MOUSE2_REPORT_ID 0x12
5959
#define DOUBLE_REPORT_ID 0xf7
60+
#define USB_BATTERY_TIMEOUT_MS 60000
61+
6062
/* These definitions are not precise, but they're close enough. (Bits
6163
* 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
6264
* to be some kind of bit mask -- 0x20 may be a near-field reading,
@@ -140,6 +142,7 @@ struct magicmouse_sc {
140142

141143
struct hid_device *hdev;
142144
struct delayed_work work;
145+
struct timer_list battery_timer;
143146
};
144147

145148
static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@@ -735,18 +738,51 @@ static void magicmouse_enable_mt_work(struct work_struct *work)
735738
hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
736739
}
737740

741+
static int magicmouse_fetch_battery(struct hid_device *hdev)
742+
{
743+
#ifdef CONFIG_HID_BATTERY_STRENGTH
744+
struct hid_report_enum *report_enum;
745+
struct hid_report *report;
746+
747+
if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE ||
748+
(hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 &&
749+
hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2))
750+
return -1;
751+
752+
report_enum = &hdev->report_enum[hdev->battery_report_type];
753+
report = report_enum->report_id_hash[hdev->battery_report_id];
754+
755+
if (!report || report->maxfield < 1)
756+
return -1;
757+
758+
if (hdev->battery_capacity == hdev->battery_max)
759+
return -1;
760+
761+
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
762+
return 0;
763+
#else
764+
return -1;
765+
#endif
766+
}
767+
768+
static void magicmouse_battery_timer_tick(struct timer_list *t)
769+
{
770+
struct magicmouse_sc *msc = from_timer(msc, t, battery_timer);
771+
struct hid_device *hdev = msc->hdev;
772+
773+
if (magicmouse_fetch_battery(hdev) == 0) {
774+
mod_timer(&msc->battery_timer,
775+
jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
776+
}
777+
}
778+
738779
static int magicmouse_probe(struct hid_device *hdev,
739780
const struct hid_device_id *id)
740781
{
741782
struct magicmouse_sc *msc;
742783
struct hid_report *report;
743784
int ret;
744785

745-
if (id->vendor == USB_VENDOR_ID_APPLE &&
746-
id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
747-
hdev->type != HID_TYPE_USBMOUSE)
748-
return -ENODEV;
749-
750786
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
751787
if (msc == NULL) {
752788
hid_err(hdev, "can't alloc magicmouse descriptor\n");
@@ -772,6 +808,16 @@ static int magicmouse_probe(struct hid_device *hdev,
772808
return ret;
773809
}
774810

811+
timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0);
812+
mod_timer(&msc->battery_timer,
813+
jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
814+
magicmouse_fetch_battery(hdev);
815+
816+
if (id->vendor == USB_VENDOR_ID_APPLE &&
817+
(id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
818+
(id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && hdev->type != HID_TYPE_USBMOUSE)))
819+
return 0;
820+
775821
if (!msc->input) {
776822
hid_err(hdev, "magicmouse input not registered\n");
777823
ret = -ENOMEM;
@@ -832,17 +878,52 @@ static void magicmouse_remove(struct hid_device *hdev)
832878
{
833879
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
834880

835-
if (msc)
881+
if (msc) {
836882
cancel_delayed_work_sync(&msc->work);
883+
del_timer_sync(&msc->battery_timer);
884+
}
837885

838886
hid_hw_stop(hdev);
839887
}
840888

889+
static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
890+
unsigned int *rsize)
891+
{
892+
/*
893+
* Change the usage from:
894+
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
895+
* 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3
896+
* To:
897+
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
898+
* 0x09, 0x02, // Usage (Mouse) 2
899+
*/
900+
if (hdev->vendor == USB_VENDOR_ID_APPLE &&
901+
(hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
902+
hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
903+
*rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
904+
hid_info(hdev,
905+
"fixing up magicmouse battery report descriptor\n");
906+
*rsize = *rsize - 1;
907+
rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
908+
if (!rdesc)
909+
return NULL;
910+
911+
rdesc[0] = 0x05;
912+
rdesc[1] = 0x01;
913+
rdesc[2] = 0x09;
914+
rdesc[3] = 0x02;
915+
}
916+
917+
return rdesc;
918+
}
919+
841920
static const struct hid_device_id magic_mice[] = {
842921
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
843922
USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
844923
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
845924
USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
925+
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
926+
USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
846927
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
847928
USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
848929
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
@@ -858,6 +939,7 @@ static struct hid_driver magicmouse_driver = {
858939
.id_table = magic_mice,
859940
.probe = magicmouse_probe,
860941
.remove = magicmouse_remove,
942+
.report_fixup = magicmouse_report_fixup,
861943
.raw_event = magicmouse_raw_event,
862944
.event = magicmouse_event,
863945
.input_mapping = magicmouse_input_mapping,

0 commit comments

Comments
 (0)