Re: [PATCH v3] ACPI: button: Report wakeup key only for power button wakeups

From: Rafael J. Wysocki (Intel)

Date: Fri Sep 18 2026 - 11:25:43 EST


On Thu, Sep 17, 2026 at 4:31 PM Baorui.Liu <baorliu@xxxxxxx> wrote:
>
> The ACPI button driver reports KEY_WAKEUP from the Power Button input
> device to let userspace know that the system was resumed by a power
> button wakeup.
>
> However, reporting KEY_WAKEUP from generic system resume paths can make
> userspace observe a Power Button wakeup even when the system was resumed
> by a different wake source.
>
> This is reproducible on an AMD Android 15 Xen guest. With a kernel
> without this fix, a non-power-button S3 resume:
>
> echo mem > /sys/power/state
> xl trigger android s3resume
>
> makes the Power Button input device report KEY_WAKEUP. The same test on
> a kernel with this fix no longer reports KEY_WAKEUP from the Power Button
> input device.
>
> Track whether a power button event/notify is observed while the ACPI
> button device is suspended, and report KEY_WAKEUP on resume only in that
> case. Do not synthesize a Power Button input event from generic ACPI
> sleep resume code.
>
> Signed-off-by: Baorui.Liu <baorliu@xxxxxxx>

Please see

https://sashiko.dev/#/patchset/20260917143116.445-1-baorliu%40amd.com

and tell me what you think.

Thanks!

> ---
> drivers/acpi/button.c | 13 ++++++++++++-
> drivers/acpi/sleep.c | 41 -----------------------------------------
> 2 files changed, 12 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
> index cdbb1023a8ee..7a708fe351fb 100644
> --- a/drivers/acpi/button.c
> +++ b/drivers/acpi/button.c
> @@ -191,6 +191,7 @@ struct acpi_button {
> bool last_state;
> ktime_t last_time;
> bool suspended;
> + bool wakeup_pending;
> bool lid_state_initialized;
> bool gpe_enabled;
> };
> @@ -476,6 +477,9 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
>
> acpi_pm_wakeup_event(button->dev);
>
> + if (button->type == ACPI_BUTTON_TYPE_POWER && button->suspended)
> + button->wakeup_pending = true;
> +
> if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE)
> return;
>
> @@ -498,6 +502,11 @@ static void acpi_button_notify_run(void *data)
>
> static u32 acpi_button_event(void *data)
> {
> + struct acpi_button *button = data;
> +
> + if (button->type == ACPI_BUTTON_TYPE_POWER && button->suspended)
> + button->wakeup_pending = true;
> +
> acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_button_notify_run, data);
> return ACPI_INTERRUPT_HANDLED;
> }
> @@ -508,6 +517,7 @@ static int acpi_button_suspend(struct device *dev)
> struct acpi_button *button = dev_get_drvdata(dev);
>
> button->suspended = true;
> + button->wakeup_pending = false;
> return 0;
> }
>
> @@ -523,12 +533,13 @@ static int acpi_button_resume(struct device *dev)
> acpi_lid_initialize_state(button);
> }
>
> - if (button->type == ACPI_BUTTON_TYPE_POWER) {
> + if (button->type == ACPI_BUTTON_TYPE_POWER && button->wakeup_pending) {
> input = button->input;
> input_report_key(input, KEY_WAKEUP, 1);
> input_sync(input);
> input_report_key(input, KEY_WAKEUP, 0);
> input_sync(input);
> + button->wakeup_pending = false;
> }
> return 0;
> }
> diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
> index 132a9df98471..7296e4bb0e4d 100644
> --- a/drivers/acpi/sleep.c
> +++ b/drivers/acpi/sleep.c
> @@ -104,8 +104,6 @@ u32 acpi_target_system_state(void)
> }
> EXPORT_SYMBOL_GPL(acpi_target_system_state);
>
> -static bool pwr_btn_event_pending;
> -
> /*
> * The ACPI specification wants us to save NVS memory regions during hibernation
> * and to restore them during the subsequent resume. Windows does that also for
> @@ -484,7 +482,6 @@ static int acpi_pm_prepare(void)
> */
> static void acpi_pm_finish(void)
> {
> - struct acpi_device *pwr_btn_adev;
> u32 acpi_state = acpi_target_sleep_state;
>
> acpi_ec_unblock_transactions();
> @@ -503,23 +500,6 @@ static void acpi_pm_finish(void)
> acpi_target_sleep_state = ACPI_STATE_S0;
>
> acpi_resume_power_resources();
> -
> - /* If we were woken with the fixed power button, provide a small
> - * hint to userspace in the form of a wakeup event on the fixed power
> - * button device (if it can be found).
> - *
> - * We delay the event generation til now, as the PM layer requires
> - * timekeeping to be running before we generate events. */
> - if (!pwr_btn_event_pending)
> - return;
> -
> - pwr_btn_event_pending = false;
> - pwr_btn_adev = acpi_dev_get_first_match_dev(ACPI_BUTTON_HID_POWERF,
> - NULL, -1);
> - if (pwr_btn_adev) {
> - pm_wakeup_event(&pwr_btn_adev->dev, 0);
> - acpi_dev_put(pwr_btn_adev);
> - }
> }
>
> /**
> @@ -626,27 +606,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
> /* Reprogram control registers */
> acpi_leave_sleep_state_prep(acpi_state);
>
> - /* ACPI 3.0 specs (P62) says that it's the responsibility
> - * of the OSPM to clear the status bit [ implying that the
> - * POWER_BUTTON event should not reach userspace ]
> - *
> - * However, we do generate a small hint for userspace in the form of
> - * a wakeup event. We flag this condition for now and generate the
> - * event later, as we're currently too early in resume to be able to
> - * generate wakeup events.
> - */
> - if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
> - acpi_event_status pwr_btn_status = ACPI_EVENT_FLAG_DISABLED;
> -
> - acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
> -
> - if (pwr_btn_status & ACPI_EVENT_FLAG_STATUS_SET) {
> - acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
> - /* Flag for later */
> - pwr_btn_event_pending = true;
> - }
> - }
> -
> /*
> * Disable all GPE and clear their status bits before interrupts are
> * enabled. Some GPEs (like wakeup GPEs) have no handlers and this can
> --
> 2.34.1
>