From: Aditya Garg <gargaditya08@xxxxxxxx> This patch adds support for the switching between the Media and Function keys on the touchbar by pressing the Fn key on Apple Internal Keyboard. Signed-off-by: Aditya Garg <gargaditya08@xxxxxxxx> --- drivers/hid/hid-appletb-kbd.c | 128 ++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c index ecac68fc7..442c4d884 100644 --- a/drivers/hid/hid-appletb-kbd.c +++ b/drivers/hid/hid-appletb-kbd.c @@ -26,6 +26,8 @@ #define APPLETB_KBD_MODE_OFF 3 #define APPLETB_KBD_MODE_MAX APPLETB_KBD_MODE_OFF +#define APPLETB_DEVID_KEYBOARD 1 + #define HID_USAGE_MODE 0x00ff0004 static int appletb_tb_def_mode = APPLETB_KBD_MODE_SPCL; @@ -35,11 +37,18 @@ MODULE_PARM_DESC(mode, "Default touchbar mode:\n" " 1 - function-keys\n" " [2] - special keys"); +static bool appletb_tb_fn_toggle = true; +module_param_named(fntoggle, appletb_tb_fn_toggle, bool, 0644); +MODULE_PARM_DESC(fntoggle, "Switch between Fn and media controls on pressing Fn key"); + struct appletb_kbd { struct hid_field *mode_field; u8 saved_mode; u8 current_mode; + struct input_handler inp_handler; + struct input_handle kbd_handle; + }; static const struct key_entry appletb_kbd_keymap[] = { @@ -172,6 +181,75 @@ static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *fiel return kbd->current_mode == APPLETB_KBD_MODE_OFF; } +static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct appletb_kbd *kbd = handle->private; + + if (type == EV_KEY && code == KEY_FN && appletb_tb_fn_toggle) { + if (value == 1) { + kbd->saved_mode = kbd->current_mode; + if (kbd->current_mode == APPLETB_KBD_MODE_SPCL) + appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_FN); + else if (kbd->current_mode == APPLETB_KBD_MODE_FN) + appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_SPCL); + } else if (value == 0) { + if (kbd->saved_mode != kbd->current_mode) + appletb_kbd_set_mode(kbd, kbd->saved_mode); + } + } +} + +static int appletb_kbd_inp_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + struct appletb_kbd *kbd = handler->private; + struct input_handle *handle; + int rc; + + if (id->driver_info == APPLETB_DEVID_KEYBOARD) { + handle = &kbd->kbd_handle; + handle->name = "tbkbd"; + } else { + return -ENOENT; + } + + if (handle->dev) + return -EEXIST; + + handle->open = 0; + handle->dev = input_get_device(dev); + handle->handler = handler; + handle->private = kbd; + + rc = input_register_handle(handle); + if (rc) + goto err_free_dev; + + rc = input_open_device(handle); + if (rc) + goto err_unregister_handle; + + return 0; + + err_unregister_handle: + input_unregister_handle(handle); + err_free_dev: + input_put_device(handle->dev); + handle->dev = NULL; + return rc; +} + +static void appletb_kbd_inp_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + + input_put_device(handle->dev); + handle->dev = NULL; +} + static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { int idx; @@ -196,6 +274,40 @@ static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_inpu return 0; } +static const struct input_device_id appletb_kbd_input_devices[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_BUS | + INPUT_DEVICE_ID_MATCH_VENDOR | + INPUT_DEVICE_ID_MATCH_KEYBIT, + .bustype = BUS_USB, + .vendor = USB_VENDOR_ID_APPLE, + .keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) }, + .driver_info = APPLETB_DEVID_KEYBOARD, + }, + { } +}; + +static bool appletb_kbd_match_internal_device(struct input_handler *handler, + struct input_dev *inp_dev) +{ + struct device *dev = &inp_dev->dev; + + /* in kernel: dev && !is_usb_device(dev) */ + while (dev && !(dev->type && dev->type->name && + !strcmp(dev->type->name, "usb_device"))) + dev = dev->parent; + + /* + * Apple labels all their internal keyboards and trackpads as such, + * instead of maintaining an ever expanding list of product-id's we + * just look at the device's product name. + */ + if (dev) + return !!strstr(to_usb_device(dev)->product, "Internal Keyboard"); + + return false; +} + static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct appletb_kbd *kbd; @@ -228,6 +340,20 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id goto stop_hw; } + kbd->inp_handler.event = appletb_kbd_inp_event; + kbd->inp_handler.connect = appletb_kbd_inp_connect; + kbd->inp_handler.disconnect = appletb_kbd_inp_disconnect; + kbd->inp_handler.name = "appletb"; + kbd->inp_handler.id_table = appletb_kbd_input_devices; + kbd->inp_handler.match = appletb_kbd_match_internal_device; + kbd->inp_handler.private = kbd; + + ret = input_register_handler(&kbd->inp_handler); + if (ret) { + dev_err_probe(dev, ret, "Unable to register keyboard handler\n"); + goto close_hw; + } + ret = appletb_kbd_set_mode(kbd, appletb_tb_def_mode); if (ret) { dev_err_probe(dev, ret, "Failed to set touchbar mode\n"); @@ -251,6 +377,8 @@ static void appletb_kbd_remove(struct hid_device *hdev) appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF); + input_unregister_handler(&kbd->inp_handler); + hid_hw_close(hdev); hid_hw_stop(hdev); } -- 2.43.0