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_UNKNOWN; 43 enum power_stype power_suspend_stype = POWER_STYPE_UNKNOWN; 44 enum power_stype power_hibernate_stype = POWER_STYPE_UNKNOWN; 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 bool power_pm_supported[POWER_STYPE_COUNT] = {0}; 50 static struct task power_pm_task; 51 52 enum power_stype 53 power_name_to_stype(const char *name) 54 { 55 enum power_stype stype; 56 57 for (stype = 0; stype < POWER_STYPE_COUNT; stype++) { 58 if (strcasecmp(name, power_stype_names[stype]) == 0) 59 return (stype); 60 } 61 return (POWER_STYPE_UNKNOWN); 62 } 63 64 const char * 65 power_stype_to_name(enum power_stype stype) 66 { 67 if (stype == POWER_STYPE_UNKNOWN) 68 return ("NONE"); 69 if (stype < POWER_STYPE_AWAKE || stype >= POWER_STYPE_COUNT) 70 return (NULL); 71 return (power_stype_names[stype]); 72 } 73 74 static int 75 sysctl_supported_stypes(SYSCTL_HANDLER_ARGS) 76 { 77 int error; 78 struct sbuf sb; 79 enum power_stype stype; 80 81 sbuf_new(&sb, NULL, 32, SBUF_AUTOEXTEND); 82 for (stype = 0; stype < POWER_STYPE_COUNT; stype++) { 83 if (power_pm_supported[stype]) 84 sbuf_printf(&sb, "%s ", power_stype_to_name(stype)); 85 } 86 sbuf_trim(&sb); 87 sbuf_finish(&sb); 88 error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); 89 sbuf_delete(&sb); 90 91 return (error); 92 } 93 94 static int 95 power_sysctl_stype(SYSCTL_HANDLER_ARGS) 96 { 97 char name[10]; 98 int err; 99 enum power_stype new_stype, old_stype; 100 101 old_stype = *(enum power_stype *)oidp->oid_arg1; 102 strlcpy(name, power_stype_to_name(old_stype), sizeof(name)); 103 err = sysctl_handle_string(oidp, name, sizeof(name), req); 104 if (err != 0 || req->newptr == NULL) 105 return (err); 106 107 new_stype = power_name_to_stype(name); 108 if (new_stype == POWER_STYPE_UNKNOWN) 109 return (EINVAL); 110 if (!power_pm_supported[new_stype]) 111 return (EOPNOTSUPP); 112 if (new_stype != old_stype) 113 *(enum power_stype *)oidp->oid_arg1 = new_stype; 114 return (0); 115 } 116 117 static SYSCTL_NODE(_kern, OID_AUTO, power, CTLFLAG_RW, 0, 118 "Generic power management related sysctls"); 119 120 SYSCTL_PROC(_kern_power, OID_AUTO, supported_stype, 121 CTLTYPE_STRING | CTLFLAG_RD, 0, 0, sysctl_supported_stypes, "A", 122 "List supported sleep types"); 123 SYSCTL_PROC(_kern_power, OID_AUTO, standby, CTLTYPE_STRING | CTLFLAG_RW, 124 &power_standby_stype, 0, power_sysctl_stype, "A", 125 "Sleep type to enter on standby"); 126 SYSCTL_PROC(_kern_power, OID_AUTO, suspend, CTLTYPE_STRING | CTLFLAG_RW, 127 &power_suspend_stype, 0, power_sysctl_stype, "A", 128 "Sleep type to enter on suspend"); 129 SYSCTL_PROC(_kern_power, OID_AUTO, hibernate, CTLTYPE_STRING | CTLFLAG_RW, 130 &power_hibernate_stype, 0, power_sysctl_stype, "A", 131 "Sleep type to enter on hibernate"); 132 133 static void 134 power_pm_deferred_fn(void *arg, int pending) 135 { 136 enum power_stype stype = (intptr_t)arg; 137 138 power_pm_fn(POWER_CMD_SUSPEND, power_pm_arg, stype); 139 } 140 141 int 142 power_pm_register(u_int pm_type, power_pm_fn_t pm_fn, void *pm_arg, 143 bool pm_supported[static POWER_STYPE_COUNT]) 144 { 145 int error; 146 147 if (power_pm_type == POWER_PM_TYPE_NONE || 148 power_pm_type == pm_type) { 149 power_pm_type = pm_type; 150 power_pm_fn = pm_fn; 151 power_pm_arg = pm_arg; 152 memcpy(power_pm_supported, pm_supported, 153 sizeof(power_pm_supported)); 154 if (power_pm_supported[POWER_STYPE_STANDBY]) 155 power_standby_stype = POWER_STYPE_STANDBY; 156 if (power_pm_supported[POWER_STYPE_SUSPEND_TO_MEM]) 157 power_suspend_stype = POWER_STYPE_SUSPEND_TO_MEM; 158 else if (power_pm_supported[POWER_STYPE_SUSPEND_TO_IDLE]) 159 power_suspend_stype = POWER_STYPE_SUSPEND_TO_IDLE; 160 if (power_pm_supported[POWER_STYPE_HIBERNATE]) 161 power_hibernate_stype = POWER_STYPE_HIBERNATE; 162 error = 0; 163 TASK_INIT(&power_pm_task, 0, power_pm_deferred_fn, NULL); 164 } else { 165 error = ENXIO; 166 } 167 168 return (error); 169 } 170 171 u_int 172 power_pm_get_type(void) 173 { 174 175 return (power_pm_type); 176 } 177 178 void 179 power_pm_suspend(int state) 180 { 181 enum power_stype stype; 182 183 if (power_pm_fn == NULL) 184 return; 185 186 switch (state) { 187 case POWER_SLEEP_STATE_STANDBY: 188 stype = power_standby_stype; 189 break; 190 case POWER_SLEEP_STATE_SUSPEND: 191 stype = power_suspend_stype; 192 break; 193 case POWER_SLEEP_STATE_HIBERNATE: 194 stype = power_hibernate_stype; 195 break; 196 default: 197 printf("%s: unknown sleep state %d\n", __func__, state); 198 return; 199 } 200 201 power_pm_task.ta_context = (void *)(intptr_t)stype; 202 taskqueue_enqueue(taskqueue_thread, &power_pm_task); 203 } 204 205 /* 206 * Power profile. 207 */ 208 209 static int power_profile_state = POWER_PROFILE_PERFORMANCE; 210 211 int 212 power_profile_get_state(void) 213 { 214 return (power_profile_state); 215 } 216 217 void 218 power_profile_set_state(int state) 219 { 220 int changed; 221 222 if (state != power_profile_state) { 223 power_profile_state = state; 224 changed = 1; 225 if (bootverbose) { 226 printf("system power profile changed to '%s'\n", 227 (state == POWER_PROFILE_PERFORMANCE) ? 228 "performance" : "economy"); 229 } 230 } else { 231 changed = 0; 232 } 233 234 if (changed) 235 EVENTHANDLER_INVOKE(power_profile_change, 0); 236 } 237