1 | /* $NetBSD: subr_autoconf.c,v 1.247 2016/07/19 07:44:03 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1996, 2000 Christopher G. Demetriou |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * 3. All advertising materials mentioning features or use of this software |
16 | * must display the following acknowledgement: |
17 | * This product includes software developed for the |
18 | * NetBSD Project. See http://www.NetBSD.org/ for |
19 | * information about NetBSD. |
20 | * 4. The name of the author may not be used to endorse or promote products |
21 | * derived from this software without specific prior written permission. |
22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
24 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
25 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
26 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
27 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
28 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
29 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
30 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
31 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
32 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
33 | * |
34 | * --(license Id: LICENSE.proto,v 1.1 2000/06/13 21:40:26 cgd Exp )-- |
35 | */ |
36 | |
37 | /* |
38 | * Copyright (c) 1992, 1993 |
39 | * The Regents of the University of California. All rights reserved. |
40 | * |
41 | * This software was developed by the Computer Systems Engineering group |
42 | * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and |
43 | * contributed to Berkeley. |
44 | * |
45 | * All advertising materials mentioning features or use of this software |
46 | * must display the following acknowledgement: |
47 | * This product includes software developed by the University of |
48 | * California, Lawrence Berkeley Laboratories. |
49 | * |
50 | * Redistribution and use in source and binary forms, with or without |
51 | * modification, are permitted provided that the following conditions |
52 | * are met: |
53 | * 1. Redistributions of source code must retain the above copyright |
54 | * notice, this list of conditions and the following disclaimer. |
55 | * 2. Redistributions in binary form must reproduce the above copyright |
56 | * notice, this list of conditions and the following disclaimer in the |
57 | * documentation and/or other materials provided with the distribution. |
58 | * 3. Neither the name of the University nor the names of its contributors |
59 | * may be used to endorse or promote products derived from this software |
60 | * without specific prior written permission. |
61 | * |
62 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
63 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
64 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
65 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
66 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
67 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
68 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
69 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
70 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
71 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
72 | * SUCH DAMAGE. |
73 | * |
74 | * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL) |
75 | * |
76 | * @(#)subr_autoconf.c 8.3 (Berkeley) 5/17/94 |
77 | */ |
78 | |
79 | #include <sys/cdefs.h> |
80 | __KERNEL_RCSID(0, "$NetBSD: subr_autoconf.c,v 1.247 2016/07/19 07:44:03 msaitoh Exp $" ); |
81 | |
82 | #ifdef _KERNEL_OPT |
83 | #include "opt_ddb.h" |
84 | #include "drvctl.h" |
85 | #endif |
86 | |
87 | #include <sys/param.h> |
88 | #include <sys/device.h> |
89 | #include <sys/disklabel.h> |
90 | #include <sys/conf.h> |
91 | #include <sys/kauth.h> |
92 | #include <sys/kmem.h> |
93 | #include <sys/systm.h> |
94 | #include <sys/kernel.h> |
95 | #include <sys/errno.h> |
96 | #include <sys/proc.h> |
97 | #include <sys/reboot.h> |
98 | #include <sys/kthread.h> |
99 | #include <sys/buf.h> |
100 | #include <sys/dirent.h> |
101 | #include <sys/mount.h> |
102 | #include <sys/namei.h> |
103 | #include <sys/unistd.h> |
104 | #include <sys/fcntl.h> |
105 | #include <sys/lockf.h> |
106 | #include <sys/callout.h> |
107 | #include <sys/devmon.h> |
108 | #include <sys/cpu.h> |
109 | #include <sys/sysctl.h> |
110 | |
111 | #include <sys/disk.h> |
112 | |
113 | #include <sys/rndsource.h> |
114 | |
115 | #include <machine/limits.h> |
116 | |
117 | /* |
118 | * Autoconfiguration subroutines. |
119 | */ |
120 | |
121 | /* |
122 | * Device autoconfiguration timings are mixed into the entropy pool. |
123 | */ |
124 | extern krndsource_t rnd_autoconf_source; |
125 | |
126 | /* |
127 | * ioconf.c exports exactly two names: cfdata and cfroots. All system |
128 | * devices and drivers are found via these tables. |
129 | */ |
130 | extern struct cfdata cfdata[]; |
131 | extern const short cfroots[]; |
132 | |
133 | /* |
134 | * List of all cfdriver structures. We use this to detect duplicates |
135 | * when other cfdrivers are loaded. |
136 | */ |
137 | struct cfdriverlist allcfdrivers = LIST_HEAD_INITIALIZER(&allcfdrivers); |
138 | extern struct cfdriver * const cfdriver_list_initial[]; |
139 | |
140 | /* |
141 | * Initial list of cfattach's. |
142 | */ |
143 | extern const struct cfattachinit cfattachinit[]; |
144 | |
145 | /* |
146 | * List of cfdata tables. We always have one such list -- the one |
147 | * built statically when the kernel was configured. |
148 | */ |
149 | struct cftablelist allcftables = TAILQ_HEAD_INITIALIZER(allcftables); |
150 | static struct cftable initcftable; |
151 | |
152 | #define ROOT ((device_t)NULL) |
153 | |
154 | struct matchinfo { |
155 | cfsubmatch_t fn; |
156 | device_t parent; |
157 | const int *locs; |
158 | void *aux; |
159 | struct cfdata *match; |
160 | int pri; |
161 | }; |
162 | |
163 | struct alldevs_foray { |
164 | int af_s; |
165 | struct devicelist af_garbage; |
166 | }; |
167 | |
168 | static char *number(char *, int); |
169 | static void mapply(struct matchinfo *, cfdata_t); |
170 | static device_t config_devalloc(const device_t, const cfdata_t, const int *); |
171 | static void config_devdelete(device_t); |
172 | static void config_devunlink(device_t, struct devicelist *); |
173 | static void config_makeroom(int, struct cfdriver *); |
174 | static void config_devlink(device_t); |
175 | static void config_alldevs_enter(struct alldevs_foray *); |
176 | static void config_alldevs_exit(struct alldevs_foray *); |
177 | static void config_add_attrib_dict(device_t); |
178 | |
179 | static void config_collect_garbage(struct devicelist *); |
180 | static void config_dump_garbage(struct devicelist *); |
181 | |
182 | static void pmflock_debug(device_t, const char *, int); |
183 | |
184 | static device_t deviter_next1(deviter_t *); |
185 | static void deviter_reinit(deviter_t *); |
186 | |
187 | struct deferred_config { |
188 | TAILQ_ENTRY(deferred_config) dc_queue; |
189 | device_t dc_dev; |
190 | void (*dc_func)(device_t); |
191 | }; |
192 | |
193 | TAILQ_HEAD(deferred_config_head, deferred_config); |
194 | |
195 | struct deferred_config_head deferred_config_queue = |
196 | TAILQ_HEAD_INITIALIZER(deferred_config_queue); |
197 | struct deferred_config_head interrupt_config_queue = |
198 | TAILQ_HEAD_INITIALIZER(interrupt_config_queue); |
199 | int interrupt_config_threads = 8; |
200 | struct deferred_config_head mountroot_config_queue = |
201 | TAILQ_HEAD_INITIALIZER(mountroot_config_queue); |
202 | int mountroot_config_threads = 2; |
203 | static lwp_t **mountroot_config_lwpids; |
204 | static size_t mountroot_config_lwpids_size; |
205 | static bool root_is_mounted = false; |
206 | |
207 | static void config_process_deferred(struct deferred_config_head *, device_t); |
208 | |
209 | /* Hooks to finalize configuration once all real devices have been found. */ |
210 | struct finalize_hook { |
211 | TAILQ_ENTRY(finalize_hook) f_list; |
212 | int (*f_func)(device_t); |
213 | device_t f_dev; |
214 | }; |
215 | static TAILQ_HEAD(, finalize_hook) config_finalize_list = |
216 | TAILQ_HEAD_INITIALIZER(config_finalize_list); |
217 | static int config_finalize_done; |
218 | |
219 | /* list of all devices */ |
220 | static struct devicelist alldevs = TAILQ_HEAD_INITIALIZER(alldevs); |
221 | static kmutex_t alldevs_mtx; |
222 | static volatile bool alldevs_garbage = false; |
223 | static volatile devgen_t alldevs_gen = 1; |
224 | static volatile int alldevs_nread = 0; |
225 | static volatile int alldevs_nwrite = 0; |
226 | |
227 | static int config_pending; /* semaphore for mountroot */ |
228 | static kmutex_t config_misc_lock; |
229 | static kcondvar_t config_misc_cv; |
230 | |
231 | static bool detachall = false; |
232 | |
233 | #define STREQ(s1, s2) \ |
234 | (*(s1) == *(s2) && strcmp((s1), (s2)) == 0) |
235 | |
236 | static bool config_initialized = false; /* config_init() has been called. */ |
237 | |
238 | static int config_do_twiddle; |
239 | static callout_t config_twiddle_ch; |
240 | |
241 | static void sysctl_detach_setup(struct sysctllog **); |
242 | |
243 | int no_devmon_insert(const char *, prop_dictionary_t); |
244 | int (*devmon_insert_vec)(const char *, prop_dictionary_t) = no_devmon_insert; |
245 | |
246 | typedef int (*cfdriver_fn)(struct cfdriver *); |
247 | static int |
248 | frob_cfdrivervec(struct cfdriver * const *cfdriverv, |
249 | cfdriver_fn drv_do, cfdriver_fn drv_undo, |
250 | const char *style, bool dopanic) |
251 | { |
252 | void (*pr)(const char *, ...) __printflike(1, 2) = |
253 | dopanic ? panic : printf; |
254 | int i, error = 0, e2 __diagused; |
255 | |
256 | for (i = 0; cfdriverv[i] != NULL; i++) { |
257 | if ((error = drv_do(cfdriverv[i])) != 0) { |
258 | pr("configure: `%s' driver %s failed: %d" , |
259 | cfdriverv[i]->cd_name, style, error); |
260 | goto bad; |
261 | } |
262 | } |
263 | |
264 | KASSERT(error == 0); |
265 | return 0; |
266 | |
267 | bad: |
268 | printf("\n" ); |
269 | for (i--; i >= 0; i--) { |
270 | e2 = drv_undo(cfdriverv[i]); |
271 | KASSERT(e2 == 0); |
272 | } |
273 | |
274 | return error; |
275 | } |
276 | |
277 | typedef int (*cfattach_fn)(const char *, struct cfattach *); |
278 | static int |
279 | frob_cfattachvec(const struct cfattachinit *cfattachv, |
280 | cfattach_fn att_do, cfattach_fn att_undo, |
281 | const char *style, bool dopanic) |
282 | { |
283 | const struct cfattachinit *cfai = NULL; |
284 | void (*pr)(const char *, ...) __printflike(1, 2) = |
285 | dopanic ? panic : printf; |
286 | int j = 0, error = 0, e2 __diagused; |
287 | |
288 | for (cfai = &cfattachv[0]; cfai->cfai_name != NULL; cfai++) { |
289 | for (j = 0; cfai->cfai_list[j] != NULL; j++) { |
290 | if ((error = att_do(cfai->cfai_name, |
291 | cfai->cfai_list[j])) != 0) { |
292 | pr("configure: attachment `%s' " |
293 | "of `%s' driver %s failed: %d" , |
294 | cfai->cfai_list[j]->ca_name, |
295 | cfai->cfai_name, style, error); |
296 | goto bad; |
297 | } |
298 | } |
299 | } |
300 | |
301 | KASSERT(error == 0); |
302 | return 0; |
303 | |
304 | bad: |
305 | /* |
306 | * Rollback in reverse order. dunno if super-important, but |
307 | * do that anyway. Although the code looks a little like |
308 | * someone did a little integration (in the math sense). |
309 | */ |
310 | printf("\n" ); |
311 | if (cfai) { |
312 | bool last; |
313 | |
314 | for (last = false; last == false; ) { |
315 | if (cfai == &cfattachv[0]) |
316 | last = true; |
317 | for (j--; j >= 0; j--) { |
318 | e2 = att_undo(cfai->cfai_name, |
319 | cfai->cfai_list[j]); |
320 | KASSERT(e2 == 0); |
321 | } |
322 | if (!last) { |
323 | cfai--; |
324 | for (j = 0; cfai->cfai_list[j] != NULL; j++) |
325 | ; |
326 | } |
327 | } |
328 | } |
329 | |
330 | return error; |
331 | } |
332 | |
333 | /* |
334 | * Initialize the autoconfiguration data structures. Normally this |
335 | * is done by configure(), but some platforms need to do this very |
336 | * early (to e.g. initialize the console). |
337 | */ |
338 | void |
339 | config_init(void) |
340 | { |
341 | |
342 | KASSERT(config_initialized == false); |
343 | |
344 | mutex_init(&alldevs_mtx, MUTEX_DEFAULT, IPL_VM); |
345 | |
346 | mutex_init(&config_misc_lock, MUTEX_DEFAULT, IPL_NONE); |
347 | cv_init(&config_misc_cv, "cfgmisc" ); |
348 | |
349 | callout_init(&config_twiddle_ch, CALLOUT_MPSAFE); |
350 | |
351 | frob_cfdrivervec(cfdriver_list_initial, |
352 | config_cfdriver_attach, NULL, "bootstrap" , true); |
353 | frob_cfattachvec(cfattachinit, |
354 | config_cfattach_attach, NULL, "bootstrap" , true); |
355 | |
356 | initcftable.ct_cfdata = cfdata; |
357 | TAILQ_INSERT_TAIL(&allcftables, &initcftable, ct_list); |
358 | |
359 | config_initialized = true; |
360 | } |
361 | |
362 | /* |
363 | * Init or fini drivers and attachments. Either all or none |
364 | * are processed (via rollback). It would be nice if this were |
365 | * atomic to outside consumers, but with the current state of |
366 | * locking ... |
367 | */ |
368 | int |
369 | config_init_component(struct cfdriver * const *cfdriverv, |
370 | const struct cfattachinit *cfattachv, struct cfdata *cfdatav) |
371 | { |
372 | int error; |
373 | |
374 | if ((error = frob_cfdrivervec(cfdriverv, |
375 | config_cfdriver_attach, config_cfdriver_detach, "init" , false))!= 0) |
376 | return error; |
377 | if ((error = frob_cfattachvec(cfattachv, |
378 | config_cfattach_attach, config_cfattach_detach, |
379 | "init" , false)) != 0) { |
380 | frob_cfdrivervec(cfdriverv, |
381 | config_cfdriver_detach, NULL, "init rollback" , true); |
382 | return error; |
383 | } |
384 | if ((error = config_cfdata_attach(cfdatav, 1)) != 0) { |
385 | frob_cfattachvec(cfattachv, |
386 | config_cfattach_detach, NULL, "init rollback" , true); |
387 | frob_cfdrivervec(cfdriverv, |
388 | config_cfdriver_detach, NULL, "init rollback" , true); |
389 | return error; |
390 | } |
391 | |
392 | return 0; |
393 | } |
394 | |
395 | int |
396 | config_fini_component(struct cfdriver * const *cfdriverv, |
397 | const struct cfattachinit *cfattachv, struct cfdata *cfdatav) |
398 | { |
399 | int error; |
400 | |
401 | if ((error = config_cfdata_detach(cfdatav)) != 0) |
402 | return error; |
403 | if ((error = frob_cfattachvec(cfattachv, |
404 | config_cfattach_detach, config_cfattach_attach, |
405 | "fini" , false)) != 0) { |
406 | if (config_cfdata_attach(cfdatav, 0) != 0) |
407 | panic("config_cfdata fini rollback failed" ); |
408 | return error; |
409 | } |
410 | if ((error = frob_cfdrivervec(cfdriverv, |
411 | config_cfdriver_detach, config_cfdriver_attach, |
412 | "fini" , false)) != 0) { |
413 | frob_cfattachvec(cfattachv, |
414 | config_cfattach_attach, NULL, "fini rollback" , true); |
415 | if (config_cfdata_attach(cfdatav, 0) != 0) |
416 | panic("config_cfdata fini rollback failed" ); |
417 | return error; |
418 | } |
419 | |
420 | return 0; |
421 | } |
422 | |
423 | void |
424 | config_init_mi(void) |
425 | { |
426 | |
427 | if (!config_initialized) |
428 | config_init(); |
429 | |
430 | sysctl_detach_setup(NULL); |
431 | } |
432 | |
433 | void |
434 | config_deferred(device_t dev) |
435 | { |
436 | config_process_deferred(&deferred_config_queue, dev); |
437 | config_process_deferred(&interrupt_config_queue, dev); |
438 | config_process_deferred(&mountroot_config_queue, dev); |
439 | } |
440 | |
441 | static void |
442 | config_interrupts_thread(void *cookie) |
443 | { |
444 | struct deferred_config *dc; |
445 | |
446 | while ((dc = TAILQ_FIRST(&interrupt_config_queue)) != NULL) { |
447 | TAILQ_REMOVE(&interrupt_config_queue, dc, dc_queue); |
448 | (*dc->dc_func)(dc->dc_dev); |
449 | config_pending_decr(dc->dc_dev); |
450 | kmem_free(dc, sizeof(*dc)); |
451 | } |
452 | kthread_exit(0); |
453 | } |
454 | |
455 | void |
456 | config_create_interruptthreads(void) |
457 | { |
458 | int i; |
459 | |
460 | for (i = 0; i < interrupt_config_threads; i++) { |
461 | (void)kthread_create(PRI_NONE, 0, NULL, |
462 | config_interrupts_thread, NULL, NULL, "configintr" ); |
463 | } |
464 | } |
465 | |
466 | static void |
467 | config_mountroot_thread(void *cookie) |
468 | { |
469 | struct deferred_config *dc; |
470 | |
471 | while ((dc = TAILQ_FIRST(&mountroot_config_queue)) != NULL) { |
472 | TAILQ_REMOVE(&mountroot_config_queue, dc, dc_queue); |
473 | (*dc->dc_func)(dc->dc_dev); |
474 | kmem_free(dc, sizeof(*dc)); |
475 | } |
476 | kthread_exit(0); |
477 | } |
478 | |
479 | void |
480 | config_create_mountrootthreads(void) |
481 | { |
482 | int i; |
483 | |
484 | if (!root_is_mounted) |
485 | root_is_mounted = true; |
486 | |
487 | mountroot_config_lwpids_size = sizeof(mountroot_config_lwpids) * |
488 | mountroot_config_threads; |
489 | mountroot_config_lwpids = kmem_alloc(mountroot_config_lwpids_size, |
490 | KM_NOSLEEP); |
491 | KASSERT(mountroot_config_lwpids); |
492 | for (i = 0; i < mountroot_config_threads; i++) { |
493 | mountroot_config_lwpids[i] = 0; |
494 | (void)kthread_create(PRI_NONE, KTHREAD_MUSTJOIN, NULL, |
495 | config_mountroot_thread, NULL, |
496 | &mountroot_config_lwpids[i], |
497 | "configroot" ); |
498 | } |
499 | } |
500 | |
501 | void |
502 | config_finalize_mountroot(void) |
503 | { |
504 | int i, error; |
505 | |
506 | for (i = 0; i < mountroot_config_threads; i++) { |
507 | if (mountroot_config_lwpids[i] == 0) |
508 | continue; |
509 | |
510 | error = kthread_join(mountroot_config_lwpids[i]); |
511 | if (error) |
512 | printf("%s: thread %x joined with error %d\n" , |
513 | __func__, i, error); |
514 | } |
515 | kmem_free(mountroot_config_lwpids, mountroot_config_lwpids_size); |
516 | } |
517 | |
518 | /* |
519 | * Announce device attach/detach to userland listeners. |
520 | */ |
521 | |
522 | int |
523 | no_devmon_insert(const char *name, prop_dictionary_t p) |
524 | { |
525 | |
526 | return ENODEV; |
527 | } |
528 | |
529 | static void |
530 | devmon_report_device(device_t dev, bool isattach) |
531 | { |
532 | prop_dictionary_t ev; |
533 | const char *parent; |
534 | const char *what; |
535 | device_t pdev = device_parent(dev); |
536 | |
537 | /* If currently no drvctl device, just return */ |
538 | if (devmon_insert_vec == no_devmon_insert) |
539 | return; |
540 | |
541 | ev = prop_dictionary_create(); |
542 | if (ev == NULL) |
543 | return; |
544 | |
545 | what = (isattach ? "device-attach" : "device-detach" ); |
546 | parent = (pdev == NULL ? "root" : device_xname(pdev)); |
547 | if (!prop_dictionary_set_cstring(ev, "device" , device_xname(dev)) || |
548 | !prop_dictionary_set_cstring(ev, "parent" , parent)) { |
549 | prop_object_release(ev); |
550 | return; |
551 | } |
552 | |
553 | if ((*devmon_insert_vec)(what, ev) != 0) |
554 | prop_object_release(ev); |
555 | } |
556 | |
557 | /* |
558 | * Add a cfdriver to the system. |
559 | */ |
560 | int |
561 | config_cfdriver_attach(struct cfdriver *cd) |
562 | { |
563 | struct cfdriver *lcd; |
564 | |
565 | /* Make sure this driver isn't already in the system. */ |
566 | LIST_FOREACH(lcd, &allcfdrivers, cd_list) { |
567 | if (STREQ(lcd->cd_name, cd->cd_name)) |
568 | return EEXIST; |
569 | } |
570 | |
571 | LIST_INIT(&cd->cd_attach); |
572 | LIST_INSERT_HEAD(&allcfdrivers, cd, cd_list); |
573 | |
574 | return 0; |
575 | } |
576 | |
577 | /* |
578 | * Remove a cfdriver from the system. |
579 | */ |
580 | int |
581 | config_cfdriver_detach(struct cfdriver *cd) |
582 | { |
583 | struct alldevs_foray af; |
584 | int i, rc = 0; |
585 | |
586 | config_alldevs_enter(&af); |
587 | /* Make sure there are no active instances. */ |
588 | for (i = 0; i < cd->cd_ndevs; i++) { |
589 | if (cd->cd_devs[i] != NULL) { |
590 | rc = EBUSY; |
591 | break; |
592 | } |
593 | } |
594 | config_alldevs_exit(&af); |
595 | |
596 | if (rc != 0) |
597 | return rc; |
598 | |
599 | /* ...and no attachments loaded. */ |
600 | if (LIST_EMPTY(&cd->cd_attach) == 0) |
601 | return EBUSY; |
602 | |
603 | LIST_REMOVE(cd, cd_list); |
604 | |
605 | KASSERT(cd->cd_devs == NULL); |
606 | |
607 | return 0; |
608 | } |
609 | |
610 | /* |
611 | * Look up a cfdriver by name. |
612 | */ |
613 | struct cfdriver * |
614 | config_cfdriver_lookup(const char *name) |
615 | { |
616 | struct cfdriver *cd; |
617 | |
618 | LIST_FOREACH(cd, &allcfdrivers, cd_list) { |
619 | if (STREQ(cd->cd_name, name)) |
620 | return cd; |
621 | } |
622 | |
623 | return NULL; |
624 | } |
625 | |
626 | /* |
627 | * Add a cfattach to the specified driver. |
628 | */ |
629 | int |
630 | config_cfattach_attach(const char *driver, struct cfattach *ca) |
631 | { |
632 | struct cfattach *lca; |
633 | struct cfdriver *cd; |
634 | |
635 | cd = config_cfdriver_lookup(driver); |
636 | if (cd == NULL) |
637 | return ESRCH; |
638 | |
639 | /* Make sure this attachment isn't already on this driver. */ |
640 | LIST_FOREACH(lca, &cd->cd_attach, ca_list) { |
641 | if (STREQ(lca->ca_name, ca->ca_name)) |
642 | return EEXIST; |
643 | } |
644 | |
645 | LIST_INSERT_HEAD(&cd->cd_attach, ca, ca_list); |
646 | |
647 | return 0; |
648 | } |
649 | |
650 | /* |
651 | * Remove a cfattach from the specified driver. |
652 | */ |
653 | int |
654 | config_cfattach_detach(const char *driver, struct cfattach *ca) |
655 | { |
656 | struct alldevs_foray af; |
657 | struct cfdriver *cd; |
658 | device_t dev; |
659 | int i, rc = 0; |
660 | |
661 | cd = config_cfdriver_lookup(driver); |
662 | if (cd == NULL) |
663 | return ESRCH; |
664 | |
665 | config_alldevs_enter(&af); |
666 | /* Make sure there are no active instances. */ |
667 | for (i = 0; i < cd->cd_ndevs; i++) { |
668 | if ((dev = cd->cd_devs[i]) == NULL) |
669 | continue; |
670 | if (dev->dv_cfattach == ca) { |
671 | rc = EBUSY; |
672 | break; |
673 | } |
674 | } |
675 | config_alldevs_exit(&af); |
676 | |
677 | if (rc != 0) |
678 | return rc; |
679 | |
680 | LIST_REMOVE(ca, ca_list); |
681 | |
682 | return 0; |
683 | } |
684 | |
685 | /* |
686 | * Look up a cfattach by name. |
687 | */ |
688 | static struct cfattach * |
689 | config_cfattach_lookup_cd(struct cfdriver *cd, const char *atname) |
690 | { |
691 | struct cfattach *ca; |
692 | |
693 | LIST_FOREACH(ca, &cd->cd_attach, ca_list) { |
694 | if (STREQ(ca->ca_name, atname)) |
695 | return ca; |
696 | } |
697 | |
698 | return NULL; |
699 | } |
700 | |
701 | /* |
702 | * Look up a cfattach by driver/attachment name. |
703 | */ |
704 | struct cfattach * |
705 | config_cfattach_lookup(const char *name, const char *atname) |
706 | { |
707 | struct cfdriver *cd; |
708 | |
709 | cd = config_cfdriver_lookup(name); |
710 | if (cd == NULL) |
711 | return NULL; |
712 | |
713 | return config_cfattach_lookup_cd(cd, atname); |
714 | } |
715 | |
716 | /* |
717 | * Apply the matching function and choose the best. This is used |
718 | * a few times and we want to keep the code small. |
719 | */ |
720 | static void |
721 | mapply(struct matchinfo *m, cfdata_t cf) |
722 | { |
723 | int pri; |
724 | |
725 | if (m->fn != NULL) { |
726 | pri = (*m->fn)(m->parent, cf, m->locs, m->aux); |
727 | } else { |
728 | pri = config_match(m->parent, cf, m->aux); |
729 | } |
730 | if (pri > m->pri) { |
731 | m->match = cf; |
732 | m->pri = pri; |
733 | } |
734 | } |
735 | |
736 | int |
737 | config_stdsubmatch(device_t parent, cfdata_t cf, const int *locs, void *aux) |
738 | { |
739 | const struct cfiattrdata *ci; |
740 | const struct cflocdesc *cl; |
741 | int nlocs, i; |
742 | |
743 | ci = cfiattr_lookup(cfdata_ifattr(cf), parent->dv_cfdriver); |
744 | KASSERT(ci); |
745 | nlocs = ci->ci_loclen; |
746 | KASSERT(!nlocs || locs); |
747 | for (i = 0; i < nlocs; i++) { |
748 | cl = &ci->ci_locdesc[i]; |
749 | if (cl->cld_defaultstr != NULL && |
750 | cf->cf_loc[i] == cl->cld_default) |
751 | continue; |
752 | if (cf->cf_loc[i] == locs[i]) |
753 | continue; |
754 | return 0; |
755 | } |
756 | |
757 | return config_match(parent, cf, aux); |
758 | } |
759 | |
760 | /* |
761 | * Helper function: check whether the driver supports the interface attribute |
762 | * and return its descriptor structure. |
763 | */ |
764 | static const struct cfiattrdata * |
765 | cfdriver_get_iattr(const struct cfdriver *cd, const char *ia) |
766 | { |
767 | const struct cfiattrdata * const *cpp; |
768 | |
769 | if (cd->cd_attrs == NULL) |
770 | return 0; |
771 | |
772 | for (cpp = cd->cd_attrs; *cpp; cpp++) { |
773 | if (STREQ((*cpp)->ci_name, ia)) { |
774 | /* Match. */ |
775 | return *cpp; |
776 | } |
777 | } |
778 | return 0; |
779 | } |
780 | |
781 | /* |
782 | * Lookup an interface attribute description by name. |
783 | * If the driver is given, consider only its supported attributes. |
784 | */ |
785 | const struct cfiattrdata * |
786 | cfiattr_lookup(const char *name, const struct cfdriver *cd) |
787 | { |
788 | const struct cfdriver *d; |
789 | const struct cfiattrdata *ia; |
790 | |
791 | if (cd) |
792 | return cfdriver_get_iattr(cd, name); |
793 | |
794 | LIST_FOREACH(d, &allcfdrivers, cd_list) { |
795 | ia = cfdriver_get_iattr(d, name); |
796 | if (ia) |
797 | return ia; |
798 | } |
799 | return 0; |
800 | } |
801 | |
802 | /* |
803 | * Determine if `parent' is a potential parent for a device spec based |
804 | * on `cfp'. |
805 | */ |
806 | static int |
807 | cfparent_match(const device_t parent, const struct cfparent *cfp) |
808 | { |
809 | struct cfdriver *pcd; |
810 | |
811 | /* We don't match root nodes here. */ |
812 | if (cfp == NULL) |
813 | return 0; |
814 | |
815 | pcd = parent->dv_cfdriver; |
816 | KASSERT(pcd != NULL); |
817 | |
818 | /* |
819 | * First, ensure this parent has the correct interface |
820 | * attribute. |
821 | */ |
822 | if (!cfdriver_get_iattr(pcd, cfp->cfp_iattr)) |
823 | return 0; |
824 | |
825 | /* |
826 | * If no specific parent device instance was specified (i.e. |
827 | * we're attaching to the attribute only), we're done! |
828 | */ |
829 | if (cfp->cfp_parent == NULL) |
830 | return 1; |
831 | |
832 | /* |
833 | * Check the parent device's name. |
834 | */ |
835 | if (STREQ(pcd->cd_name, cfp->cfp_parent) == 0) |
836 | return 0; /* not the same parent */ |
837 | |
838 | /* |
839 | * Make sure the unit number matches. |
840 | */ |
841 | if (cfp->cfp_unit == DVUNIT_ANY || /* wildcard */ |
842 | cfp->cfp_unit == parent->dv_unit) |
843 | return 1; |
844 | |
845 | /* Unit numbers don't match. */ |
846 | return 0; |
847 | } |
848 | |
849 | /* |
850 | * Helper for config_cfdata_attach(): check all devices whether it could be |
851 | * parent any attachment in the config data table passed, and rescan. |
852 | */ |
853 | static void |
854 | rescan_with_cfdata(const struct cfdata *cf) |
855 | { |
856 | device_t d; |
857 | const struct cfdata *cf1; |
858 | deviter_t di; |
859 | |
860 | |
861 | /* |
862 | * "alldevs" is likely longer than a modules's cfdata, so make it |
863 | * the outer loop. |
864 | */ |
865 | for (d = deviter_first(&di, 0); d != NULL; d = deviter_next(&di)) { |
866 | |
867 | if (!(d->dv_cfattach->ca_rescan)) |
868 | continue; |
869 | |
870 | for (cf1 = cf; cf1->cf_name; cf1++) { |
871 | |
872 | if (!cfparent_match(d, cf1->cf_pspec)) |
873 | continue; |
874 | |
875 | (*d->dv_cfattach->ca_rescan)(d, |
876 | cfdata_ifattr(cf1), cf1->cf_loc); |
877 | |
878 | config_deferred(d); |
879 | } |
880 | } |
881 | deviter_release(&di); |
882 | } |
883 | |
884 | /* |
885 | * Attach a supplemental config data table and rescan potential |
886 | * parent devices if required. |
887 | */ |
888 | int |
889 | config_cfdata_attach(cfdata_t cf, int scannow) |
890 | { |
891 | struct cftable *ct; |
892 | |
893 | ct = kmem_alloc(sizeof(*ct), KM_SLEEP); |
894 | ct->ct_cfdata = cf; |
895 | TAILQ_INSERT_TAIL(&allcftables, ct, ct_list); |
896 | |
897 | if (scannow) |
898 | rescan_with_cfdata(cf); |
899 | |
900 | return 0; |
901 | } |
902 | |
903 | /* |
904 | * Helper for config_cfdata_detach: check whether a device is |
905 | * found through any attachment in the config data table. |
906 | */ |
907 | static int |
908 | dev_in_cfdata(device_t d, cfdata_t cf) |
909 | { |
910 | const struct cfdata *cf1; |
911 | |
912 | for (cf1 = cf; cf1->cf_name; cf1++) |
913 | if (d->dv_cfdata == cf1) |
914 | return 1; |
915 | |
916 | return 0; |
917 | } |
918 | |
919 | /* |
920 | * Detach a supplemental config data table. Detach all devices found |
921 | * through that table (and thus keeping references to it) before. |
922 | */ |
923 | int |
924 | config_cfdata_detach(cfdata_t cf) |
925 | { |
926 | device_t d; |
927 | int error = 0; |
928 | struct cftable *ct; |
929 | deviter_t di; |
930 | |
931 | for (d = deviter_first(&di, DEVITER_F_RW); d != NULL; |
932 | d = deviter_next(&di)) { |
933 | if (!dev_in_cfdata(d, cf)) |
934 | continue; |
935 | if ((error = config_detach(d, 0)) != 0) |
936 | break; |
937 | } |
938 | deviter_release(&di); |
939 | if (error) { |
940 | aprint_error_dev(d, "unable to detach instance\n" ); |
941 | return error; |
942 | } |
943 | |
944 | TAILQ_FOREACH(ct, &allcftables, ct_list) { |
945 | if (ct->ct_cfdata == cf) { |
946 | TAILQ_REMOVE(&allcftables, ct, ct_list); |
947 | kmem_free(ct, sizeof(*ct)); |
948 | return 0; |
949 | } |
950 | } |
951 | |
952 | /* not found -- shouldn't happen */ |
953 | return EINVAL; |
954 | } |
955 | |
956 | /* |
957 | * Invoke the "match" routine for a cfdata entry on behalf of |
958 | * an external caller, usually a "submatch" routine. |
959 | */ |
960 | int |
961 | config_match(device_t parent, cfdata_t cf, void *aux) |
962 | { |
963 | struct cfattach *ca; |
964 | |
965 | ca = config_cfattach_lookup(cf->cf_name, cf->cf_atname); |
966 | if (ca == NULL) { |
967 | /* No attachment for this entry, oh well. */ |
968 | return 0; |
969 | } |
970 | |
971 | return (*ca->ca_match)(parent, cf, aux); |
972 | } |
973 | |
974 | /* |
975 | * Iterate over all potential children of some device, calling the given |
976 | * function (default being the child's match function) for each one. |
977 | * Nonzero returns are matches; the highest value returned is considered |
978 | * the best match. Return the `found child' if we got a match, or NULL |
979 | * otherwise. The `aux' pointer is simply passed on through. |
980 | * |
981 | * Note that this function is designed so that it can be used to apply |
982 | * an arbitrary function to all potential children (its return value |
983 | * can be ignored). |
984 | */ |
985 | cfdata_t |
986 | config_search_loc(cfsubmatch_t fn, device_t parent, |
987 | const char *ifattr, const int *locs, void *aux) |
988 | { |
989 | struct cftable *ct; |
990 | cfdata_t cf; |
991 | struct matchinfo m; |
992 | |
993 | KASSERT(config_initialized); |
994 | KASSERT(!ifattr || cfdriver_get_iattr(parent->dv_cfdriver, ifattr)); |
995 | |
996 | m.fn = fn; |
997 | m.parent = parent; |
998 | m.locs = locs; |
999 | m.aux = aux; |
1000 | m.match = NULL; |
1001 | m.pri = 0; |
1002 | |
1003 | TAILQ_FOREACH(ct, &allcftables, ct_list) { |
1004 | for (cf = ct->ct_cfdata; cf->cf_name; cf++) { |
1005 | |
1006 | /* We don't match root nodes here. */ |
1007 | if (!cf->cf_pspec) |
1008 | continue; |
1009 | |
1010 | /* |
1011 | * Skip cf if no longer eligible, otherwise scan |
1012 | * through parents for one matching `parent', and |
1013 | * try match function. |
1014 | */ |
1015 | if (cf->cf_fstate == FSTATE_FOUND) |
1016 | continue; |
1017 | if (cf->cf_fstate == FSTATE_DNOTFOUND || |
1018 | cf->cf_fstate == FSTATE_DSTAR) |
1019 | continue; |
1020 | |
1021 | /* |
1022 | * If an interface attribute was specified, |
1023 | * consider only children which attach to |
1024 | * that attribute. |
1025 | */ |
1026 | if (ifattr && !STREQ(ifattr, cfdata_ifattr(cf))) |
1027 | continue; |
1028 | |
1029 | if (cfparent_match(parent, cf->cf_pspec)) |
1030 | mapply(&m, cf); |
1031 | } |
1032 | } |
1033 | return m.match; |
1034 | } |
1035 | |
1036 | cfdata_t |
1037 | config_search_ia(cfsubmatch_t fn, device_t parent, const char *ifattr, |
1038 | void *aux) |
1039 | { |
1040 | |
1041 | return config_search_loc(fn, parent, ifattr, NULL, aux); |
1042 | } |
1043 | |
1044 | /* |
1045 | * Find the given root device. |
1046 | * This is much like config_search, but there is no parent. |
1047 | * Don't bother with multiple cfdata tables; the root node |
1048 | * must always be in the initial table. |
1049 | */ |
1050 | cfdata_t |
1051 | config_rootsearch(cfsubmatch_t fn, const char *rootname, void *aux) |
1052 | { |
1053 | cfdata_t cf; |
1054 | const short *p; |
1055 | struct matchinfo m; |
1056 | |
1057 | m.fn = fn; |
1058 | m.parent = ROOT; |
1059 | m.aux = aux; |
1060 | m.match = NULL; |
1061 | m.pri = 0; |
1062 | m.locs = 0; |
1063 | /* |
1064 | * Look at root entries for matching name. We do not bother |
1065 | * with found-state here since only one root should ever be |
1066 | * searched (and it must be done first). |
1067 | */ |
1068 | for (p = cfroots; *p >= 0; p++) { |
1069 | cf = &cfdata[*p]; |
1070 | if (strcmp(cf->cf_name, rootname) == 0) |
1071 | mapply(&m, cf); |
1072 | } |
1073 | return m.match; |
1074 | } |
1075 | |
1076 | static const char * const msgs[3] = { "" , " not configured\n" , " unsupported\n" }; |
1077 | |
1078 | /* |
1079 | * The given `aux' argument describes a device that has been found |
1080 | * on the given parent, but not necessarily configured. Locate the |
1081 | * configuration data for that device (using the submatch function |
1082 | * provided, or using candidates' cd_match configuration driver |
1083 | * functions) and attach it, and return its device_t. If the device was |
1084 | * not configured, call the given `print' function and return NULL. |
1085 | */ |
1086 | device_t |
1087 | config_found_sm_loc(device_t parent, |
1088 | const char *ifattr, const int *locs, void *aux, |
1089 | cfprint_t print, cfsubmatch_t submatch) |
1090 | { |
1091 | cfdata_t cf; |
1092 | |
1093 | if ((cf = config_search_loc(submatch, parent, ifattr, locs, aux))) |
1094 | return(config_attach_loc(parent, cf, locs, aux, print)); |
1095 | if (print) { |
1096 | if (config_do_twiddle && cold) |
1097 | twiddle(); |
1098 | aprint_normal("%s" , msgs[(*print)(aux, device_xname(parent))]); |
1099 | } |
1100 | |
1101 | /* |
1102 | * This has the effect of mixing in a single timestamp to the |
1103 | * entropy pool. Experiments indicate the estimator will almost |
1104 | * always attribute one bit of entropy to this sample; analysis |
1105 | * of device attach/detach timestamps on FreeBSD indicates 4 |
1106 | * bits of entropy/sample so this seems appropriately conservative. |
1107 | */ |
1108 | rnd_add_uint32(&rnd_autoconf_source, 0); |
1109 | return NULL; |
1110 | } |
1111 | |
1112 | device_t |
1113 | config_found_ia(device_t parent, const char *ifattr, void *aux, |
1114 | cfprint_t print) |
1115 | { |
1116 | |
1117 | return config_found_sm_loc(parent, ifattr, NULL, aux, print, NULL); |
1118 | } |
1119 | |
1120 | device_t |
1121 | config_found(device_t parent, void *aux, cfprint_t print) |
1122 | { |
1123 | |
1124 | return config_found_sm_loc(parent, NULL, NULL, aux, print, NULL); |
1125 | } |
1126 | |
1127 | /* |
1128 | * As above, but for root devices. |
1129 | */ |
1130 | device_t |
1131 | config_rootfound(const char *rootname, void *aux) |
1132 | { |
1133 | cfdata_t cf; |
1134 | |
1135 | if ((cf = config_rootsearch(NULL, rootname, aux)) != NULL) |
1136 | return config_attach(ROOT, cf, aux, NULL); |
1137 | aprint_error("root device %s not configured\n" , rootname); |
1138 | return NULL; |
1139 | } |
1140 | |
1141 | /* just like sprintf(buf, "%d") except that it works from the end */ |
1142 | static char * |
1143 | number(char *ep, int n) |
1144 | { |
1145 | |
1146 | *--ep = 0; |
1147 | while (n >= 10) { |
1148 | *--ep = (n % 10) + '0'; |
1149 | n /= 10; |
1150 | } |
1151 | *--ep = n + '0'; |
1152 | return ep; |
1153 | } |
1154 | |
1155 | /* |
1156 | * Expand the size of the cd_devs array if necessary. |
1157 | * |
1158 | * The caller must hold alldevs_mtx. config_makeroom() may release and |
1159 | * re-acquire alldevs_mtx, so callers should re-check conditions such |
1160 | * as alldevs_nwrite == 0 and alldevs_nread == 0 when config_makeroom() |
1161 | * returns. |
1162 | */ |
1163 | static void |
1164 | config_makeroom(int n, struct cfdriver *cd) |
1165 | { |
1166 | int ondevs, nndevs; |
1167 | device_t *osp, *nsp; |
1168 | |
1169 | alldevs_nwrite++; |
1170 | |
1171 | for (nndevs = MAX(4, cd->cd_ndevs); nndevs <= n; nndevs += nndevs) |
1172 | ; |
1173 | |
1174 | while (n >= cd->cd_ndevs) { |
1175 | /* |
1176 | * Need to expand the array. |
1177 | */ |
1178 | ondevs = cd->cd_ndevs; |
1179 | osp = cd->cd_devs; |
1180 | |
1181 | /* Release alldevs_mtx around allocation, which may |
1182 | * sleep. |
1183 | */ |
1184 | mutex_exit(&alldevs_mtx); |
1185 | nsp = kmem_alloc(sizeof(device_t[nndevs]), KM_SLEEP); |
1186 | if (nsp == NULL) |
1187 | panic("%s: could not expand cd_devs" , __func__); |
1188 | mutex_enter(&alldevs_mtx); |
1189 | |
1190 | /* If another thread moved the array while we did |
1191 | * not hold alldevs_mtx, try again. |
1192 | */ |
1193 | if (cd->cd_devs != osp) { |
1194 | mutex_exit(&alldevs_mtx); |
1195 | kmem_free(nsp, sizeof(device_t[nndevs])); |
1196 | mutex_enter(&alldevs_mtx); |
1197 | continue; |
1198 | } |
1199 | |
1200 | memset(nsp + ondevs, 0, sizeof(device_t[nndevs - ondevs])); |
1201 | if (ondevs != 0) |
1202 | memcpy(nsp, cd->cd_devs, sizeof(device_t[ondevs])); |
1203 | |
1204 | cd->cd_ndevs = nndevs; |
1205 | cd->cd_devs = nsp; |
1206 | if (ondevs != 0) { |
1207 | mutex_exit(&alldevs_mtx); |
1208 | kmem_free(osp, sizeof(device_t[ondevs])); |
1209 | mutex_enter(&alldevs_mtx); |
1210 | } |
1211 | } |
1212 | alldevs_nwrite--; |
1213 | } |
1214 | |
1215 | /* |
1216 | * Put dev into the devices list. |
1217 | */ |
1218 | static void |
1219 | config_devlink(device_t dev) |
1220 | { |
1221 | |
1222 | mutex_enter(&alldevs_mtx); |
1223 | |
1224 | KASSERT(device_cfdriver(dev)->cd_devs[dev->dv_unit] == dev); |
1225 | |
1226 | dev->dv_add_gen = alldevs_gen; |
1227 | /* It is safe to add a device to the tail of the list while |
1228 | * readers and writers are in the list. |
1229 | */ |
1230 | TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); |
1231 | mutex_exit(&alldevs_mtx); |
1232 | } |
1233 | |
1234 | static void |
1235 | config_devfree(device_t dev) |
1236 | { |
1237 | int priv = (dev->dv_flags & DVF_PRIV_ALLOC); |
1238 | |
1239 | if (dev->dv_cfattach->ca_devsize > 0) |
1240 | kmem_free(dev->dv_private, dev->dv_cfattach->ca_devsize); |
1241 | if (priv) |
1242 | kmem_free(dev, sizeof(*dev)); |
1243 | } |
1244 | |
1245 | /* |
1246 | * Caller must hold alldevs_mtx. |
1247 | */ |
1248 | static void |
1249 | config_devunlink(device_t dev, struct devicelist *garbage) |
1250 | { |
1251 | struct device_garbage *dg = &dev->dv_garbage; |
1252 | cfdriver_t cd = device_cfdriver(dev); |
1253 | int i; |
1254 | |
1255 | KASSERT(mutex_owned(&alldevs_mtx)); |
1256 | |
1257 | /* Unlink from device list. Link to garbage list. */ |
1258 | TAILQ_REMOVE(&alldevs, dev, dv_list); |
1259 | TAILQ_INSERT_TAIL(garbage, dev, dv_list); |
1260 | |
1261 | /* Remove from cfdriver's array. */ |
1262 | cd->cd_devs[dev->dv_unit] = NULL; |
1263 | |
1264 | /* |
1265 | * If the device now has no units in use, unlink its softc array. |
1266 | */ |
1267 | for (i = 0; i < cd->cd_ndevs; i++) { |
1268 | if (cd->cd_devs[i] != NULL) |
1269 | break; |
1270 | } |
1271 | /* Nothing found. Unlink, now. Deallocate, later. */ |
1272 | if (i == cd->cd_ndevs) { |
1273 | dg->dg_ndevs = cd->cd_ndevs; |
1274 | dg->dg_devs = cd->cd_devs; |
1275 | cd->cd_devs = NULL; |
1276 | cd->cd_ndevs = 0; |
1277 | } |
1278 | } |
1279 | |
1280 | static void |
1281 | config_devdelete(device_t dev) |
1282 | { |
1283 | struct device_garbage *dg = &dev->dv_garbage; |
1284 | device_lock_t dvl = device_getlock(dev); |
1285 | |
1286 | if (dg->dg_devs != NULL) |
1287 | kmem_free(dg->dg_devs, sizeof(device_t[dg->dg_ndevs])); |
1288 | |
1289 | cv_destroy(&dvl->dvl_cv); |
1290 | mutex_destroy(&dvl->dvl_mtx); |
1291 | |
1292 | KASSERT(dev->dv_properties != NULL); |
1293 | prop_object_release(dev->dv_properties); |
1294 | |
1295 | if (dev->dv_activity_handlers) |
1296 | panic("%s with registered handlers" , __func__); |
1297 | |
1298 | if (dev->dv_locators) { |
1299 | size_t amount = *--dev->dv_locators; |
1300 | kmem_free(dev->dv_locators, amount); |
1301 | } |
1302 | |
1303 | config_devfree(dev); |
1304 | } |
1305 | |
1306 | static int |
1307 | config_unit_nextfree(cfdriver_t cd, cfdata_t cf) |
1308 | { |
1309 | int unit; |
1310 | |
1311 | if (cf->cf_fstate == FSTATE_STAR) { |
1312 | for (unit = cf->cf_unit; unit < cd->cd_ndevs; unit++) |
1313 | if (cd->cd_devs[unit] == NULL) |
1314 | break; |
1315 | /* |
1316 | * unit is now the unit of the first NULL device pointer, |
1317 | * or max(cd->cd_ndevs,cf->cf_unit). |
1318 | */ |
1319 | } else { |
1320 | unit = cf->cf_unit; |
1321 | if (unit < cd->cd_ndevs && cd->cd_devs[unit] != NULL) |
1322 | unit = -1; |
1323 | } |
1324 | return unit; |
1325 | } |
1326 | |
1327 | static int |
1328 | config_unit_alloc(device_t dev, cfdriver_t cd, cfdata_t cf) |
1329 | { |
1330 | struct alldevs_foray af; |
1331 | int unit; |
1332 | |
1333 | config_alldevs_enter(&af); |
1334 | for (;;) { |
1335 | unit = config_unit_nextfree(cd, cf); |
1336 | if (unit == -1) |
1337 | break; |
1338 | if (unit < cd->cd_ndevs) { |
1339 | cd->cd_devs[unit] = dev; |
1340 | dev->dv_unit = unit; |
1341 | break; |
1342 | } |
1343 | config_makeroom(unit, cd); |
1344 | } |
1345 | config_alldevs_exit(&af); |
1346 | |
1347 | return unit; |
1348 | } |
1349 | |
1350 | static device_t |
1351 | config_devalloc(const device_t parent, const cfdata_t cf, const int *locs) |
1352 | { |
1353 | cfdriver_t cd; |
1354 | cfattach_t ca; |
1355 | size_t lname, lunit; |
1356 | const char *xunit; |
1357 | int myunit; |
1358 | char num[10]; |
1359 | device_t dev; |
1360 | void *dev_private; |
1361 | const struct cfiattrdata *ia; |
1362 | device_lock_t dvl; |
1363 | |
1364 | cd = config_cfdriver_lookup(cf->cf_name); |
1365 | if (cd == NULL) |
1366 | return NULL; |
1367 | |
1368 | ca = config_cfattach_lookup_cd(cd, cf->cf_atname); |
1369 | if (ca == NULL) |
1370 | return NULL; |
1371 | |
1372 | /* get memory for all device vars */ |
1373 | KASSERTMSG((ca->ca_flags & DVF_PRIV_ALLOC) |
1374 | || ca->ca_devsize >= sizeof(struct device), |
1375 | "%s: %s (%zu < %zu)" , __func__, cf->cf_atname, ca->ca_devsize, |
1376 | sizeof(struct device)); |
1377 | if (ca->ca_devsize > 0) { |
1378 | dev_private = kmem_zalloc(ca->ca_devsize, KM_SLEEP); |
1379 | if (dev_private == NULL) |
1380 | panic("config_devalloc: memory allocation for device " |
1381 | "softc failed" ); |
1382 | } else { |
1383 | KASSERT(ca->ca_flags & DVF_PRIV_ALLOC); |
1384 | dev_private = NULL; |
1385 | } |
1386 | |
1387 | if ((ca->ca_flags & DVF_PRIV_ALLOC) != 0) { |
1388 | dev = kmem_zalloc(sizeof(*dev), KM_SLEEP); |
1389 | } else { |
1390 | dev = dev_private; |
1391 | #ifdef DIAGNOSTIC |
1392 | printf("%s has not been converted to device_t\n" , cd->cd_name); |
1393 | #endif |
1394 | } |
1395 | if (dev == NULL) |
1396 | panic("config_devalloc: memory allocation for device_t failed" ); |
1397 | |
1398 | dev->dv_class = cd->cd_class; |
1399 | dev->dv_cfdata = cf; |
1400 | dev->dv_cfdriver = cd; |
1401 | dev->dv_cfattach = ca; |
1402 | dev->dv_activity_count = 0; |
1403 | dev->dv_activity_handlers = NULL; |
1404 | dev->dv_private = dev_private; |
1405 | dev->dv_flags = ca->ca_flags; /* inherit flags from class */ |
1406 | |
1407 | myunit = config_unit_alloc(dev, cd, cf); |
1408 | if (myunit == -1) { |
1409 | config_devfree(dev); |
1410 | return NULL; |
1411 | } |
1412 | |
1413 | /* compute length of name and decimal expansion of unit number */ |
1414 | lname = strlen(cd->cd_name); |
1415 | xunit = number(&num[sizeof(num)], myunit); |
1416 | lunit = &num[sizeof(num)] - xunit; |
1417 | if (lname + lunit > sizeof(dev->dv_xname)) |
1418 | panic("config_devalloc: device name too long" ); |
1419 | |
1420 | dvl = device_getlock(dev); |
1421 | |
1422 | mutex_init(&dvl->dvl_mtx, MUTEX_DEFAULT, IPL_NONE); |
1423 | cv_init(&dvl->dvl_cv, "pmfsusp" ); |
1424 | |
1425 | memcpy(dev->dv_xname, cd->cd_name, lname); |
1426 | memcpy(dev->dv_xname + lname, xunit, lunit); |
1427 | dev->dv_parent = parent; |
1428 | if (parent != NULL) |
1429 | dev->dv_depth = parent->dv_depth + 1; |
1430 | else |
1431 | dev->dv_depth = 0; |
1432 | dev->dv_flags |= DVF_ACTIVE; /* always initially active */ |
1433 | if (locs) { |
1434 | KASSERT(parent); /* no locators at root */ |
1435 | ia = cfiattr_lookup(cfdata_ifattr(cf), parent->dv_cfdriver); |
1436 | dev->dv_locators = |
1437 | kmem_alloc(sizeof(int [ia->ci_loclen + 1]), KM_SLEEP); |
1438 | *dev->dv_locators++ = sizeof(int [ia->ci_loclen + 1]); |
1439 | memcpy(dev->dv_locators, locs, sizeof(int [ia->ci_loclen])); |
1440 | } |
1441 | dev->dv_properties = prop_dictionary_create(); |
1442 | KASSERT(dev->dv_properties != NULL); |
1443 | |
1444 | prop_dictionary_set_cstring_nocopy(dev->dv_properties, |
1445 | "device-driver" , dev->dv_cfdriver->cd_name); |
1446 | prop_dictionary_set_uint16(dev->dv_properties, |
1447 | "device-unit" , dev->dv_unit); |
1448 | if (parent != NULL) { |
1449 | prop_dictionary_set_cstring(dev->dv_properties, |
1450 | "device-parent" , device_xname(parent)); |
1451 | } |
1452 | |
1453 | if (dev->dv_cfdriver->cd_attrs != NULL) |
1454 | config_add_attrib_dict(dev); |
1455 | |
1456 | return dev; |
1457 | } |
1458 | |
1459 | /* |
1460 | * Create an array of device attach attributes and add it |
1461 | * to the device's dv_properties dictionary. |
1462 | * |
1463 | * <key>interface-attributes</key> |
1464 | * <array> |
1465 | * <dict> |
1466 | * <key>attribute-name</key> |
1467 | * <string>foo</string> |
1468 | * <key>locators</key> |
1469 | * <array> |
1470 | * <dict> |
1471 | * <key>loc-name</key> |
1472 | * <string>foo-loc1</string> |
1473 | * </dict> |
1474 | * <dict> |
1475 | * <key>loc-name</key> |
1476 | * <string>foo-loc2</string> |
1477 | * <key>default</key> |
1478 | * <string>foo-loc2-default</string> |
1479 | * </dict> |
1480 | * ... |
1481 | * </array> |
1482 | * </dict> |
1483 | * ... |
1484 | * </array> |
1485 | */ |
1486 | |
1487 | static void |
1488 | config_add_attrib_dict(device_t dev) |
1489 | { |
1490 | int i, j; |
1491 | const struct cfiattrdata *ci; |
1492 | prop_dictionary_t attr_dict, loc_dict; |
1493 | prop_array_t attr_array, loc_array; |
1494 | |
1495 | if ((attr_array = prop_array_create()) == NULL) |
1496 | return; |
1497 | |
1498 | for (i = 0; ; i++) { |
1499 | if ((ci = dev->dv_cfdriver->cd_attrs[i]) == NULL) |
1500 | break; |
1501 | if ((attr_dict = prop_dictionary_create()) == NULL) |
1502 | break; |
1503 | prop_dictionary_set_cstring_nocopy(attr_dict, "attribute-name" , |
1504 | ci->ci_name); |
1505 | |
1506 | /* Create an array of the locator names and defaults */ |
1507 | |
1508 | if (ci->ci_loclen != 0 && |
1509 | (loc_array = prop_array_create()) != NULL) { |
1510 | for (j = 0; j < ci->ci_loclen; j++) { |
1511 | loc_dict = prop_dictionary_create(); |
1512 | if (loc_dict == NULL) |
1513 | continue; |
1514 | prop_dictionary_set_cstring_nocopy(loc_dict, |
1515 | "loc-name" , ci->ci_locdesc[j].cld_name); |
1516 | if (ci->ci_locdesc[j].cld_defaultstr != NULL) |
1517 | prop_dictionary_set_cstring_nocopy( |
1518 | loc_dict, "default" , |
1519 | ci->ci_locdesc[j].cld_defaultstr); |
1520 | prop_array_set(loc_array, j, loc_dict); |
1521 | prop_object_release(loc_dict); |
1522 | } |
1523 | prop_dictionary_set_and_rel(attr_dict, "locators" , |
1524 | loc_array); |
1525 | } |
1526 | prop_array_add(attr_array, attr_dict); |
1527 | prop_object_release(attr_dict); |
1528 | } |
1529 | if (i == 0) |
1530 | prop_object_release(attr_array); |
1531 | else |
1532 | prop_dictionary_set_and_rel(dev->dv_properties, |
1533 | "interface-attributes" , attr_array); |
1534 | |
1535 | return; |
1536 | } |
1537 | |
1538 | /* |
1539 | * Attach a found device. |
1540 | */ |
1541 | device_t |
1542 | config_attach_loc(device_t parent, cfdata_t cf, |
1543 | const int *locs, void *aux, cfprint_t print) |
1544 | { |
1545 | device_t dev; |
1546 | struct cftable *ct; |
1547 | const char *drvname; |
1548 | |
1549 | dev = config_devalloc(parent, cf, locs); |
1550 | if (!dev) |
1551 | panic("config_attach: allocation of device softc failed" ); |
1552 | |
1553 | /* XXX redundant - see below? */ |
1554 | if (cf->cf_fstate != FSTATE_STAR) { |
1555 | KASSERT(cf->cf_fstate == FSTATE_NOTFOUND); |
1556 | cf->cf_fstate = FSTATE_FOUND; |
1557 | } |
1558 | |
1559 | config_devlink(dev); |
1560 | |
1561 | if (config_do_twiddle && cold) |
1562 | twiddle(); |
1563 | else |
1564 | aprint_naive("Found " ); |
1565 | /* |
1566 | * We want the next two printfs for normal, verbose, and quiet, |
1567 | * but not silent (in which case, we're twiddling, instead). |
1568 | */ |
1569 | if (parent == ROOT) { |
1570 | aprint_naive("%s (root)" , device_xname(dev)); |
1571 | aprint_normal("%s (root)" , device_xname(dev)); |
1572 | } else { |
1573 | aprint_naive("%s at %s" , device_xname(dev), |
1574 | device_xname(parent)); |
1575 | aprint_normal("%s at %s" , device_xname(dev), |
1576 | device_xname(parent)); |
1577 | if (print) |
1578 | (void) (*print)(aux, NULL); |
1579 | } |
1580 | |
1581 | /* |
1582 | * Before attaching, clobber any unfound devices that are |
1583 | * otherwise identical. |
1584 | * XXX code above is redundant? |
1585 | */ |
1586 | drvname = dev->dv_cfdriver->cd_name; |
1587 | TAILQ_FOREACH(ct, &allcftables, ct_list) { |
1588 | for (cf = ct->ct_cfdata; cf->cf_name; cf++) { |
1589 | if (STREQ(cf->cf_name, drvname) && |
1590 | cf->cf_unit == dev->dv_unit) { |
1591 | if (cf->cf_fstate == FSTATE_NOTFOUND) |
1592 | cf->cf_fstate = FSTATE_FOUND; |
1593 | } |
1594 | } |
1595 | } |
1596 | device_register(dev, aux); |
1597 | |
1598 | /* Let userland know */ |
1599 | devmon_report_device(dev, true); |
1600 | |
1601 | (*dev->dv_cfattach->ca_attach)(parent, dev, aux); |
1602 | |
1603 | if (!device_pmf_is_registered(dev)) |
1604 | aprint_debug_dev(dev, "WARNING: power management not " |
1605 | "supported\n" ); |
1606 | |
1607 | config_process_deferred(&deferred_config_queue, dev); |
1608 | |
1609 | device_register_post_config(dev, aux); |
1610 | return dev; |
1611 | } |
1612 | |
1613 | device_t |
1614 | config_attach(device_t parent, cfdata_t cf, void *aux, cfprint_t print) |
1615 | { |
1616 | |
1617 | return config_attach_loc(parent, cf, NULL, aux, print); |
1618 | } |
1619 | |
1620 | /* |
1621 | * As above, but for pseudo-devices. Pseudo-devices attached in this |
1622 | * way are silently inserted into the device tree, and their children |
1623 | * attached. |
1624 | * |
1625 | * Note that because pseudo-devices are attached silently, any information |
1626 | * the attach routine wishes to print should be prefixed with the device |
1627 | * name by the attach routine. |
1628 | */ |
1629 | device_t |
1630 | config_attach_pseudo(cfdata_t cf) |
1631 | { |
1632 | device_t dev; |
1633 | |
1634 | dev = config_devalloc(ROOT, cf, NULL); |
1635 | if (!dev) |
1636 | return NULL; |
1637 | |
1638 | /* XXX mark busy in cfdata */ |
1639 | |
1640 | if (cf->cf_fstate != FSTATE_STAR) { |
1641 | KASSERT(cf->cf_fstate == FSTATE_NOTFOUND); |
1642 | cf->cf_fstate = FSTATE_FOUND; |
1643 | } |
1644 | |
1645 | config_devlink(dev); |
1646 | |
1647 | #if 0 /* XXXJRT not yet */ |
1648 | device_register(dev, NULL); /* like a root node */ |
1649 | #endif |
1650 | |
1651 | /* Let userland know */ |
1652 | devmon_report_device(dev, true); |
1653 | |
1654 | (*dev->dv_cfattach->ca_attach)(ROOT, dev, NULL); |
1655 | |
1656 | config_process_deferred(&deferred_config_queue, dev); |
1657 | return dev; |
1658 | } |
1659 | |
1660 | /* |
1661 | * Caller must hold alldevs_mtx. |
1662 | */ |
1663 | static void |
1664 | config_collect_garbage(struct devicelist *garbage) |
1665 | { |
1666 | device_t dv; |
1667 | |
1668 | KASSERT(!cpu_intr_p()); |
1669 | KASSERT(!cpu_softintr_p()); |
1670 | KASSERT(mutex_owned(&alldevs_mtx)); |
1671 | |
1672 | while (alldevs_nwrite == 0 && alldevs_nread == 0 && alldevs_garbage) { |
1673 | TAILQ_FOREACH(dv, &alldevs, dv_list) { |
1674 | if (dv->dv_del_gen != 0) |
1675 | break; |
1676 | } |
1677 | if (dv == NULL) { |
1678 | alldevs_garbage = false; |
1679 | break; |
1680 | } |
1681 | config_devunlink(dv, garbage); |
1682 | } |
1683 | KASSERT(mutex_owned(&alldevs_mtx)); |
1684 | } |
1685 | |
1686 | static void |
1687 | config_dump_garbage(struct devicelist *garbage) |
1688 | { |
1689 | device_t dv; |
1690 | |
1691 | while ((dv = TAILQ_FIRST(garbage)) != NULL) { |
1692 | TAILQ_REMOVE(garbage, dv, dv_list); |
1693 | config_devdelete(dv); |
1694 | } |
1695 | } |
1696 | |
1697 | /* |
1698 | * Detach a device. Optionally forced (e.g. because of hardware |
1699 | * removal) and quiet. Returns zero if successful, non-zero |
1700 | * (an error code) otherwise. |
1701 | * |
1702 | * Note that this code wants to be run from a process context, so |
1703 | * that the detach can sleep to allow processes which have a device |
1704 | * open to run and unwind their stacks. |
1705 | */ |
1706 | int |
1707 | config_detach(device_t dev, int flags) |
1708 | { |
1709 | struct alldevs_foray af; |
1710 | struct cftable *ct; |
1711 | cfdata_t cf; |
1712 | const struct cfattach *ca; |
1713 | struct cfdriver *cd; |
1714 | #ifdef DIAGNOSTIC |
1715 | device_t d; |
1716 | #endif |
1717 | int rv = 0; |
1718 | |
1719 | #ifdef DIAGNOSTIC |
1720 | cf = dev->dv_cfdata; |
1721 | if (cf != NULL && cf->cf_fstate != FSTATE_FOUND && |
1722 | cf->cf_fstate != FSTATE_STAR) |
1723 | panic("config_detach: %s: bad device fstate %d" , |
1724 | device_xname(dev), cf ? cf->cf_fstate : -1); |
1725 | #endif |
1726 | cd = dev->dv_cfdriver; |
1727 | KASSERT(cd != NULL); |
1728 | |
1729 | ca = dev->dv_cfattach; |
1730 | KASSERT(ca != NULL); |
1731 | |
1732 | mutex_enter(&alldevs_mtx); |
1733 | if (dev->dv_del_gen != 0) { |
1734 | mutex_exit(&alldevs_mtx); |
1735 | #ifdef DIAGNOSTIC |
1736 | printf("%s: %s is already detached\n" , __func__, |
1737 | device_xname(dev)); |
1738 | #endif /* DIAGNOSTIC */ |
1739 | return ENOENT; |
1740 | } |
1741 | alldevs_nwrite++; |
1742 | mutex_exit(&alldevs_mtx); |
1743 | |
1744 | if (!detachall && |
1745 | (flags & (DETACH_SHUTDOWN|DETACH_FORCE)) == DETACH_SHUTDOWN && |
1746 | (dev->dv_flags & DVF_DETACH_SHUTDOWN) == 0) { |
1747 | rv = EOPNOTSUPP; |
1748 | } else if (ca->ca_detach != NULL) { |
1749 | rv = (*ca->ca_detach)(dev, flags); |
1750 | } else |
1751 | rv = EOPNOTSUPP; |
1752 | |
1753 | /* |
1754 | * If it was not possible to detach the device, then we either |
1755 | * panic() (for the forced but failed case), or return an error. |
1756 | * |
1757 | * If it was possible to detach the device, ensure that the |
1758 | * device is deactivated. |
1759 | */ |
1760 | if (rv == 0) |
1761 | dev->dv_flags &= ~DVF_ACTIVE; |
1762 | else if ((flags & DETACH_FORCE) == 0) |
1763 | goto out; |
1764 | else { |
1765 | panic("config_detach: forced detach of %s failed (%d)" , |
1766 | device_xname(dev), rv); |
1767 | } |
1768 | |
1769 | /* |
1770 | * The device has now been successfully detached. |
1771 | */ |
1772 | |
1773 | /* Let userland know */ |
1774 | devmon_report_device(dev, false); |
1775 | |
1776 | #ifdef DIAGNOSTIC |
1777 | /* |
1778 | * Sanity: If you're successfully detached, you should have no |
1779 | * children. (Note that because children must be attached |
1780 | * after parents, we only need to search the latter part of |
1781 | * the list.) |
1782 | */ |
1783 | for (d = TAILQ_NEXT(dev, dv_list); d != NULL; |
1784 | d = TAILQ_NEXT(d, dv_list)) { |
1785 | if (d->dv_parent == dev && d->dv_del_gen == 0) { |
1786 | printf("config_detach: detached device %s" |
1787 | " has children %s\n" , device_xname(dev), |
1788 | device_xname(d)); |
1789 | panic("config_detach" ); |
1790 | } |
1791 | } |
1792 | #endif |
1793 | |
1794 | /* notify the parent that the child is gone */ |
1795 | if (dev->dv_parent) { |
1796 | device_t p = dev->dv_parent; |
1797 | if (p->dv_cfattach->ca_childdetached) |
1798 | (*p->dv_cfattach->ca_childdetached)(p, dev); |
1799 | } |
1800 | |
1801 | /* |
1802 | * Mark cfdata to show that the unit can be reused, if possible. |
1803 | */ |
1804 | TAILQ_FOREACH(ct, &allcftables, ct_list) { |
1805 | for (cf = ct->ct_cfdata; cf->cf_name; cf++) { |
1806 | if (STREQ(cf->cf_name, cd->cd_name)) { |
1807 | if (cf->cf_fstate == FSTATE_FOUND && |
1808 | cf->cf_unit == dev->dv_unit) |
1809 | cf->cf_fstate = FSTATE_NOTFOUND; |
1810 | } |
1811 | } |
1812 | } |
1813 | |
1814 | if (dev->dv_cfdata != NULL && (flags & DETACH_QUIET) == 0) |
1815 | aprint_normal_dev(dev, "detached\n" ); |
1816 | |
1817 | out: |
1818 | config_alldevs_enter(&af); |
1819 | KASSERT(alldevs_nwrite != 0); |
1820 | --alldevs_nwrite; |
1821 | if (rv == 0 && dev->dv_del_gen == 0) { |
1822 | if (alldevs_nwrite == 0 && alldevs_nread == 0) |
1823 | config_devunlink(dev, &af.af_garbage); |
1824 | else { |
1825 | dev->dv_del_gen = alldevs_gen; |
1826 | alldevs_garbage = true; |
1827 | } |
1828 | } |
1829 | config_alldevs_exit(&af); |
1830 | |
1831 | return rv; |
1832 | } |
1833 | |
1834 | int |
1835 | config_detach_children(device_t parent, int flags) |
1836 | { |
1837 | device_t dv; |
1838 | deviter_t di; |
1839 | int error = 0; |
1840 | |
1841 | for (dv = deviter_first(&di, DEVITER_F_RW); dv != NULL; |
1842 | dv = deviter_next(&di)) { |
1843 | if (device_parent(dv) != parent) |
1844 | continue; |
1845 | if ((error = config_detach(dv, flags)) != 0) |
1846 | break; |
1847 | } |
1848 | deviter_release(&di); |
1849 | return error; |
1850 | } |
1851 | |
1852 | device_t |
1853 | shutdown_first(struct shutdown_state *s) |
1854 | { |
1855 | if (!s->initialized) { |
1856 | deviter_init(&s->di, DEVITER_F_SHUTDOWN|DEVITER_F_LEAVES_FIRST); |
1857 | s->initialized = true; |
1858 | } |
1859 | return shutdown_next(s); |
1860 | } |
1861 | |
1862 | device_t |
1863 | shutdown_next(struct shutdown_state *s) |
1864 | { |
1865 | device_t dv; |
1866 | |
1867 | while ((dv = deviter_next(&s->di)) != NULL && !device_is_active(dv)) |
1868 | ; |
1869 | |
1870 | if (dv == NULL) |
1871 | s->initialized = false; |
1872 | |
1873 | return dv; |
1874 | } |
1875 | |
1876 | bool |
1877 | config_detach_all(int how) |
1878 | { |
1879 | static struct shutdown_state s; |
1880 | device_t curdev; |
1881 | bool progress = false; |
1882 | int flags; |
1883 | |
1884 | if ((how & (RB_NOSYNC|RB_DUMP)) != 0) |
1885 | return false; |
1886 | |
1887 | if ((how & RB_POWERDOWN) == RB_POWERDOWN) |
1888 | flags = DETACH_SHUTDOWN | DETACH_POWEROFF; |
1889 | else |
1890 | flags = DETACH_SHUTDOWN; |
1891 | |
1892 | for (curdev = shutdown_first(&s); curdev != NULL; |
1893 | curdev = shutdown_next(&s)) { |
1894 | aprint_debug(" detaching %s, " , device_xname(curdev)); |
1895 | if (config_detach(curdev, flags) == 0) { |
1896 | progress = true; |
1897 | aprint_debug("success." ); |
1898 | } else |
1899 | aprint_debug("failed." ); |
1900 | } |
1901 | return progress; |
1902 | } |
1903 | |
1904 | static bool |
1905 | device_is_ancestor_of(device_t ancestor, device_t descendant) |
1906 | { |
1907 | device_t dv; |
1908 | |
1909 | for (dv = descendant; dv != NULL; dv = device_parent(dv)) { |
1910 | if (device_parent(dv) == ancestor) |
1911 | return true; |
1912 | } |
1913 | return false; |
1914 | } |
1915 | |
1916 | int |
1917 | config_deactivate(device_t dev) |
1918 | { |
1919 | deviter_t di; |
1920 | const struct cfattach *ca; |
1921 | device_t descendant; |
1922 | int s, rv = 0, oflags; |
1923 | |
1924 | for (descendant = deviter_first(&di, DEVITER_F_ROOT_FIRST); |
1925 | descendant != NULL; |
1926 | descendant = deviter_next(&di)) { |
1927 | if (dev != descendant && |
1928 | !device_is_ancestor_of(dev, descendant)) |
1929 | continue; |
1930 | |
1931 | if ((descendant->dv_flags & DVF_ACTIVE) == 0) |
1932 | continue; |
1933 | |
1934 | ca = descendant->dv_cfattach; |
1935 | oflags = descendant->dv_flags; |
1936 | |
1937 | descendant->dv_flags &= ~DVF_ACTIVE; |
1938 | if (ca->ca_activate == NULL) |
1939 | continue; |
1940 | s = splhigh(); |
1941 | rv = (*ca->ca_activate)(descendant, DVACT_DEACTIVATE); |
1942 | splx(s); |
1943 | if (rv != 0) |
1944 | descendant->dv_flags = oflags; |
1945 | } |
1946 | deviter_release(&di); |
1947 | return rv; |
1948 | } |
1949 | |
1950 | /* |
1951 | * Defer the configuration of the specified device until all |
1952 | * of its parent's devices have been attached. |
1953 | */ |
1954 | void |
1955 | config_defer(device_t dev, void (*func)(device_t)) |
1956 | { |
1957 | struct deferred_config *dc; |
1958 | |
1959 | if (dev->dv_parent == NULL) |
1960 | panic("config_defer: can't defer config of a root device" ); |
1961 | |
1962 | #ifdef DIAGNOSTIC |
1963 | TAILQ_FOREACH(dc, &deferred_config_queue, dc_queue) { |
1964 | if (dc->dc_dev == dev) |
1965 | panic("config_defer: deferred twice" ); |
1966 | } |
1967 | #endif |
1968 | |
1969 | dc = kmem_alloc(sizeof(*dc), KM_SLEEP); |
1970 | if (dc == NULL) |
1971 | panic("config_defer: unable to allocate callback" ); |
1972 | |
1973 | dc->dc_dev = dev; |
1974 | dc->dc_func = func; |
1975 | TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue); |
1976 | config_pending_incr(dev); |
1977 | } |
1978 | |
1979 | /* |
1980 | * Defer some autoconfiguration for a device until after interrupts |
1981 | * are enabled. |
1982 | */ |
1983 | void |
1984 | config_interrupts(device_t dev, void (*func)(device_t)) |
1985 | { |
1986 | struct deferred_config *dc; |
1987 | |
1988 | /* |
1989 | * If interrupts are enabled, callback now. |
1990 | */ |
1991 | if (cold == 0) { |
1992 | (*func)(dev); |
1993 | return; |
1994 | } |
1995 | |
1996 | #ifdef DIAGNOSTIC |
1997 | TAILQ_FOREACH(dc, &interrupt_config_queue, dc_queue) { |
1998 | if (dc->dc_dev == dev) |
1999 | panic("config_interrupts: deferred twice" ); |
2000 | } |
2001 | #endif |
2002 | |
2003 | dc = kmem_alloc(sizeof(*dc), KM_SLEEP); |
2004 | if (dc == NULL) |
2005 | panic("config_interrupts: unable to allocate callback" ); |
2006 | |
2007 | dc->dc_dev = dev; |
2008 | dc->dc_func = func; |
2009 | TAILQ_INSERT_TAIL(&interrupt_config_queue, dc, dc_queue); |
2010 | config_pending_incr(dev); |
2011 | } |
2012 | |
2013 | /* |
2014 | * Defer some autoconfiguration for a device until after root file system |
2015 | * is mounted (to load firmware etc). |
2016 | */ |
2017 | void |
2018 | config_mountroot(device_t dev, void (*func)(device_t)) |
2019 | { |
2020 | struct deferred_config *dc; |
2021 | |
2022 | /* |
2023 | * If root file system is mounted, callback now. |
2024 | */ |
2025 | if (root_is_mounted) { |
2026 | (*func)(dev); |
2027 | return; |
2028 | } |
2029 | |
2030 | #ifdef DIAGNOSTIC |
2031 | TAILQ_FOREACH(dc, &mountroot_config_queue, dc_queue) { |
2032 | if (dc->dc_dev == dev) |
2033 | panic("%s: deferred twice" , __func__); |
2034 | } |
2035 | #endif |
2036 | |
2037 | dc = kmem_alloc(sizeof(*dc), KM_SLEEP); |
2038 | if (dc == NULL) |
2039 | panic("%s: unable to allocate callback" , __func__); |
2040 | |
2041 | dc->dc_dev = dev; |
2042 | dc->dc_func = func; |
2043 | TAILQ_INSERT_TAIL(&mountroot_config_queue, dc, dc_queue); |
2044 | } |
2045 | |
2046 | /* |
2047 | * Process a deferred configuration queue. |
2048 | */ |
2049 | static void |
2050 | config_process_deferred(struct deferred_config_head *queue, device_t parent) |
2051 | { |
2052 | struct deferred_config *dc, *ndc; |
2053 | |
2054 | for (dc = TAILQ_FIRST(queue); dc != NULL; dc = ndc) { |
2055 | ndc = TAILQ_NEXT(dc, dc_queue); |
2056 | if (parent == NULL || dc->dc_dev->dv_parent == parent) { |
2057 | TAILQ_REMOVE(queue, dc, dc_queue); |
2058 | (*dc->dc_func)(dc->dc_dev); |
2059 | config_pending_decr(dc->dc_dev); |
2060 | kmem_free(dc, sizeof(*dc)); |
2061 | } |
2062 | } |
2063 | } |
2064 | |
2065 | /* |
2066 | * Manipulate the config_pending semaphore. |
2067 | */ |
2068 | void |
2069 | config_pending_incr(device_t dev) |
2070 | { |
2071 | |
2072 | mutex_enter(&config_misc_lock); |
2073 | config_pending++; |
2074 | #ifdef DEBUG_AUTOCONF |
2075 | printf("%s: %s %d\n" , __func__, device_xname(dev), config_pending); |
2076 | #endif |
2077 | mutex_exit(&config_misc_lock); |
2078 | } |
2079 | |
2080 | void |
2081 | config_pending_decr(device_t dev) |
2082 | { |
2083 | |
2084 | #ifdef DIAGNOSTIC |
2085 | if (config_pending == 0) |
2086 | panic("config_pending_decr: config_pending == 0" ); |
2087 | #endif |
2088 | mutex_enter(&config_misc_lock); |
2089 | config_pending--; |
2090 | #ifdef DEBUG_AUTOCONF |
2091 | printf("%s: %s %d\n" , __func__, device_xname(dev), config_pending); |
2092 | #endif |
2093 | if (config_pending == 0) |
2094 | cv_broadcast(&config_misc_cv); |
2095 | mutex_exit(&config_misc_lock); |
2096 | } |
2097 | |
2098 | /* |
2099 | * Register a "finalization" routine. Finalization routines are |
2100 | * called iteratively once all real devices have been found during |
2101 | * autoconfiguration, for as long as any one finalizer has done |
2102 | * any work. |
2103 | */ |
2104 | int |
2105 | config_finalize_register(device_t dev, int (*fn)(device_t)) |
2106 | { |
2107 | struct finalize_hook *f; |
2108 | |
2109 | /* |
2110 | * If finalization has already been done, invoke the |
2111 | * callback function now. |
2112 | */ |
2113 | if (config_finalize_done) { |
2114 | while ((*fn)(dev) != 0) |
2115 | /* loop */ ; |
2116 | return 0; |
2117 | } |
2118 | |
2119 | /* Ensure this isn't already on the list. */ |
2120 | TAILQ_FOREACH(f, &config_finalize_list, f_list) { |
2121 | if (f->f_func == fn && f->f_dev == dev) |
2122 | return EEXIST; |
2123 | } |
2124 | |
2125 | f = kmem_alloc(sizeof(*f), KM_SLEEP); |
2126 | f->f_func = fn; |
2127 | f->f_dev = dev; |
2128 | TAILQ_INSERT_TAIL(&config_finalize_list, f, f_list); |
2129 | |
2130 | return 0; |
2131 | } |
2132 | |
2133 | void |
2134 | config_finalize(void) |
2135 | { |
2136 | struct finalize_hook *f; |
2137 | struct pdevinit *pdev; |
2138 | extern struct pdevinit pdevinit[]; |
2139 | int errcnt, rv; |
2140 | |
2141 | /* |
2142 | * Now that device driver threads have been created, wait for |
2143 | * them to finish any deferred autoconfiguration. |
2144 | */ |
2145 | mutex_enter(&config_misc_lock); |
2146 | while (config_pending != 0) |
2147 | cv_wait(&config_misc_cv, &config_misc_lock); |
2148 | mutex_exit(&config_misc_lock); |
2149 | |
2150 | KERNEL_LOCK(1, NULL); |
2151 | |
2152 | /* Attach pseudo-devices. */ |
2153 | for (pdev = pdevinit; pdev->pdev_attach != NULL; pdev++) |
2154 | (*pdev->pdev_attach)(pdev->pdev_count); |
2155 | |
2156 | /* Run the hooks until none of them does any work. */ |
2157 | do { |
2158 | rv = 0; |
2159 | TAILQ_FOREACH(f, &config_finalize_list, f_list) |
2160 | rv |= (*f->f_func)(f->f_dev); |
2161 | } while (rv != 0); |
2162 | |
2163 | config_finalize_done = 1; |
2164 | |
2165 | /* Now free all the hooks. */ |
2166 | while ((f = TAILQ_FIRST(&config_finalize_list)) != NULL) { |
2167 | TAILQ_REMOVE(&config_finalize_list, f, f_list); |
2168 | kmem_free(f, sizeof(*f)); |
2169 | } |
2170 | |
2171 | KERNEL_UNLOCK_ONE(NULL); |
2172 | |
2173 | errcnt = aprint_get_error_count(); |
2174 | if ((boothowto & (AB_QUIET|AB_SILENT)) != 0 && |
2175 | (boothowto & AB_VERBOSE) == 0) { |
2176 | mutex_enter(&config_misc_lock); |
2177 | if (config_do_twiddle) { |
2178 | config_do_twiddle = 0; |
2179 | printf_nolog(" done.\n" ); |
2180 | } |
2181 | mutex_exit(&config_misc_lock); |
2182 | } |
2183 | if (errcnt != 0) { |
2184 | printf("WARNING: %d error%s while detecting hardware; " |
2185 | "check system log.\n" , errcnt, |
2186 | errcnt == 1 ? "" : "s" ); |
2187 | } |
2188 | } |
2189 | |
2190 | void |
2191 | config_twiddle_init(void) |
2192 | { |
2193 | |
2194 | if ((boothowto & (AB_SILENT|AB_VERBOSE)) == AB_SILENT) { |
2195 | config_do_twiddle = 1; |
2196 | } |
2197 | callout_setfunc(&config_twiddle_ch, config_twiddle_fn, NULL); |
2198 | } |
2199 | |
2200 | void |
2201 | config_twiddle_fn(void *cookie) |
2202 | { |
2203 | |
2204 | mutex_enter(&config_misc_lock); |
2205 | if (config_do_twiddle) { |
2206 | twiddle(); |
2207 | callout_schedule(&config_twiddle_ch, mstohz(100)); |
2208 | } |
2209 | mutex_exit(&config_misc_lock); |
2210 | } |
2211 | |
2212 | static void |
2213 | config_alldevs_enter(struct alldevs_foray *af) |
2214 | { |
2215 | TAILQ_INIT(&af->af_garbage); |
2216 | mutex_enter(&alldevs_mtx); |
2217 | config_collect_garbage(&af->af_garbage); |
2218 | } |
2219 | |
2220 | static void |
2221 | config_alldevs_exit(struct alldevs_foray *af) |
2222 | { |
2223 | mutex_exit(&alldevs_mtx); |
2224 | config_dump_garbage(&af->af_garbage); |
2225 | } |
2226 | |
2227 | /* |
2228 | * device_lookup: |
2229 | * |
2230 | * Look up a device instance for a given driver. |
2231 | */ |
2232 | device_t |
2233 | device_lookup(cfdriver_t cd, int unit) |
2234 | { |
2235 | device_t dv; |
2236 | |
2237 | mutex_enter(&alldevs_mtx); |
2238 | if (unit < 0 || unit >= cd->cd_ndevs) |
2239 | dv = NULL; |
2240 | else if ((dv = cd->cd_devs[unit]) != NULL && dv->dv_del_gen != 0) |
2241 | dv = NULL; |
2242 | mutex_exit(&alldevs_mtx); |
2243 | |
2244 | return dv; |
2245 | } |
2246 | |
2247 | /* |
2248 | * device_lookup_private: |
2249 | * |
2250 | * Look up a softc instance for a given driver. |
2251 | */ |
2252 | void * |
2253 | device_lookup_private(cfdriver_t cd, int unit) |
2254 | { |
2255 | |
2256 | return device_private(device_lookup(cd, unit)); |
2257 | } |
2258 | |
2259 | /* |
2260 | * device_find_by_xname: |
2261 | * |
2262 | * Returns the device of the given name or NULL if it doesn't exist. |
2263 | */ |
2264 | device_t |
2265 | device_find_by_xname(const char *name) |
2266 | { |
2267 | device_t dv; |
2268 | deviter_t di; |
2269 | |
2270 | for (dv = deviter_first(&di, 0); dv != NULL; dv = deviter_next(&di)) { |
2271 | if (strcmp(device_xname(dv), name) == 0) |
2272 | break; |
2273 | } |
2274 | deviter_release(&di); |
2275 | |
2276 | return dv; |
2277 | } |
2278 | |
2279 | /* |
2280 | * device_find_by_driver_unit: |
2281 | * |
2282 | * Returns the device of the given driver name and unit or |
2283 | * NULL if it doesn't exist. |
2284 | */ |
2285 | device_t |
2286 | device_find_by_driver_unit(const char *name, int unit) |
2287 | { |
2288 | struct cfdriver *cd; |
2289 | |
2290 | if ((cd = config_cfdriver_lookup(name)) == NULL) |
2291 | return NULL; |
2292 | return device_lookup(cd, unit); |
2293 | } |
2294 | |
2295 | /* |
2296 | * Power management related functions. |
2297 | */ |
2298 | |
2299 | bool |
2300 | device_pmf_is_registered(device_t dev) |
2301 | { |
2302 | return (dev->dv_flags & DVF_POWER_HANDLERS) != 0; |
2303 | } |
2304 | |
2305 | bool |
2306 | device_pmf_driver_suspend(device_t dev, const pmf_qual_t *qual) |
2307 | { |
2308 | if ((dev->dv_flags & DVF_DRIVER_SUSPENDED) != 0) |
2309 | return true; |
2310 | if ((dev->dv_flags & DVF_CLASS_SUSPENDED) == 0) |
2311 | return false; |
2312 | if (pmf_qual_depth(qual) <= DEVACT_LEVEL_DRIVER && |
2313 | dev->dv_driver_suspend != NULL && |
2314 | !(*dev->dv_driver_suspend)(dev, qual)) |
2315 | return false; |
2316 | |
2317 | dev->dv_flags |= DVF_DRIVER_SUSPENDED; |
2318 | return true; |
2319 | } |
2320 | |
2321 | bool |
2322 | device_pmf_driver_resume(device_t dev, const pmf_qual_t *qual) |
2323 | { |
2324 | if ((dev->dv_flags & DVF_DRIVER_SUSPENDED) == 0) |
2325 | return true; |
2326 | if ((dev->dv_flags & DVF_BUS_SUSPENDED) != 0) |
2327 | return false; |
2328 | if (pmf_qual_depth(qual) <= DEVACT_LEVEL_DRIVER && |
2329 | dev->dv_driver_resume != NULL && |
2330 | !(*dev->dv_driver_resume)(dev, qual)) |
2331 | return false; |
2332 | |
2333 | dev->dv_flags &= ~DVF_DRIVER_SUSPENDED; |
2334 | return true; |
2335 | } |
2336 | |
2337 | bool |
2338 | device_pmf_driver_shutdown(device_t dev, int how) |
2339 | { |
2340 | |
2341 | if (*dev->dv_driver_shutdown != NULL && |
2342 | !(*dev->dv_driver_shutdown)(dev, how)) |
2343 | return false; |
2344 | return true; |
2345 | } |
2346 | |
2347 | bool |
2348 | device_pmf_driver_register(device_t dev, |
2349 | bool (*suspend)(device_t, const pmf_qual_t *), |
2350 | bool (*resume)(device_t, const pmf_qual_t *), |
2351 | bool (*shutdown)(device_t, int)) |
2352 | { |
2353 | dev->dv_driver_suspend = suspend; |
2354 | dev->dv_driver_resume = resume; |
2355 | dev->dv_driver_shutdown = shutdown; |
2356 | dev->dv_flags |= DVF_POWER_HANDLERS; |
2357 | return true; |
2358 | } |
2359 | |
2360 | static const char * |
2361 | curlwp_name(void) |
2362 | { |
2363 | if (curlwp->l_name != NULL) |
2364 | return curlwp->l_name; |
2365 | else |
2366 | return curlwp->l_proc->p_comm; |
2367 | } |
2368 | |
2369 | void |
2370 | device_pmf_driver_deregister(device_t dev) |
2371 | { |
2372 | device_lock_t dvl = device_getlock(dev); |
2373 | |
2374 | dev->dv_driver_suspend = NULL; |
2375 | dev->dv_driver_resume = NULL; |
2376 | |
2377 | mutex_enter(&dvl->dvl_mtx); |
2378 | dev->dv_flags &= ~DVF_POWER_HANDLERS; |
2379 | while (dvl->dvl_nlock > 0 || dvl->dvl_nwait > 0) { |
2380 | /* Wake a thread that waits for the lock. That |
2381 | * thread will fail to acquire the lock, and then |
2382 | * it will wake the next thread that waits for the |
2383 | * lock, or else it will wake us. |
2384 | */ |
2385 | cv_signal(&dvl->dvl_cv); |
2386 | pmflock_debug(dev, __func__, __LINE__); |
2387 | cv_wait(&dvl->dvl_cv, &dvl->dvl_mtx); |
2388 | pmflock_debug(dev, __func__, __LINE__); |
2389 | } |
2390 | mutex_exit(&dvl->dvl_mtx); |
2391 | } |
2392 | |
2393 | bool |
2394 | device_pmf_driver_child_register(device_t dev) |
2395 | { |
2396 | device_t parent = device_parent(dev); |
2397 | |
2398 | if (parent == NULL || parent->dv_driver_child_register == NULL) |
2399 | return true; |
2400 | return (*parent->dv_driver_child_register)(dev); |
2401 | } |
2402 | |
2403 | void |
2404 | device_pmf_driver_set_child_register(device_t dev, |
2405 | bool (*child_register)(device_t)) |
2406 | { |
2407 | dev->dv_driver_child_register = child_register; |
2408 | } |
2409 | |
2410 | static void |
2411 | pmflock_debug(device_t dev, const char *func, int line) |
2412 | { |
2413 | device_lock_t dvl = device_getlock(dev); |
2414 | |
2415 | aprint_debug_dev(dev, |
2416 | "%s.%d, %s dvl_nlock %d dvl_nwait %d dv_flags %x\n" , func, line, |
2417 | curlwp_name(), dvl->dvl_nlock, dvl->dvl_nwait, dev->dv_flags); |
2418 | } |
2419 | |
2420 | static bool |
2421 | device_pmf_lock1(device_t dev) |
2422 | { |
2423 | device_lock_t dvl = device_getlock(dev); |
2424 | |
2425 | while (device_pmf_is_registered(dev) && |
2426 | dvl->dvl_nlock > 0 && dvl->dvl_holder != curlwp) { |
2427 | dvl->dvl_nwait++; |
2428 | pmflock_debug(dev, __func__, __LINE__); |
2429 | cv_wait(&dvl->dvl_cv, &dvl->dvl_mtx); |
2430 | pmflock_debug(dev, __func__, __LINE__); |
2431 | dvl->dvl_nwait--; |
2432 | } |
2433 | if (!device_pmf_is_registered(dev)) { |
2434 | pmflock_debug(dev, __func__, __LINE__); |
2435 | /* We could not acquire the lock, but some other thread may |
2436 | * wait for it, also. Wake that thread. |
2437 | */ |
2438 | cv_signal(&dvl->dvl_cv); |
2439 | return false; |
2440 | } |
2441 | dvl->dvl_nlock++; |
2442 | dvl->dvl_holder = curlwp; |
2443 | pmflock_debug(dev, __func__, __LINE__); |
2444 | return true; |
2445 | } |
2446 | |
2447 | bool |
2448 | device_pmf_lock(device_t dev) |
2449 | { |
2450 | bool rc; |
2451 | device_lock_t dvl = device_getlock(dev); |
2452 | |
2453 | mutex_enter(&dvl->dvl_mtx); |
2454 | rc = device_pmf_lock1(dev); |
2455 | mutex_exit(&dvl->dvl_mtx); |
2456 | |
2457 | return rc; |
2458 | } |
2459 | |
2460 | void |
2461 | device_pmf_unlock(device_t dev) |
2462 | { |
2463 | device_lock_t dvl = device_getlock(dev); |
2464 | |
2465 | KASSERT(dvl->dvl_nlock > 0); |
2466 | mutex_enter(&dvl->dvl_mtx); |
2467 | if (--dvl->dvl_nlock == 0) |
2468 | dvl->dvl_holder = NULL; |
2469 | cv_signal(&dvl->dvl_cv); |
2470 | pmflock_debug(dev, __func__, __LINE__); |
2471 | mutex_exit(&dvl->dvl_mtx); |
2472 | } |
2473 | |
2474 | device_lock_t |
2475 | device_getlock(device_t dev) |
2476 | { |
2477 | return &dev->dv_lock; |
2478 | } |
2479 | |
2480 | void * |
2481 | device_pmf_bus_private(device_t dev) |
2482 | { |
2483 | return dev->dv_bus_private; |
2484 | } |
2485 | |
2486 | bool |
2487 | device_pmf_bus_suspend(device_t dev, const pmf_qual_t *qual) |
2488 | { |
2489 | if ((dev->dv_flags & DVF_BUS_SUSPENDED) != 0) |
2490 | return true; |
2491 | if ((dev->dv_flags & DVF_CLASS_SUSPENDED) == 0 || |
2492 | (dev->dv_flags & DVF_DRIVER_SUSPENDED) == 0) |
2493 | return false; |
2494 | if (pmf_qual_depth(qual) <= DEVACT_LEVEL_BUS && |
2495 | dev->dv_bus_suspend != NULL && |
2496 | !(*dev->dv_bus_suspend)(dev, qual)) |
2497 | return false; |
2498 | |
2499 | dev->dv_flags |= DVF_BUS_SUSPENDED; |
2500 | return true; |
2501 | } |
2502 | |
2503 | bool |
2504 | device_pmf_bus_resume(device_t dev, const pmf_qual_t *qual) |
2505 | { |
2506 | if ((dev->dv_flags & DVF_BUS_SUSPENDED) == 0) |
2507 | return true; |
2508 | if (pmf_qual_depth(qual) <= DEVACT_LEVEL_BUS && |
2509 | dev->dv_bus_resume != NULL && |
2510 | !(*dev->dv_bus_resume)(dev, qual)) |
2511 | return false; |
2512 | |
2513 | dev->dv_flags &= ~DVF_BUS_SUSPENDED; |
2514 | return true; |
2515 | } |
2516 | |
2517 | bool |
2518 | device_pmf_bus_shutdown(device_t dev, int how) |
2519 | { |
2520 | |
2521 | if (*dev->dv_bus_shutdown != NULL && |
2522 | !(*dev->dv_bus_shutdown)(dev, how)) |
2523 | return false; |
2524 | return true; |
2525 | } |
2526 | |
2527 | void |
2528 | device_pmf_bus_register(device_t dev, void *priv, |
2529 | bool (*suspend)(device_t, const pmf_qual_t *), |
2530 | bool (*resume)(device_t, const pmf_qual_t *), |
2531 | bool (*shutdown)(device_t, int), void (*deregister)(device_t)) |
2532 | { |
2533 | dev->dv_bus_private = priv; |
2534 | dev->dv_bus_resume = resume; |
2535 | dev->dv_bus_suspend = suspend; |
2536 | dev->dv_bus_shutdown = shutdown; |
2537 | dev->dv_bus_deregister = deregister; |
2538 | } |
2539 | |
2540 | void |
2541 | device_pmf_bus_deregister(device_t dev) |
2542 | { |
2543 | if (dev->dv_bus_deregister == NULL) |
2544 | return; |
2545 | (*dev->dv_bus_deregister)(dev); |
2546 | dev->dv_bus_private = NULL; |
2547 | dev->dv_bus_suspend = NULL; |
2548 | dev->dv_bus_resume = NULL; |
2549 | dev->dv_bus_deregister = NULL; |
2550 | } |
2551 | |
2552 | void * |
2553 | device_pmf_class_private(device_t dev) |
2554 | { |
2555 | return dev->dv_class_private; |
2556 | } |
2557 | |
2558 | bool |
2559 | device_pmf_class_suspend(device_t dev, const pmf_qual_t *qual) |
2560 | { |
2561 | if ((dev->dv_flags & DVF_CLASS_SUSPENDED) != 0) |
2562 | return true; |
2563 | if (pmf_qual_depth(qual) <= DEVACT_LEVEL_CLASS && |
2564 | dev->dv_class_suspend != NULL && |
2565 | !(*dev->dv_class_suspend)(dev, qual)) |
2566 | return false; |
2567 | |
2568 | dev->dv_flags |= DVF_CLASS_SUSPENDED; |
2569 | return true; |
2570 | } |
2571 | |
2572 | bool |
2573 | device_pmf_class_resume(device_t dev, const pmf_qual_t *qual) |
2574 | { |
2575 | if ((dev->dv_flags & DVF_CLASS_SUSPENDED) == 0) |
2576 | return true; |
2577 | if ((dev->dv_flags & DVF_BUS_SUSPENDED) != 0 || |
2578 | (dev->dv_flags & DVF_DRIVER_SUSPENDED) != 0) |
2579 | return false; |
2580 | if (pmf_qual_depth(qual) <= DEVACT_LEVEL_CLASS && |
2581 | dev->dv_class_resume != NULL && |
2582 | !(*dev->dv_class_resume)(dev, qual)) |
2583 | return false; |
2584 | |
2585 | dev->dv_flags &= ~DVF_CLASS_SUSPENDED; |
2586 | return true; |
2587 | } |
2588 | |
2589 | void |
2590 | device_pmf_class_register(device_t dev, void *priv, |
2591 | bool (*suspend)(device_t, const pmf_qual_t *), |
2592 | bool (*resume)(device_t, const pmf_qual_t *), |
2593 | void (*deregister)(device_t)) |
2594 | { |
2595 | dev->dv_class_private = priv; |
2596 | dev->dv_class_suspend = suspend; |
2597 | dev->dv_class_resume = resume; |
2598 | dev->dv_class_deregister = deregister; |
2599 | } |
2600 | |
2601 | void |
2602 | device_pmf_class_deregister(device_t dev) |
2603 | { |
2604 | if (dev->dv_class_deregister == NULL) |
2605 | return; |
2606 | (*dev->dv_class_deregister)(dev); |
2607 | dev->dv_class_private = NULL; |
2608 | dev->dv_class_suspend = NULL; |
2609 | dev->dv_class_resume = NULL; |
2610 | dev->dv_class_deregister = NULL; |
2611 | } |
2612 | |
2613 | bool |
2614 | device_active(device_t dev, devactive_t type) |
2615 | { |
2616 | size_t i; |
2617 | |
2618 | if (dev->dv_activity_count == 0) |
2619 | return false; |
2620 | |
2621 | for (i = 0; i < dev->dv_activity_count; ++i) { |
2622 | if (dev->dv_activity_handlers[i] == NULL) |
2623 | break; |
2624 | (*dev->dv_activity_handlers[i])(dev, type); |
2625 | } |
2626 | |
2627 | return true; |
2628 | } |
2629 | |
2630 | bool |
2631 | device_active_register(device_t dev, void (*handler)(device_t, devactive_t)) |
2632 | { |
2633 | void (**new_handlers)(device_t, devactive_t); |
2634 | void (**old_handlers)(device_t, devactive_t); |
2635 | size_t i, old_size, new_size; |
2636 | int s; |
2637 | |
2638 | old_handlers = dev->dv_activity_handlers; |
2639 | old_size = dev->dv_activity_count; |
2640 | |
2641 | KASSERT(old_size == 0 || old_handlers != NULL); |
2642 | |
2643 | for (i = 0; i < old_size; ++i) { |
2644 | KASSERT(old_handlers[i] != handler); |
2645 | if (old_handlers[i] == NULL) { |
2646 | old_handlers[i] = handler; |
2647 | return true; |
2648 | } |
2649 | } |
2650 | |
2651 | new_size = old_size + 4; |
2652 | new_handlers = kmem_alloc(sizeof(void *[new_size]), KM_SLEEP); |
2653 | |
2654 | for (i = 0; i < old_size; ++i) |
2655 | new_handlers[i] = old_handlers[i]; |
2656 | new_handlers[old_size] = handler; |
2657 | for (i = old_size+1; i < new_size; ++i) |
2658 | new_handlers[i] = NULL; |
2659 | |
2660 | s = splhigh(); |
2661 | dev->dv_activity_count = new_size; |
2662 | dev->dv_activity_handlers = new_handlers; |
2663 | splx(s); |
2664 | |
2665 | if (old_size > 0) |
2666 | kmem_free(old_handlers, sizeof(void * [old_size])); |
2667 | |
2668 | return true; |
2669 | } |
2670 | |
2671 | void |
2672 | device_active_deregister(device_t dev, void (*handler)(device_t, devactive_t)) |
2673 | { |
2674 | void (**old_handlers)(device_t, devactive_t); |
2675 | size_t i, old_size; |
2676 | int s; |
2677 | |
2678 | old_handlers = dev->dv_activity_handlers; |
2679 | old_size = dev->dv_activity_count; |
2680 | |
2681 | for (i = 0; i < old_size; ++i) { |
2682 | if (old_handlers[i] == handler) |
2683 | break; |
2684 | if (old_handlers[i] == NULL) |
2685 | return; /* XXX panic? */ |
2686 | } |
2687 | |
2688 | if (i == old_size) |
2689 | return; /* XXX panic? */ |
2690 | |
2691 | for (; i < old_size - 1; ++i) { |
2692 | if ((old_handlers[i] = old_handlers[i + 1]) != NULL) |
2693 | continue; |
2694 | |
2695 | if (i == 0) { |
2696 | s = splhigh(); |
2697 | dev->dv_activity_count = 0; |
2698 | dev->dv_activity_handlers = NULL; |
2699 | splx(s); |
2700 | kmem_free(old_handlers, sizeof(void *[old_size])); |
2701 | } |
2702 | return; |
2703 | } |
2704 | old_handlers[i] = NULL; |
2705 | } |
2706 | |
2707 | /* Return true iff the device_t `dev' exists at generation `gen'. */ |
2708 | static bool |
2709 | device_exists_at(device_t dv, devgen_t gen) |
2710 | { |
2711 | return (dv->dv_del_gen == 0 || dv->dv_del_gen > gen) && |
2712 | dv->dv_add_gen <= gen; |
2713 | } |
2714 | |
2715 | static bool |
2716 | deviter_visits(const deviter_t *di, device_t dv) |
2717 | { |
2718 | return device_exists_at(dv, di->di_gen); |
2719 | } |
2720 | |
2721 | /* |
2722 | * Device Iteration |
2723 | * |
2724 | * deviter_t: a device iterator. Holds state for a "walk" visiting |
2725 | * each device_t's in the device tree. |
2726 | * |
2727 | * deviter_init(di, flags): initialize the device iterator `di' |
2728 | * to "walk" the device tree. deviter_next(di) will return |
2729 | * the first device_t in the device tree, or NULL if there are |
2730 | * no devices. |
2731 | * |
2732 | * `flags' is one or more of DEVITER_F_RW, indicating that the |
2733 | * caller intends to modify the device tree by calling |
2734 | * config_detach(9) on devices in the order that the iterator |
2735 | * returns them; DEVITER_F_ROOT_FIRST, asking for the devices |
2736 | * nearest the "root" of the device tree to be returned, first; |
2737 | * DEVITER_F_LEAVES_FIRST, asking for the devices furthest from |
2738 | * the root of the device tree, first; and DEVITER_F_SHUTDOWN, |
2739 | * indicating both that deviter_init() should not respect any |
2740 | * locks on the device tree, and that deviter_next(di) may run |
2741 | * in more than one LWP before the walk has finished. |
2742 | * |
2743 | * Only one DEVITER_F_RW iterator may be in the device tree at |
2744 | * once. |
2745 | * |
2746 | * DEVITER_F_SHUTDOWN implies DEVITER_F_RW. |
2747 | * |
2748 | * Results are undefined if the flags DEVITER_F_ROOT_FIRST and |
2749 | * DEVITER_F_LEAVES_FIRST are used in combination. |
2750 | * |
2751 | * deviter_first(di, flags): initialize the device iterator `di' |
2752 | * and return the first device_t in the device tree, or NULL |
2753 | * if there are no devices. The statement |
2754 | * |
2755 | * dv = deviter_first(di); |
2756 | * |
2757 | * is shorthand for |
2758 | * |
2759 | * deviter_init(di); |
2760 | * dv = deviter_next(di); |
2761 | * |
2762 | * deviter_next(di): return the next device_t in the device tree, |
2763 | * or NULL if there are no more devices. deviter_next(di) |
2764 | * is undefined if `di' was not initialized with deviter_init() or |
2765 | * deviter_first(). |
2766 | * |
2767 | * deviter_release(di): stops iteration (subsequent calls to |
2768 | * deviter_next() will return NULL), releases any locks and |
2769 | * resources held by the device iterator. |
2770 | * |
2771 | * Device iteration does not return device_t's in any particular |
2772 | * order. An iterator will never return the same device_t twice. |
2773 | * Device iteration is guaranteed to complete---i.e., if deviter_next(di) |
2774 | * is called repeatedly on the same `di', it will eventually return |
2775 | * NULL. It is ok to attach/detach devices during device iteration. |
2776 | */ |
2777 | void |
2778 | deviter_init(deviter_t *di, deviter_flags_t flags) |
2779 | { |
2780 | device_t dv; |
2781 | |
2782 | memset(di, 0, sizeof(*di)); |
2783 | |
2784 | mutex_enter(&alldevs_mtx); |
2785 | if ((flags & DEVITER_F_SHUTDOWN) != 0) |
2786 | flags |= DEVITER_F_RW; |
2787 | |
2788 | if ((flags & DEVITER_F_RW) != 0) |
2789 | alldevs_nwrite++; |
2790 | else |
2791 | alldevs_nread++; |
2792 | di->di_gen = alldevs_gen++; |
2793 | mutex_exit(&alldevs_mtx); |
2794 | |
2795 | di->di_flags = flags; |
2796 | |
2797 | switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { |
2798 | case DEVITER_F_LEAVES_FIRST: |
2799 | TAILQ_FOREACH(dv, &alldevs, dv_list) { |
2800 | if (!deviter_visits(di, dv)) |
2801 | continue; |
2802 | di->di_curdepth = MAX(di->di_curdepth, dv->dv_depth); |
2803 | } |
2804 | break; |
2805 | case DEVITER_F_ROOT_FIRST: |
2806 | TAILQ_FOREACH(dv, &alldevs, dv_list) { |
2807 | if (!deviter_visits(di, dv)) |
2808 | continue; |
2809 | di->di_maxdepth = MAX(di->di_maxdepth, dv->dv_depth); |
2810 | } |
2811 | break; |
2812 | default: |
2813 | break; |
2814 | } |
2815 | |
2816 | deviter_reinit(di); |
2817 | } |
2818 | |
2819 | static void |
2820 | deviter_reinit(deviter_t *di) |
2821 | { |
2822 | if ((di->di_flags & DEVITER_F_RW) != 0) |
2823 | di->di_prev = TAILQ_LAST(&alldevs, devicelist); |
2824 | else |
2825 | di->di_prev = TAILQ_FIRST(&alldevs); |
2826 | } |
2827 | |
2828 | device_t |
2829 | deviter_first(deviter_t *di, deviter_flags_t flags) |
2830 | { |
2831 | deviter_init(di, flags); |
2832 | return deviter_next(di); |
2833 | } |
2834 | |
2835 | static device_t |
2836 | deviter_next2(deviter_t *di) |
2837 | { |
2838 | device_t dv; |
2839 | |
2840 | dv = di->di_prev; |
2841 | |
2842 | if (dv == NULL) |
2843 | return NULL; |
2844 | |
2845 | if ((di->di_flags & DEVITER_F_RW) != 0) |
2846 | di->di_prev = TAILQ_PREV(dv, devicelist, dv_list); |
2847 | else |
2848 | di->di_prev = TAILQ_NEXT(dv, dv_list); |
2849 | |
2850 | return dv; |
2851 | } |
2852 | |
2853 | static device_t |
2854 | deviter_next1(deviter_t *di) |
2855 | { |
2856 | device_t dv; |
2857 | |
2858 | do { |
2859 | dv = deviter_next2(di); |
2860 | } while (dv != NULL && !deviter_visits(di, dv)); |
2861 | |
2862 | return dv; |
2863 | } |
2864 | |
2865 | device_t |
2866 | deviter_next(deviter_t *di) |
2867 | { |
2868 | device_t dv = NULL; |
2869 | |
2870 | switch (di->di_flags & (DEVITER_F_LEAVES_FIRST|DEVITER_F_ROOT_FIRST)) { |
2871 | case 0: |
2872 | return deviter_next1(di); |
2873 | case DEVITER_F_LEAVES_FIRST: |
2874 | while (di->di_curdepth >= 0) { |
2875 | if ((dv = deviter_next1(di)) == NULL) { |
2876 | di->di_curdepth--; |
2877 | deviter_reinit(di); |
2878 | } else if (dv->dv_depth == di->di_curdepth) |
2879 | break; |
2880 | } |
2881 | return dv; |
2882 | case DEVITER_F_ROOT_FIRST: |
2883 | while (di->di_curdepth <= di->di_maxdepth) { |
2884 | if ((dv = deviter_next1(di)) == NULL) { |
2885 | di->di_curdepth++; |
2886 | deviter_reinit(di); |
2887 | } else if (dv->dv_depth == di->di_curdepth) |
2888 | break; |
2889 | } |
2890 | return dv; |
2891 | default: |
2892 | return NULL; |
2893 | } |
2894 | } |
2895 | |
2896 | void |
2897 | deviter_release(deviter_t *di) |
2898 | { |
2899 | bool rw = (di->di_flags & DEVITER_F_RW) != 0; |
2900 | |
2901 | mutex_enter(&alldevs_mtx); |
2902 | if (rw) |
2903 | --alldevs_nwrite; |
2904 | else |
2905 | --alldevs_nread; |
2906 | /* XXX wake a garbage-collection thread */ |
2907 | mutex_exit(&alldevs_mtx); |
2908 | } |
2909 | |
2910 | const char * |
2911 | cfdata_ifattr(const struct cfdata *cf) |
2912 | { |
2913 | return cf->cf_pspec->cfp_iattr; |
2914 | } |
2915 | |
2916 | bool |
2917 | ifattr_match(const char *snull, const char *t) |
2918 | { |
2919 | return (snull == NULL) || strcmp(snull, t) == 0; |
2920 | } |
2921 | |
2922 | void |
2923 | null_childdetached(device_t self, device_t child) |
2924 | { |
2925 | /* do nothing */ |
2926 | } |
2927 | |
2928 | static void |
2929 | sysctl_detach_setup(struct sysctllog **clog) |
2930 | { |
2931 | |
2932 | sysctl_createv(clog, 0, NULL, NULL, |
2933 | CTLFLAG_PERMANENT | CTLFLAG_READWRITE, |
2934 | CTLTYPE_BOOL, "detachall" , |
2935 | SYSCTL_DESCR("Detach all devices at shutdown" ), |
2936 | NULL, 0, &detachall, 0, |
2937 | CTL_KERN, CTL_CREATE, CTL_EOL); |
2938 | } |
2939 | |