1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2001 Mitsuru IWASAKI 5 * All rights reserved. 6 * Copyright (c) 2025 The FreeBSD Foundation 7 * 8 * Portions of this software were developed by Aymeric Wibo 9 * <obiwac@freebsd.org> under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/eventhandler.h> 35 #include <sys/power.h> 36 #include <sys/proc.h> 37 #include <sys/sbuf.h> 38 #include <sys/sysctl.h> 39 #include <sys/systm.h> 40 #include <sys/taskqueue.h> 41 42 enum power_stype power_standby_stype = POWER_STYPE_STANDBY; 43 enum power_stype power_suspend_stype = POWER_STYPE_SUSPEND_TO_IDLE; 44 enum power_stype power_hibernate_stype = POWER_STYPE_HIBERNATE; 45 46 static u_int power_pm_type = POWER_PM_TYPE_NONE; 47 static power_pm_fn_t power_pm_fn = NULL; 48 static void *power_pm_arg = NULL; 49 static struct task power_pm_task; 50 51 enum power_stype 52 power_name_to_stype(const char *name) 53 { 54 enum power_stype stype; 55 56 for (stype = 0; stype < POWER_STYPE_COUNT; stype++) { 57 if (strcasecmp(name, power_stype_names[stype]) == 0) 58 return (stype); 59 } 60 return (POWER_STYPE_UNKNOWN); 61 } 62 63 const char * 64 power_stype_to_name(enum power_stype stype) 65 { 66 if (stype == POWER_STYPE_UNKNOWN) 67 return ("NONE"); 68 if (stype < POWER_STYPE_AWAKE || stype >= POWER_STYPE_COUNT) 69 return (NULL); 70 return (power_stype_names[stype]); 71 } 72 73 static int 74 power_sysctl_stype(SYSCTL_HANDLER_ARGS) 75 { 76 char name[10]; 77 int err; 78 enum power_stype new_stype, old_stype; 79 80 old_stype = *(enum power_stype *)oidp->oid_arg1; 81 strlcpy(name, power_stype_to_name(old_stype), sizeof(name)); 82 err = sysctl_handle_string(oidp, name, sizeof(name), req); 83 if (err != 0 || req->newptr == NULL) 84 return (err); 85 86 new_stype = power_name_to_stype(name); 87 if (new_stype == POWER_STYPE_UNKNOWN) 88 return (EINVAL); 89 /* TODO Check to see if the new stype is supported. */ 90 if (new_stype != old_stype) 91 *(enum power_stype *)oidp->oid_arg1 = new_stype; 92 return (0); 93 } 94 95 static SYSCTL_NODE(_kern, OID_AUTO, power, CTLFLAG_RW, 0, 96 "Generic power management related sysctls"); 97 98 SYSCTL_PROC(_kern_power, OID_AUTO, standby, CTLTYPE_STRING | CTLFLAG_RW, 99 &power_standby_stype, 0, power_sysctl_stype, "A", 100 "Sleep type to enter on standby"); 101 SYSCTL_PROC(_kern_power, OID_AUTO, suspend, CTLTYPE_STRING | CTLFLAG_RW, 102 &power_suspend_stype, 0, power_sysctl_stype, "A", 103 "Sleep type to enter on suspend"); 104 SYSCTL_PROC(_kern_power, OID_AUTO, hibernate, CTLTYPE_STRING | CTLFLAG_RW, 105 &power_hibernate_stype, 0, power_sysctl_stype, "A", 106 "Sleep type to enter on hibernate"); 107 108 static void 109 power_pm_deferred_fn(void *arg, int pending) 110 { 111 enum power_stype stype = (intptr_t)arg; 112 113 power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, stype); 114 } 115 116 int 117 power_pm_register(u_int pm_type, power_pm_fn_t pm_fn, void *pm_arg) 118 { 119 int error; 120 121 if (power_pm_type == POWER_PM_TYPE_NONE || 122 power_pm_type == pm_type) { 123 power_pm_type = pm_type; 124 power_pm_fn = pm_fn; 125 power_pm_arg = pm_arg; 126 error = 0; 127 TASK_INIT(&power_pm_task, 0, power_pm_deferred_fn, NULL); 128 } else { 129 error = ENXIO; 130 } 131 132 return (error); 133 } 134 135 u_int 136 power_pm_get_type(void) 137 { 138 139 return (power_pm_type); 140 } 141 142 void 143 power_pm_suspend(int state) 144 { 145 enum power_stype stype; 146 147 if (power_pm_fn == NULL) 148 return; 149 150 switch (state) { 151 case POWER_SLEEP_STATE_STANDBY: 152 stype = power_standby_stype; 153 break; 154 case POWER_SLEEP_STATE_SUSPEND: 155 stype = power_suspend_stype; 156 break; 157 case POWER_SLEEP_STATE_HIBERNATE: 158 stype = power_hibernate_stype; 159 break; 160 default: 161 printf("%s: unknown sleep state %d\n", __func__, state); 162 return; 163 } 164 165 power_pm_task.ta_context = (void *)(intptr_t)stype; 166 taskqueue_enqueue(taskqueue_thread, &power_pm_task); 167 } 168 169 /* 170 * Power profile. 171 */ 172 173 static int power_profile_state = POWER_PROFILE_PERFORMANCE; 174 175 int 176 power_profile_get_state(void) 177 { 178 return (power_profile_state); 179 } 180 181 void 182 power_profile_set_state(int state) 183 { 184 int changed; 185 186 if (state != power_profile_state) { 187 power_profile_state = state; 188 changed = 1; 189 if (bootverbose) { 190 printf("system power profile changed to '%s'\n", 191 (state == POWER_PROFILE_PERFORMANCE) ? 192 "performance" : "economy"); 193 } 194 } else { 195 changed = 0; 196 } 197 198 if (changed) 199 EVENTHANDLER_INVOKE(power_profile_change, 0); 200 } 201