The branch main has been updated by obiwac: URL: https://cgit.FreeBSD.org/src/commit/?id=c43473dc9b8349103f78107300457ca35e437882
commit c43473dc9b8349103f78107300457ca35e437882 Author: Aymeric Wibo <obi...@freebsd.org> AuthorDate: 2025-09-14 21:33:33 +0000 Commit: Aymeric Wibo <obi...@freebsd.org> CommitDate: 2025-09-14 21:48:46 +0000 sys/power: Generic sleep types Pull out the sleep types (stype) from ACPI, as was previously being done in D48732, and pass this sleep type to `power_pm_fn` instead of passing the existing sleep state. This is a little awkward because we already kinda have generic sleep states (`POWER_SLEEP_STATE_*`), but these are not precise enough to build upon. This revision also adds generic equivalents to `hw.acpi.suspend_state` etc sysctls, e.g. `kern.power.suspend`. Reviewed by: markj, mckusick (mentor) Approved by: markj, mckusick (mentor) Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D52036 --- sys/dev/acpica/acpi.c | 58 ++++++++++++++++-------------- sys/dev/acpica/acpivar.h | 1 + sys/kern/subr_power.c | 92 ++++++++++++++++++++++++++++++++++++++++++++---- sys/sys/power.h | 52 +++++++++++++++++++++++++-- 4 files changed, 168 insertions(+), 35 deletions(-) diff --git a/sys/dev/acpica/acpi.c b/sys/dev/acpica/acpi.c index a2159b12876f..574d3aacbcde 100644 --- a/sys/dev/acpica/acpi.c +++ b/sys/dev/acpica/acpi.c @@ -4,6 +4,10 @@ * Copyright (c) 2000, 2001 Michael Smith * Copyright (c) 2000 BSDi * All rights reserved. + * Copyright (c) 2025 The FreeBSD Foundation + * + * Portions of this software were developed by Aymeric Wibo + * <obi...@freebsd.org> under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -181,7 +185,8 @@ static const char *acpi_sstate2sname(int sstate); static int acpi_supported_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_sleep_state_sysctl(SYSCTL_HANDLER_ARGS); static int acpi_debug_objects_sysctl(SYSCTL_HANDLER_ARGS); -static int acpi_pm_func(u_long cmd, void *arg, ...); +static int acpi_stype_to_sstate(struct acpi_softc *sc, enum power_stype stype); +static int acpi_pm_func(u_long cmd, void *arg, enum power_stype stype); static void acpi_enable_pcie(void); static void acpi_reset_interfaces(device_t dev); @@ -741,6 +746,28 @@ acpi_attach(device_t dev) return_VALUE (error); } +static int +acpi_stype_to_sstate(struct acpi_softc *sc, enum power_stype stype) +{ + switch (stype) { + case POWER_STYPE_AWAKE: + return (ACPI_STATE_S0); + case POWER_STYPE_STANDBY: + return (sc->acpi_standby_sx); + case POWER_STYPE_SUSPEND_TO_MEM: + return (ACPI_STATE_S3); + case POWER_STYPE_HIBERNATE: + return (ACPI_STATE_S4); + case POWER_STYPE_POWEROFF: + return (ACPI_STATE_S5); + case POWER_STYPE_SUSPEND_TO_IDLE: + case POWER_STYPE_COUNT: + case POWER_STYPE_UNKNOWN: + return (ACPI_STATE_UNKNOWN); + } + return (ACPI_STATE_UNKNOWN); +} + static void acpi_set_power_children(device_t dev, int state) { @@ -4621,12 +4648,10 @@ acpi_reset_interfaces(device_t dev) } static int -acpi_pm_func(u_long cmd, void *arg, ...) +acpi_pm_func(u_long cmd, void *arg, enum power_stype stype) { - int state, acpi_state; - int error; + int error, sstate; struct acpi_softc *sc; - va_list ap; error = 0; switch (cmd) { @@ -4636,27 +4661,8 @@ acpi_pm_func(u_long cmd, void *arg, ...) error = EINVAL; goto out; } - - va_start(ap, arg); - state = va_arg(ap, int); - va_end(ap); - - switch (state) { - case POWER_SLEEP_STATE_STANDBY: - acpi_state = sc->acpi_standby_sx; - break; - case POWER_SLEEP_STATE_SUSPEND: - acpi_state = sc->acpi_suspend_sx; - break; - case POWER_SLEEP_STATE_HIBERNATE: - acpi_state = ACPI_STATE_S4; - break; - default: - error = EINVAL; - goto out; - } - - if (ACPI_FAILURE(acpi_EnterSleepState(sc, acpi_state))) + sstate = acpi_stype_to_sstate(sc, stype); + if (ACPI_FAILURE(acpi_EnterSleepState(sc, sstate))) error = ENXIO; break; default: diff --git a/sys/dev/acpica/acpivar.h b/sys/dev/acpica/acpivar.h index 7495a010432b..fac32d832598 100644 --- a/sys/dev/acpica/acpivar.h +++ b/sys/dev/acpica/acpivar.h @@ -40,6 +40,7 @@ #include <sys/ktr.h> #include <sys/lock.h> #include <sys/mutex.h> +#include <sys/power.h> #include <sys/selinfo.h> #include <sys/sx.h> #include <sys/sysctl.h> diff --git a/sys/kern/subr_power.c b/sys/kern/subr_power.c index db0e7bf5b0e3..eb5bd03f5018 100644 --- a/sys/kern/subr_power.c +++ b/sys/kern/subr_power.c @@ -3,6 +3,10 @@ * * Copyright (c) 2001 Mitsuru IWASAKI * All rights reserved. + * Copyright (c) 2025 The FreeBSD Foundation + * + * Portions of this software were developed by Aymeric Wibo + * <obi...@freebsd.org> under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,20 +34,83 @@ #include <sys/eventhandler.h> #include <sys/power.h> #include <sys/proc.h> +#include <sys/sbuf.h> +#include <sys/sysctl.h> #include <sys/systm.h> #include <sys/taskqueue.h> +enum power_stype power_standby_stype = POWER_STYPE_STANDBY; +enum power_stype power_suspend_stype = POWER_STYPE_SUSPEND_TO_IDLE; +enum power_stype power_hibernate_stype = POWER_STYPE_HIBERNATE; + static u_int power_pm_type = POWER_PM_TYPE_NONE; static power_pm_fn_t power_pm_fn = NULL; static void *power_pm_arg = NULL; static struct task power_pm_task; +enum power_stype +power_name_to_stype(const char *name) +{ + enum power_stype stype; + + for (stype = 0; stype < POWER_STYPE_COUNT; stype++) { + if (strcasecmp(name, power_stype_names[stype]) == 0) + return (stype); + } + return (POWER_STYPE_UNKNOWN); +} + +const char * +power_stype_to_name(enum power_stype stype) +{ + if (stype == POWER_STYPE_UNKNOWN) + return ("NONE"); + if (stype < POWER_STYPE_AWAKE || stype >= POWER_STYPE_COUNT) + return (NULL); + return (power_stype_names[stype]); +} + +static int +power_sysctl_stype(SYSCTL_HANDLER_ARGS) +{ + char name[10]; + int err; + enum power_stype new_stype, old_stype; + + old_stype = *(enum power_stype *)oidp->oid_arg1; + strlcpy(name, power_stype_to_name(old_stype), sizeof(name)); + err = sysctl_handle_string(oidp, name, sizeof(name), req); + if (err != 0 || req->newptr == NULL) + return (err); + + new_stype = power_name_to_stype(name); + if (new_stype == POWER_STYPE_UNKNOWN) + return (EINVAL); + /* TODO Check to see if the new stype is supported. */ + if (new_stype != old_stype) + *(enum power_stype *)oidp->oid_arg1 = new_stype; + return (0); +} + +static SYSCTL_NODE(_kern, OID_AUTO, power, CTLFLAG_RW, 0, + "Generic power management related sysctls"); + +SYSCTL_PROC(_kern_power, OID_AUTO, standby, CTLTYPE_STRING | CTLFLAG_RW, + &power_standby_stype, 0, power_sysctl_stype, "A", + "Sleep type to enter on standby"); +SYSCTL_PROC(_kern_power, OID_AUTO, suspend, CTLTYPE_STRING | CTLFLAG_RW, + &power_suspend_stype, 0, power_sysctl_stype, "A", + "Sleep type to enter on suspend"); +SYSCTL_PROC(_kern_power, OID_AUTO, hibernate, CTLTYPE_STRING | CTLFLAG_RW, + &power_hibernate_stype, 0, power_sysctl_stype, "A", + "Sleep type to enter on hibernate"); + static void power_pm_deferred_fn(void *arg, int pending) { - int state = (intptr_t)arg; + enum power_stype stype = (intptr_t)arg; - power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, state); + power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, stype); } int @@ -75,14 +142,27 @@ power_pm_get_type(void) void power_pm_suspend(int state) { + enum power_stype stype; + if (power_pm_fn == NULL) return; - if (state != POWER_SLEEP_STATE_STANDBY && - state != POWER_SLEEP_STATE_SUSPEND && - state != POWER_SLEEP_STATE_HIBERNATE) + switch (state) { + case POWER_SLEEP_STATE_STANDBY: + stype = power_standby_stype; + break; + case POWER_SLEEP_STATE_SUSPEND: + stype = power_suspend_stype; + break; + case POWER_SLEEP_STATE_HIBERNATE: + stype = power_hibernate_stype; + break; + default: + printf("%s: unknown sleep state %d\n", __func__, state); return; - power_pm_task.ta_context = (void *)(intptr_t)state; + } + + power_pm_task.ta_context = (void *)(intptr_t)stype; taskqueue_enqueue(taskqueue_thread, &power_pm_task); } diff --git a/sys/sys/power.h b/sys/sys/power.h index 3ee021b0e587..44d7fc354423 100644 --- a/sys/sys/power.h +++ b/sys/sys/power.h @@ -3,6 +3,10 @@ * * Copyright (c) 2001 Mitsuru IWASAKI * All rights reserved. + * Copyright (c) 2025 The FreeBSD Foundation + * + * Portions of this software were developed by Aymeric Wibo + * <obi...@freebsd.org> under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,6 +35,7 @@ #ifdef _KERNEL #include <sys/_eventhandler.h> +#include <sys/types.h> /* Power management system type */ #define POWER_PM_TYPE_ACPI 0x01 @@ -39,13 +44,54 @@ /* Commands for Power management function */ #define POWER_CMD_SUSPEND 0x00 -/* Sleep state */ +/* + * Sleep state. + * + * These are high-level sleep states that the system can enter. They map to + * a specific generic sleep type (enum power_stype). + */ #define POWER_SLEEP_STATE_STANDBY 0x00 #define POWER_SLEEP_STATE_SUSPEND 0x01 #define POWER_SLEEP_STATE_HIBERNATE 0x02 -typedef int (*power_pm_fn_t)(u_long, void*, ...); -extern int power_pm_register(u_int, power_pm_fn_t, void *); +/* + * Sleep type. + * + * These are the specific generic methods of entering a sleep state. E.g. + * POWER_SLEEP_STATE_SUSPEND could be set to enter either suspend-to-RAM (which + * is S3 on ACPI systems), or suspend-to-idle (S0ix on ACPI systems). This + * would be done through the kern.power.suspend sysctl. + */ +enum power_stype { + POWER_STYPE_AWAKE, + POWER_STYPE_STANDBY, + POWER_STYPE_SUSPEND_TO_MEM, + POWER_STYPE_SUSPEND_TO_IDLE, + POWER_STYPE_HIBERNATE, + POWER_STYPE_POWEROFF, + POWER_STYPE_COUNT, + POWER_STYPE_UNKNOWN, +}; + +static const char * const power_stype_names[POWER_STYPE_COUNT] = { + [POWER_STYPE_AWAKE] = "awake", + [POWER_STYPE_STANDBY] = "standby", + [POWER_STYPE_SUSPEND_TO_MEM] = "s2mem", + [POWER_STYPE_SUSPEND_TO_IDLE] = "s2idle", + [POWER_STYPE_HIBERNATE] = "hibernate", + [POWER_STYPE_POWEROFF] = "poweroff", +}; + +extern enum power_stype power_standby_stype; +extern enum power_stype power_suspend_stype; +extern enum power_stype power_hibernate_stype; + +extern enum power_stype power_name_to_stype(const char *_name); +extern const char *power_stype_to_name(enum power_stype _stype); + +typedef int (*power_pm_fn_t)(u_long _cmd, void* _arg, enum power_stype _stype); +extern int power_pm_register(u_int _pm_type, power_pm_fn_t _pm_fn, + void *_pm_arg); extern u_int power_pm_get_type(void); extern void power_pm_suspend(int);