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