kern_pmf.c revision 1.10.4.4 1 1.10.4.4 yamt /* $NetBSD: kern_pmf.c,v 1.10.4.4 2008/02/27 08:36:55 yamt Exp $ */
2 1.10.4.2 yamt
3 1.10.4.2 yamt /*-
4 1.10.4.2 yamt * Copyright (c) 2007 Jared D. McNeill <jmcneill (at) invisible.ca>
5 1.10.4.2 yamt * All rights reserved.
6 1.10.4.2 yamt *
7 1.10.4.2 yamt * Redistribution and use in source and binary forms, with or without
8 1.10.4.2 yamt * modification, are permitted provided that the following conditions
9 1.10.4.2 yamt * are met:
10 1.10.4.2 yamt * 1. Redistributions of source code must retain the above copyright
11 1.10.4.2 yamt * notice, this list of conditions and the following disclaimer.
12 1.10.4.2 yamt * 2. Redistributions in binary form must reproduce the above copyright
13 1.10.4.2 yamt * notice, this list of conditions and the following disclaimer in the
14 1.10.4.2 yamt * documentation and/or other materials provided with the distribution.
15 1.10.4.2 yamt * 3. All advertising materials mentioning features or use of this software
16 1.10.4.2 yamt * must display the following acknowledgement:
17 1.10.4.2 yamt * This product includes software developed by Jared D. McNeill.
18 1.10.4.2 yamt * 4. Neither the name of The NetBSD Foundation nor the names of its
19 1.10.4.2 yamt * contributors may be used to endorse or promote products derived
20 1.10.4.2 yamt * from this software without specific prior written permission.
21 1.10.4.2 yamt *
22 1.10.4.2 yamt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 1.10.4.2 yamt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 1.10.4.2 yamt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 1.10.4.2 yamt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 1.10.4.2 yamt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 1.10.4.2 yamt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 1.10.4.2 yamt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 1.10.4.2 yamt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 1.10.4.2 yamt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 1.10.4.2 yamt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 1.10.4.2 yamt * POSSIBILITY OF SUCH DAMAGE.
33 1.10.4.2 yamt */
34 1.10.4.2 yamt
35 1.10.4.2 yamt #include <sys/cdefs.h>
36 1.10.4.4 yamt __KERNEL_RCSID(0, "$NetBSD: kern_pmf.c,v 1.10.4.4 2008/02/27 08:36:55 yamt Exp $");
37 1.10.4.2 yamt
38 1.10.4.2 yamt #include <sys/types.h>
39 1.10.4.2 yamt #include <sys/param.h>
40 1.10.4.2 yamt #include <sys/malloc.h>
41 1.10.4.2 yamt #include <sys/buf.h>
42 1.10.4.2 yamt #include <sys/callout.h>
43 1.10.4.2 yamt #include <sys/kernel.h>
44 1.10.4.2 yamt #include <sys/device.h>
45 1.10.4.2 yamt #include <sys/pmf.h>
46 1.10.4.2 yamt #include <sys/queue.h>
47 1.10.4.2 yamt #include <sys/syscallargs.h> /* for sys_sync */
48 1.10.4.2 yamt #include <sys/workqueue.h>
49 1.10.4.2 yamt #include <prop/proplib.h>
50 1.10.4.2 yamt
51 1.10.4.4 yamt /* XXX ugly special case, but for now the only client */
52 1.10.4.4 yamt #include "wsdisplay.h"
53 1.10.4.4 yamt #if NWSDISPLAY > 0
54 1.10.4.4 yamt #include <dev/wscons/wsdisplayvar.h>
55 1.10.4.4 yamt #endif
56 1.10.4.4 yamt
57 1.10.4.2 yamt #ifdef PMF_DEBUG
58 1.10.4.2 yamt int pmf_debug_event;
59 1.10.4.2 yamt int pmf_debug_idle;
60 1.10.4.2 yamt int pmf_debug_transition;
61 1.10.4.2 yamt
62 1.10.4.2 yamt #define PMF_EVENT_PRINTF(x) if (pmf_debug_event) printf x
63 1.10.4.2 yamt #define PMF_IDLE_PRINTF(x) if (pmf_debug_idle) printf x
64 1.10.4.2 yamt #define PMF_TRANSITION_PRINTF(x) if (pmf_debug_transition) printf x
65 1.10.4.2 yamt #define PMF_TRANSITION_PRINTF2(y,x) if (pmf_debug_transition>y) printf x
66 1.10.4.2 yamt #else
67 1.10.4.2 yamt #define PMF_EVENT_PRINTF(x) do { } while (0)
68 1.10.4.2 yamt #define PMF_IDLE_PRINTF(x) do { } while (0)
69 1.10.4.2 yamt #define PMF_TRANSITION_PRINTF(x) do { } while (0)
70 1.10.4.2 yamt #define PMF_TRANSITION_PRINTF2(y,x) do { } while (0)
71 1.10.4.2 yamt #endif
72 1.10.4.2 yamt
73 1.10.4.2 yamt /* #define PMF_DEBUG */
74 1.10.4.2 yamt
75 1.10.4.2 yamt MALLOC_DEFINE(M_PMF, "pmf", "device pmf messaging memory");
76 1.10.4.2 yamt
77 1.10.4.2 yamt static prop_dictionary_t pmf_platform = NULL;
78 1.10.4.2 yamt static struct workqueue *pmf_event_workqueue;
79 1.10.4.2 yamt
80 1.10.4.2 yamt typedef struct pmf_event_handler {
81 1.10.4.2 yamt TAILQ_ENTRY(pmf_event_handler) pmf_link;
82 1.10.4.2 yamt pmf_generic_event_t pmf_event;
83 1.10.4.2 yamt void (*pmf_handler)(device_t);
84 1.10.4.2 yamt device_t pmf_device;
85 1.10.4.2 yamt bool pmf_global;
86 1.10.4.2 yamt } pmf_event_handler_t;
87 1.10.4.2 yamt
88 1.10.4.2 yamt static TAILQ_HEAD(, pmf_event_handler) pmf_all_events =
89 1.10.4.2 yamt TAILQ_HEAD_INITIALIZER(pmf_all_events);
90 1.10.4.2 yamt
91 1.10.4.2 yamt typedef struct pmf_event_workitem {
92 1.10.4.2 yamt struct work pew_work;
93 1.10.4.2 yamt pmf_generic_event_t pew_event;
94 1.10.4.2 yamt device_t pew_device;
95 1.10.4.2 yamt } pmf_event_workitem_t;
96 1.10.4.2 yamt
97 1.10.4.2 yamt static void
98 1.10.4.2 yamt pmf_event_worker(struct work *wk, void *dummy)
99 1.10.4.2 yamt {
100 1.10.4.2 yamt pmf_event_workitem_t *pew;
101 1.10.4.2 yamt pmf_event_handler_t *event;
102 1.10.4.2 yamt
103 1.10.4.2 yamt pew = (void *)wk;
104 1.10.4.2 yamt KASSERT(wk == &pew->pew_work);
105 1.10.4.2 yamt KASSERT(pew != NULL);
106 1.10.4.2 yamt
107 1.10.4.2 yamt TAILQ_FOREACH(event, &pmf_all_events, pmf_link) {
108 1.10.4.2 yamt if (event->pmf_event != pew->pew_event)
109 1.10.4.2 yamt continue;
110 1.10.4.2 yamt if (event->pmf_device == pew->pew_device || event->pmf_global)
111 1.10.4.2 yamt (*event->pmf_handler)(event->pmf_device);
112 1.10.4.2 yamt }
113 1.10.4.2 yamt
114 1.10.4.2 yamt free(pew, M_TEMP);
115 1.10.4.2 yamt
116 1.10.4.2 yamt return;
117 1.10.4.2 yamt }
118 1.10.4.2 yamt
119 1.10.4.2 yamt static bool
120 1.10.4.2 yamt pmf_check_system_drivers(void)
121 1.10.4.2 yamt {
122 1.10.4.2 yamt device_t curdev;
123 1.10.4.2 yamt bool unsupported_devs;
124 1.10.4.2 yamt
125 1.10.4.2 yamt unsupported_devs = false;
126 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
127 1.10.4.2 yamt if (device_pmf_is_registered(curdev))
128 1.10.4.2 yamt continue;
129 1.10.4.2 yamt if (!unsupported_devs)
130 1.10.4.2 yamt printf("Devices without power management support:");
131 1.10.4.2 yamt printf(" %s", device_xname(curdev));
132 1.10.4.2 yamt unsupported_devs = true;
133 1.10.4.2 yamt }
134 1.10.4.2 yamt if (unsupported_devs) {
135 1.10.4.2 yamt printf("\n");
136 1.10.4.2 yamt return false;
137 1.10.4.2 yamt }
138 1.10.4.2 yamt return true;
139 1.10.4.2 yamt }
140 1.10.4.2 yamt
141 1.10.4.2 yamt bool
142 1.10.4.2 yamt pmf_system_bus_resume(void)
143 1.10.4.2 yamt {
144 1.10.4.2 yamt int depth, maxdepth;
145 1.10.4.2 yamt bool rv;
146 1.10.4.2 yamt device_t curdev;
147 1.10.4.2 yamt
148 1.10.4.2 yamt maxdepth = 0;
149 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
150 1.10.4.2 yamt if (curdev->dv_depth > maxdepth)
151 1.10.4.2 yamt maxdepth = curdev->dv_depth;
152 1.10.4.2 yamt }
153 1.10.4.2 yamt ++maxdepth;
154 1.10.4.2 yamt
155 1.10.4.2 yamt aprint_debug("Powering devices:");
156 1.10.4.2 yamt /* D0 handlers are run in order */
157 1.10.4.2 yamt depth = 0;
158 1.10.4.2 yamt rv = true;
159 1.10.4.2 yamt for (depth = 0; depth < maxdepth; ++depth) {
160 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
161 1.10.4.2 yamt if (!device_pmf_is_registered(curdev))
162 1.10.4.2 yamt continue;
163 1.10.4.2 yamt if (device_is_active(curdev) ||
164 1.10.4.2 yamt !device_is_enabled(curdev))
165 1.10.4.2 yamt continue;
166 1.10.4.2 yamt if (curdev->dv_depth != depth)
167 1.10.4.2 yamt continue;
168 1.10.4.2 yamt
169 1.10.4.2 yamt aprint_debug(" %s", device_xname(curdev));
170 1.10.4.2 yamt
171 1.10.4.2 yamt if (!device_pmf_bus_resume(curdev))
172 1.10.4.2 yamt aprint_debug("(failed)");
173 1.10.4.2 yamt }
174 1.10.4.2 yamt }
175 1.10.4.2 yamt aprint_debug("\n");
176 1.10.4.2 yamt
177 1.10.4.2 yamt return rv;
178 1.10.4.2 yamt }
179 1.10.4.2 yamt
180 1.10.4.2 yamt bool
181 1.10.4.2 yamt pmf_system_resume(void)
182 1.10.4.2 yamt {
183 1.10.4.2 yamt int depth, maxdepth;
184 1.10.4.2 yamt bool rv;
185 1.10.4.2 yamt device_t curdev, parent;
186 1.10.4.2 yamt
187 1.10.4.2 yamt if (!pmf_check_system_drivers())
188 1.10.4.2 yamt return false;
189 1.10.4.2 yamt
190 1.10.4.2 yamt maxdepth = 0;
191 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
192 1.10.4.2 yamt if (curdev->dv_depth > maxdepth)
193 1.10.4.2 yamt maxdepth = curdev->dv_depth;
194 1.10.4.2 yamt }
195 1.10.4.2 yamt ++maxdepth;
196 1.10.4.2 yamt
197 1.10.4.2 yamt aprint_debug("Resuming devices:");
198 1.10.4.2 yamt /* D0 handlers are run in order */
199 1.10.4.2 yamt depth = 0;
200 1.10.4.2 yamt rv = true;
201 1.10.4.2 yamt for (depth = 0; depth < maxdepth; ++depth) {
202 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
203 1.10.4.2 yamt if (device_is_active(curdev) ||
204 1.10.4.2 yamt !device_is_enabled(curdev))
205 1.10.4.2 yamt continue;
206 1.10.4.2 yamt if (curdev->dv_depth != depth)
207 1.10.4.2 yamt continue;
208 1.10.4.2 yamt parent = device_parent(curdev);
209 1.10.4.2 yamt if (parent != NULL &&
210 1.10.4.2 yamt !device_is_active(parent))
211 1.10.4.2 yamt continue;
212 1.10.4.2 yamt
213 1.10.4.2 yamt aprint_debug(" %s", device_xname(curdev));
214 1.10.4.2 yamt
215 1.10.4.2 yamt if (!pmf_device_resume(curdev)) {
216 1.10.4.2 yamt rv = false;
217 1.10.4.2 yamt aprint_debug("(failed)");
218 1.10.4.2 yamt }
219 1.10.4.2 yamt }
220 1.10.4.2 yamt }
221 1.10.4.2 yamt aprint_debug(".\n");
222 1.10.4.2 yamt
223 1.10.4.4 yamt KERNEL_UNLOCK_ONE(0);
224 1.10.4.4 yamt #if NWSDISPLAY > 0
225 1.10.4.4 yamt if (rv)
226 1.10.4.4 yamt wsdisplay_handlex(1);
227 1.10.4.4 yamt #endif
228 1.10.4.2 yamt return rv;
229 1.10.4.2 yamt }
230 1.10.4.2 yamt
231 1.10.4.2 yamt bool
232 1.10.4.2 yamt pmf_system_suspend(void)
233 1.10.4.2 yamt {
234 1.10.4.2 yamt int depth, maxdepth;
235 1.10.4.2 yamt device_t curdev;
236 1.10.4.2 yamt
237 1.10.4.2 yamt if (!pmf_check_system_drivers())
238 1.10.4.2 yamt return false;
239 1.10.4.4 yamt #if NWSDISPLAY > 0
240 1.10.4.4 yamt if (wsdisplay_handlex(0))
241 1.10.4.4 yamt return false;
242 1.10.4.4 yamt #endif
243 1.10.4.4 yamt KERNEL_LOCK(1, 0);
244 1.10.4.2 yamt
245 1.10.4.2 yamt /*
246 1.10.4.2 yamt * Flush buffers only if the shutdown didn't do so
247 1.10.4.2 yamt * already and if there was no panic.
248 1.10.4.2 yamt */
249 1.10.4.2 yamt if (doing_shutdown == 0 && panicstr == NULL) {
250 1.10.4.2 yamt printf("Flushing disk caches: ");
251 1.10.4.2 yamt sys_sync(NULL, NULL, NULL);
252 1.10.4.2 yamt if (buf_syncwait() != 0)
253 1.10.4.2 yamt printf("giving up\n");
254 1.10.4.2 yamt else
255 1.10.4.2 yamt printf("done\n");
256 1.10.4.2 yamt }
257 1.10.4.2 yamt
258 1.10.4.2 yamt aprint_debug("Suspending devices:");
259 1.10.4.2 yamt
260 1.10.4.2 yamt maxdepth = 0;
261 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
262 1.10.4.2 yamt if (curdev->dv_depth > maxdepth)
263 1.10.4.2 yamt maxdepth = curdev->dv_depth;
264 1.10.4.2 yamt }
265 1.10.4.2 yamt
266 1.10.4.2 yamt for (depth = maxdepth; depth >= 0; --depth) {
267 1.10.4.2 yamt TAILQ_FOREACH_REVERSE(curdev, &alldevs, devicelist, dv_list) {
268 1.10.4.2 yamt if (curdev->dv_depth != depth)
269 1.10.4.2 yamt continue;
270 1.10.4.2 yamt if (!device_is_active(curdev))
271 1.10.4.2 yamt continue;
272 1.10.4.2 yamt
273 1.10.4.2 yamt aprint_debug(" %s", device_xname(curdev));
274 1.10.4.2 yamt
275 1.10.4.2 yamt /* XXX joerg check return value and abort suspend */
276 1.10.4.2 yamt if (!pmf_device_suspend(curdev))
277 1.10.4.2 yamt aprint_debug("(failed)");
278 1.10.4.2 yamt }
279 1.10.4.2 yamt }
280 1.10.4.2 yamt
281 1.10.4.2 yamt aprint_debug(".\n");
282 1.10.4.2 yamt
283 1.10.4.2 yamt return true;
284 1.10.4.2 yamt }
285 1.10.4.2 yamt
286 1.10.4.2 yamt void
287 1.10.4.2 yamt pmf_system_shutdown(void)
288 1.10.4.2 yamt {
289 1.10.4.2 yamt int depth, maxdepth;
290 1.10.4.2 yamt device_t curdev;
291 1.10.4.2 yamt
292 1.10.4.2 yamt aprint_debug("Shutting down devices:");
293 1.10.4.2 yamt
294 1.10.4.2 yamt maxdepth = 0;
295 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
296 1.10.4.2 yamt if (curdev->dv_depth > maxdepth)
297 1.10.4.2 yamt maxdepth = curdev->dv_depth;
298 1.10.4.2 yamt }
299 1.10.4.2 yamt
300 1.10.4.2 yamt for (depth = maxdepth; depth >= 0; --depth) {
301 1.10.4.2 yamt TAILQ_FOREACH_REVERSE(curdev, &alldevs, devicelist, dv_list) {
302 1.10.4.2 yamt if (curdev->dv_depth != depth)
303 1.10.4.2 yamt continue;
304 1.10.4.2 yamt if (!device_is_active(curdev))
305 1.10.4.2 yamt continue;
306 1.10.4.2 yamt
307 1.10.4.2 yamt aprint_debug(" %s", device_xname(curdev));
308 1.10.4.2 yamt
309 1.10.4.2 yamt if (!device_pmf_is_registered(curdev))
310 1.10.4.2 yamt continue;
311 1.10.4.2 yamt if (!device_pmf_class_suspend(curdev)) {
312 1.10.4.2 yamt aprint_debug("(failed)");
313 1.10.4.2 yamt continue;
314 1.10.4.2 yamt }
315 1.10.4.2 yamt if (!device_pmf_driver_suspend(curdev)) {
316 1.10.4.2 yamt aprint_debug("(failed)");
317 1.10.4.2 yamt continue;
318 1.10.4.2 yamt }
319 1.10.4.2 yamt }
320 1.10.4.2 yamt }
321 1.10.4.2 yamt
322 1.10.4.2 yamt aprint_debug(".\n");
323 1.10.4.2 yamt }
324 1.10.4.2 yamt
325 1.10.4.2 yamt bool
326 1.10.4.2 yamt pmf_set_platform(const char *key, const char *value)
327 1.10.4.2 yamt {
328 1.10.4.2 yamt if (pmf_platform == NULL)
329 1.10.4.2 yamt pmf_platform = prop_dictionary_create();
330 1.10.4.2 yamt if (pmf_platform == NULL)
331 1.10.4.2 yamt return false;
332 1.10.4.2 yamt
333 1.10.4.2 yamt return prop_dictionary_set_cstring(pmf_platform, key, value);
334 1.10.4.2 yamt }
335 1.10.4.2 yamt
336 1.10.4.2 yamt const char *
337 1.10.4.2 yamt pmf_get_platform(const char *key)
338 1.10.4.2 yamt {
339 1.10.4.2 yamt const char *value;
340 1.10.4.2 yamt
341 1.10.4.2 yamt if (pmf_platform == NULL)
342 1.10.4.2 yamt return NULL;
343 1.10.4.2 yamt
344 1.10.4.2 yamt if (!prop_dictionary_get_cstring_nocopy(pmf_platform, key, &value))
345 1.10.4.2 yamt return NULL;
346 1.10.4.2 yamt
347 1.10.4.2 yamt return value;
348 1.10.4.2 yamt }
349 1.10.4.2 yamt
350 1.10.4.2 yamt bool
351 1.10.4.2 yamt pmf_device_register(device_t dev,
352 1.10.4.2 yamt bool (*suspend)(device_t), bool (*resume)(device_t))
353 1.10.4.2 yamt {
354 1.10.4.2 yamt device_pmf_driver_register(dev, suspend, resume);
355 1.10.4.2 yamt
356 1.10.4.2 yamt if (!device_pmf_driver_child_register(dev)) {
357 1.10.4.2 yamt device_pmf_driver_deregister(dev);
358 1.10.4.2 yamt return false;
359 1.10.4.2 yamt }
360 1.10.4.2 yamt
361 1.10.4.2 yamt return true;
362 1.10.4.2 yamt }
363 1.10.4.2 yamt
364 1.10.4.2 yamt void
365 1.10.4.2 yamt pmf_device_deregister(device_t dev)
366 1.10.4.2 yamt {
367 1.10.4.2 yamt device_pmf_class_deregister(dev);
368 1.10.4.2 yamt device_pmf_bus_deregister(dev);
369 1.10.4.2 yamt device_pmf_driver_deregister(dev);
370 1.10.4.2 yamt }
371 1.10.4.2 yamt
372 1.10.4.2 yamt bool
373 1.10.4.2 yamt pmf_device_suspend(device_t dev)
374 1.10.4.2 yamt {
375 1.10.4.2 yamt PMF_TRANSITION_PRINTF(("%s: suspend enter\n", device_xname(dev)));
376 1.10.4.2 yamt if (!device_pmf_is_registered(dev))
377 1.10.4.2 yamt return false;
378 1.10.4.2 yamt PMF_TRANSITION_PRINTF2(1, ("%s: class suspend\n", device_xname(dev)));
379 1.10.4.2 yamt if (!device_pmf_class_suspend(dev))
380 1.10.4.2 yamt return false;
381 1.10.4.2 yamt PMF_TRANSITION_PRINTF2(1, ("%s: driver suspend\n", device_xname(dev)));
382 1.10.4.2 yamt if (!device_pmf_driver_suspend(dev))
383 1.10.4.2 yamt return false;
384 1.10.4.2 yamt PMF_TRANSITION_PRINTF2(1, ("%s: bus suspend\n", device_xname(dev)));
385 1.10.4.2 yamt if (!device_pmf_bus_suspend(dev))
386 1.10.4.2 yamt return false;
387 1.10.4.2 yamt PMF_TRANSITION_PRINTF(("%s: suspend exit\n", device_xname(dev)));
388 1.10.4.2 yamt return true;
389 1.10.4.2 yamt }
390 1.10.4.2 yamt
391 1.10.4.2 yamt bool
392 1.10.4.2 yamt pmf_device_resume(device_t dev)
393 1.10.4.2 yamt {
394 1.10.4.2 yamt PMF_TRANSITION_PRINTF(("%s: resume enter\n", device_xname(dev)));
395 1.10.4.2 yamt if (!device_pmf_is_registered(dev))
396 1.10.4.2 yamt return false;
397 1.10.4.2 yamt PMF_TRANSITION_PRINTF2(1, ("%s: bus resume\n", device_xname(dev)));
398 1.10.4.2 yamt if (!device_pmf_bus_resume(dev))
399 1.10.4.2 yamt return false;
400 1.10.4.2 yamt PMF_TRANSITION_PRINTF2(1, ("%s: driver resume\n", device_xname(dev)));
401 1.10.4.2 yamt if (!device_pmf_driver_resume(dev))
402 1.10.4.2 yamt return false;
403 1.10.4.2 yamt PMF_TRANSITION_PRINTF2(1, ("%s: class resume\n", device_xname(dev)));
404 1.10.4.2 yamt if (!device_pmf_class_resume(dev))
405 1.10.4.2 yamt return false;
406 1.10.4.2 yamt PMF_TRANSITION_PRINTF(("%s: resume exit\n", device_xname(dev)));
407 1.10.4.2 yamt return true;
408 1.10.4.2 yamt }
409 1.10.4.2 yamt
410 1.10.4.2 yamt bool
411 1.10.4.2 yamt pmf_device_recursive_suspend(device_t dv)
412 1.10.4.2 yamt {
413 1.10.4.2 yamt device_t curdev;
414 1.10.4.2 yamt
415 1.10.4.2 yamt if (!device_is_active(dv))
416 1.10.4.2 yamt return true;
417 1.10.4.2 yamt
418 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
419 1.10.4.2 yamt if (device_parent(curdev) != dv)
420 1.10.4.2 yamt continue;
421 1.10.4.2 yamt if (!pmf_device_recursive_suspend(curdev))
422 1.10.4.2 yamt return false;
423 1.10.4.2 yamt }
424 1.10.4.2 yamt
425 1.10.4.2 yamt return pmf_device_suspend(dv);
426 1.10.4.2 yamt }
427 1.10.4.2 yamt
428 1.10.4.2 yamt bool
429 1.10.4.2 yamt pmf_device_recursive_resume(device_t dv)
430 1.10.4.2 yamt {
431 1.10.4.2 yamt device_t parent;
432 1.10.4.2 yamt
433 1.10.4.2 yamt if (device_is_active(dv))
434 1.10.4.2 yamt return true;
435 1.10.4.2 yamt
436 1.10.4.2 yamt parent = device_parent(dv);
437 1.10.4.2 yamt if (parent != NULL) {
438 1.10.4.2 yamt if (!pmf_device_recursive_resume(parent))
439 1.10.4.2 yamt return false;
440 1.10.4.2 yamt }
441 1.10.4.2 yamt
442 1.10.4.2 yamt return pmf_device_resume(dv);
443 1.10.4.2 yamt }
444 1.10.4.2 yamt
445 1.10.4.2 yamt bool
446 1.10.4.2 yamt pmf_device_resume_subtree(device_t dv)
447 1.10.4.2 yamt {
448 1.10.4.2 yamt device_t curdev;
449 1.10.4.2 yamt
450 1.10.4.2 yamt if (!pmf_device_recursive_resume(dv))
451 1.10.4.2 yamt return false;
452 1.10.4.2 yamt
453 1.10.4.2 yamt TAILQ_FOREACH(curdev, &alldevs, dv_list) {
454 1.10.4.2 yamt if (device_parent(curdev) != dv)
455 1.10.4.2 yamt continue;
456 1.10.4.2 yamt if (!pmf_device_resume_subtree(curdev))
457 1.10.4.2 yamt return false;
458 1.10.4.2 yamt }
459 1.10.4.2 yamt return true;
460 1.10.4.2 yamt }
461 1.10.4.2 yamt
462 1.10.4.2 yamt #include <net/if.h>
463 1.10.4.2 yamt
464 1.10.4.2 yamt static bool
465 1.10.4.2 yamt pmf_class_network_suspend(device_t dev)
466 1.10.4.2 yamt {
467 1.10.4.2 yamt struct ifnet *ifp = device_pmf_class_private(dev);
468 1.10.4.2 yamt int s;
469 1.10.4.2 yamt
470 1.10.4.2 yamt s = splnet();
471 1.10.4.2 yamt (*ifp->if_stop)(ifp, 1);
472 1.10.4.2 yamt splx(s);
473 1.10.4.2 yamt
474 1.10.4.2 yamt return true;
475 1.10.4.2 yamt }
476 1.10.4.2 yamt
477 1.10.4.2 yamt static bool
478 1.10.4.2 yamt pmf_class_network_resume(device_t dev)
479 1.10.4.2 yamt {
480 1.10.4.2 yamt struct ifnet *ifp = device_pmf_class_private(dev);
481 1.10.4.2 yamt int s;
482 1.10.4.2 yamt
483 1.10.4.2 yamt s = splnet();
484 1.10.4.2 yamt if (ifp->if_flags & IFF_UP) {
485 1.10.4.2 yamt ifp->if_flags &= ~IFF_RUNNING;
486 1.10.4.2 yamt (*ifp->if_init)(ifp);
487 1.10.4.2 yamt (*ifp->if_start)(ifp);
488 1.10.4.2 yamt }
489 1.10.4.2 yamt splx(s);
490 1.10.4.2 yamt
491 1.10.4.2 yamt return true;
492 1.10.4.2 yamt }
493 1.10.4.2 yamt
494 1.10.4.2 yamt void
495 1.10.4.2 yamt pmf_class_network_register(device_t dev, struct ifnet *ifp)
496 1.10.4.2 yamt {
497 1.10.4.2 yamt device_pmf_class_register(dev, ifp, pmf_class_network_suspend,
498 1.10.4.2 yamt pmf_class_network_resume, NULL);
499 1.10.4.2 yamt }
500 1.10.4.2 yamt
501 1.10.4.2 yamt bool
502 1.10.4.2 yamt pmf_event_inject(device_t dv, pmf_generic_event_t ev)
503 1.10.4.2 yamt {
504 1.10.4.2 yamt pmf_event_workitem_t *pew;
505 1.10.4.2 yamt
506 1.10.4.2 yamt pew = malloc(sizeof(pmf_event_workitem_t), M_TEMP, M_NOWAIT);
507 1.10.4.2 yamt if (pew == NULL) {
508 1.10.4.2 yamt PMF_EVENT_PRINTF(("%s: PMF event %d dropped (no memory)\n",
509 1.10.4.2 yamt dv ? device_xname(dv) : "<anonymous>", ev));
510 1.10.4.2 yamt return false;
511 1.10.4.2 yamt }
512 1.10.4.2 yamt
513 1.10.4.2 yamt pew->pew_event = ev;
514 1.10.4.2 yamt pew->pew_device = dv;
515 1.10.4.2 yamt
516 1.10.4.2 yamt workqueue_enqueue(pmf_event_workqueue, (void *)pew, NULL);
517 1.10.4.2 yamt PMF_EVENT_PRINTF(("%s: PMF event %d injected\n",
518 1.10.4.2 yamt dv ? device_xname(dv) : "<anonymous>", ev));
519 1.10.4.2 yamt
520 1.10.4.2 yamt return true;
521 1.10.4.2 yamt }
522 1.10.4.2 yamt
523 1.10.4.2 yamt bool
524 1.10.4.2 yamt pmf_event_register(device_t dv, pmf_generic_event_t ev,
525 1.10.4.2 yamt void (*handler)(device_t), bool global)
526 1.10.4.2 yamt {
527 1.10.4.2 yamt pmf_event_handler_t *event;
528 1.10.4.2 yamt
529 1.10.4.2 yamt event = malloc(sizeof(*event), M_DEVBUF, M_WAITOK);
530 1.10.4.2 yamt event->pmf_event = ev;
531 1.10.4.2 yamt event->pmf_handler = handler;
532 1.10.4.2 yamt event->pmf_device = dv;
533 1.10.4.2 yamt event->pmf_global = global;
534 1.10.4.2 yamt TAILQ_INSERT_TAIL(&pmf_all_events, event, pmf_link);
535 1.10.4.2 yamt
536 1.10.4.2 yamt return true;
537 1.10.4.2 yamt }
538 1.10.4.2 yamt
539 1.10.4.2 yamt void
540 1.10.4.2 yamt pmf_event_deregister(device_t dv, pmf_generic_event_t ev,
541 1.10.4.2 yamt void (*handler)(device_t), bool global)
542 1.10.4.2 yamt {
543 1.10.4.2 yamt pmf_event_handler_t *event;
544 1.10.4.2 yamt
545 1.10.4.2 yamt TAILQ_FOREACH(event, &pmf_all_events, pmf_link) {
546 1.10.4.2 yamt if (event->pmf_event != ev)
547 1.10.4.2 yamt continue;
548 1.10.4.2 yamt if (event->pmf_device != dv)
549 1.10.4.2 yamt continue;
550 1.10.4.2 yamt if (event->pmf_global != global)
551 1.10.4.2 yamt continue;
552 1.10.4.2 yamt if (event->pmf_handler != handler)
553 1.10.4.2 yamt continue;
554 1.10.4.2 yamt TAILQ_REMOVE(&pmf_all_events, event, pmf_link);
555 1.10.4.3 yamt free(event, M_DEVBUF);
556 1.10.4.2 yamt return;
557 1.10.4.2 yamt }
558 1.10.4.2 yamt }
559 1.10.4.2 yamt
560 1.10.4.2 yamt struct display_class_softc {
561 1.10.4.2 yamt TAILQ_ENTRY(display_class_softc) dc_link;
562 1.10.4.2 yamt device_t dc_dev;
563 1.10.4.2 yamt };
564 1.10.4.2 yamt
565 1.10.4.2 yamt static TAILQ_HEAD(, display_class_softc) all_displays;
566 1.10.4.2 yamt static callout_t global_idle_counter;
567 1.10.4.2 yamt static int idle_timeout = 30;
568 1.10.4.2 yamt
569 1.10.4.2 yamt static void
570 1.10.4.2 yamt input_idle(void *dummy)
571 1.10.4.2 yamt {
572 1.10.4.2 yamt PMF_IDLE_PRINTF(("Input idle handler called\n"));
573 1.10.4.2 yamt pmf_event_inject(NULL, PMFE_DISPLAY_OFF);
574 1.10.4.2 yamt }
575 1.10.4.2 yamt
576 1.10.4.2 yamt static void
577 1.10.4.2 yamt input_activity_handler(device_t dv, devactive_t type)
578 1.10.4.2 yamt {
579 1.10.4.2 yamt if (!TAILQ_EMPTY(&all_displays))
580 1.10.4.2 yamt callout_schedule(&global_idle_counter, idle_timeout * hz);
581 1.10.4.2 yamt }
582 1.10.4.2 yamt
583 1.10.4.2 yamt static void
584 1.10.4.2 yamt pmf_class_input_deregister(device_t dv)
585 1.10.4.2 yamt {
586 1.10.4.2 yamt device_active_deregister(dv, input_activity_handler);
587 1.10.4.2 yamt }
588 1.10.4.2 yamt
589 1.10.4.2 yamt bool
590 1.10.4.2 yamt pmf_class_input_register(device_t dv)
591 1.10.4.2 yamt {
592 1.10.4.2 yamt if (!device_active_register(dv, input_activity_handler))
593 1.10.4.2 yamt return false;
594 1.10.4.2 yamt
595 1.10.4.2 yamt device_pmf_class_register(dv, NULL, NULL, NULL,
596 1.10.4.2 yamt pmf_class_input_deregister);
597 1.10.4.2 yamt
598 1.10.4.2 yamt return true;
599 1.10.4.2 yamt }
600 1.10.4.2 yamt
601 1.10.4.2 yamt static void
602 1.10.4.2 yamt pmf_class_display_deregister(device_t dv)
603 1.10.4.2 yamt {
604 1.10.4.2 yamt struct display_class_softc *sc = device_pmf_class_private(dv);
605 1.10.4.2 yamt int s;
606 1.10.4.2 yamt
607 1.10.4.2 yamt s = splsoftclock();
608 1.10.4.2 yamt TAILQ_REMOVE(&all_displays, sc, dc_link);
609 1.10.4.2 yamt if (TAILQ_EMPTY(&all_displays))
610 1.10.4.2 yamt callout_stop(&global_idle_counter);
611 1.10.4.2 yamt splx(s);
612 1.10.4.2 yamt
613 1.10.4.2 yamt free(sc, M_DEVBUF);
614 1.10.4.2 yamt }
615 1.10.4.2 yamt
616 1.10.4.2 yamt bool
617 1.10.4.2 yamt pmf_class_display_register(device_t dv)
618 1.10.4.2 yamt {
619 1.10.4.2 yamt struct display_class_softc *sc;
620 1.10.4.2 yamt int s;
621 1.10.4.2 yamt
622 1.10.4.2 yamt sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
623 1.10.4.2 yamt
624 1.10.4.2 yamt s = splsoftclock();
625 1.10.4.2 yamt if (TAILQ_EMPTY(&all_displays))
626 1.10.4.2 yamt callout_schedule(&global_idle_counter, idle_timeout * hz);
627 1.10.4.2 yamt
628 1.10.4.2 yamt TAILQ_INSERT_HEAD(&all_displays, sc, dc_link);
629 1.10.4.2 yamt splx(s);
630 1.10.4.2 yamt
631 1.10.4.2 yamt device_pmf_class_register(dv, sc, NULL, NULL,
632 1.10.4.2 yamt pmf_class_display_deregister);
633 1.10.4.2 yamt
634 1.10.4.2 yamt return true;
635 1.10.4.2 yamt }
636 1.10.4.2 yamt
637 1.10.4.2 yamt void
638 1.10.4.2 yamt pmf_init(void)
639 1.10.4.2 yamt {
640 1.10.4.2 yamt int err;
641 1.10.4.2 yamt
642 1.10.4.2 yamt KASSERT(pmf_event_workqueue == NULL);
643 1.10.4.2 yamt err = workqueue_create(&pmf_event_workqueue, "pmfevent",
644 1.10.4.2 yamt pmf_event_worker, NULL, PRI_NONE, IPL_VM, 0);
645 1.10.4.2 yamt if (err)
646 1.10.4.2 yamt panic("couldn't create pmfevent workqueue");
647 1.10.4.2 yamt
648 1.10.4.2 yamt callout_init(&global_idle_counter, 0);
649 1.10.4.2 yamt callout_setfunc(&global_idle_counter, input_idle, NULL);
650 1.10.4.2 yamt }
651