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
power_name_to_stype(const char * name)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 *
power_stype_to_name(enum power_stype stype)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
power_sysctl_stype(SYSCTL_HANDLER_ARGS)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
power_pm_deferred_fn(void * arg,int pending)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
power_pm_register(u_int pm_type,power_pm_fn_t pm_fn,void * pm_arg)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
power_pm_get_type(void)136 power_pm_get_type(void)
137 {
138
139 return (power_pm_type);
140 }
141
142 void
power_pm_suspend(int state)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
power_profile_get_state(void)176 power_profile_get_state(void)
177 {
178 return (power_profile_state);
179 }
180
181 void
power_profile_set_state(int state)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