blob: ee7a7da276ddfa7471f6ab4651d972929bc4ca09 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0-or-later
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00002/*
3 * button.c - ACPI Button Driver
4 *
5 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
6 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00007 */
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 PREFIX "ACPI: "
25
26#define ACPI_BUTTON_CLASS "button"
27#define ACPI_BUTTON_FILE_INFO "info"
28#define ACPI_BUTTON_FILE_STATE "state"
29#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
30#define ACPI_BUTTON_NOTIFY_STATUS 0x80
31
32#define ACPI_BUTTON_SUBCLASS_POWER "power"
33#define ACPI_BUTTON_HID_POWER "PNP0C0C"
34#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
35#define ACPI_BUTTON_TYPE_POWER 0x01
36
37#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
38#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
39#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
40#define ACPI_BUTTON_TYPE_SLEEP 0x03
41
42#define ACPI_BUTTON_SUBCLASS_LID "lid"
43#define ACPI_BUTTON_HID_LID "PNP0C0D"
44#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
45#define ACPI_BUTTON_TYPE_LID 0x05
46
47#define ACPI_BUTTON_LID_INIT_IGNORE 0x00
48#define ACPI_BUTTON_LID_INIT_OPEN 0x01
49#define ACPI_BUTTON_LID_INIT_METHOD 0x02
50
51#define _COMPONENT ACPI_BUTTON_COMPONENT
52ACPI_MODULE_NAME("button");
53
54MODULE_AUTHOR("Paul Diefenbaugh");
55MODULE_DESCRIPTION("ACPI Button Driver");
56MODULE_LICENSE("GPL");
57
58static const struct acpi_device_id button_device_ids[] = {
59 {ACPI_BUTTON_HID_LID, 0},
60 {ACPI_BUTTON_HID_SLEEP, 0},
61 {ACPI_BUTTON_HID_SLEEPF, 0},
62 {ACPI_BUTTON_HID_POWER, 0},
63 {ACPI_BUTTON_HID_POWERF, 0},
64 {"", 0},
65};
66MODULE_DEVICE_TABLE(acpi, button_device_ids);
67
68/*
69 * Some devices which don't even have a lid in anyway have a broken _LID
70 * method (e.g. pointing to a floating gpio pin) causing spurious LID events.
71 */
72static const struct dmi_system_id lid_blacklst[] = {
73 {
74 /* GP-electronic T701 */
75 .matches = {
76 DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
77 DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
78 DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
79 },
80 },
Olivier Deprez0e641232021-09-23 10:07:05 +020081 {
82 /*
83 * Medion Akoya E2215T, notification of the LID device only
84 * happens on close, not on open and _LID always returns closed.
85 */
86 .matches = {
87 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
88 DMI_MATCH(DMI_PRODUCT_NAME, "E2215T"),
89 },
90 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
91 },
92 {
93 /*
94 * Medion Akoya E2228T, notification of the LID device only
95 * happens on close, not on open and _LID always returns closed.
96 */
97 .matches = {
98 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
99 DMI_MATCH(DMI_PRODUCT_NAME, "E2228T"),
100 },
101 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
102 },
103 {
104 /*
105 * Razer Blade Stealth 13 late 2019, notification of the LID device
106 * only happens on close, not on open and _LID always returns closed.
107 */
108 .matches = {
109 DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
110 DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"),
111 },
112 .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
113 },
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000114 {}
115};
116
117static int acpi_button_add(struct acpi_device *device);
118static int acpi_button_remove(struct acpi_device *device);
119static void acpi_button_notify(struct acpi_device *device, u32 event);
120
121#ifdef CONFIG_PM_SLEEP
122static int acpi_button_suspend(struct device *dev);
123static int acpi_button_resume(struct device *dev);
124#else
125#define acpi_button_suspend NULL
126#define acpi_button_resume NULL
127#endif
128static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
129
130static struct acpi_driver acpi_button_driver = {
131 .name = "button",
132 .class = ACPI_BUTTON_CLASS,
133 .ids = button_device_ids,
134 .ops = {
135 .add = acpi_button_add,
136 .remove = acpi_button_remove,
137 .notify = acpi_button_notify,
138 },
139 .drv.pm = &acpi_button_pm,
140};
141
142struct acpi_button {
143 unsigned int type;
144 struct input_dev *input;
145 char phys[32]; /* for input device */
146 unsigned long pushed;
147 int last_state;
148 ktime_t last_time;
149 bool suspended;
Olivier Deprez0e641232021-09-23 10:07:05 +0200150 bool lid_state_initialized;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000151};
152
153static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
154static struct acpi_device *lid_device;
155static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
156
157static unsigned long lid_report_interval __read_mostly = 500;
158module_param(lid_report_interval, ulong, 0644);
159MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
160
161/* --------------------------------------------------------------------------
162 FS Interface (/proc)
163 -------------------------------------------------------------------------- */
164
165static struct proc_dir_entry *acpi_button_dir;
166static struct proc_dir_entry *acpi_lid_dir;
167
168static int acpi_lid_evaluate_state(struct acpi_device *device)
169{
170 unsigned long long lid_state;
171 acpi_status status;
172
173 status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
174 if (ACPI_FAILURE(status))
175 return -ENODEV;
176
177 return lid_state ? 1 : 0;
178}
179
180static int acpi_lid_notify_state(struct acpi_device *device, int state)
181{
182 struct acpi_button *button = acpi_driver_data(device);
183 int ret;
184 ktime_t next_report;
185 bool do_update;
186
187 /*
188 * In lid_init_state=ignore mode, if user opens/closes lid
189 * frequently with "open" missing, and "last_time" is also updated
190 * frequently, "close" cannot be delivered to the userspace.
191 * So "last_time" is only updated after a timeout or an actual
192 * switch.
193 */
194 if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
195 button->last_state != !!state)
196 do_update = true;
197 else
198 do_update = false;
199
200 next_report = ktime_add(button->last_time,
201 ms_to_ktime(lid_report_interval));
202 if (button->last_state == !!state &&
203 ktime_after(ktime_get(), next_report)) {
204 /* Complain the buggy firmware */
205 pr_warn_once("The lid device is not compliant to SW_LID.\n");
206
207 /*
208 * Send the unreliable complement switch event:
209 *
210 * On most platforms, the lid device is reliable. However
211 * there are exceptions:
212 * 1. Platforms returning initial lid state as "close" by
213 * default after booting/resuming:
214 * https://bugzilla.kernel.org/show_bug.cgi?id=89211
215 * https://bugzilla.kernel.org/show_bug.cgi?id=106151
216 * 2. Platforms never reporting "open" events:
217 * https://bugzilla.kernel.org/show_bug.cgi?id=106941
218 * On these buggy platforms, the usage model of the ACPI
219 * lid device actually is:
220 * 1. The initial returning value of _LID may not be
221 * reliable.
222 * 2. The open event may not be reliable.
223 * 3. The close event is reliable.
224 *
225 * But SW_LID is typed as input switch event, the input
226 * layer checks if the event is redundant. Hence if the
227 * state is not switched, the userspace cannot see this
228 * platform triggered reliable event. By inserting a
229 * complement switch event, it then is guaranteed that the
230 * platform triggered reliable one can always be seen by
231 * the userspace.
232 */
233 if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
234 do_update = true;
235 /*
236 * Do generate complement switch event for "close"
237 * as "close" is reliable and wrong "open" won't
238 * trigger unexpected behaviors.
239 * Do not generate complement switch event for
240 * "open" as "open" is not reliable and wrong
241 * "close" will trigger unexpected behaviors.
242 */
243 if (!state) {
244 input_report_switch(button->input,
245 SW_LID, state);
246 input_sync(button->input);
247 }
248 }
249 }
250 /* Send the platform triggered reliable event */
251 if (do_update) {
252 acpi_handle_debug(device->handle, "ACPI LID %s\n",
253 state ? "open" : "closed");
254 input_report_switch(button->input, SW_LID, !state);
255 input_sync(button->input);
256 button->last_state = !!state;
257 button->last_time = ktime_get();
258 }
259
260 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
261 if (ret == NOTIFY_DONE)
262 ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
263 device);
264 if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
265 /*
266 * It is also regarded as success if the notifier_chain
267 * returns NOTIFY_OK or NOTIFY_DONE.
268 */
269 ret = 0;
270 }
271 return ret;
272}
273
274static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
275 void *offset)
276{
277 struct acpi_device *device = seq->private;
278 int state;
279
280 state = acpi_lid_evaluate_state(device);
281 seq_printf(seq, "state: %s\n",
282 state < 0 ? "unsupported" : (state ? "open" : "closed"));
283 return 0;
284}
285
286static int acpi_button_add_fs(struct acpi_device *device)
287{
288 struct acpi_button *button = acpi_driver_data(device);
289 struct proc_dir_entry *entry = NULL;
290 int ret = 0;
291
292 /* procfs I/F for ACPI lid device only */
293 if (button->type != ACPI_BUTTON_TYPE_LID)
294 return 0;
295
296 if (acpi_button_dir || acpi_lid_dir) {
297 printk(KERN_ERR PREFIX "More than one Lid device found!\n");
298 return -EEXIST;
299 }
300
301 /* create /proc/acpi/button */
302 acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
303 if (!acpi_button_dir)
304 return -ENODEV;
305
306 /* create /proc/acpi/button/lid */
307 acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
308 if (!acpi_lid_dir) {
309 ret = -ENODEV;
310 goto remove_button_dir;
311 }
312
313 /* create /proc/acpi/button/lid/LID/ */
314 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
315 if (!acpi_device_dir(device)) {
316 ret = -ENODEV;
317 goto remove_lid_dir;
318 }
319
320 /* create /proc/acpi/button/lid/LID/state */
321 entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
322 acpi_device_dir(device), acpi_button_state_seq_show,
323 device);
324 if (!entry) {
325 ret = -ENODEV;
326 goto remove_dev_dir;
327 }
328
329done:
330 return ret;
331
332remove_dev_dir:
333 remove_proc_entry(acpi_device_bid(device),
334 acpi_lid_dir);
335 acpi_device_dir(device) = NULL;
336remove_lid_dir:
337 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
338 acpi_lid_dir = NULL;
339remove_button_dir:
340 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
341 acpi_button_dir = NULL;
342 goto done;
343}
344
345static int acpi_button_remove_fs(struct acpi_device *device)
346{
347 struct acpi_button *button = acpi_driver_data(device);
348
349 if (button->type != ACPI_BUTTON_TYPE_LID)
350 return 0;
351
352 remove_proc_entry(ACPI_BUTTON_FILE_STATE,
353 acpi_device_dir(device));
354 remove_proc_entry(acpi_device_bid(device),
355 acpi_lid_dir);
356 acpi_device_dir(device) = NULL;
357 remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
358 acpi_lid_dir = NULL;
359 remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
360 acpi_button_dir = NULL;
361
362 return 0;
363}
364
365/* --------------------------------------------------------------------------
366 Driver Interface
367 -------------------------------------------------------------------------- */
368int acpi_lid_notifier_register(struct notifier_block *nb)
369{
370 return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
371}
372EXPORT_SYMBOL(acpi_lid_notifier_register);
373
374int acpi_lid_notifier_unregister(struct notifier_block *nb)
375{
376 return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
377}
378EXPORT_SYMBOL(acpi_lid_notifier_unregister);
379
380int acpi_lid_open(void)
381{
382 if (!lid_device)
383 return -ENODEV;
384
385 return acpi_lid_evaluate_state(lid_device);
386}
387EXPORT_SYMBOL(acpi_lid_open);
388
389static int acpi_lid_update_state(struct acpi_device *device,
390 bool signal_wakeup)
391{
392 int state;
393
394 state = acpi_lid_evaluate_state(device);
395 if (state < 0)
396 return state;
397
398 if (state && signal_wakeup)
399 acpi_pm_wakeup_event(&device->dev);
400
401 return acpi_lid_notify_state(device, state);
402}
403
404static void acpi_lid_initialize_state(struct acpi_device *device)
405{
Olivier Deprez0e641232021-09-23 10:07:05 +0200406 struct acpi_button *button = acpi_driver_data(device);
407
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000408 switch (lid_init_state) {
409 case ACPI_BUTTON_LID_INIT_OPEN:
410 (void)acpi_lid_notify_state(device, 1);
411 break;
412 case ACPI_BUTTON_LID_INIT_METHOD:
413 (void)acpi_lid_update_state(device, false);
414 break;
415 case ACPI_BUTTON_LID_INIT_IGNORE:
416 default:
417 break;
418 }
Olivier Deprez0e641232021-09-23 10:07:05 +0200419
420 button->lid_state_initialized = true;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000421}
422
423static void acpi_button_notify(struct acpi_device *device, u32 event)
424{
425 struct acpi_button *button = acpi_driver_data(device);
426 struct input_dev *input;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000427
428 switch (event) {
429 case ACPI_FIXED_HARDWARE_EVENT:
430 event = ACPI_BUTTON_NOTIFY_STATUS;
431 /* fall through */
432 case ACPI_BUTTON_NOTIFY_STATUS:
433 input = button->input;
434 if (button->type == ACPI_BUTTON_TYPE_LID) {
Olivier Deprez0e641232021-09-23 10:07:05 +0200435 if (button->lid_state_initialized)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000436 acpi_lid_update_state(device, true);
437 } else {
438 int keycode;
439
440 acpi_pm_wakeup_event(&device->dev);
441 if (button->suspended)
442 break;
443
444 keycode = test_bit(KEY_SLEEP, input->keybit) ?
445 KEY_SLEEP : KEY_POWER;
446 input_report_key(input, keycode, 1);
447 input_sync(input);
448 input_report_key(input, keycode, 0);
449 input_sync(input);
450
451 acpi_bus_generate_netlink_event(
452 device->pnp.device_class,
453 dev_name(&device->dev),
454 event, ++button->pushed);
455 }
456 break;
457 default:
458 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
459 "Unsupported event [0x%x]\n", event));
460 break;
461 }
462}
463
464#ifdef CONFIG_PM_SLEEP
465static int acpi_button_suspend(struct device *dev)
466{
467 struct acpi_device *device = to_acpi_device(dev);
468 struct acpi_button *button = acpi_driver_data(device);
469
470 button->suspended = true;
471 return 0;
472}
473
474static int acpi_button_resume(struct device *dev)
475{
476 struct acpi_device *device = to_acpi_device(dev);
477 struct acpi_button *button = acpi_driver_data(device);
478
479 button->suspended = false;
Olivier Deprez0e641232021-09-23 10:07:05 +0200480 if (button->type == ACPI_BUTTON_TYPE_LID) {
David Brazdil0f672f62019-12-10 10:32:29 +0000481 button->last_state = !!acpi_lid_evaluate_state(device);
482 button->last_time = ktime_get();
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000483 acpi_lid_initialize_state(device);
David Brazdil0f672f62019-12-10 10:32:29 +0000484 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000485 return 0;
486}
487#endif
488
489static int acpi_lid_input_open(struct input_dev *input)
490{
491 struct acpi_device *device = input_get_drvdata(input);
492 struct acpi_button *button = acpi_driver_data(device);
493
494 button->last_state = !!acpi_lid_evaluate_state(device);
495 button->last_time = ktime_get();
496 acpi_lid_initialize_state(device);
497
498 return 0;
499}
500
501static int acpi_button_add(struct acpi_device *device)
502{
503 struct acpi_button *button;
504 struct input_dev *input;
505 const char *hid = acpi_device_hid(device);
506 char *name, *class;
507 int error;
508
509 if (!strcmp(hid, ACPI_BUTTON_HID_LID) && dmi_check_system(lid_blacklst))
510 return -ENODEV;
511
512 button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
513 if (!button)
514 return -ENOMEM;
515
516 device->driver_data = button;
517
518 button->input = input = input_allocate_device();
519 if (!input) {
520 error = -ENOMEM;
521 goto err_free_button;
522 }
523
524 name = acpi_device_name(device);
525 class = acpi_device_class(device);
526
527 if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
528 !strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
529 button->type = ACPI_BUTTON_TYPE_POWER;
530 strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
531 sprintf(class, "%s/%s",
532 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
533 } else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
534 !strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
535 button->type = ACPI_BUTTON_TYPE_SLEEP;
536 strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
537 sprintf(class, "%s/%s",
538 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
539 } else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
540 button->type = ACPI_BUTTON_TYPE_LID;
541 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
542 sprintf(class, "%s/%s",
543 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
544 input->open = acpi_lid_input_open;
545 } else {
546 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
547 error = -ENODEV;
548 goto err_free_input;
549 }
550
551 error = acpi_button_add_fs(device);
552 if (error)
553 goto err_free_input;
554
555 snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
556
557 input->name = name;
558 input->phys = button->phys;
559 input->id.bustype = BUS_HOST;
560 input->id.product = button->type;
561 input->dev.parent = &device->dev;
562
563 switch (button->type) {
564 case ACPI_BUTTON_TYPE_POWER:
565 input_set_capability(input, EV_KEY, KEY_POWER);
566 break;
567
568 case ACPI_BUTTON_TYPE_SLEEP:
569 input_set_capability(input, EV_KEY, KEY_SLEEP);
570 break;
571
572 case ACPI_BUTTON_TYPE_LID:
573 input_set_capability(input, EV_SW, SW_LID);
574 break;
575 }
576
577 input_set_drvdata(input, device);
578 error = input_register_device(input);
579 if (error)
580 goto err_remove_fs;
581 if (button->type == ACPI_BUTTON_TYPE_LID) {
582 /*
583 * This assumes there's only one lid device, or if there are
584 * more we only care about the last one...
585 */
586 lid_device = device;
587 }
588
589 device_init_wakeup(&device->dev, true);
590 printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
591 return 0;
592
593 err_remove_fs:
594 acpi_button_remove_fs(device);
595 err_free_input:
596 input_free_device(input);
597 err_free_button:
598 kfree(button);
599 return error;
600}
601
602static int acpi_button_remove(struct acpi_device *device)
603{
604 struct acpi_button *button = acpi_driver_data(device);
605
606 acpi_button_remove_fs(device);
607 input_unregister_device(button->input);
608 kfree(button);
609 return 0;
610}
611
612static int param_set_lid_init_state(const char *val,
613 const struct kernel_param *kp)
614{
615 int result = 0;
616
617 if (!strncmp(val, "open", sizeof("open") - 1)) {
618 lid_init_state = ACPI_BUTTON_LID_INIT_OPEN;
619 pr_info("Notify initial lid state as open\n");
620 } else if (!strncmp(val, "method", sizeof("method") - 1)) {
621 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
622 pr_info("Notify initial lid state with _LID return value\n");
623 } else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) {
624 lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE;
625 pr_info("Do not notify initial lid state\n");
626 } else
627 result = -EINVAL;
628 return result;
629}
630
631static int param_get_lid_init_state(char *buffer,
632 const struct kernel_param *kp)
633{
634 switch (lid_init_state) {
635 case ACPI_BUTTON_LID_INIT_OPEN:
636 return sprintf(buffer, "open");
637 case ACPI_BUTTON_LID_INIT_METHOD:
638 return sprintf(buffer, "method");
639 case ACPI_BUTTON_LID_INIT_IGNORE:
640 return sprintf(buffer, "ignore");
641 default:
642 return sprintf(buffer, "invalid");
643 }
644 return 0;
645}
646
647module_param_call(lid_init_state,
648 param_set_lid_init_state, param_get_lid_init_state,
649 NULL, 0644);
650MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
651
652static int acpi_button_register_driver(struct acpi_driver *driver)
653{
654 /*
655 * Modules such as nouveau.ko and i915.ko have a link time dependency
656 * on acpi_lid_open(), and would therefore not be loadable on ACPI
657 * capable kernels booted in non-ACPI mode if the return value of
658 * acpi_bus_register_driver() is returned from here with ACPI disabled
659 * when this driver is built as a module.
660 */
661 if (acpi_disabled)
662 return 0;
663
664 return acpi_bus_register_driver(driver);
665}
666
667static void acpi_button_unregister_driver(struct acpi_driver *driver)
668{
669 if (!acpi_disabled)
670 acpi_bus_unregister_driver(driver);
671}
672
673module_driver(acpi_button_driver, acpi_button_register_driver,
674 acpi_button_unregister_driver);