Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 14 Sep 2025 21:52:48 GMT
From:      Aymeric Wibo <obiwac@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: c43473dc9b83 - main - sys/power: Generic sleep types
Message-ID:  <202509142152.58ELqm78052548@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by obiwac:

URL: https://cgit.FreeBSD.org/src/commit/?id=c43473dc9b8349103f78107300457ca35e437882

commit c43473dc9b8349103f78107300457ca35e437882
Author:     Aymeric Wibo <obiwac@FreeBSD.org>
AuthorDate: 2025-09-14 21:33:33 +0000
Commit:     Aymeric Wibo <obiwac@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
+ * <obiwac@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
+ * <obiwac@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
+ * <obiwac@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);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202509142152.58ELqm78052548>