diff options
Diffstat (limited to 'sys/kern/subr_power.c')
-rw-r--r-- | sys/kern/subr_power.c | 92 |
1 files changed, 86 insertions, 6 deletions
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); } |