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