Page MenuHomeFreeBSD

D57712.diff
No OneTemporary

D57712.diff

diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c
--- a/sys/dev/acpica/acpi.c
+++ b/sys/dev/acpica/acpi.c
@@ -3792,6 +3792,13 @@
EVENTHANDLER_INVOKE(power_resume, stype);
+ /*
+ * Record the resume time so that acpi_event_{power,sleep}_button_sleep()
+ * can ignore a button press that firmware/EC replays as the wake event
+ * as part of this resume (see acpi_button_resume_replay()).
+ */
+ sc->acpi_resume_sbt = getsbinuptime();
+
/* Allow another sleep request after a while. */
callout_schedule(&acpi_sleep_timer, hz * ACPI_MINIMUM_AWAKETIME);
@@ -4147,6 +4154,48 @@
/*
* ACPICA Event Handlers (FixedEvent, also called from button notify handler)
*/
+/*
+ * The window after resume during which a button "press" is assumed to be the
+ * firmware/EC replaying the wake event rather than a genuine press.
+ *
+ * Per the ACPI specification a power or sleep button that is also a system
+ * wake source must report the wake as a wakeup notification (Notify 0x02),
+ * not a button press (Notify 0x80). Some firmware violates this: the
+ * embedded controller queues the physical key press that woke the machine
+ * and flushes it through its normal _Qxx query path as soon as it is
+ * reinitialized on resume. The replayed press is therefore indistinguishable
+ * from a real one and is delivered synchronously with resume (observed within
+ * a few milliseconds). Honoring it would re-enter the sleep state, producing
+ * a resume->suspend loop.
+ *
+ * A deliberate press cannot occur this quickly -- it arrives well after the
+ * display has come back -- so a sub-second window cleanly separates the
+ * replayed wake event from a real press without ignoring genuine input for
+ * any user-perceptible time. A sub-second comparison (rather than a
+ * whole-second one) is used so a replay near a one-second boundary is not
+ * misclassified.
+ */
+#define ACPI_BUTTON_REPLAY_WINDOW (SBT_1S / 2)
+
+static bool
+acpi_button_resume_replay(struct acpi_softc *sc, const char *which)
+{
+ sbintime_t elapsed;
+
+ if (sc->acpi_resume_sbt == 0)
+ return (false);
+ elapsed = getsbinuptime() - sc->acpi_resume_sbt;
+ if (elapsed < 0 || elapsed >= ACPI_BUTTON_REPLAY_WINDOW)
+ return (false);
+ if (bootverbose) {
+ device_printf(sc->acpi_dev,
+ "ignoring %s button press %jd us after resume "
+ "(firmware replayed the wake event)\n",
+ which, (intmax_t)(elapsed / SBT_1US));
+ }
+ return (true);
+}
+
static void
acpi_invoke_sleep_eventhandler(void *context)
{
@@ -4173,6 +4222,8 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
#if defined(__amd64__) || defined(__i386__)
+ if (acpi_button_resume_replay(sc, "power"))
+ return_VALUE (ACPI_INTERRUPT_HANDLED);
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpi_invoke_sleep_eventhandler, &sc->acpi_power_button_stype)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
@@ -4203,6 +4254,8 @@
ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
+ if (acpi_button_resume_replay(sc, "sleep"))
+ return_VALUE (ACPI_INTERRUPT_HANDLED);
if (ACPI_FAILURE(AcpiOsExecute(OSL_NOTIFY_HANDLER,
acpi_invoke_sleep_eventhandler, &sc->acpi_sleep_button_stype)))
return_VALUE (ACPI_INTERRUPT_NOT_HANDLED);
diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h
--- a/sys/dev/acpica/acpivar.h
+++ b/sys/dev/acpica/acpivar.h
@@ -56,6 +56,7 @@
int acpi_enabled;
enum power_stype acpi_stype;
int acpi_sleep_disabled;
+ sbintime_t acpi_resume_sbt; /* Uptime at last resume. */
/* Supported sleep states and types. */
bool acpi_supported_stypes[POWER_STYPE_COUNT];

File Metadata

Mime Type
text/plain
Expires
Wed, Jun 24, 12:45 AM (15 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34265898
Default Alt Text
D57712.diff (3 KB)

Event Timeline