#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"
 
 #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);
                "[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 {
        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
                         "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;
 }
 
                return -ENOMEM;
        }
 
+       asc->hdev = hdev;
        asc->quirks = quirks;
 
        hid_set_drvdata(hdev, asc);
                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 },
        { 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),
        .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,