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