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