1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | #include <linux/module.h> |
3 | #include <linux/sched/signal.h> |
4 | #include <linux/acpi.h> |
5 | #include <acpi/button.h> |
6 | |
7 | MODULE_AUTHOR("Josh Triplett" ); |
8 | MODULE_DESCRIPTION("ACPI Tiny Power Button Driver" ); |
9 | MODULE_LICENSE("GPL" ); |
10 | |
11 | static int power_signal __read_mostly = CONFIG_ACPI_TINY_POWER_BUTTON_SIGNAL; |
12 | module_param(power_signal, int, 0644); |
13 | MODULE_PARM_DESC(power_signal, "Power button sends this signal to init" ); |
14 | |
15 | static const struct acpi_device_id tiny_power_button_device_ids[] = { |
16 | { ACPI_BUTTON_HID_POWER, 0 }, |
17 | { ACPI_BUTTON_HID_POWERF, 0 }, |
18 | { "" , 0 }, |
19 | }; |
20 | MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids); |
21 | |
22 | static void acpi_tiny_power_button_notify(acpi_handle handle, u32 event, void *data) |
23 | { |
24 | kill_cad_pid(sig: power_signal, priv: 1); |
25 | } |
26 | |
27 | static void acpi_tiny_power_button_notify_run(void *not_used) |
28 | { |
29 | acpi_tiny_power_button_notify(NULL, ACPI_FIXED_HARDWARE_EVENT, NULL); |
30 | } |
31 | |
32 | static u32 acpi_tiny_power_button_event(void *not_used) |
33 | { |
34 | acpi_os_execute(type: OSL_NOTIFY_HANDLER, function: acpi_tiny_power_button_notify_run, NULL); |
35 | return ACPI_INTERRUPT_HANDLED; |
36 | } |
37 | |
38 | static int acpi_tiny_power_button_add(struct acpi_device *device) |
39 | { |
40 | acpi_status status; |
41 | |
42 | if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { |
43 | status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, |
44 | handler: acpi_tiny_power_button_event, |
45 | NULL); |
46 | } else { |
47 | status = acpi_install_notify_handler(device: device->handle, |
48 | ACPI_DEVICE_NOTIFY, |
49 | handler: acpi_tiny_power_button_notify, |
50 | NULL); |
51 | } |
52 | if (ACPI_FAILURE(status)) |
53 | return -ENODEV; |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static void acpi_tiny_power_button_remove(struct acpi_device *device) |
59 | { |
60 | if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { |
61 | acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, |
62 | handler: acpi_tiny_power_button_event); |
63 | } else { |
64 | acpi_remove_notify_handler(device: device->handle, ACPI_DEVICE_NOTIFY, |
65 | handler: acpi_tiny_power_button_notify); |
66 | } |
67 | acpi_os_wait_events_complete(); |
68 | } |
69 | |
70 | static struct acpi_driver acpi_tiny_power_button_driver = { |
71 | .name = "tiny-power-button" , |
72 | .class = "tiny-power-button" , |
73 | .ids = tiny_power_button_device_ids, |
74 | .ops = { |
75 | .add = acpi_tiny_power_button_add, |
76 | .remove = acpi_tiny_power_button_remove, |
77 | }, |
78 | }; |
79 | |
80 | module_acpi_driver(acpi_tiny_power_button_driver); |
81 | |