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; 42 acpi_status status; 43 44 device = ACPI_COMPANION(&pdev->dev); 45 if (!device) 46 return -ENODEV; 47 48 if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { 49 status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, 50 acpi_tiny_power_button_event, 51 NULL); 52 } else { 53 status = acpi_install_notify_handler(device->handle, 54 ACPI_DEVICE_NOTIFY, 55 acpi_tiny_power_button_notify, 56 NULL); 57 } 58 if (ACPI_FAILURE(status)) 59 return -ENODEV; 60 61 return 0; 62 } 63 64 static void acpi_tiny_power_button_remove(struct platform_device *pdev) 65 { 66 struct acpi_device *device = ACPI_COMPANION(&pdev->dev); 67 68 if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { 69 acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, 70 acpi_tiny_power_button_event); 71 } else { 72 acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, 73 acpi_tiny_power_button_notify); 74 } 75 acpi_os_wait_events_complete(); 76 } 77 78 static struct platform_driver acpi_tiny_power_button_driver = { 79 .probe = acpi_tiny_power_button_probe, 80 .remove = acpi_tiny_power_button_remove, 81 .driver = { 82 .name = "acpi-tiny-power-button", 83 .acpi_match_table = tiny_power_button_device_ids, 84 }, 85 }; 86 87 module_platform_driver(acpi_tiny_power_button_driver); 88