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
This commit is contained in:
Aymeric Wibo
2026-05-10 11:04:32 +02:00
parent 4329663a86
commit d649825e83
5 changed files with 81 additions and 19 deletions
+1 -1
View File
@@ -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:
+2 -2
View File
@@ -3987,10 +3987,10 @@ scgetc(sc_softc_t *sc, u_int flags, struct sc_cnstate *sp)
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:
+2 -2
View File
@@ -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);
}
+56 -3
View File
@@ -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);
}
/*
+20 -11
View File
@@ -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.