git: d649825e8348 - main - power: Power device and ioctl for state transitions

From: Aymeric Wibo <obiwac_at_FreeBSD.org>
Date: Wed, 13 May 2026 11:56:55 UTC
The branch main has been updated by obiwac:

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

commit d649825e83482f796f492f2000b20ce1b5db6f63
Author:     Aymeric Wibo <obiwac@FreeBSD.org>
AuthorDate: 2026-05-10 09:04:32 +0000
Commit:     Aymeric Wibo <obiwac@FreeBSD.org>
CommitDate: 2026-05-13 11:55:48 +0000

    power: Power device and ioctl for state transitions
    
    Create new /dev/power node with super simple ioctl for initiating sleep
    state transitions.
    
    This is meant as a generic interface to replace the ACPI- and
    APM-specific interfaces. This allows for non-ACPI states to be entered,
    such as suspend-to-idle when setting kern.power.suspend=suspend_to_idle.
    
    Reviewed by:    markj, olce
    Approved by:    markj, olce
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D55508
---
 sys/dev/acpi_support/acpi_ibm.c |  2 +-
 sys/dev/syscons/syscons.c       |  4 +--
 sys/dev/vt/vt_core.c            |  4 +--
 sys/kern/subr_power.c           | 59 ++++++++++++++++++++++++++++++++++++++---
 sys/sys/power.h                 | 31 ++++++++++++++--------
 5 files changed, 81 insertions(+), 19 deletions(-)

diff --git a/sys/dev/acpi_support/acpi_ibm.c b/sys/dev/acpi_support/acpi_ibm.c
index 2967832a0878..693d793532c1 100644
--- a/sys/dev/acpi_support/acpi_ibm.c
+++ b/sys/dev/acpi_support/acpi_ibm.c
@@ -1453,7 +1453,7 @@ acpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg)
 	 * instead of suspend-to-RAM.
 	 */
 	case IBM_EVENT_SUSPEND_TO_RAM:
-		power_pm_suspend(POWER_TRANSITION_SUSPEND);
+		(void)power_pm_suspend(POWER_TRANSITION_SUSPEND);
 		break;
 
 	case IBM_EVENT_BLUETOOTH:
diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c
index fe440b68f209..9167da23efd8 100644
--- a/sys/dev/syscons/syscons.c
+++ b/sys/dev/syscons/syscons.c
@@ -3987,10 +3987,10 @@ next_code:
 				break;
 
 			case SUSP:
-				power_pm_suspend(POWER_TRANSITION_SUSPEND);
+				(void)power_pm_suspend(POWER_TRANSITION_SUSPEND);
 				break;
 			case STBY:
-				power_pm_suspend(POWER_TRANSITION_STANDBY);
+				(void)power_pm_suspend(POWER_TRANSITION_STANDBY);
 				break;
 
 			case DBG:
diff --git a/sys/dev/vt/vt_core.c b/sys/dev/vt/vt_core.c
index 641f6c014937..db54cb426844 100644
--- a/sys/dev/vt/vt_core.c
+++ b/sys/dev/vt/vt_core.c
@@ -804,11 +804,11 @@ vt_machine_kbdevent(struct vt_device *vd, int c)
 		return (1);
 	case SPCLKEY | STBY: /* XXX Not present in kbdcontrol parser. */
 		/* Put machine into Stand-By mode. */
-		power_pm_suspend(POWER_TRANSITION_STANDBY);
+		(void)power_pm_suspend(POWER_TRANSITION_STANDBY);
 		return (1);
 	case SPCLKEY | SUSP: /* kbdmap(5) keyword `susp`. */
 		/* Suspend machine. */
-		power_pm_suspend(POWER_TRANSITION_SUSPEND);
+		(void)power_pm_suspend(POWER_TRANSITION_SUSPEND);
 		return (1);
 	}
 
diff --git a/sys/kern/subr_power.c b/sys/kern/subr_power.c
index 47682a0ee75e..ffd91db83e96 100644
--- a/sys/kern/subr_power.c
+++ b/sys/kern/subr_power.c
@@ -31,7 +31,10 @@
  */
 
 #include <sys/param.h>
+#include <sys/conf.h>
 #include <sys/eventhandler.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
 #include <sys/power.h>
 #include <sys/proc.h>
 #include <sys/sbuf.h>
@@ -49,6 +52,54 @@ static void		*power_pm_arg	= NULL;
 static bool		 power_pm_supported[POWER_STYPE_COUNT] = {0};
 static struct task	 power_pm_task;
 
+static d_ioctl_t	 power_ioctl;
+
+static struct cdevsw power_cdevsw = {
+	.d_version	= D_VERSION,
+	.d_ioctl	= power_ioctl,
+	.d_name		= "power",
+};
+
+static void
+power_init(void *unused)
+{
+	struct make_dev_args args;
+	struct cdev *dev;
+
+	make_dev_args_init(&args);
+	args.mda_devsw = &power_cdevsw;
+	args.mda_uid = UID_ROOT;
+	args.mda_gid = GID_OPERATOR;
+	args.mda_mode = 0660;
+	if (make_dev_s(&args, &dev, "power") != 0)
+		printf("Failed to create power device");
+}
+SYSINIT(powerdev, SI_SUB_PSEUDO, SI_ORDER_ANY, power_init, NULL);
+
+static int
+power_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+    struct thread *td)
+{
+	int err = 0;
+	uint32_t trans;
+
+	if ((fflag & FWRITE) == 0)
+		return (EPERM);
+
+	switch (cmd) {
+	case PIOTRANSITION:
+		trans = *(uint32_t *)data;
+		/* Check for overflow */
+		if ((enum power_transition)trans != trans)
+			return (EINVAL);
+		err = power_pm_suspend((enum power_transition)trans);
+		break;
+	default:
+		err = EINVAL;
+	}
+	return (err);
+}
+
 enum power_stype
 power_name_to_stype(const char *name)
 {
@@ -175,13 +226,13 @@ power_pm_get_type(void)
 	return (power_pm_type);
 }
 
-void
+int
 power_pm_suspend(enum power_transition trans)
 {
 	enum power_stype	stype;
 
 	if (power_pm_fn == NULL)
-		return;
+		return (ENXIO);
 
 	switch (trans) {
 	case POWER_TRANSITION_STANDBY:
@@ -196,11 +247,13 @@ power_pm_suspend(enum power_transition trans)
 	default:
 		printf("%s: unknown sleep state transition %d\n", __func__,
 		    trans);
-		return;
+		return (EINVAL);
 	}
 
 	power_pm_task.ta_context = (void *)(intptr_t)stype;
 	taskqueue_enqueue(taskqueue_thread, &power_pm_task);
+
+	return (0);
 }
 
 /*
diff --git a/sys/sys/power.h b/sys/sys/power.h
index e420717e1aa7..a1497d1a6524 100644
--- a/sys/sys/power.h
+++ b/sys/sys/power.h
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2001 Mitsuru IWASAKI
  * All rights reserved.
- * Copyright (c) 2025 The FreeBSD Foundation
+ * Copyright (c) 2025-2026 The FreeBSD Foundation
  *
  * Portions of this software were developed by Aymeric Wibo
  * <obiwac@freebsd.org> under sponsorship from the FreeBSD Foundation.
@@ -32,17 +32,9 @@
 
 #ifndef _SYS_POWER_H_
 #define _SYS_POWER_H_
-#ifdef _KERNEL
 
-#include <sys/_eventhandler.h>
 #include <sys/types.h>
-
-/* Power management system type */
-#define POWER_PM_TYPE_ACPI		0x01
-#define POWER_PM_TYPE_NONE		0xff
-
-/* Commands for Power management function */
-#define POWER_CMD_SUSPEND		0x00
+#include <sys/ioccom.h>
 
 /*
  * Sleep state transition requests.
@@ -55,8 +47,25 @@ enum power_transition {
 	POWER_TRANSITION_STANDBY,
 	POWER_TRANSITION_SUSPEND,
 	POWER_TRANSITION_HIBERNATE,
+	POWER_TRANSITION_COUNT,
 };
 
+/*
+ * Power ioctls.
+ */
+#define	PIOTRANSITION	_IOW('T', 1, uint32_t)
+
+#ifdef _KERNEL
+
+#include <sys/_eventhandler.h>
+
+/* Power management system type */
+#define POWER_PM_TYPE_ACPI		0x01
+#define POWER_PM_TYPE_NONE		0xff
+
+/* Commands for Power management function */
+#define POWER_CMD_SUSPEND		0x00
+
 /*
  * Sleep type.
  *
@@ -97,7 +106,7 @@ extern int	 power_pm_register(u_int _pm_type, power_pm_fn_t _pm_fn,
 			void *_pm_arg,
 			bool _pm_supported[static POWER_STYPE_COUNT]);
 extern u_int	 power_pm_get_type(void);
-extern void	 power_pm_suspend(enum power_transition _trans);
+extern int	 power_pm_suspend(enum power_transition _trans);
 
 /*
  * System power API.