subr_autoconf.c revision 1.29 1 /* $NetBSD: subr_autoconf.c,v 1.29 1998/06/09 18:46:12 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1992, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This software was developed by the Computer Systems Engineering group
8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9 * contributed to Berkeley.
10 *
11 * All advertising materials mentioning features or use of this software
12 * must display the following acknowledgement:
13 * This product includes software developed by the University of
14 * California, Lawrence Berkeley Laboratories.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL)
45 *
46 * @(#)subr_autoconf.c 8.3 (Berkeley) 5/17/94
47 */
48
49 #include <sys/param.h>
50 #include <sys/device.h>
51 #include <sys/malloc.h>
52 #include <sys/systm.h>
53 #include <machine/limits.h>
54
55 /*
56 * Autoconfiguration subroutines.
57 */
58
59 /*
60 * ioconf.c exports exactly two names: cfdata and cfroots. All system
61 * devices and drivers are found via these tables.
62 */
63 extern struct cfdata cfdata[];
64 extern short cfroots[];
65
66 #define ROOT ((struct device *)NULL)
67
68 #ifdef __BROKEN_INDIRECT_CONFIG
69 struct device *config_make_softc __P((struct device *, struct cfdata *));
70 #endif
71
72 struct matchinfo {
73 cfmatch_t fn;
74 struct device *parent;
75 void *aux;
76 #ifdef __BROKEN_INDIRECT_CONFIG
77 void *match;
78 int indirect;
79 #else
80 struct cfdata *match;
81 #endif
82 int pri;
83 };
84
85 static char *number __P((char *, int));
86 static void mapply __P((struct matchinfo *, struct cfdata *));
87
88 struct deferred_config {
89 TAILQ_ENTRY(deferred_config) dc_queue;
90 struct device *dc_dev;
91 void (*dc_func) __P((struct device *));
92 };
93
94 TAILQ_HEAD(, deferred_config) deferred_config_queue;
95
96 static void config_process_deferred_children __P((struct device *));
97
98 struct devicelist alldevs; /* list of all devices */
99 struct evcntlist allevents; /* list of all event counters */
100
101 /*
102 * Initialize autoconfiguration data structures.
103 */
104 void
105 config_init()
106 {
107
108 TAILQ_INIT(&deferred_config_queue);
109 TAILQ_INIT(&alldevs);
110 TAILQ_INIT(&allevents);
111 }
112
113 /*
114 * Apply the matching function and choose the best. This is used
115 * a few times and we want to keep the code small.
116 */
117 static void
118 mapply(m, cf)
119 register struct matchinfo *m;
120 register struct cfdata *cf;
121 {
122 register int pri;
123 #ifdef __BROKEN_INDIRECT_CONFIG
124 void *match;
125
126 if (m->indirect)
127 match = config_make_softc(m->parent, cf);
128 else
129 match = cf;
130 #endif
131
132 if (m->fn != NULL)
133 #ifdef __BROKEN_INDIRECT_CONFIG
134 pri = (*m->fn)(m->parent, match, m->aux);
135 #else
136 pri = (*m->fn)(m->parent, cf, m->aux);
137 #endif
138 else {
139 if (cf->cf_attach->ca_match == NULL) {
140 panic("mapply: no match function for '%s' device\n",
141 cf->cf_driver->cd_name);
142 }
143 #ifdef __BROKEN_INDIRECT_CONFIG
144 pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux);
145 #else
146 pri = (*cf->cf_attach->ca_match)(m->parent, cf, m->aux);
147 #endif
148 }
149 if (pri > m->pri) {
150 #ifdef __BROKEN_INDIRECT_CONFIG
151 if (m->indirect && m->match)
152 free(m->match, M_DEVBUF);
153 m->match = match;
154 #else
155 m->match = cf;
156 #endif
157 m->pri = pri;
158 #ifdef __BROKEN_INDIRECT_CONFIG
159 } else {
160 if (m->indirect)
161 free(match, M_DEVBUF);
162 #endif
163 }
164 }
165
166 /*
167 * Iterate over all potential children of some device, calling the given
168 * function (default being the child's match function) for each one.
169 * Nonzero returns are matches; the highest value returned is considered
170 * the best match. Return the `found child' if we got a match, or NULL
171 * otherwise. The `aux' pointer is simply passed on through.
172 *
173 * Note that this function is designed so that it can be used to apply
174 * an arbitrary function to all potential children (its return value
175 * can be ignored).
176 */
177 #ifdef __BROKEN_INDIRECT_CONFIG
178 void *
179 #else
180 struct cfdata *
181 #endif
182 config_search(fn, parent, aux)
183 cfmatch_t fn;
184 register struct device *parent;
185 void *aux;
186 {
187 register struct cfdata *cf;
188 register short *p;
189 struct matchinfo m;
190
191 m.fn = fn;
192 m.parent = parent;
193 m.aux = aux;
194 m.match = NULL;
195 #ifdef __BROKEN_INDIRECT_CONFIG
196 m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
197 #endif
198 m.pri = 0;
199 for (cf = cfdata; cf->cf_driver; cf++) {
200 /*
201 * Skip cf if no longer eligible, otherwise scan through
202 * parents for one matching `parent', and try match function.
203 */
204 if (cf->cf_fstate == FSTATE_FOUND)
205 continue;
206 for (p = cf->cf_parents; *p >= 0; p++)
207 if (parent->dv_cfdata == &cfdata[*p])
208 mapply(&m, cf);
209 }
210 return (m.match);
211 }
212
213 #ifdef __BROKEN_INDIRECT_CONFIG
214 /*
215 * Iterate over all potential children of some device, calling the given
216 * function for each one.
217 *
218 * Note that this function is designed so that it can be used to apply
219 * an arbitrary function to all potential children (its return value
220 * can be ignored).
221 */
222 void
223 config_scan(fn, parent)
224 cfscan_t fn;
225 register struct device *parent;
226 {
227 register struct cfdata *cf;
228 register short *p;
229 void *match;
230 int indirect;
231
232 indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
233 for (cf = cfdata; cf->cf_driver; cf++) {
234 /*
235 * Skip cf if no longer eligible, otherwise scan through
236 * parents for one matching `parent', and try match function.
237 */
238 if (cf->cf_fstate == FSTATE_FOUND)
239 continue;
240 for (p = cf->cf_parents; *p >= 0; p++)
241 if (parent->dv_cfdata == &cfdata[*p]) {
242 if (indirect)
243 match = config_make_softc(parent, cf);
244 else
245 match = cf;
246 (*fn)(parent, match);
247 }
248 }
249 }
250 #endif /* __BROKEN_INDIRECT_CONFIG */
251
252 /*
253 * Find the given root device.
254 * This is much like config_search, but there is no parent.
255 */
256 #ifdef __BROKEN_INDIRECT_CONFIG
257 void *
258 #else
259 struct cfdata *
260 #endif
261 config_rootsearch(fn, rootname, aux)
262 register cfmatch_t fn;
263 register char *rootname;
264 register void *aux;
265 {
266 register struct cfdata *cf;
267 register short *p;
268 struct matchinfo m;
269
270 m.fn = fn;
271 m.parent = ROOT;
272 m.aux = aux;
273 m.match = NULL;
274 #ifdef __BROKEN_INDIRECT_CONFIG
275 m.indirect = 0;
276 #endif
277 m.pri = 0;
278 /*
279 * Look at root entries for matching name. We do not bother
280 * with found-state here since only one root should ever be
281 * searched (and it must be done first).
282 */
283 for (p = cfroots; *p >= 0; p++) {
284 cf = &cfdata[*p];
285 if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
286 mapply(&m, cf);
287 }
288 return (m.match);
289 }
290
291 static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
292
293 /*
294 * The given `aux' argument describes a device that has been found
295 * on the given parent, but not necessarily configured. Locate the
296 * configuration data for that device (using the submatch function
297 * provided, or using candidates' cd_match configuration driver
298 * functions) and attach it, and return true. If the device was
299 * not configured, call the given `print' function and return 0.
300 */
301 struct device *
302 config_found_sm(parent, aux, print, submatch)
303 struct device *parent;
304 void *aux;
305 cfprint_t print;
306 cfmatch_t submatch;
307 {
308 #ifdef __BROKEN_INDIRECT_CONFIG
309 void *match;
310
311 if ((match = config_search(submatch, parent, aux)) != NULL)
312 return (config_attach(parent, match, aux, print));
313 #else
314 struct cfdata *cf;
315
316 if ((cf = config_search(submatch, parent, aux)) != NULL)
317 return (config_attach(parent, cf, aux, print));
318 #endif
319 if (print)
320 printf(msgs[(*print)(aux, parent->dv_xname)]);
321 return (NULL);
322 }
323
324 /*
325 * As above, but for root devices.
326 */
327 struct device *
328 config_rootfound(rootname, aux)
329 char *rootname;
330 void *aux;
331 {
332 #ifdef __BROKEN_INDIRECT_CONFIG
333 void *match;
334
335 if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
336 return (config_attach(ROOT, match, aux, (cfprint_t)NULL));
337 #else
338 struct cfdata *cf;
339
340 if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
341 return (config_attach(ROOT, cf, aux, (cfprint_t)NULL));
342 #endif
343 printf("root device %s not configured\n", rootname);
344 return (NULL);
345 }
346
347 /* just like sprintf(buf, "%d") except that it works from the end */
348 static char *
349 number(ep, n)
350 register char *ep;
351 register int n;
352 {
353
354 *--ep = 0;
355 while (n >= 10) {
356 *--ep = (n % 10) + '0';
357 n /= 10;
358 }
359 *--ep = n + '0';
360 return (ep);
361 }
362
363 /*
364 * Attach a found device. Allocates memory for device variables.
365 */
366 #ifdef __BROKEN_INDIRECT_CONFIG
367 struct device *
368 config_attach(parent, match, aux, print)
369 register struct device *parent;
370 void *match;
371 register void *aux;
372 cfprint_t print;
373 {
374 register struct cfdata *cf;
375 register struct device *dev;
376 register struct cfdriver *cd;
377 register struct cfattach *ca;
378
379 if (parent && parent->dv_cfdata->cf_driver->cd_indirect) {
380 dev = match;
381 cf = dev->dv_cfdata;
382 } else {
383 cf = match;
384 dev = config_make_softc(parent, cf);
385 }
386
387 cd = cf->cf_driver;
388 ca = cf->cf_attach;
389 cd->cd_devs[cf->cf_unit] = dev;
390
391 if (cf->cf_fstate == FSTATE_STAR)
392 cf->cf_unit++;
393 else {
394 KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
395 cf->cf_fstate = FSTATE_FOUND;
396 }
397
398 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
399
400 if (parent == ROOT)
401 printf("%s (root)", dev->dv_xname);
402 else {
403 printf("%s at %s", dev->dv_xname, parent->dv_xname);
404 if (print)
405 (void) (*print)(aux, (char *)0);
406 }
407
408 /*
409 * Before attaching, clobber any unfound devices that are
410 * otherwise identical, or bump the unit number on all starred
411 * cfdata for this device.
412 */
413 for (cf = cfdata; cf->cf_driver; cf++)
414 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) {
415 if (cf->cf_fstate == FSTATE_NOTFOUND)
416 cf->cf_fstate = FSTATE_FOUND;
417 if (cf->cf_fstate == FSTATE_STAR)
418 cf->cf_unit++;
419 }
420 #if defined(__alpha__) || defined(hp300) || defined(__i386__)
421 device_register(dev, aux);
422 #endif
423 (*ca->ca_attach)(parent, dev, aux);
424 config_process_deferred_children(dev);
425 return (dev);
426 }
427
428 struct device *
429 config_make_softc(parent, cf)
430 struct device *parent;
431 struct cfdata *cf;
432 {
433 register struct device *dev;
434 register struct cfdriver *cd;
435 register struct cfattach *ca;
436 register size_t lname, lunit;
437 register char *xunit;
438 char num[10];
439
440 cd = cf->cf_driver;
441 ca = cf->cf_attach;
442 if (ca->ca_devsize < sizeof(struct device))
443 panic("config_make_softc");
444
445 /* compute length of name and decimal expansion of unit number */
446 lname = strlen(cd->cd_name);
447 xunit = number(&num[sizeof num], cf->cf_unit);
448 lunit = &num[sizeof num] - xunit;
449 if (lname + lunit >= sizeof(dev->dv_xname))
450 panic("config_attach: device name too long");
451
452 /* get memory for all device vars */
453 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
454 if (!dev)
455 panic("config_attach: memory allocation for device softc failed");
456 bzero(dev, ca->ca_devsize);
457 dev->dv_class = cd->cd_class;
458 dev->dv_cfdata = cf;
459 dev->dv_unit = cf->cf_unit;
460 bcopy(cd->cd_name, dev->dv_xname, lname);
461 bcopy(xunit, dev->dv_xname + lname, lunit);
462 dev->dv_parent = parent;
463
464 /* put this device in the devices array */
465 if (dev->dv_unit >= cd->cd_ndevs) {
466 /*
467 * Need to expand the array.
468 */
469 int old = cd->cd_ndevs, new;
470 void **nsp;
471
472 if (old == 0)
473 new = MINALLOCSIZE / sizeof(void *);
474 else
475 new = old * 2;
476 while (new <= dev->dv_unit)
477 new *= 2;
478 cd->cd_ndevs = new;
479 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
480 if (nsp == 0)
481 panic("config_attach: %sing dev array",
482 old != 0 ? "expand" : "creat");
483 bzero(nsp + old, (new - old) * sizeof(void *));
484 if (old != 0) {
485 bcopy(cd->cd_devs, nsp, old * sizeof(void *));
486 free(cd->cd_devs, M_DEVBUF);
487 }
488 cd->cd_devs = nsp;
489 }
490 if (cd->cd_devs[dev->dv_unit])
491 panic("config_attach: duplicate %s", dev->dv_xname);
492
493 return (dev);
494 }
495 #else /* __BROKEN_INDIRECT_CONFIG */
496 struct device *
497 config_attach(parent, cf, aux, print)
498 register struct device *parent;
499 register struct cfdata *cf;
500 register void *aux;
501 cfprint_t print;
502 {
503 register struct device *dev;
504 register struct cfdriver *cd;
505 register struct cfattach *ca;
506 register size_t lname, lunit;
507 register char *xunit;
508 int myunit;
509 char num[10];
510
511 cd = cf->cf_driver;
512 ca = cf->cf_attach;
513 if (ca->ca_devsize < sizeof(struct device))
514 panic("config_attach");
515 myunit = cf->cf_unit;
516 if (cf->cf_fstate == FSTATE_STAR)
517 cf->cf_unit++;
518 else {
519 KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
520 cf->cf_fstate = FSTATE_FOUND;
521 }
522
523 /* compute length of name and decimal expansion of unit number */
524 lname = strlen(cd->cd_name);
525 xunit = number(&num[sizeof num], myunit);
526 lunit = &num[sizeof num] - xunit;
527 if (lname + lunit >= sizeof(dev->dv_xname))
528 panic("config_attach: device name too long");
529
530 /* get memory for all device vars */
531 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
532 if (!dev)
533 panic("config_attach: memory allocation for device softc failed");
534 bzero(dev, ca->ca_devsize);
535 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); /* link up */
536 dev->dv_class = cd->cd_class;
537 dev->dv_cfdata = cf;
538 dev->dv_unit = myunit;
539 bcopy(cd->cd_name, dev->dv_xname, lname);
540 bcopy(xunit, dev->dv_xname + lname, lunit);
541 dev->dv_parent = parent;
542
543 if (parent == ROOT)
544 printf("%s (root)", dev->dv_xname);
545 else {
546 printf("%s at %s", dev->dv_xname, parent->dv_xname);
547 if (print)
548 (void) (*print)(aux, (char *)0);
549 }
550
551 /* put this device in the devices array */
552 if (dev->dv_unit >= cd->cd_ndevs) {
553 /*
554 * Need to expand the array.
555 */
556 int old = cd->cd_ndevs, new;
557 void **nsp;
558
559 if (old == 0)
560 new = MINALLOCSIZE / sizeof(void *);
561 else
562 new = old * 2;
563 while (new <= dev->dv_unit)
564 new *= 2;
565 cd->cd_ndevs = new;
566 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
567 if (nsp == 0)
568 panic("config_attach: %sing dev array",
569 old != 0 ? "expand" : "creat");
570 bzero(nsp + old, (new - old) * sizeof(void *));
571 if (old != 0) {
572 bcopy(cd->cd_devs, nsp, old * sizeof(void *));
573 free(cd->cd_devs, M_DEVBUF);
574 }
575 cd->cd_devs = nsp;
576 }
577 if (cd->cd_devs[dev->dv_unit])
578 panic("config_attach: duplicate %s", dev->dv_xname);
579 cd->cd_devs[dev->dv_unit] = dev;
580
581 /*
582 * Before attaching, clobber any unfound devices that are
583 * otherwise identical, or bump the unit number on all starred
584 * cfdata for this device.
585 */
586 for (cf = cfdata; cf->cf_driver; cf++)
587 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) {
588 if (cf->cf_fstate == FSTATE_NOTFOUND)
589 cf->cf_fstate = FSTATE_FOUND;
590 if (cf->cf_fstate == FSTATE_STAR)
591 cf->cf_unit++;
592 }
593 #if defined(__alpha__) || defined(hp300) || defined(__i386__)
594 device_register(dev, aux);
595 #endif
596 (*ca->ca_attach)(parent, dev, aux);
597 config_process_deferred_children(dev);
598 return (dev);
599 }
600 #endif /* __BROKEN_INDIRECT_CONFIG */
601
602 /*
603 * Defer the configuration of the specified device until all
604 * of its parent's devices have been attached.
605 */
606 void
607 config_defer(dev, func)
608 struct device *dev;
609 void (*func) __P((struct device *));
610 {
611 struct deferred_config *dc;
612
613 if (dev->dv_parent == NULL)
614 panic("config_defer: can't defer config of a root device");
615
616 #ifdef DIAGNOSTIC
617 for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL;
618 dc = TAILQ_NEXT(dc, dc_queue)) {
619 if (dc->dc_dev == dev)
620 panic("config_defer: deferred twice");
621 }
622 #endif
623
624 if ((dc = malloc(sizeof(*dc), M_DEVBUF, M_NOWAIT)) == NULL)
625 panic("config_defer: can't allocate defer structure");
626
627 dc->dc_dev = dev;
628 dc->dc_func = func;
629 TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue);
630 }
631
632 /*
633 * Process the deferred configuration queue for a device.
634 */
635 static void
636 config_process_deferred_children(parent)
637 struct device *parent;
638 {
639 struct deferred_config *dc, *ndc;
640
641 for (dc = TAILQ_FIRST(&deferred_config_queue);
642 dc != NULL; dc = ndc) {
643 ndc = TAILQ_NEXT(dc, dc_queue);
644 if (dc->dc_dev->dv_parent == parent) {
645 TAILQ_REMOVE(&deferred_config_queue, dc, dc_queue);
646 (*dc->dc_func)(dc->dc_dev);
647 free(dc, M_DEVBUF);
648 }
649 }
650 }
651
652 /*
653 * Attach an event. These must come from initially-zero space (see
654 * commented-out assignments below), but that occurs naturally for
655 * device instance variables.
656 */
657 void
658 evcnt_attach(dev, name, ev)
659 struct device *dev;
660 const char *name;
661 struct evcnt *ev;
662 {
663
664 #ifdef DIAGNOSTIC
665 if (strlen(name) >= sizeof(ev->ev_name))
666 panic("evcnt_attach");
667 #endif
668 /* ev->ev_next = NULL; */
669 ev->ev_dev = dev;
670 /* ev->ev_count = 0; */
671 strcpy(ev->ev_name, name);
672 TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
673 }
674