Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/hid/hid-appletb-kbd.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Apple Touch Bar Keyboard Mode Driver
4
*
5
* Copyright (c) 2017-2018 Ronald Tschalär
6
* Copyright (c) 2022-2023 Kerem Karabay <[email protected]>
7
* Copyright (c) 2024-2025 Aditya Garg <[email protected]>
8
*/
9
10
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
12
#include <linux/hid.h>
13
#include <linux/usb.h>
14
#include <linux/input.h>
15
#include <linux/sysfs.h>
16
#include <linux/bitops.h>
17
#include <linux/module.h>
18
#include <linux/string.h>
19
#include <linux/backlight.h>
20
#include <linux/timer.h>
21
#include <linux/input/sparse-keymap.h>
22
23
#include "hid-ids.h"
24
25
#define APPLETB_KBD_MODE_ESC 0
26
#define APPLETB_KBD_MODE_FN 1
27
#define APPLETB_KBD_MODE_SPCL 2
28
#define APPLETB_KBD_MODE_OFF 3
29
#define APPLETB_KBD_MODE_MAX APPLETB_KBD_MODE_OFF
30
31
#define APPLETB_DEVID_KEYBOARD 1
32
#define APPLETB_DEVID_TRACKPAD 2
33
34
#define HID_USAGE_MODE 0x00ff0004
35
36
static int appletb_tb_def_mode = APPLETB_KBD_MODE_SPCL;
37
module_param_named(mode, appletb_tb_def_mode, int, 0444);
38
MODULE_PARM_DESC(mode, "Default touchbar mode:\n"
39
" 0 - escape key only\n"
40
" 1 - function-keys\n"
41
" [2] - special keys");
42
43
static bool appletb_tb_fn_toggle = true;
44
module_param_named(fntoggle, appletb_tb_fn_toggle, bool, 0644);
45
MODULE_PARM_DESC(fntoggle, "Switch between Fn and media controls on pressing Fn key");
46
47
static bool appletb_tb_autodim = true;
48
module_param_named(autodim, appletb_tb_autodim, bool, 0644);
49
MODULE_PARM_DESC(autodim, "Automatically dim and turn off the Touch Bar after some time");
50
51
static int appletb_tb_dim_timeout = 60;
52
module_param_named(dim_timeout, appletb_tb_dim_timeout, int, 0644);
53
MODULE_PARM_DESC(dim_timeout, "Dim timeout in sec");
54
55
static int appletb_tb_idle_timeout = 15;
56
module_param_named(idle_timeout, appletb_tb_idle_timeout, int, 0644);
57
MODULE_PARM_DESC(idle_timeout, "Idle timeout in sec");
58
59
struct appletb_kbd {
60
struct hid_field *mode_field;
61
struct input_handler inp_handler;
62
struct input_handle kbd_handle;
63
struct input_handle tpd_handle;
64
struct backlight_device *backlight_dev;
65
struct timer_list inactivity_timer;
66
bool has_dimmed;
67
bool has_turned_off;
68
u8 saved_mode;
69
u8 current_mode;
70
};
71
72
static const struct key_entry appletb_kbd_keymap[] = {
73
{ KE_KEY, KEY_ESC, { KEY_ESC } },
74
{ KE_KEY, KEY_F1, { KEY_BRIGHTNESSDOWN } },
75
{ KE_KEY, KEY_F2, { KEY_BRIGHTNESSUP } },
76
{ KE_KEY, KEY_F3, { KEY_RESERVED } },
77
{ KE_KEY, KEY_F4, { KEY_RESERVED } },
78
{ KE_KEY, KEY_F5, { KEY_KBDILLUMDOWN } },
79
{ KE_KEY, KEY_F6, { KEY_KBDILLUMUP } },
80
{ KE_KEY, KEY_F7, { KEY_PREVIOUSSONG } },
81
{ KE_KEY, KEY_F8, { KEY_PLAYPAUSE } },
82
{ KE_KEY, KEY_F9, { KEY_NEXTSONG } },
83
{ KE_KEY, KEY_F10, { KEY_MUTE } },
84
{ KE_KEY, KEY_F11, { KEY_VOLUMEDOWN } },
85
{ KE_KEY, KEY_F12, { KEY_VOLUMEUP } },
86
{ KE_END, 0 }
87
};
88
89
static int appletb_kbd_set_mode(struct appletb_kbd *kbd, u8 mode)
90
{
91
struct hid_report *report = kbd->mode_field->report;
92
struct hid_device *hdev = report->device;
93
int ret;
94
95
ret = hid_hw_power(hdev, PM_HINT_FULLON);
96
if (ret) {
97
hid_err(hdev, "Device didn't resume (%pe)\n", ERR_PTR(ret));
98
return ret;
99
}
100
101
ret = hid_set_field(kbd->mode_field, 0, mode);
102
if (ret) {
103
hid_err(hdev, "Failed to set mode field to %u (%pe)\n", mode, ERR_PTR(ret));
104
goto power_normal;
105
}
106
107
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
108
109
kbd->current_mode = mode;
110
111
power_normal:
112
hid_hw_power(hdev, PM_HINT_NORMAL);
113
114
return ret;
115
}
116
117
static ssize_t mode_show(struct device *dev,
118
struct device_attribute *attr, char *buf)
119
{
120
struct appletb_kbd *kbd = dev_get_drvdata(dev);
121
122
return sysfs_emit(buf, "%d\n", kbd->current_mode);
123
}
124
125
static ssize_t mode_store(struct device *dev,
126
struct device_attribute *attr,
127
const char *buf, size_t size)
128
{
129
struct appletb_kbd *kbd = dev_get_drvdata(dev);
130
u8 mode;
131
int ret;
132
133
ret = kstrtou8(buf, 0, &mode);
134
if (ret)
135
return ret;
136
137
if (mode > APPLETB_KBD_MODE_MAX)
138
return -EINVAL;
139
140
ret = appletb_kbd_set_mode(kbd, mode);
141
142
return ret < 0 ? ret : size;
143
}
144
static DEVICE_ATTR_RW(mode);
145
146
static struct attribute *appletb_kbd_attrs[] = {
147
&dev_attr_mode.attr,
148
NULL
149
};
150
ATTRIBUTE_GROUPS(appletb_kbd);
151
152
static int appletb_tb_key_to_slot(unsigned int code)
153
{
154
switch (code) {
155
case KEY_ESC:
156
return 0;
157
case KEY_F1 ... KEY_F10:
158
return code - KEY_F1 + 1;
159
case KEY_F11 ... KEY_F12:
160
return code - KEY_F11 + 11;
161
162
default:
163
return -EINVAL;
164
}
165
}
166
167
static void appletb_inactivity_timer(struct timer_list *t)
168
{
169
struct appletb_kbd *kbd = timer_container_of(kbd, t, inactivity_timer);
170
171
if (kbd->backlight_dev && appletb_tb_autodim) {
172
if (!kbd->has_dimmed) {
173
backlight_device_set_brightness(kbd->backlight_dev, 1);
174
kbd->has_dimmed = true;
175
mod_timer(&kbd->inactivity_timer,
176
jiffies + secs_to_jiffies(appletb_tb_idle_timeout));
177
} else if (!kbd->has_turned_off) {
178
backlight_device_set_brightness(kbd->backlight_dev, 0);
179
kbd->has_turned_off = true;
180
}
181
}
182
}
183
184
static void reset_inactivity_timer(struct appletb_kbd *kbd)
185
{
186
if (kbd->backlight_dev && appletb_tb_autodim) {
187
if (kbd->has_dimmed || kbd->has_turned_off) {
188
backlight_device_set_brightness(kbd->backlight_dev, 2);
189
kbd->has_dimmed = false;
190
kbd->has_turned_off = false;
191
}
192
mod_timer(&kbd->inactivity_timer,
193
jiffies + secs_to_jiffies(appletb_tb_dim_timeout));
194
}
195
}
196
197
static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *field,
198
struct hid_usage *usage, __s32 value)
199
{
200
struct appletb_kbd *kbd = hid_get_drvdata(hdev);
201
struct key_entry *translation;
202
struct input_dev *input;
203
int slot;
204
205
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD || usage->type != EV_KEY)
206
return 0;
207
208
input = field->hidinput->input;
209
210
/*
211
* Skip non-touch-bar keys.
212
*
213
* Either the touch bar itself or usbhid generate a slew of key-down
214
* events for all the meta keys. None of which we're at all interested
215
* in.
216
*/
217
slot = appletb_tb_key_to_slot(usage->code);
218
if (slot < 0)
219
return 0;
220
221
reset_inactivity_timer(kbd);
222
223
translation = sparse_keymap_entry_from_scancode(input, usage->code);
224
225
if (translation && kbd->current_mode == APPLETB_KBD_MODE_SPCL) {
226
input_event(input, usage->type, translation->keycode, value);
227
228
return 1;
229
}
230
231
return kbd->current_mode == APPLETB_KBD_MODE_OFF;
232
}
233
234
static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type,
235
unsigned int code, int value)
236
{
237
struct appletb_kbd *kbd = handle->private;
238
239
reset_inactivity_timer(kbd);
240
241
if (type == EV_KEY && code == KEY_FN && appletb_tb_fn_toggle &&
242
(kbd->current_mode == APPLETB_KBD_MODE_SPCL ||
243
kbd->current_mode == APPLETB_KBD_MODE_FN)) {
244
if (value == 1) {
245
kbd->saved_mode = kbd->current_mode;
246
appletb_kbd_set_mode(kbd, kbd->current_mode == APPLETB_KBD_MODE_SPCL
247
? APPLETB_KBD_MODE_FN : APPLETB_KBD_MODE_SPCL);
248
} else if (value == 0) {
249
if (kbd->saved_mode != kbd->current_mode)
250
appletb_kbd_set_mode(kbd, kbd->saved_mode);
251
}
252
}
253
}
254
255
static int appletb_kbd_inp_connect(struct input_handler *handler,
256
struct input_dev *dev,
257
const struct input_device_id *id)
258
{
259
struct appletb_kbd *kbd = handler->private;
260
struct input_handle *handle;
261
int rc;
262
263
if (id->driver_info == APPLETB_DEVID_KEYBOARD) {
264
handle = &kbd->kbd_handle;
265
handle->name = "tbkbd";
266
} else if (id->driver_info == APPLETB_DEVID_TRACKPAD) {
267
handle = &kbd->tpd_handle;
268
handle->name = "tbtpd";
269
} else {
270
return -ENOENT;
271
}
272
273
if (handle->dev)
274
return -EEXIST;
275
276
handle->open = 0;
277
handle->dev = input_get_device(dev);
278
handle->handler = handler;
279
handle->private = kbd;
280
281
rc = input_register_handle(handle);
282
if (rc)
283
goto err_free_dev;
284
285
rc = input_open_device(handle);
286
if (rc)
287
goto err_unregister_handle;
288
289
return 0;
290
291
err_unregister_handle:
292
input_unregister_handle(handle);
293
err_free_dev:
294
input_put_device(handle->dev);
295
handle->dev = NULL;
296
return rc;
297
}
298
299
static void appletb_kbd_inp_disconnect(struct input_handle *handle)
300
{
301
input_close_device(handle);
302
input_unregister_handle(handle);
303
304
input_put_device(handle->dev);
305
handle->dev = NULL;
306
}
307
308
static int appletb_kbd_input_configured(struct hid_device *hdev, struct hid_input *hidinput)
309
{
310
int idx;
311
struct input_dev *input = hidinput->input;
312
313
/*
314
* Clear various input capabilities that are blindly set by the hid
315
* driver (usbkbd.c)
316
*/
317
memset(input->evbit, 0, sizeof(input->evbit));
318
memset(input->keybit, 0, sizeof(input->keybit));
319
memset(input->ledbit, 0, sizeof(input->ledbit));
320
321
__set_bit(EV_REP, input->evbit);
322
323
sparse_keymap_setup(input, appletb_kbd_keymap, NULL);
324
325
for (idx = 0; appletb_kbd_keymap[idx].type != KE_END; idx++)
326
input_set_capability(input, EV_KEY, appletb_kbd_keymap[idx].code);
327
328
return 0;
329
}
330
331
static const struct input_device_id appletb_kbd_input_devices[] = {
332
{
333
.flags = INPUT_DEVICE_ID_MATCH_BUS |
334
INPUT_DEVICE_ID_MATCH_VENDOR |
335
INPUT_DEVICE_ID_MATCH_KEYBIT,
336
.bustype = BUS_USB,
337
.vendor = USB_VENDOR_ID_APPLE,
338
.keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) },
339
.driver_info = APPLETB_DEVID_KEYBOARD,
340
},
341
{
342
.flags = INPUT_DEVICE_ID_MATCH_BUS |
343
INPUT_DEVICE_ID_MATCH_VENDOR |
344
INPUT_DEVICE_ID_MATCH_KEYBIT,
345
.bustype = BUS_USB,
346
.vendor = USB_VENDOR_ID_APPLE,
347
.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
348
.driver_info = APPLETB_DEVID_TRACKPAD,
349
},
350
{ }
351
};
352
353
static bool appletb_kbd_match_internal_device(struct input_handler *handler,
354
struct input_dev *inp_dev)
355
{
356
struct device *dev = &inp_dev->dev;
357
358
/* in kernel: dev && !is_usb_device(dev) */
359
while (dev && !(dev->type && dev->type->name &&
360
!strcmp(dev->type->name, "usb_device")))
361
dev = dev->parent;
362
363
/*
364
* Apple labels all their internal keyboards and trackpads as such,
365
* instead of maintaining an ever expanding list of product-id's we
366
* just look at the device's product name.
367
*/
368
if (dev)
369
return !!strstr(to_usb_device(dev)->product, "Internal Keyboard");
370
371
return false;
372
}
373
374
static int appletb_kbd_probe(struct hid_device *hdev, const struct hid_device_id *id)
375
{
376
struct appletb_kbd *kbd;
377
struct device *dev = &hdev->dev;
378
struct hid_field *mode_field;
379
int ret;
380
381
ret = hid_parse(hdev);
382
if (ret)
383
return dev_err_probe(dev, ret, "HID parse failed\n");
384
385
mode_field = hid_find_field(hdev, HID_OUTPUT_REPORT,
386
HID_GD_KEYBOARD, HID_USAGE_MODE);
387
if (!mode_field)
388
return -ENODEV;
389
390
kbd = devm_kzalloc(dev, sizeof(*kbd), GFP_KERNEL);
391
if (!kbd)
392
return -ENOMEM;
393
394
kbd->mode_field = mode_field;
395
396
ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT);
397
if (ret)
398
return dev_err_probe(dev, ret, "HID hw start failed\n");
399
400
ret = hid_hw_open(hdev);
401
if (ret) {
402
dev_err_probe(dev, ret, "HID hw open failed\n");
403
goto stop_hw;
404
}
405
406
kbd->backlight_dev = backlight_device_get_by_name("appletb_backlight");
407
if (!kbd->backlight_dev) {
408
dev_err_probe(dev, -ENODEV, "Failed to get backlight device\n");
409
} else {
410
backlight_device_set_brightness(kbd->backlight_dev, 2);
411
timer_setup(&kbd->inactivity_timer, appletb_inactivity_timer, 0);
412
mod_timer(&kbd->inactivity_timer,
413
jiffies + secs_to_jiffies(appletb_tb_dim_timeout));
414
}
415
416
kbd->inp_handler.event = appletb_kbd_inp_event;
417
kbd->inp_handler.connect = appletb_kbd_inp_connect;
418
kbd->inp_handler.disconnect = appletb_kbd_inp_disconnect;
419
kbd->inp_handler.name = "appletb";
420
kbd->inp_handler.id_table = appletb_kbd_input_devices;
421
kbd->inp_handler.match = appletb_kbd_match_internal_device;
422
kbd->inp_handler.private = kbd;
423
424
ret = input_register_handler(&kbd->inp_handler);
425
if (ret) {
426
dev_err_probe(dev, ret, "Unable to register keyboard handler\n");
427
goto close_hw;
428
}
429
430
ret = appletb_kbd_set_mode(kbd, appletb_tb_def_mode);
431
if (ret) {
432
dev_err_probe(dev, ret, "Failed to set touchbar mode\n");
433
goto unregister_handler;
434
}
435
436
hid_set_drvdata(hdev, kbd);
437
438
return 0;
439
440
unregister_handler:
441
input_unregister_handler(&kbd->inp_handler);
442
close_hw:
443
if (kbd->backlight_dev) {
444
put_device(&kbd->backlight_dev->dev);
445
timer_delete_sync(&kbd->inactivity_timer);
446
}
447
hid_hw_close(hdev);
448
stop_hw:
449
hid_hw_stop(hdev);
450
return ret;
451
}
452
453
static void appletb_kbd_remove(struct hid_device *hdev)
454
{
455
struct appletb_kbd *kbd = hid_get_drvdata(hdev);
456
457
appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
458
459
input_unregister_handler(&kbd->inp_handler);
460
if (kbd->backlight_dev) {
461
put_device(&kbd->backlight_dev->dev);
462
timer_delete_sync(&kbd->inactivity_timer);
463
}
464
465
hid_hw_close(hdev);
466
hid_hw_stop(hdev);
467
}
468
469
#ifdef CONFIG_PM
470
static int appletb_kbd_suspend(struct hid_device *hdev, pm_message_t msg)
471
{
472
struct appletb_kbd *kbd = hid_get_drvdata(hdev);
473
474
kbd->saved_mode = kbd->current_mode;
475
appletb_kbd_set_mode(kbd, APPLETB_KBD_MODE_OFF);
476
477
return 0;
478
}
479
480
static int appletb_kbd_reset_resume(struct hid_device *hdev)
481
{
482
struct appletb_kbd *kbd = hid_get_drvdata(hdev);
483
484
appletb_kbd_set_mode(kbd, kbd->saved_mode);
485
486
return 0;
487
}
488
#endif
489
490
static const struct hid_device_id appletb_kbd_hid_ids[] = {
491
/* MacBook Pro's 2018, 2019, with T2 chip: iBridge Display */
492
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) },
493
{ }
494
};
495
MODULE_DEVICE_TABLE(hid, appletb_kbd_hid_ids);
496
497
static struct hid_driver appletb_kbd_hid_driver = {
498
.name = "hid-appletb-kbd",
499
.id_table = appletb_kbd_hid_ids,
500
.probe = appletb_kbd_probe,
501
.remove = appletb_kbd_remove,
502
.event = appletb_kbd_hid_event,
503
.input_configured = appletb_kbd_input_configured,
504
#ifdef CONFIG_PM
505
.suspend = appletb_kbd_suspend,
506
.reset_resume = appletb_kbd_reset_resume,
507
#endif
508
.driver.dev_groups = appletb_kbd_groups,
509
};
510
module_hid_driver(appletb_kbd_hid_driver);
511
512
/* The backlight driver should be loaded before the keyboard driver is initialised */
513
MODULE_SOFTDEP("pre: hid_appletb_bl");
514
515
MODULE_AUTHOR("Ronald Tschalär");
516
MODULE_AUTHOR("Kerem Karabay <[email protected]>");
517
MODULE_AUTHOR("Aditya Garg <[email protected]>");
518
MODULE_DESCRIPTION("MacBook Pro Touch Bar Keyboard Mode driver");
519
MODULE_LICENSE("GPL");
520
521