On Aug 17 2024, Aditya Garg wrote: > From: Kerem Karabay <kekrby@xxxxxxxxx> > > The Touch Bars found on x86 Macs support two USB configurations: one > where the device presents itself as a HID keyboard and can display > predefined sets of keys, and one where the operating system has full > control over what is displayed. This commit adds a driver for the > display functionality of the first configuration. > > Note that currently only T2 Macs are supported. > > This driver is based on previous work done by Ronald Tschalär > <ronald@xxxxxxxxxxxxx>. > > Signed-off-by: Kerem Karabay <kekrby@xxxxxxxxx> > Co-developed-by: Aditya Garg <gargaditya08@xxxxxxxx> > Signed-off-by: Aditya Garg <gargaditya08@xxxxxxxx> > --- > .../ABI/testing/sysfs-driver-hid-appletb-kbd | 13 + > drivers/hid/Kconfig | 11 + > drivers/hid/Makefile | 1 + > drivers/hid/hid-appletb-kbd.c | 304 ++++++++++++++++++ > drivers/hid/hid-quirks.c | 4 +- > 5 files changed, 332 insertions(+), 1 deletion(-) > create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd > create mode 100644 drivers/hid/hid-appletb-kbd.c > > diff --git a/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd b/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd > new file mode 100644 > index 000000000..2a19584d0 > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-driver-hid-appletb-kbd > @@ -0,0 +1,13 @@ > +What: /sys/bus/hid/drivers/hid-appletb-kbd/<dev>/mode > +Date: September, 2023 > +KernelVersion: 6.5 > +Contact: linux-input@xxxxxxxxxxxxxxx > +Description: > + The set of keys displayed on the Touch Bar. > + Valid values are: > + == ================= > + 0 Escape key only > + 1 Function keys > + 2 Media/brightness keys > + 3 None > + == ================= > diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig > index 4988c1fb2..72b665eda 100644 > --- a/drivers/hid/Kconfig > +++ b/drivers/hid/Kconfig > @@ -158,6 +158,17 @@ config HID_APPLETB_BL > To compile this driver as a module, choose M here: the > module will be called hid-appletb-bl. > > +config HID_APPLETB_KBD > + tristate "Apple Touch Bar Keyboard Mode" > + depends on USB_HID > + help > + Say Y here if you want support for the keyboard mode (escape, > + function, media and brightness keys) of Touch Bars on x86 MacBook > + Pros. > + > + To compile this driver as a module, choose M here: the > + module will be called hid-appletb-kbd. > + > config HID_ASUS > tristate "Asus" > depends on USB_HID > diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile > index 1d825a474..d903c9a26 100644 > --- a/drivers/hid/Makefile > +++ b/drivers/hid/Makefile > @@ -30,6 +30,7 @@ obj-$(CONFIG_HID_ACRUX) += hid-axff.o > obj-$(CONFIG_HID_APPLE) += hid-apple.o > obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o > obj-$(CONFIG_HID_APPLETB_BL) += hid-appletb-bl.o > +obj-$(CONFIG_HID_APPLETB_KBD) += hid-appletb-kbd.o > obj-$(CONFIG_HID_CREATIVE_SB0540) += hid-creative-sb0540.o > obj-$(CONFIG_HID_ASUS) += hid-asus.o > obj-$(CONFIG_HID_AUREAL) += hid-aureal.o > diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c > new file mode 100644 > index 000000000..ecac68fc7 > --- /dev/null > +++ b/drivers/hid/hid-appletb-kbd.c > @@ -0,0 +1,304 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Apple Touch Bar Keyboard Mode Driver > + * > + * Copyright (c) 2017-2018 Ronald Tschalär > + * Copyright (c) 2022-2023 Kerem Karabay <kekrby@xxxxxxxxx> > + * Copyright (c) 2024 Aditya Garg <gargaditya08@xxxxxxxx> > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/hid.h> > +#include <linux/usb.h> > +#include <linux/input.h> > +#include <linux/sysfs.h> > +#include <linux/bitops.h> > +#include <linux/module.h> > +#include <linux/string.h> > +#include <linux/input/sparse-keymap.h> > + > +#include "hid-ids.h" > + > +#define APPLETB_KBD_MODE_ESC 0 > +#define APPLETB_KBD_MODE_FN 1 > +#define APPLETB_KBD_MODE_SPCL 2 > +#define APPLETB_KBD_MODE_OFF 3 > +#define APPLETB_KBD_MODE_MAX APPLETB_KBD_MODE_OFF > + > +#define HID_USAGE_MODE 0x00ff0004 > + > +static int appletb_tb_def_mode = APPLETB_KBD_MODE_SPCL; > +module_param_named(mode, appletb_tb_def_mode, int, 0444); > +MODULE_PARM_DESC(mode, "Default touchbar mode:\n" > + " 0 - escape key only\n" > + " 1 - function-keys\n" > + " [2] - special keys"); > + > +struct appletb_kbd { > + struct hid_field *mode_field; > + > + u8 saved_mode; > + u8 current_mode; > +}; > + > +static const struct key_entry appletb_kbd_keymap[] = { > + { KE_KEY, KEY_ESC, { KEY_ESC } }, > + { KE_KEY, KEY_F1, { KEY_BRIGHTNESSDOWN } }, > + { KE_KEY, KEY_F2, { KEY_BRIGHTNESSUP } }, > + { KE_KEY, KEY_F3, { KEY_RESERVED } }, > + { KE_KEY, KEY_F4, { KEY_RESERVED } }, > + { KE_KEY, KEY_F5, { KEY_KBDILLUMDOWN } }, > + { KE_KEY, KEY_F6, { KEY_KBDILLUMUP } }, > + { KE_KEY, KEY_F7, { KEY_PREVIOUSSONG } }, > + { KE_KEY, KEY_F8, { KEY_PLAYPAUSE } }, > + { KE_KEY, KEY_F9, { KEY_NEXTSONG } }, > + { KE_KEY, KEY_F10, { KEY_MUTE } }, > + { KE_KEY, KEY_F11, { KEY_VOLUMEDOWN } }, > + { KE_KEY, KEY_F12, { KEY_VOLUMEUP } }, > + { KE_END, 0 } > +}; > + > +static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode) > +{ > + struct hid_report *report = kbd->mode_field->report; > + struct hid_device *hdev = report->device; > + int ret; > + > + ret = hid_hw_power(hdev, PM_HINT_FULLON); > + if (ret) { > + hid_err(hdev, "Device didn't resume (%pe)\n", ERR_PTR(ret)); > + return ret; > + } > + > + ret = hid_set_field(kbd->mode_field, 0, mode); > + if (ret) { > + hid_err(hdev, "Failed to set mode field to %u (%pe)\n", mode, ERR_PTR(ret)); > + goto power_normal; > + } > + > + hid_hw_request(hdev, report, HID_REQ_SET_REPORT); > + > + kbd->current_mode = mode; > + > +power_normal: > + hid_hw_power(hdev, PM_HINT_NORMAL); > + > + return ret; > +} > + > +static ssize_t mode_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + struct appletb_kbd *kbd = dev_get_drvdata(dev); > + > + return sysfs_emit(buf, "%d\n", kbd->current_mode); > +} > + > +static ssize_t mode_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t size) > +{ > + struct appletb_kbd *kbd = dev_get_drvdata(dev); > + u8 mode; > + int ret; > + > + ret = kstrtou8(buf, 0, &mode); > + if (ret) > + return ret; > + > + if (mode > APPLETB_KBD_MODE_MAX) > + return -EINVAL; > + > + ret = appletb_kbd_set_mode(kbd, mode); > + > + return ret < 0 ? ret : size; > +} > +static DEVICE_ATTR_RW(mode); > + > +struct attribute *appletb_kbd_attrs[] = { > + &dev_attr_mode.attr, > + NULL > +}; > +ATTRIBUTE_GROUPS(appletb_kbd); > + > +static int appletb_tb_key_to_slot(unsigned int code) > +{ > + switch (code) { > + case KEY_ESC: > + return 0; > + case KEY_F1 ... KEY_F10: > + return code - KEY_F1 + 1; > + case KEY_F11 ... KEY_F12: > + return code - KEY_F11 + 11; > + > + default: > + return -EINVAL; > + } > +} > + > +static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *field, > + struct hid_usage *usage, __s32 value) > +{ > + struct appletb_kbd *kbd = hid_get_drvdata(hdev); > + struct key_entry *translation; > + struct input_dev *input; > + int slot; > + > + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || usage->type != EV_KEY) > + return 0; > + > + input = field->hidinput->input; > + > + /* > + * Skip non-touch-bar keys. > + * > + * Either the touch bar itself or usbhid generate a slew of key-down > + * events for all the meta keys. None of which we're at all interested > + * in. > + */ > + slot = appletb_tb_key_to_slot(usage->code); > + if (slot < 0) > + return 0; > + > + translation = sparse_keymap_entry_from_scancode(input, usage->code); You are missing a Kconfig depends or select here because when enabling this module, we can get ERROR: modpost: "sparse_keymap_setup" [drivers/hid/hid-appletb-kbd.ko] undefined! ERROR: modpost: "sparse_keymap_entry_from_scancode" [drivers/hid/hid-appletb-kbd.ko] undefined! FWIW, I tried "depend on INPUT_SPARSEKMAP" and "select INPUT_SPARSEKMAP", but in both cases the error was there, so I suspect there is one extra config to select (could be my script but if I can enter this state, I believe others will get into it as well). Cheers, Benjamin > + > + if (translation && kbd->current_mode == APPLETB_KBD_MODE_SPCL) { > + input_event(input, usage->type, translation->keycode, value); > + > + return 1; > + } > + > + return kbd->current_mode == APPLETB_KBD_MODE_OFF; > +} > + > +static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_input *hidinput) > +{ > + int idx; > + struct input_dev *input = hidinput->input; > + > + /* > + * Clear various input capabilities that are blindly set by the hid > + * driver (usbkbd.c) > + */ > + memset(input->evbit, 0, sizeof(input->evbit)); > + memset(input->keybit, 0, sizeof(input->keybit)); > + memset(input->ledbit, 0, sizeof(input->ledbit)); > + > + __set_bit(EV_REP, input->evbit); > + > + sparse_keymap_setup(input, appletb_kbd_keymap, NULL); > + > + for (idx = 0; appletb_kbd_keymap[idx].type != KE_END; idx++) { > + input_set_capability(input, EV_KEY, appletb_kbd_keymap[idx].code); > + } > + > + return 0; > +} > + > +static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id) > +{ > + struct appletb_kbd *kbd; > + struct device *dev = &hdev->dev; > + struct hid_field *mode_field; > + int ret; > + > + ret = hid_parse(hdev); > + if (ret) > + return dev_err_probe(dev, ret, "HID parse failed\n"); > + > + mode_field = hid_find_field(hdev, HID_OUTPUT_REPORT, > + HID_GD_KEYBOARD, HID_USAGE_MODE); > + if (!mode_field) > + return -ENODEV; > + > + kbd = devm_kzalloc(dev, sizeof(*kbd), GFP_KERNEL); > + if (!kbd) > + return -ENOMEM; > + > + kbd->mode_field = mode_field; > + > + ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT); > + if (ret) > + return dev_err_probe(dev, ret, "HID hw start failed\n"); > + > + ret = hid_hw_open(hdev); > + if (ret) { > + dev_err_probe(dev, ret, "HID hw open failed\n"); > + goto stop_hw; > + } > + > + ret = appletb_kbd_set_mode(kbd, appletb_tb_def_mode); > + if (ret) { > + dev_err_probe(dev, ret, "Failed to set touchbar mode\n"); > + goto close_hw; > + } > + > + hid_set_drvdata(hdev, kbd); > + > + return 0; > + > +close_hw: > + hid_hw_close(hdev); > +stop_hw: > + hid_hw_stop(hdev); > + return ret; > +} > + > +static void appletb_kbd_remove(struct hid_device *hdev) > +{ > + struct appletb_kbd *kbd = hid_get_drvdata(hdev); > + > + appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); > + > + hid_hw_close(hdev); > + hid_hw_stop(hdev); > +} > + > +#ifdef CONFIG_PM > +static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg) > +{ > + struct appletb_kbd *kbd = hid_get_drvdata(hdev); > + > + kbd->saved_mode = kbd->current_mode; > + appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); > + > + return 0; > +} > + > +static int appletb_kbd_reset_resume(struct hid_device *hdev) > +{ > + struct appletb_kbd *kbd = hid_get_drvdata(hdev); > + > + appletb_kbd_set_mode(kbd, kbd->saved_mode); > + > + return 0; > +} > +#endif > + > +static const struct hid_device_id appletb_kbd_hid_ids[] = { > + /* MacBook Pro's 2018, 2019, with T2 chip: iBridge Display */ > + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, > + { } > +}; > +MODULE_DEVICE_TABLE(hid, appletb_kbd_hid_ids); > + > +static struct hid_driver appletb_kbd_hid_driver = { > + .name = "hid-appletb-kbd", > + .id_table = appletb_kbd_hid_ids, > + .probe = appletb_kbd_probe, > + .remove = appletb_kbd_remove, > + .event = appletb_kbd_hid_event, > + .input_configured = appletb_kbd_input_configured, > +#ifdef CONFIG_PM > + .suspend = appletb_kbd_suspend, > + .reset_resume = appletb_kbd_reset_resume, > +#endif > + .driver.dev_groups = appletb_kbd_groups, > +}; > +module_hid_driver(appletb_kbd_hid_driver); > + > +MODULE_AUTHOR("Ronald Tschalär"); > +MODULE_AUTHOR("Kerem Karabay <kekrby@xxxxxxxxx>"); > +MODULE_DESCRIPTION("MacBookPro Touch Bar Keyboard Mode Driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c > index 818d41a35..7c576d654 100644 > --- a/drivers/hid/hid-quirks.c > +++ b/drivers/hid/hid-quirks.c > @@ -328,7 +328,6 @@ static const struct hid_device_id hid_have_special_driver[] = { > { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, > { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) }, > { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) }, > - { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, > #endif > #if IS_ENABLED(CONFIG_HID_APPLEIR) > { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, > @@ -340,6 +339,9 @@ static const struct hid_device_id hid_have_special_driver[] = { > #if IS_ENABLED(CONFIG_HID_APPLETB_BL) > { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT) }, > #endif > +#if IS_ENABLED(CONFIG_HID_APPLETB_KBD) > + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, > +#endif > #if IS_ENABLED(CONFIG_HID_ASUS) > { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD) }, > { HID_I2C_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD) }, > -- > 2.43.0 >