# \[PATCH 1/3\] HID: apple: Do not reset quirks when the Fn key is not found

- Subsystems: HID CORE LAYER, THE REST
- Maintainers: Jiri Kosina, Benjamin Tissoires, Linus Torvalds
- List: linux-input
- 4 messages, 2 authors
- Page: https://ratatoskr.run/linux-input/2021/11/1578428/t

## \[PATCH 1/3\] HID: apple: Do not reset quirks when the Fn key is not found

- From: José Expósito \<hidden\>
- Date: 2021-11-18 07:30:23 
- Message: https://ratatoskr.run/linux-input/2021/11/1578428.md

```text
When a keyboard without a function key is detected, instead of removing
all quirks, remove only the APPLE_HAS_FN quirk.

Signed-off-by: José Expósito <redacted>
---
 drivers/hid/hid-apple.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 2c9c5faa74a9..a4ca5ed00e5f 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -428,7 +428,7 @@ static int apple_input_configured(struct hid_device *hdev,
 
 	if ((asc->quirks & APPLE_HAS_FN) && !asc->fn_found) {
 		hid_info(hdev, "Fn key not found (Apple Wireless Keyboard clone?), disabling Fn key handling\n");
-		asc->quirks = 0;
+		asc->quirks &= ~APPLE_HAS_FN;
 	}
 
 	return 0;
-- 
2.25.1

```

## \[PATCH 2/3\] HID: apple: Use BIT to define quirks

- From: José Expósito \<hidden\>
- Date: 2021-11-18 07:30:27 
- Message: https://ratatoskr.run/linux-input/2021/11/1578429.md

```text
Replace the existing quirk hardcoded values with the BIT macro in order
to simplify including new quirks.

Signed-off-by: José Expósito <redacted>
---
 drivers/hid/hid-apple.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index a4ca5ed00e5f..b34aeed292a2 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -21,15 +21,15 @@
 
 #include "hid-ids.h"
 
-#define APPLE_RDESC_JIS		0x0001
-#define APPLE_IGNORE_MOUSE	0x0002
-#define APPLE_HAS_FN		0x0004
-/* 0x0008 reserved, was: APPLE_HIDDEV */
-#define APPLE_ISO_TILDE_QUIRK	0x0010
-#define APPLE_MIGHTYMOUSE	0x0020
-#define APPLE_INVERT_HWHEEL	0x0040
-/* 0x0080 reserved, was: APPLE_IGNORE_HIDINPUT */
-#define APPLE_NUMLOCK_EMULATION	0x0100
+#define APPLE_RDESC_JIS		BIT(0)
+#define APPLE_IGNORE_MOUSE	BIT(1)
+#define APPLE_HAS_FN		BIT(2)
+/* BIT(3) reserved, was: APPLE_HIDDEV */
+#define APPLE_ISO_TILDE_QUIRK	BIT(4)
+#define APPLE_MIGHTYMOUSE	BIT(5)
+#define APPLE_INVERT_HWHEEL	BIT(6)
+/* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */
+#define APPLE_NUMLOCK_EMULATION	BIT(8)
 
 #define APPLE_FLAG_FKEY		0x01
 
-- 
2.25.1

```

## \[PATCH 3/3\] HID: apple: Report Magic Keyboard battery over USB

- From: José Expósito \<hidden\>
- Date: 2021-11-18 07:30:29 
- Message: https://ratatoskr.run/linux-input/2021/11/1578430.md

```text
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 <redacted>
---
 drivers/hid/hid-apple.c | 87 ++++++++++++++++++++++++++++++++++++++++-
 1 file 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,
-- 
2.25.1

```

## Re: \[PATCH 1/3\] HID: apple: Do not reset quirks when the Fn key is not found

- From: Jiri Kosina \<jikos@kernel.org\>
- Date: 2021-11-19 15:05:18 
- Message: https://ratatoskr.run/linux-input/2021/11/1580069.md

```text
On Thu, 18 Nov 2021, José Expósito wrote:

> When a keyboard without a function key is detected, instead of removing
> all quirks, remove only the APPLE_HAS_FN quirk.
> 
> Signed-off-by: José Expósito <redacted>

The series is now in hid.git#for-5.17/apple. Thanks,

-- 
Jiri Kosina
SUSE Labs

```
