From 01ab82746c5d79e0fd90ae603372c3fef4698fe1 Mon Sep 17 00:00:00 2001 From: Philipp Paulweber Date: Sat, 7 Sep 2019 00:59:24 +0200 Subject: [PATCH] Patch: hid keyboard backlight support * integrated patch provided by @MCMrARM to enable keyboard backlight for `MacBookPro15,x` and `MacBookAir8,x` as well * see: https://github.com/MCMrARM/mbp2018-etc/tree/master/apple-hid * see: https://github.com/Dunedan/mbp-2016-linux/issues/110#issuecomment-526213306 --- patch-linux-hid.diff | 174 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 164 insertions(+), 10 deletions(-) diff --git a/patch-linux-hid.diff b/patch-linux-hid.diff index 2d0a977..17fdec5 100644 --- a/patch-linux-hid.diff +++ b/patch-linux-hid.diff @@ -1,40 +1,194 @@ diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c -index 1cb41992a..58197f729 100644 +index 1cb41992a..d12b33d43 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c -@@ -557,6 +557,10 @@ static const struct hid_device_id apple_devices[] = { +@@ -6,6 +6,7 @@ + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2008 Jiri Slaby ++ * Copyright (c) 2019 Paul Pawlowski + */ + + /* +@@ -33,6 +34,7 @@ + #define APPLE_INVERT_HWHEEL 0x0040 + #define APPLE_IGNORE_HIDINPUT 0x0080 + #define APPLE_NUMLOCK_EMULATION 0x0100 ++#define APPLE_BACKLIGHT_CTL 0x0200 + + #define APPLE_FLAG_FKEY 0x01 + +@@ -54,11 +56,18 @@ MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") + "(For people who want to keep Windows PC keyboard muscle memory. " + "[0] = as-is, Mac layout. 1 = swapped, Windows layout.)"); + ++struct apple_sc_backlight; + struct apple_sc { + unsigned long quirks; + unsigned int fn_on; + DECLARE_BITMAP(pressed_fn, KEY_CNT); + DECLARE_BITMAP(pressed_numlock, KEY_CNT); ++ struct apple_sc_backlight *backlight; ++}; ++struct apple_sc_backlight { ++ struct led_classdev cdev; ++ struct hid_device *hdev; ++ unsigned short backlight_off, backlight_on_min, backlight_on_max; + }; + + struct apple_key_translation { +@@ -366,6 +375,11 @@ static int apple_input_mapped(struct hid_device *hdev, struct hid_input *hi, + return 0; + } + ++static int apple_init_backlight(struct hid_device *hdev); ++static int apple_set_backlight(struct hid_device *hdev, u16 value, u16 rate); ++static int apple_led_set_backlight(struct led_classdev *led_cdev, ++ enum led_brightness brightness); ++ + static int apple_probe(struct hid_device *hdev, + const struct hid_device_id *id) + { +@@ -401,9 +415,106 @@ static int apple_probe(struct hid_device *hdev, + return ret; + } + ++ if (quirks & APPLE_BACKLIGHT_CTL) ++ apple_init_backlight(hdev); ++ + return 0; + } + ++struct apple_backlight_config_report { ++ u8 report_id; ++ u8 version; ++ u16 backlight_off, backlight_on_min, backlight_on_max; ++}; ++struct apple_backlight_set_report { ++ u8 report_id; ++ u8 version; ++ u16 backlight; ++ u16 rate; ++}; ++ ++static bool apple_check_backlight_support(struct hid_device *hdev) ++{ ++ int i; ++ unsigned hid; ++ struct hid_report *report; ++ ++ list_for_each_entry(report, &hdev->report_enum[HID_INPUT_REPORT].report_list, list) { ++ for (i = 0; i < report->maxfield; i++) { ++ hid = report->field[i]->usage->hid; ++ if ((hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR && (hid & HID_USAGE) == 0xf) ++ return true; ++ } ++ } ++ return false; ++} ++ ++static int apple_init_backlight(struct hid_device *hdev) ++{ ++ int ret; ++ struct apple_sc *asc = hid_get_drvdata(hdev); ++ struct apple_backlight_config_report *rep; ++ ++ if (!apple_check_backlight_support(hdev)) ++ return -EINVAL; ++ ++ rep = kmalloc(0x200, GFP_KERNEL); ++ ret = hid_hw_raw_request(hdev, 0xBFu, (u8 *) rep, sizeof(*rep), HID_FEATURE_REPORT, HID_REQ_GET_REPORT); ++ if (ret < 0) { ++ hid_err(hdev, "backlight request failed\n"); ++ return ret; ++ } ++ if (ret < 8 || rep->version != 1) { ++ hid_err(hdev, "backlight config struct: bad version %i\n", rep->version); ++ kfree(rep); ++ return -EINVAL; ++ } ++ ++ hid_dbg(hdev, "backlight config: off=%u, on_min=%u, on_max=%u\n", ++ rep->backlight_off, rep->backlight_on_min, rep->backlight_on_max); ++ ++ asc->backlight = devm_kzalloc(&hdev->dev, sizeof(*asc->backlight), GFP_KERNEL); ++ if (!asc->backlight) { ++ kfree(rep); ++ return -ENOMEM; ++ } ++ ++ asc->backlight->hdev = hdev; ++ asc->backlight->cdev.name = "apple::kbd_backlight"; ++ asc->backlight->cdev.max_brightness = rep->backlight_on_max; ++ asc->backlight->cdev.brightness_set_blocking = apple_led_set_backlight; ++ kfree(rep); ++ ++ apple_set_backlight(hdev, 0, 0); ++ ++ return devm_led_classdev_register(&hdev->dev, &asc->backlight->cdev); ++} ++ ++static int apple_set_backlight(struct hid_device *hdev, u16 value, u16 rate) ++{ ++ int ret; ++ struct apple_backlight_set_report *rep; ++ ++ rep = kmalloc(sizeof(*rep), GFP_KERNEL); ++ rep->report_id = 0xB0; ++ rep->version = 1; ++ rep->backlight = value; ++ rep->rate = rate; ++ ++ ret = hid_hw_raw_request(hdev, 0xB0u, (u8 *) rep, sizeof(*rep), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); ++ kfree(rep); ++ if (ret) ++ return ret; ++ return 0; ++} ++ ++static int apple_led_set_backlight(struct led_classdev *led_cdev, ++ enum led_brightness brightness) ++{ ++ struct apple_sc_backlight *backlight = container_of(led_cdev, struct apple_sc_backlight, cdev); ++ return apple_set_backlight(backlight->hdev, brightness, 0); ++} ++ + 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 }, +@@ -557,6 +668,10 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS), .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBA8_1), -+ .driver_data = APPLE_HAS_FN }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBA8_X), ++ .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBP15_X), -+ .driver_data = APPLE_HAS_FN }, ++ .driver_data = APPLE_HAS_FN | APPLE_BACKLIGHT_CTL }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h -index adce58f24..1ee41d05c 100644 +index adce58f24..1c3150d40 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -170,6 +170,8 @@ #define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI 0x0272 #define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO 0x0273 #define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS 0x0274 -+#define USB_DEVICE_ID_APPLE_MBA8_1 0x027a ++#define USB_DEVICE_ID_APPLE_MBA8_X 0x027a +#define USB_DEVICE_ID_APPLE_MBP15_X 0x027b #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b #define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c -index 77ffba48c..3853d11d3 100644 +index 77ffba48c..93c5f0297 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -269,6 +269,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBA8_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBA8_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBP15_X) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) }, @@ -43,7 +197,7 @@ index 77ffba48c..3853d11d3 100644 { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING9_JIS) }, -+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBA8_1) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBA8_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MBP15_X) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },