diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/hid-apple.c | 87 |
1 files changed, 85 insertions, 2 deletions
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index b34aeed292a2..2bd8276411e0 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -16,8 +16,10 @@ #include <linux/device.h> #include <linux/hid.h> +#include <linux/jiffies.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/timer.h> #include "hid-ids.h" @@ -30,10 +32,12 @@ #define APPLE_INVERT_HWHEEL BIT(6) /* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */ #define APPLE_NUMLOCK_EMULATION BIT(8) +#define APPLE_RDESC_BATTERY BIT(9) #define APPLE_FLAG_FKEY 0x01 #define HID_COUNTRY_INTERNATIONAL_ISO 13 +#define APPLE_BATTERY_TIMEOUT_MS 60000 static unsigned int fnmode = 1; module_param(fnmode, uint, 0644); @@ -58,10 +62,12 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. " "[0] = as-is, Mac layout, 1 = swapped, PC layout)"); struct apple_sc { + struct hid_device *hdev; unsigned long quirks; unsigned int fn_on; unsigned int fn_found; DECLARE_BITMAP(pressed_numlock, KEY_CNT); + struct timer_list battery_timer; }; struct apple_key_translation { @@ -333,6 +339,43 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field, return 0; } +static int apple_fetch_battery(struct hid_device *hdev) +{ +#ifdef CONFIG_HID_BATTERY_STRENGTH + struct apple_sc *asc = hid_get_drvdata(hdev); + struct hid_report_enum *report_enum; + struct hid_report *report; + + if (!(asc->quirks & APPLE_RDESC_BATTERY) || !hdev->battery) + 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 apple_battery_timer_tick(struct timer_list *t) +{ + struct apple_sc *asc = from_timer(asc, t, battery_timer); + struct hid_device *hdev = asc->hdev; + + if (apple_fetch_battery(hdev) == 0) { + mod_timer(&asc->battery_timer, + jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); + } +} + /* * MacBook JIS keyboard has wrong logical maximum * Magic Keyboard JIS has wrong logical maximum @@ -354,6 +397,30 @@ static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, "fixing up MacBook JIS keyboard report descriptor\n"); rdesc[53] = rdesc[59] = 0xe7; } + + /* + * 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, 0x06, // Usage (Keyboard) 2 + */ + if ((asc->quirks & APPLE_RDESC_BATTERY) && *rsize == 83 && + rdesc[46] == 0x84 && rdesc[58] == 0x85) { + hid_info(hdev, + "fixing up Magic Keyboard 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] = 0x06; + } + return rdesc; } @@ -447,6 +514,7 @@ static int apple_probe(struct hid_device *hdev, return -ENOMEM; } + asc->hdev = hdev; asc->quirks = quirks; hid_set_drvdata(hdev, asc); @@ -463,9 +531,23 @@ static int apple_probe(struct hid_device *hdev, return ret; } + timer_setup(&asc->battery_timer, apple_battery_timer_tick, 0); + mod_timer(&asc->battery_timer, + jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS)); + apple_fetch_battery(hdev); + return 0; } +static void apple_remove(struct hid_device *hdev) +{ + struct apple_sc *asc = hid_get_drvdata(hdev); + + del_timer_sync(&asc->battery_timer); + + hid_hw_stop(hdev); +} + static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, @@ -540,11 +622,11 @@ static const struct hid_device_id apple_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015), - .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), @@ -650,6 +732,7 @@ static struct hid_driver apple_driver = { .id_table = apple_devices, .report_fixup = apple_report_fixup, .probe = apple_probe, + .remove = apple_remove, .event = apple_event, .input_mapping = apple_input_mapping, .input_mapped = apple_input_mapped, |