Skip to content

Commit 6e14329

Browse files
JoseExpositoJiri Kosina
authored andcommitted
HID: apple: Report Magic Keyboard battery over USB
When connected over USB, the Apple Magic Keyboard 2015 registers 3 different 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. Add a new quirk to fix the battery report descriptor and 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 7f52ece commit 6e14329

1 file changed

Lines changed: 85 additions & 2 deletions

File tree

drivers/hid/hid-apple.c

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
#include <linux/device.h>
1818
#include <linux/hid.h>
19+
#include <linux/jiffies.h>
1920
#include <linux/module.h>
2021
#include <linux/slab.h>
22+
#include <linux/timer.h>
2123

2224
#include "hid-ids.h"
2325

@@ -30,10 +32,12 @@
3032
#define APPLE_INVERT_HWHEEL BIT(6)
3133
/* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */
3234
#define APPLE_NUMLOCK_EMULATION BIT(8)
35+
#define APPLE_RDESC_BATTERY BIT(9)
3336

3437
#define APPLE_FLAG_FKEY 0x01
3538

3639
#define HID_COUNTRY_INTERNATIONAL_ISO 13
40+
#define APPLE_BATTERY_TIMEOUT_MS 60000
3741

3842
static unsigned int fnmode = 1;
3943
module_param(fnmode, uint, 0644);
@@ -58,10 +62,12 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. "
5862
"[0] = as-is, Mac layout, 1 = swapped, PC layout)");
5963

6064
struct apple_sc {
65+
struct hid_device *hdev;
6166
unsigned long quirks;
6267
unsigned int fn_on;
6368
unsigned int fn_found;
6469
DECLARE_BITMAP(pressed_numlock, KEY_CNT);
70+
struct timer_list battery_timer;
6571
};
6672

6773
struct apple_key_translation {
@@ -333,6 +339,43 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
333339
return 0;
334340
}
335341

342+
static int apple_fetch_battery(struct hid_device *hdev)
343+
{
344+
#ifdef CONFIG_HID_BATTERY_STRENGTH
345+
struct apple_sc *asc = hid_get_drvdata(hdev);
346+
struct hid_report_enum *report_enum;
347+
struct hid_report *report;
348+
349+
if (!(asc->quirks & APPLE_RDESC_BATTERY) || !hdev->battery)
350+
return -1;
351+
352+
report_enum = &hdev->report_enum[hdev->battery_report_type];
353+
report = report_enum->report_id_hash[hdev->battery_report_id];
354+
355+
if (!report || report->maxfield < 1)
356+
return -1;
357+
358+
if (hdev->battery_capacity == hdev->battery_max)
359+
return -1;
360+
361+
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
362+
return 0;
363+
#else
364+
return -1;
365+
#endif
366+
}
367+
368+
static void apple_battery_timer_tick(struct timer_list *t)
369+
{
370+
struct apple_sc *asc = from_timer(asc, t, battery_timer);
371+
struct hid_device *hdev = asc->hdev;
372+
373+
if (apple_fetch_battery(hdev) == 0) {
374+
mod_timer(&asc->battery_timer,
375+
jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS));
376+
}
377+
}
378+
336379
/*
337380
* MacBook JIS keyboard has wrong logical maximum
338381
* Magic Keyboard JIS has wrong logical maximum
@@ -354,6 +397,30 @@ static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
354397
"fixing up MacBook JIS keyboard report descriptor\n");
355398
rdesc[53] = rdesc[59] = 0xe7;
356399
}
400+
401+
/*
402+
* Change the usage from:
403+
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
404+
* 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3
405+
* To:
406+
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
407+
* 0x09, 0x06, // Usage (Keyboard) 2
408+
*/
409+
if ((asc->quirks & APPLE_RDESC_BATTERY) && *rsize == 83 &&
410+
rdesc[46] == 0x84 && rdesc[58] == 0x85) {
411+
hid_info(hdev,
412+
"fixing up Magic Keyboard battery report descriptor\n");
413+
*rsize = *rsize - 1;
414+
rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
415+
if (!rdesc)
416+
return NULL;
417+
418+
rdesc[0] = 0x05;
419+
rdesc[1] = 0x01;
420+
rdesc[2] = 0x09;
421+
rdesc[3] = 0x06;
422+
}
423+
357424
return rdesc;
358425
}
359426

@@ -447,6 +514,7 @@ static int apple_probe(struct hid_device *hdev,
447514
return -ENOMEM;
448515
}
449516

517+
asc->hdev = hdev;
450518
asc->quirks = quirks;
451519

452520
hid_set_drvdata(hdev, asc);
@@ -463,9 +531,23 @@ static int apple_probe(struct hid_device *hdev,
463531
return ret;
464532
}
465533

534+
timer_setup(&asc->battery_timer, apple_battery_timer_tick, 0);
535+
mod_timer(&asc->battery_timer,
536+
jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS));
537+
apple_fetch_battery(hdev);
538+
466539
return 0;
467540
}
468541

542+
static void apple_remove(struct hid_device *hdev)
543+
{
544+
struct apple_sc *asc = hid_get_drvdata(hdev);
545+
546+
del_timer_sync(&asc->battery_timer);
547+
548+
hid_hw_stop(hdev);
549+
}
550+
469551
static const struct hid_device_id apple_devices[] = {
470552
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
471553
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
@@ -540,11 +622,11 @@ static const struct hid_device_id apple_devices[] = {
540622
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
541623
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
542624
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015),
543-
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
625+
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
544626
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015),
545627
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
546628
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015),
547-
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
629+
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
548630
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015),
549631
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
550632
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
@@ -650,6 +732,7 @@ static struct hid_driver apple_driver = {
650732
.id_table = apple_devices,
651733
.report_fixup = apple_report_fixup,
652734
.probe = apple_probe,
735+
.remove = apple_remove,
653736
.event = apple_event,
654737
.input_mapping = apple_input_mapping,
655738
.input_mapped = apple_input_mapped,

0 commit comments

Comments
 (0)