xref: /netbsd-src/sys/kern/subr_autoconf.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: subr_autoconf.c,v 1.27 1997/09/20 14:16:43 drochner 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.1 (Berkeley) 6/10/93
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 devicelist alldevs;		/* list of all devices */
89 struct evcntlist allevents;		/* list of all event counters */
90 
91 /*
92  * Initialize autoconfiguration data structures.
93  */
94 void
95 config_init()
96 {
97 
98 	TAILQ_INIT(&alldevs);
99 	TAILQ_INIT(&allevents);
100 }
101 
102 /*
103  * Apply the matching function and choose the best.  This is used
104  * a few times and we want to keep the code small.
105  */
106 static void
107 mapply(m, cf)
108 	register struct matchinfo *m;
109 	register struct cfdata *cf;
110 {
111 	register int pri;
112 #ifdef __BROKEN_INDIRECT_CONFIG
113 	void *match;
114 
115 	if (m->indirect)
116 		match = config_make_softc(m->parent, cf);
117 	else
118 		match = cf;
119 #endif
120 
121 	if (m->fn != NULL)
122 #ifdef __BROKEN_INDIRECT_CONFIG
123 		pri = (*m->fn)(m->parent, match, m->aux);
124 #else
125 		pri = (*m->fn)(m->parent, cf, m->aux);
126 #endif
127 	else {
128 	        if (cf->cf_attach->ca_match == NULL) {
129 			panic("mapply: no match function for '%s' device\n",
130 			    cf->cf_driver->cd_name);
131 		}
132 #ifdef __BROKEN_INDIRECT_CONFIG
133 		pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux);
134 #else
135 		pri = (*cf->cf_attach->ca_match)(m->parent, cf, m->aux);
136 #endif
137 	}
138 	if (pri > m->pri) {
139 #ifdef __BROKEN_INDIRECT_CONFIG
140 		if (m->indirect && m->match)
141 			free(m->match, M_DEVBUF);
142 		m->match = match;
143 #else
144 		m->match = cf;
145 #endif
146 		m->pri = pri;
147 #ifdef __BROKEN_INDIRECT_CONFIG
148 	} else {
149 		if (m->indirect)
150 			free(match, M_DEVBUF);
151 #endif
152 	}
153 }
154 
155 /*
156  * Iterate over all potential children of some device, calling the given
157  * function (default being the child's match function) for each one.
158  * Nonzero returns are matches; the highest value returned is considered
159  * the best match.  Return the `found child' if we got a match, or NULL
160  * otherwise.  The `aux' pointer is simply passed on through.
161  *
162  * Note that this function is designed so that it can be used to apply
163  * an arbitrary function to all potential children (its return value
164  * can be ignored).
165  */
166 #ifdef __BROKEN_INDIRECT_CONFIG
167 void *
168 #else
169 struct cfdata *
170 #endif
171 config_search(fn, parent, aux)
172 	cfmatch_t fn;
173 	register struct device *parent;
174 	void *aux;
175 {
176 	register struct cfdata *cf;
177 	register short *p;
178 	struct matchinfo m;
179 
180 	m.fn = fn;
181 	m.parent = parent;
182 	m.aux = aux;
183 	m.match = NULL;
184 #ifdef __BROKEN_INDIRECT_CONFIG
185 	m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
186 #endif
187 	m.pri = 0;
188 	for (cf = cfdata; cf->cf_driver; cf++) {
189 		/*
190 		 * Skip cf if no longer eligible, otherwise scan through
191 		 * parents for one matching `parent', and try match function.
192 		 */
193 		if (cf->cf_fstate == FSTATE_FOUND)
194 			continue;
195 		for (p = cf->cf_parents; *p >= 0; p++)
196 			if (parent->dv_cfdata == &cfdata[*p])
197 				mapply(&m, cf);
198 	}
199 	return (m.match);
200 }
201 
202 #ifdef __BROKEN_INDIRECT_CONFIG
203 /*
204  * Iterate over all potential children of some device, calling the given
205  * function for each one.
206  *
207  * Note that this function is designed so that it can be used to apply
208  * an arbitrary function to all potential children (its return value
209  * can be ignored).
210  */
211 void
212 config_scan(fn, parent)
213 	cfscan_t fn;
214 	register struct device *parent;
215 {
216 	register struct cfdata *cf;
217 	register short *p;
218 	void *match;
219 	int indirect;
220 
221 	indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
222 	for (cf = cfdata; cf->cf_driver; cf++) {
223 		/*
224 		 * Skip cf if no longer eligible, otherwise scan through
225 		 * parents for one matching `parent', and try match function.
226 		 */
227 		if (cf->cf_fstate == FSTATE_FOUND)
228 			continue;
229 		for (p = cf->cf_parents; *p >= 0; p++)
230 			if (parent->dv_cfdata == &cfdata[*p]) {
231 				if (indirect)
232 					match = config_make_softc(parent, cf);
233 				else
234 					match = cf;
235 				(*fn)(parent, match);
236 			}
237 	}
238 }
239 #endif /* __BROKEN_INDIRECT_CONFIG */
240 
241 /*
242  * Find the given root device.
243  * This is much like config_search, but there is no parent.
244  */
245 #ifdef __BROKEN_INDIRECT_CONFIG
246 void *
247 #else
248 struct cfdata *
249 #endif
250 config_rootsearch(fn, rootname, aux)
251 	register cfmatch_t fn;
252 	register char *rootname;
253 	register void *aux;
254 {
255 	register struct cfdata *cf;
256 	register short *p;
257 	struct matchinfo m;
258 
259 	m.fn = fn;
260 	m.parent = ROOT;
261 	m.aux = aux;
262 	m.match = NULL;
263 #ifdef __BROKEN_INDIRECT_CONFIG
264 	m.indirect = 0;
265 #endif
266 	m.pri = 0;
267 	/*
268 	 * Look at root entries for matching name.  We do not bother
269 	 * with found-state here since only one root should ever be
270 	 * searched (and it must be done first).
271 	 */
272 	for (p = cfroots; *p >= 0; p++) {
273 		cf = &cfdata[*p];
274 		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
275 			mapply(&m, cf);
276 	}
277 	return (m.match);
278 }
279 
280 static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
281 
282 /*
283  * The given `aux' argument describes a device that has been found
284  * on the given parent, but not necessarily configured.  Locate the
285  * configuration data for that device (using the submatch function
286  * provided, or using candidates' cd_match configuration driver
287  * functions) and attach it, and return true.  If the device was
288  * not configured, call the given `print' function and return 0.
289  */
290 struct device *
291 config_found_sm(parent, aux, print, submatch)
292 	struct device *parent;
293 	void *aux;
294 	cfprint_t print;
295 	cfmatch_t submatch;
296 {
297 #ifdef __BROKEN_INDIRECT_CONFIG
298 	void *match;
299 
300 	if ((match = config_search(submatch, parent, aux)) != NULL)
301 		return (config_attach(parent, match, aux, print));
302 #else
303 	struct cfdata *cf;
304 
305 	if ((cf = config_search(submatch, parent, aux)) != NULL)
306 		return (config_attach(parent, cf, aux, print));
307 #endif
308 	if (print)
309 		printf(msgs[(*print)(aux, parent->dv_xname)]);
310 	return (NULL);
311 }
312 
313 /*
314  * As above, but for root devices.
315  */
316 struct device *
317 config_rootfound(rootname, aux)
318 	char *rootname;
319 	void *aux;
320 {
321 #ifdef __BROKEN_INDIRECT_CONFIG
322 	void *match;
323 
324 	if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
325 		return (config_attach(ROOT, match, aux, (cfprint_t)NULL));
326 #else
327 	struct cfdata *cf;
328 
329 	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
330 		return (config_attach(ROOT, cf, aux, (cfprint_t)NULL));
331 #endif
332 	printf("root device %s not configured\n", rootname);
333 	return (NULL);
334 }
335 
336 /* just like sprintf(buf, "%d") except that it works from the end */
337 static char *
338 number(ep, n)
339 	register char *ep;
340 	register int n;
341 {
342 
343 	*--ep = 0;
344 	while (n >= 10) {
345 		*--ep = (n % 10) + '0';
346 		n /= 10;
347 	}
348 	*--ep = n + '0';
349 	return (ep);
350 }
351 
352 /*
353  * Attach a found device.  Allocates memory for device variables.
354  */
355 #ifdef __BROKEN_INDIRECT_CONFIG
356 struct device *
357 config_attach(parent, match, aux, print)
358 	register struct device *parent;
359 	void *match;
360 	register void *aux;
361 	cfprint_t print;
362 {
363 	register struct cfdata *cf;
364 	register struct device *dev;
365 	register struct cfdriver *cd;
366 	register struct cfattach *ca;
367 
368 	if (parent && parent->dv_cfdata->cf_driver->cd_indirect) {
369 		dev = match;
370 		cf = dev->dv_cfdata;
371 	} else {
372 		cf = match;
373 		dev = config_make_softc(parent, cf);
374 	}
375 
376 	cd = cf->cf_driver;
377 	ca = cf->cf_attach;
378 	cd->cd_devs[cf->cf_unit] = dev;
379 
380 	if (cf->cf_fstate == FSTATE_STAR)
381 		cf->cf_unit++;
382 	else {
383 		KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
384 		cf->cf_fstate = FSTATE_FOUND;
385 	}
386 
387 	TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
388 
389 	if (parent == ROOT)
390 		printf("%s (root)", dev->dv_xname);
391 	else {
392 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
393 		if (print)
394 			(void) (*print)(aux, (char *)0);
395 	}
396 
397 	/*
398 	 * Before attaching, clobber any unfound devices that are
399 	 * otherwise identical, or bump the unit number on all starred
400 	 * cfdata for this device.
401 	 */
402 	for (cf = cfdata; cf->cf_driver; cf++)
403 		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) {
404 			if (cf->cf_fstate == FSTATE_NOTFOUND)
405 				cf->cf_fstate = FSTATE_FOUND;
406 			if (cf->cf_fstate == FSTATE_STAR)
407 				cf->cf_unit++;
408 		}
409 #if defined(__alpha__) || defined(hp300) || defined(__i386__)
410 	device_register(dev, aux);
411 #endif
412 	(*ca->ca_attach)(parent, dev, aux);
413 	return (dev);
414 }
415 
416 struct device *
417 config_make_softc(parent, cf)
418 	struct device *parent;
419 	struct cfdata *cf;
420 {
421 	register struct device *dev;
422 	register struct cfdriver *cd;
423 	register struct cfattach *ca;
424 	register size_t lname, lunit;
425 	register char *xunit;
426 	char num[10];
427 
428 	cd = cf->cf_driver;
429 	ca = cf->cf_attach;
430 	if (ca->ca_devsize < sizeof(struct device))
431 		panic("config_make_softc");
432 
433 	/* compute length of name and decimal expansion of unit number */
434 	lname = strlen(cd->cd_name);
435 	xunit = number(&num[sizeof num], cf->cf_unit);
436 	lunit = &num[sizeof num] - xunit;
437 	if (lname + lunit >= sizeof(dev->dv_xname))
438 		panic("config_attach: device name too long");
439 
440 	/* get memory for all device vars */
441 	dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
442 	if (!dev)
443 	    panic("config_attach: memory allocation for device softc failed");
444 	bzero(dev, ca->ca_devsize);
445 	dev->dv_class = cd->cd_class;
446 	dev->dv_cfdata = cf;
447 	dev->dv_unit = cf->cf_unit;
448 	bcopy(cd->cd_name, dev->dv_xname, lname);
449 	bcopy(xunit, dev->dv_xname + lname, lunit);
450 	dev->dv_parent = parent;
451 
452 	/* put this device in the devices array */
453 	if (dev->dv_unit >= cd->cd_ndevs) {
454 		/*
455 		 * Need to expand the array.
456 		 */
457 		int old = cd->cd_ndevs, new;
458 		void **nsp;
459 
460 		if (old == 0)
461 			new = MINALLOCSIZE / sizeof(void *);
462 		else
463 			new = old * 2;
464 		while (new <= dev->dv_unit)
465 			new *= 2;
466 		cd->cd_ndevs = new;
467 		nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
468 		if (nsp == 0)
469 			panic("config_attach: %sing dev array",
470 			    old != 0 ? "expand" : "creat");
471 		bzero(nsp + old, (new - old) * sizeof(void *));
472 		if (old != 0) {
473 			bcopy(cd->cd_devs, nsp, old * sizeof(void *));
474 			free(cd->cd_devs, M_DEVBUF);
475 		}
476 		cd->cd_devs = nsp;
477 	}
478 	if (cd->cd_devs[dev->dv_unit])
479 		panic("config_attach: duplicate %s", dev->dv_xname);
480 
481 	return (dev);
482 }
483 #else /* __BROKEN_INDIRECT_CONFIG */
484 struct device *
485 config_attach(parent, cf, aux, print)
486 	register struct device *parent;
487 	register struct cfdata *cf;
488 	register void *aux;
489 	cfprint_t print;
490 {
491 	register struct device *dev;
492 	register struct cfdriver *cd;
493 	register struct cfattach *ca;
494 	register size_t lname, lunit;
495 	register char *xunit;
496 	int myunit;
497 	char num[10];
498 
499 	cd = cf->cf_driver;
500 	ca = cf->cf_attach;
501 	if (ca->ca_devsize < sizeof(struct device))
502 		panic("config_attach");
503 	myunit = cf->cf_unit;
504 	if (cf->cf_fstate == FSTATE_STAR)
505 		cf->cf_unit++;
506 	else {
507 		KASSERT(cf->cf_fstate == FSTATE_NOTFOUND);
508 		cf->cf_fstate = FSTATE_FOUND;
509 	}
510 
511 	/* compute length of name and decimal expansion of unit number */
512 	lname = strlen(cd->cd_name);
513 	xunit = number(&num[sizeof num], myunit);
514 	lunit = &num[sizeof num] - xunit;
515 	if (lname + lunit >= sizeof(dev->dv_xname))
516 		panic("config_attach: device name too long");
517 
518 	/* get memory for all device vars */
519 	dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
520 	if (!dev)
521 	    panic("config_attach: memory allocation for device softc failed");
522 	bzero(dev, ca->ca_devsize);
523 	TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);	/* link up */
524 	dev->dv_class = cd->cd_class;
525 	dev->dv_cfdata = cf;
526 	dev->dv_unit = myunit;
527 	bcopy(cd->cd_name, dev->dv_xname, lname);
528 	bcopy(xunit, dev->dv_xname + lname, lunit);
529 	dev->dv_parent = parent;
530 	if (parent == ROOT)
531 		printf("%s (root)", dev->dv_xname);
532 	else {
533 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
534 		if (print)
535 			(void) (*print)(aux, (char *)0);
536 	}
537 
538 	/* put this device in the devices array */
539 	if (dev->dv_unit >= cd->cd_ndevs) {
540 		/*
541 		 * Need to expand the array.
542 		 */
543 		int old = cd->cd_ndevs, new;
544 		void **nsp;
545 
546 		if (old == 0)
547 			new = MINALLOCSIZE / sizeof(void *);
548 		else
549 			new = old * 2;
550 		while (new <= dev->dv_unit)
551 			new *= 2;
552 		cd->cd_ndevs = new;
553 		nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
554 		if (nsp == 0)
555 			panic("config_attach: %sing dev array",
556 			    old != 0 ? "expand" : "creat");
557 		bzero(nsp + old, (new - old) * sizeof(void *));
558 		if (old != 0) {
559 			bcopy(cd->cd_devs, nsp, old * sizeof(void *));
560 			free(cd->cd_devs, M_DEVBUF);
561 		}
562 		cd->cd_devs = nsp;
563 	}
564 	if (cd->cd_devs[dev->dv_unit])
565 		panic("config_attach: duplicate %s", dev->dv_xname);
566 	cd->cd_devs[dev->dv_unit] = dev;
567 
568 	/*
569 	 * Before attaching, clobber any unfound devices that are
570 	 * otherwise identical, or bump the unit number on all starred
571 	 * cfdata for this device.
572 	 */
573 	for (cf = cfdata; cf->cf_driver; cf++)
574 		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) {
575 			if (cf->cf_fstate == FSTATE_NOTFOUND)
576 				cf->cf_fstate = FSTATE_FOUND;
577 			if (cf->cf_fstate == FSTATE_STAR)
578 				cf->cf_unit++;
579 		}
580 #if defined(__alpha__) || defined(hp300) || defined(__i386__)
581 	device_register(dev, aux);
582 #endif
583 	(*ca->ca_attach)(parent, dev, aux);
584 	return (dev);
585 }
586 #endif /* __BROKEN_INDIRECT_CONFIG */
587 
588 /*
589  * Attach an event.  These must come from initially-zero space (see
590  * commented-out assignments below), but that occurs naturally for
591  * device instance variables.
592  */
593 void
594 evcnt_attach(dev, name, ev)
595 	struct device *dev;
596 	const char *name;
597 	struct evcnt *ev;
598 {
599 
600 #ifdef DIAGNOSTIC
601 	if (strlen(name) >= sizeof(ev->ev_name))
602 		panic("evcnt_attach");
603 #endif
604 	/* ev->ev_next = NULL; */
605 	ev->ev_dev = dev;
606 	/* ev->ev_count = 0; */
607 	strcpy(ev->ev_name, name);
608 	TAILQ_INSERT_TAIL(&allevents, ev, ev_list);
609 }
610