Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/button.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* button.c - ACPI Button Driver
4
*
5
* Copyright (C) 2001, 2002 Andy Grover <[email protected]>
6
* Copyright (C) 2001, 2002 Paul Diefenbaugh <[email protected]>
7
*/
8
9
#define pr_fmt(fmt) "ACPI: button: " fmt
10
11
#include <linux/compiler.h>
12
#include <linux/kernel.h>
13
#include <linux/module.h>
14
#include <linux/init.h>
15
#include <linux/types.h>
16
#include <linux/proc_fs.h>
17
#include <linux/seq_file.h>
18
#include <linux/input.h>
19
#include <linux/slab.h>
20
#include <linux/acpi.h>
21
#include <linux/dmi.h>
22
#include <acpi/button.h>
23
24
#define ACPI_BUTTON_CLASS "button"
25
#define ACPI_BUTTON_FILE_STATE "state"
26
#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
27
#define ACPI_BUTTON_NOTIFY_WAKE 0x02
28
#define ACPI_BUTTON_NOTIFY_STATUS 0x80
29
30
#define ACPI_BUTTON_SUBCLASS_POWER "power"
31
#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
32
#define ACPI_BUTTON_TYPE_POWER 0x01
33
34
#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
35
#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
36
#define ACPI_BUTTON_TYPE_SLEEP 0x03
37
38
#define ACPI_BUTTON_SUBCLASS_LID "lid"
39
#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
40
#define ACPI_BUTTON_TYPE_LID 0x05
41
42
enum {
43
ACPI_BUTTON_LID_INIT_IGNORE,
44
ACPI_BUTTON_LID_INIT_OPEN,
45
ACPI_BUTTON_LID_INIT_METHOD,
46
ACPI_BUTTON_LID_INIT_DISABLED,
47
};
48
49
static const char * const lid_init_state_str[] = {
50
[ACPI_BUTTON_LID_INIT_IGNORE] = "ignore",
51
[ACPI_BUTTON_LID_INIT_OPEN] = "open",
52
[ACPI_BUTTON_LID_INIT_METHOD] = "method",
53
[ACPI_BUTTON_LID_INIT_DISABLED] = "disabled",
54
};
55
56
MODULE_AUTHOR("Paul Diefenbaugh");
57
MODULE_DESCRIPTION("ACPI Button Driver");
58
MODULE_LICENSE("GPL");
59
60
static const struct acpi_device_id button_device_ids[] = {
61
{ACPI_BUTTON_HID_LID, 0},
62
{ACPI_BUTTON_HID_SLEEP, 0},
63
{ACPI_BUTTON_HID_SLEEPF, 0},
64
{ACPI_BUTTON_HID_POWER, 0},
65
{ACPI_BUTTON_HID_POWERF, 0},
66
{"", 0},
67
};
68
MODULE_DEVICE_TABLE(acpi, button_device_ids);
69
70
/* Please keep this list sorted alphabetically by vendor and model */
71
static const struct dmi_system_id dmi_lid_quirks[] = {
72
{
73
/* GP-electronic T701, _LID method points to a floating GPIO */
74
.matches = {
75
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
76
DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
77
DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
78
},
79
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
80
},
81
{
82
/* Nextbook Ares 8A tablet, _LID device always reports lid closed */
83
.matches = {
84
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
85
DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"),
86
DMI_MATCH(DMI_BIOS_VERSION, "M882"),
87
},
88
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
89
},
90
{
91
/*
92
* Lenovo Yoga 9 14ITL5, initial notification of the LID device
93
* never happens.
94
*/
95
.matches = {
96
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
97
DMI_MATCH(DMI_PRODUCT_NAME, "82BG"),
98
},
99
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
100
},
101
{
102
/*
103
* Medion Akoya E2215T, notification of the LID device only
104
* happens on close, not on open and _LID always returns closed.
105
*/
106
.matches = {
107
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
108
DMI_MATCH(DMI_PRODUCT_NAME, "E2215T"),
109
},
110
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
111
},
112
{
113
/*
114
* Medion Akoya E2228T, notification of the LID device only
115
* happens on close, not on open and _LID always returns closed.
116
*/
117
.matches = {
118
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
119
DMI_MATCH(DMI_PRODUCT_NAME, "E2228T"),
120
},
121
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
122
},
123
{
124
/*
125
* Razer Blade Stealth 13 late 2019, notification of the LID device
126
* only happens on close, not on open and _LID always returns closed.
127
*/
128
.matches = {
129
DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
130
DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"),
131
},
132
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
133
},
134
{
135
/*
136
* Samsung galaxybook2 ,initial _LID device notification returns
137
* lid closed.
138
*/
139
.matches = {
140
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
141
DMI_MATCH(DMI_PRODUCT_NAME, "750XED"),
142
},
143
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
144
},
145
{}
146
};
147
148
static int acpi_button_add(struct acpi_device *device);
149
static void acpi_button_remove(struct acpi_device *device);
150
151
#ifdef CONFIG_PM_SLEEP
152
static int acpi_button_suspend(struct device *dev);
153
static int acpi_button_resume(struct device *dev);
154
#else
155
#define acpi_button_suspend NULL
156
#define acpi_button_resume NULL
157
#endif
158
static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
159
160
static struct acpi_driver acpi_button_driver = {
161
.name = "button",
162
.class = ACPI_BUTTON_CLASS,
163
.ids = button_device_ids,
164
.ops = {
165
.add = acpi_button_add,
166
.remove = acpi_button_remove,
167
},
168
.drv.pm = &acpi_button_pm,
169
};
170
171
struct acpi_button {
172
unsigned int type;
173
struct input_dev *input;
174
char phys[32]; /* for input device */
175
unsigned long pushed;
176
int last_state;
177
ktime_t last_time;
178
bool suspended;
179
bool lid_state_initialized;
180
};
181
182
static struct acpi_device *lid_device;
183
static long lid_init_state = -1;
184
185
static unsigned long lid_report_interval __read_mostly = 500;
186
module_param(lid_report_interval, ulong, 0644);
187
MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
188
189
/* FS Interface (/proc) */
190
static struct proc_dir_entry *acpi_button_dir;
191
static struct proc_dir_entry *acpi_lid_dir;
192
193
static int acpi_lid_evaluate_state(struct acpi_device *device)
194
{
195
unsigned long long lid_state;
196
acpi_status status;
197
198
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
199
if (ACPI_FAILURE(status))
200
return -ENODEV;
201
202
return lid_state ? 1 : 0;
203
}
204
205
static int acpi_lid_notify_state(struct acpi_device *device, int state)
206
{
207
struct acpi_button *button = acpi_driver_data(device);
208
ktime_t next_report;
209
bool do_update;
210
211
/*
212
* In lid_init_state=ignore mode, if user opens/closes lid
213
* frequently with "open" missing, and "last_time" is also updated
214
* frequently, "close" cannot be delivered to the userspace.
215
* So "last_time" is only updated after a timeout or an actual
216
* switch.
217
*/
218
if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
219
button->last_state != !!state)
220
do_update = true;
221
else
222
do_update = false;
223
224
next_report = ktime_add(button->last_time,
225
ms_to_ktime(lid_report_interval));
226
if (button->last_state == !!state &&
227
ktime_after(ktime_get(), next_report)) {
228
/* Complain the buggy firmware */
229
pr_warn_once("The lid device is not compliant to SW_LID.\n");
230
231
/*
232
* Send the unreliable complement switch event:
233
*
234
* On most platforms, the lid device is reliable. However
235
* there are exceptions:
236
* 1. Platforms returning initial lid state as "close" by
237
* default after booting/resuming:
238
* https://bugzilla.kernel.org/show_bug.cgi?id=89211
239
* https://bugzilla.kernel.org/show_bug.cgi?id=106151
240
* 2. Platforms never reporting "open" events:
241
* https://bugzilla.kernel.org/show_bug.cgi?id=106941
242
* On these buggy platforms, the usage model of the ACPI
243
* lid device actually is:
244
* 1. The initial returning value of _LID may not be
245
* reliable.
246
* 2. The open event may not be reliable.
247
* 3. The close event is reliable.
248
*
249
* But SW_LID is typed as input switch event, the input
250
* layer checks if the event is redundant. Hence if the
251
* state is not switched, the userspace cannot see this
252
* platform triggered reliable event. By inserting a
253
* complement switch event, it then is guaranteed that the
254
* platform triggered reliable one can always be seen by
255
* the userspace.
256
*/
257
if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
258
do_update = true;
259
/*
260
* Do generate complement switch event for "close"
261
* as "close" is reliable and wrong "open" won't
262
* trigger unexpected behaviors.
263
* Do not generate complement switch event for
264
* "open" as "open" is not reliable and wrong
265
* "close" will trigger unexpected behaviors.
266
*/
267
if (!state) {
268
input_report_switch(button->input,
269
SW_LID, state);
270
input_sync(button->input);
271
}
272
}
273
}
274
/* Send the platform triggered reliable event */
275
if (do_update) {
276
acpi_handle_debug(device->handle, "ACPI LID %s\n",
277
state ? "open" : "closed");
278
input_report_switch(button->input, SW_LID, !state);
279
input_sync(button->input);
280
button->last_state = !!state;
281
button->last_time = ktime_get();
282
}
283
284
return 0;
285
}
286
287
static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
288
void *offset)
289
{
290
struct acpi_device *device = seq->private;
291
int state;
292
293
state = acpi_lid_evaluate_state(device);
294
seq_printf(seq, "state: %s\n",
295
state < 0 ? "unsupported" : (state ? "open" : "closed"));
296
return 0;
297
}
298
299
static int acpi_button_add_fs(struct acpi_device *device)
300
{
301
struct acpi_button *button = acpi_driver_data(device);
302
struct proc_dir_entry *entry = NULL;
303
int ret = 0;
304
305
/* procfs I/F for ACPI lid device only */
306
if (button->type != ACPI_BUTTON_TYPE_LID)
307
return 0;
308
309
if (acpi_button_dir || acpi_lid_dir) {
310
pr_info("More than one Lid device found!\n");
311
return -EEXIST;
312
}
313
314
/* create /proc/acpi/button */
315
acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
316
if (!acpi_button_dir)
317
return -ENODEV;
318
319
/* create /proc/acpi/button/lid */
320
acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
321
if (!acpi_lid_dir) {
322
ret = -ENODEV;
323
goto remove_button_dir;
324
}
325
326
/* create /proc/acpi/button/lid/LID/ */
327
acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
328
if (!acpi_device_dir(device)) {
329
ret = -ENODEV;
330
goto remove_lid_dir;
331
}
332
333
/* create /proc/acpi/button/lid/LID/state */
334
entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
335
acpi_device_dir(device), acpi_button_state_seq_show,
336
device);
337
if (!entry) {
338
ret = -ENODEV;
339
goto remove_dev_dir;
340
}
341
342
done:
343
return ret;
344
345
remove_dev_dir:
346
remove_proc_entry(acpi_device_bid(device),
347
acpi_lid_dir);
348
acpi_device_dir(device) = NULL;
349
remove_lid_dir:
350
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
351
acpi_lid_dir = NULL;
352
remove_button_dir:
353
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
354
acpi_button_dir = NULL;
355
goto done;
356
}
357
358
static int acpi_button_remove_fs(struct acpi_device *device)
359
{
360
struct acpi_button *button = acpi_driver_data(device);
361
362
if (button->type != ACPI_BUTTON_TYPE_LID)
363
return 0;
364
365
remove_proc_entry(ACPI_BUTTON_FILE_STATE,
366
acpi_device_dir(device));
367
remove_proc_entry(acpi_device_bid(device),
368
acpi_lid_dir);
369
acpi_device_dir(device) = NULL;
370
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
371
acpi_lid_dir = NULL;
372
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
373
acpi_button_dir = NULL;
374
375
return 0;
376
}
377
378
/* Driver Interface */
379
int acpi_lid_open(void)
380
{
381
if (!lid_device)
382
return -ENODEV;
383
384
return acpi_lid_evaluate_state(lid_device);
385
}
386
EXPORT_SYMBOL(acpi_lid_open);
387
388
static int acpi_lid_update_state(struct acpi_device *device,
389
bool signal_wakeup)
390
{
391
int state;
392
393
state = acpi_lid_evaluate_state(device);
394
if (state < 0)
395
return state;
396
397
if (state && signal_wakeup)
398
acpi_pm_wakeup_event(&device->dev);
399
400
return acpi_lid_notify_state(device, state);
401
}
402
403
static void acpi_lid_initialize_state(struct acpi_device *device)
404
{
405
struct acpi_button *button = acpi_driver_data(device);
406
407
switch (lid_init_state) {
408
case ACPI_BUTTON_LID_INIT_OPEN:
409
(void)acpi_lid_notify_state(device, 1);
410
break;
411
case ACPI_BUTTON_LID_INIT_METHOD:
412
(void)acpi_lid_update_state(device, false);
413
break;
414
case ACPI_BUTTON_LID_INIT_IGNORE:
415
default:
416
break;
417
}
418
419
button->lid_state_initialized = true;
420
}
421
422
static void acpi_lid_notify(acpi_handle handle, u32 event, void *data)
423
{
424
struct acpi_device *device = data;
425
struct acpi_button *button;
426
427
if (event != ACPI_BUTTON_NOTIFY_STATUS) {
428
acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
429
event);
430
return;
431
}
432
433
button = acpi_driver_data(device);
434
if (!button->lid_state_initialized)
435
return;
436
437
acpi_lid_update_state(device, true);
438
}
439
440
static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
441
{
442
struct acpi_device *device = data;
443
struct acpi_button *button;
444
struct input_dev *input;
445
int keycode;
446
447
switch (event) {
448
case ACPI_BUTTON_NOTIFY_STATUS:
449
break;
450
case ACPI_BUTTON_NOTIFY_WAKE:
451
break;
452
default:
453
acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n",
454
event);
455
return;
456
}
457
458
acpi_pm_wakeup_event(&device->dev);
459
460
button = acpi_driver_data(device);
461
if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE)
462
return;
463
464
input = button->input;
465
keycode = test_bit(KEY_SLEEP, input->keybit) ? KEY_SLEEP : KEY_POWER;
466
467
input_report_key(input, keycode, 1);
468
input_sync(input);
469
input_report_key(input, keycode, 0);
470
input_sync(input);
471
472
acpi_bus_generate_netlink_event(device->pnp.device_class,
473
dev_name(&device->dev),
474
event, ++button->pushed);
475
}
476
477
static void acpi_button_notify_run(void *data)
478
{
479
acpi_button_notify(NULL, ACPI_BUTTON_NOTIFY_STATUS, data);
480
}
481
482
static u32 acpi_button_event(void *data)
483
{
484
acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_button_notify_run, data);
485
return ACPI_INTERRUPT_HANDLED;
486
}
487
488
#ifdef CONFIG_PM_SLEEP
489
static int acpi_button_suspend(struct device *dev)
490
{
491
struct acpi_device *device = to_acpi_device(dev);
492
struct acpi_button *button = acpi_driver_data(device);
493
494
button->suspended = true;
495
return 0;
496
}
497
498
static int acpi_button_resume(struct device *dev)
499
{
500
struct input_dev *input;
501
struct acpi_device *device = to_acpi_device(dev);
502
struct acpi_button *button = acpi_driver_data(device);
503
504
button->suspended = false;
505
if (button->type == ACPI_BUTTON_TYPE_LID) {
506
button->last_state = !!acpi_lid_evaluate_state(device);
507
button->last_time = ktime_get();
508
acpi_lid_initialize_state(device);
509
}
510
511
if (button->type == ACPI_BUTTON_TYPE_POWER) {
512
input = button->input;
513
input_report_key(input, KEY_WAKEUP, 1);
514
input_sync(input);
515
input_report_key(input, KEY_WAKEUP, 0);
516
input_sync(input);
517
}
518
return 0;
519
}
520
#endif
521
522
static int acpi_lid_input_open(struct input_dev *input)
523
{
524
struct acpi_device *device = input_get_drvdata(input);
525
struct acpi_button *button = acpi_driver_data(device);
526
527
button->last_state = !!acpi_lid_evaluate_state(device);
528
button->last_time = ktime_get();
529
acpi_lid_initialize_state(device);
530
531
return 0;
532
}
533
534
static int acpi_button_add(struct acpi_device *device)
535
{
536
acpi_notify_handler handler;
537
struct acpi_button *button;
538
struct input_dev *input;
539
const char *hid = acpi_device_hid(device);
540
acpi_status status;
541
char *name, *class;
542
int error = 0;
543
544
if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
545
lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
546
return -ENODEV;
547
548
button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
549
if (!button)
550
return -ENOMEM;
551
552
device->driver_data = button;
553
554
button->input = input = input_allocate_device();
555
if (!input) {
556
error = -ENOMEM;
557
goto err_free_button;
558
}
559
560
name = acpi_device_name(device);
561
class = acpi_device_class(device);
562
563
if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
564
!strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
565
button->type = ACPI_BUTTON_TYPE_POWER;
566
handler = acpi_button_notify;
567
strscpy(name, ACPI_BUTTON_DEVICE_NAME_POWER, MAX_ACPI_DEVICE_NAME_LEN);
568
sprintf(class, "%s/%s",
569
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
570
} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
571
!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
572
button->type = ACPI_BUTTON_TYPE_SLEEP;
573
handler = acpi_button_notify;
574
strscpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP, MAX_ACPI_DEVICE_NAME_LEN);
575
sprintf(class, "%s/%s",
576
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
577
} else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
578
button->type = ACPI_BUTTON_TYPE_LID;
579
handler = acpi_lid_notify;
580
strscpy(name, ACPI_BUTTON_DEVICE_NAME_LID, MAX_ACPI_DEVICE_NAME_LEN);
581
sprintf(class, "%s/%s",
582
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
583
input->open = acpi_lid_input_open;
584
} else {
585
pr_info("Unsupported hid [%s]\n", hid);
586
error = -ENODEV;
587
}
588
589
if (!error)
590
error = acpi_button_add_fs(device);
591
592
if (error) {
593
input_free_device(input);
594
goto err_free_button;
595
}
596
597
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
598
599
input->name = name;
600
input->phys = button->phys;
601
input->id.bustype = BUS_HOST;
602
input->id.product = button->type;
603
input->dev.parent = &device->dev;
604
605
switch (button->type) {
606
case ACPI_BUTTON_TYPE_POWER:
607
input_set_capability(input, EV_KEY, KEY_POWER);
608
input_set_capability(input, EV_KEY, KEY_WAKEUP);
609
break;
610
611
case ACPI_BUTTON_TYPE_SLEEP:
612
input_set_capability(input, EV_KEY, KEY_SLEEP);
613
break;
614
615
case ACPI_BUTTON_TYPE_LID:
616
input_set_capability(input, EV_SW, SW_LID);
617
break;
618
}
619
620
input_set_drvdata(input, device);
621
error = input_register_device(input);
622
if (error)
623
goto err_remove_fs;
624
625
switch (device->device_type) {
626
case ACPI_BUS_TYPE_POWER_BUTTON:
627
status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
628
acpi_button_event,
629
device);
630
break;
631
case ACPI_BUS_TYPE_SLEEP_BUTTON:
632
status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
633
acpi_button_event,
634
device);
635
break;
636
default:
637
status = acpi_install_notify_handler(device->handle,
638
ACPI_ALL_NOTIFY, handler,
639
device);
640
break;
641
}
642
if (ACPI_FAILURE(status)) {
643
error = -ENODEV;
644
goto err_input_unregister;
645
}
646
647
if (button->type == ACPI_BUTTON_TYPE_LID) {
648
/*
649
* This assumes there's only one lid device, or if there are
650
* more we only care about the last one...
651
*/
652
lid_device = device;
653
}
654
655
device_init_wakeup(&device->dev, true);
656
pr_info("%s [%s]\n", name, acpi_device_bid(device));
657
return 0;
658
659
err_input_unregister:
660
input_unregister_device(input);
661
err_remove_fs:
662
acpi_button_remove_fs(device);
663
err_free_button:
664
kfree(button);
665
return error;
666
}
667
668
static void acpi_button_remove(struct acpi_device *device)
669
{
670
struct acpi_button *button = acpi_driver_data(device);
671
672
switch (device->device_type) {
673
case ACPI_BUS_TYPE_POWER_BUTTON:
674
acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
675
acpi_button_event);
676
break;
677
case ACPI_BUS_TYPE_SLEEP_BUTTON:
678
acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
679
acpi_button_event);
680
break;
681
default:
682
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
683
button->type == ACPI_BUTTON_TYPE_LID ?
684
acpi_lid_notify :
685
acpi_button_notify);
686
break;
687
}
688
acpi_os_wait_events_complete();
689
690
acpi_button_remove_fs(device);
691
input_unregister_device(button->input);
692
kfree(button);
693
}
694
695
static int param_set_lid_init_state(const char *val,
696
const struct kernel_param *kp)
697
{
698
int i;
699
700
i = sysfs_match_string(lid_init_state_str, val);
701
if (i < 0)
702
return i;
703
704
lid_init_state = i;
705
pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
706
return 0;
707
}
708
709
static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
710
{
711
int i, c = 0;
712
713
for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
714
if (i == lid_init_state)
715
c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
716
else
717
c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
718
719
buf[c - 1] = '\n'; /* Replace the final space with a newline */
720
721
return c;
722
}
723
724
module_param_call(lid_init_state,
725
param_set_lid_init_state, param_get_lid_init_state,
726
NULL, 0644);
727
MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
728
729
static int acpi_button_register_driver(struct acpi_driver *driver)
730
{
731
const struct dmi_system_id *dmi_id;
732
733
if (lid_init_state == -1) {
734
dmi_id = dmi_first_match(dmi_lid_quirks);
735
if (dmi_id)
736
lid_init_state = (long)dmi_id->driver_data;
737
else
738
lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
739
}
740
741
/*
742
* Modules such as nouveau.ko and i915.ko have a link time dependency
743
* on acpi_lid_open(), and would therefore not be loadable on ACPI
744
* capable kernels booted in non-ACPI mode if the return value of
745
* acpi_bus_register_driver() is returned from here with ACPI disabled
746
* when this driver is built as a module.
747
*/
748
if (acpi_disabled)
749
return 0;
750
751
return acpi_bus_register_driver(driver);
752
}
753
754
static void acpi_button_unregister_driver(struct acpi_driver *driver)
755
{
756
if (!acpi_disabled)
757
acpi_bus_unregister_driver(driver);
758
}
759
760
module_driver(acpi_button_driver, acpi_button_register_driver,
761
acpi_button_unregister_driver);
762
763