Skip to content

Commit 7d62ba8

Browse files
AdityaGarg8Jiri Kosina
authored andcommitted
HID: hid-appletb-kbd: add support for fn toggle between media and function mode
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 <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 8e9b915 commit 7d62ba8

File tree

1 file changed

+128
-0
lines changed

1 file changed

+128
-0
lines changed

drivers/hid/hid-appletb-kbd.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#define APPLETB_KBD_MODE_OFF 3
2727
#define APPLETB_KBD_MODE_MAX APPLETB_KBD_MODE_OFF
2828

29+
#define APPLETB_DEVID_KEYBOARD 1
30+
2931
#define HID_USAGE_MODE 0x00ff0004
3032

3133
static int appletb_tb_def_mode = APPLETB_KBD_MODE_SPCL;
@@ -35,11 +37,18 @@ MODULE_PARM_DESC(mode, "Default touchbar mode:\n"
3537
" 1 - function-keys\n"
3638
" [2] - special keys");
3739

40+
static bool appletb_tb_fn_toggle = true;
41+
module_param_named(fntoggle, appletb_tb_fn_toggle, bool, 0644);
42+
MODULE_PARM_DESC(fntoggle, "Switch between Fn and media controls on pressing Fn key");
43+
3844
struct appletb_kbd {
3945
struct hid_field *mode_field;
4046

4147
u8 saved_mode;
4248
u8 current_mode;
49+
struct input_handler inp_handler;
50+
struct input_handle kbd_handle;
51+
4352
};
4453

4554
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
172181
return kbd->current_mode == APPLETB_KBD_MODE_OFF;
173182
}
174183

184+
static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type,
185+
unsigned int code, int value)
186+
{
187+
struct appletb_kbd *kbd = handle->private;
188+
189+
if (type == EV_KEY && code == KEY_FN && appletb_tb_fn_toggle) {
190+
if (value == 1) {
191+
kbd->saved_mode = kbd->current_mode;
192+
if (kbd->current_mode == APPLETB_KBD_MODE_SPCL)
193+
appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_FN);
194+
else if (kbd->current_mode == APPLETB_KBD_MODE_FN)
195+
appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_SPCL);
196+
} else if (value == 0) {
197+
if (kbd->saved_mode != kbd->current_mode)
198+
appletb_kbd_set_mode(kbd, kbd->saved_mode);
199+
}
200+
}
201+
}
202+
203+
static int appletb_kbd_inp_connect(struct input_handler *handler,
204+
struct input_dev *dev,
205+
const struct input_device_id *id)
206+
{
207+
struct appletb_kbd *kbd = handler->private;
208+
struct input_handle *handle;
209+
int rc;
210+
211+
if (id->driver_info == APPLETB_DEVID_KEYBOARD) {
212+
handle = &kbd->kbd_handle;
213+
handle->name = "tbkbd";
214+
} else {
215+
return -ENOENT;
216+
}
217+
218+
if (handle->dev)
219+
return -EEXIST;
220+
221+
handle->open = 0;
222+
handle->dev = input_get_device(dev);
223+
handle->handler = handler;
224+
handle->private = kbd;
225+
226+
rc = input_register_handle(handle);
227+
if (rc)
228+
goto err_free_dev;
229+
230+
rc = input_open_device(handle);
231+
if (rc)
232+
goto err_unregister_handle;
233+
234+
return 0;
235+
236+
err_unregister_handle:
237+
input_unregister_handle(handle);
238+
err_free_dev:
239+
input_put_device(handle->dev);
240+
handle->dev = NULL;
241+
return rc;
242+
}
243+
244+
static void appletb_kbd_inp_disconnect(struct input_handle *handle)
245+
{
246+
input_close_device(handle);
247+
input_unregister_handle(handle);
248+
249+
input_put_device(handle->dev);
250+
handle->dev = NULL;
251+
}
252+
175253
static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_input *hidinput)
176254
{
177255
int idx;
@@ -195,6 +273,40 @@ static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_inpu
195273
return 0;
196274
}
197275

276+
static const struct input_device_id appletb_kbd_input_devices[] = {
277+
{
278+
.flags = INPUT_DEVICE_ID_MATCH_BUS |
279+
INPUT_DEVICE_ID_MATCH_VENDOR |
280+
INPUT_DEVICE_ID_MATCH_KEYBIT,
281+
.bustype = BUS_USB,
282+
.vendor = USB_VENDOR_ID_APPLE,
283+
.keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) },
284+
.driver_info = APPLETB_DEVID_KEYBOARD,
285+
},
286+
{ }
287+
};
288+
289+
static bool appletb_kbd_match_internal_device(struct input_handler *handler,
290+
struct input_dev *inp_dev)
291+
{
292+
struct device *dev = &inp_dev->dev;
293+
294+
/* in kernel: dev && !is_usb_device(dev) */
295+
while (dev && !(dev->type && dev->type->name &&
296+
!strcmp(dev->type->name, "usb_device")))
297+
dev = dev->parent;
298+
299+
/*
300+
* Apple labels all their internal keyboards and trackpads as such,
301+
* instead of maintaining an ever expanding list of product-id's we
302+
* just look at the device's product name.
303+
*/
304+
if (dev)
305+
return !!strstr(to_usb_device(dev)->product, "Internal Keyboard");
306+
307+
return false;
308+
}
309+
198310
static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id)
199311
{
200312
struct appletb_kbd *kbd;
@@ -227,6 +339,20 @@ static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id
227339
goto stop_hw;
228340
}
229341

342+
kbd->inp_handler.event = appletb_kbd_inp_event;
343+
kbd->inp_handler.connect = appletb_kbd_inp_connect;
344+
kbd->inp_handler.disconnect = appletb_kbd_inp_disconnect;
345+
kbd->inp_handler.name = "appletb";
346+
kbd->inp_handler.id_table = appletb_kbd_input_devices;
347+
kbd->inp_handler.match = appletb_kbd_match_internal_device;
348+
kbd->inp_handler.private = kbd;
349+
350+
ret = input_register_handler(&kbd->inp_handler);
351+
if (ret) {
352+
dev_err_probe(dev, ret, "Unable to register keyboard handler\n");
353+
goto close_hw;
354+
}
355+
230356
ret = appletb_kbd_set_mode(kbd, appletb_tb_def_mode);
231357
if (ret) {
232358
dev_err_probe(dev, ret, "Failed to set touchbar mode\n");
@@ -250,6 +376,8 @@ static void appletb_kbd_remove(struct hid_device *hdev)
250376

251377
appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
252378

379+
input_unregister_handler(&kbd->inp_handler);
380+
253381
hid_hw_close(hdev);
254382
hid_hw_stop(hdev);
255383
}

0 commit comments

Comments
 (0)