xref: /freebsd/sys/kern/subr_power.c (revision 87e2b532ec9e9504ac743931ffae528679a1f4c0)
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