xref: /csrg-svn/sys/kern/subr_autoconf.c (revision 56896)
152592Smckusick /*
252592Smckusick  * Copyright (c) 1992 Regents of the University of California.
352592Smckusick  * All rights reserved.
452592Smckusick  *
554121Smckusick  * This software was developed by the Computer Systems Engineering group
654121Smckusick  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
754121Smckusick  * contributed to Berkeley.
852592Smckusick  *
9*56896Storek  * All advertising materials mentioning features or use of this software
10*56896Storek  * must display the following acknowledgement:
11*56896Storek  *	This product includes software developed by the University of
12*56896Storek  *	California, Lawrence Berkeley Laboratories.
13*56896Storek  *
1452592Smckusick  * %sccs.include.redist.c%
1552592Smckusick  *
16*56896Storek  *	@(#)subr_autoconf.c	7.5 (Berkeley) 11/18/92
1752592Smckusick  *
18*56896Storek  * from: $Header: subr_autoconf.c,v 1.9 92/11/19 01:59:01 torek Exp $ (LBL)
1952592Smckusick  */
2052592Smckusick 
2156517Sbostic #include <sys/param.h>
2256517Sbostic #include <sys/device.h>
2356517Sbostic #include <sys/malloc.h>
2452592Smckusick 
2552592Smckusick /*
2652592Smckusick  * Autoconfiguration subroutines.
2752592Smckusick  */
2852592Smckusick 
29*56896Storek /*
30*56896Storek  * ioconf.c exports exactly two names: cfdata and cfroots.  All system
31*56896Storek  * devices and drivers are found via these tables.
32*56896Storek  */
33*56896Storek extern struct cfdata cfdata[];
34*56896Storek extern short cfroots[];
3552592Smckusick 
3652592Smckusick #define	ROOT ((struct device *)NULL)
3752592Smckusick 
3854121Smckusick struct matchinfo {
3954121Smckusick 	cfmatch_t fn;
4054121Smckusick 	struct	device *parent;
4154121Smckusick 	void	*aux;
4254121Smckusick 	struct	cfdata *match;
4354121Smckusick 	int	pri;
4454121Smckusick };
4554121Smckusick 
4652592Smckusick /*
4754121Smckusick  * Apply the matching function and choose the best.  This is used
4854121Smckusick  * a few times and we want to keep the code small.
4954121Smckusick  */
5054121Smckusick static void
5154121Smckusick mapply(m, cf)
5254121Smckusick 	register struct matchinfo *m;
5354121Smckusick 	register struct cfdata *cf;
5454121Smckusick {
5554121Smckusick 	register int pri;
5654121Smckusick 
5754121Smckusick 	if (m->fn != NULL)
5854121Smckusick 		pri = (*m->fn)(m->parent, cf, m->aux);
5954121Smckusick 	else
6054121Smckusick 		pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
6154121Smckusick 	if (pri > m->pri) {
6254121Smckusick 		m->match = cf;
6354121Smckusick 		m->pri = pri;
6454121Smckusick 	}
6554121Smckusick }
6654121Smckusick 
6754121Smckusick /*
6852592Smckusick  * Iterate over all potential children of some device, calling the given
6952592Smckusick  * function (default being the child's match function) for each one.
7052592Smckusick  * Nonzero returns are matches; the highest value returned is considered
7152592Smckusick  * the best match.  Return the `found child' if we got a match, or NULL
7252592Smckusick  * otherwise.  The `aux' pointer is simply passed on through.
7352592Smckusick  *
7452592Smckusick  * Note that this function is designed so that it can be used to apply
7552592Smckusick  * an arbitrary function to all potential children (its return value
7652592Smckusick  * can be ignored).
7752592Smckusick  */
7852592Smckusick struct cfdata *
7954121Smckusick config_search(fn, parent, aux)
8054121Smckusick 	cfmatch_t fn;
8154121Smckusick 	register struct device *parent;
8254121Smckusick 	void *aux;
8352592Smckusick {
84*56896Storek 	register struct cfdata *cf;
8552592Smckusick 	register short *p;
8654121Smckusick 	struct matchinfo m;
8752592Smckusick 
8854121Smckusick 	m.fn = fn;
8954121Smckusick 	m.parent = parent;
9054121Smckusick 	m.aux = aux;
9154121Smckusick 	m.match = NULL;
9254121Smckusick 	m.pri = 0;
9352592Smckusick 	for (cf = cfdata; cf->cf_driver; cf++) {
9452592Smckusick 		/*
95*56896Storek 		 * Skip cf if no longer eligible, otherwise scan through
96*56896Storek 		 * parents for one matching `parent', and try match function.
9752592Smckusick 		 */
98*56896Storek 		if (cf->cf_fstate == FSTATE_FOUND)
9952592Smckusick 			continue;
100*56896Storek 		for (p = cf->cf_parents; *p >= 0; p++)
101*56896Storek 			if (parent->dv_cfdata == &cfdata[*p])
10254121Smckusick 				mapply(&m, cf);
10352592Smckusick 	}
10454121Smckusick 	return (m.match);
10552592Smckusick }
10652592Smckusick 
10752592Smckusick /*
10852592Smckusick  * Find the given root device.
10952592Smckusick  * This is much like config_search, but there is no parent.
11052592Smckusick  */
11152592Smckusick struct cfdata *
11252592Smckusick config_rootsearch(fn, rootname, aux)
11352592Smckusick 	register cfmatch_t fn;
11452592Smckusick 	register char *rootname;
11552592Smckusick 	register void *aux;
11652592Smckusick {
11754121Smckusick 	register struct cfdata *cf;
118*56896Storek 	register short *p;
11954121Smckusick 	struct matchinfo m;
12052592Smckusick 
12154121Smckusick 	m.fn = fn;
12254121Smckusick 	m.parent = ROOT;
12354121Smckusick 	m.aux = aux;
12454121Smckusick 	m.match = NULL;
12554121Smckusick 	m.pri = 0;
12654121Smckusick 	/*
12754121Smckusick 	 * Look at root entries for matching name.  We do not bother
128*56896Storek 	 * with found-state here since only one root should ever be
129*56896Storek 	 * searched (and it must be done first).
13054121Smckusick 	 */
131*56896Storek 	for (p = cfroots; *p >= 0; p++) {
132*56896Storek 		cf = &cfdata[*p];
133*56896Storek 		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
13454121Smckusick 			mapply(&m, cf);
135*56896Storek 	}
13654121Smckusick 	return (m.match);
13752592Smckusick }
13852592Smckusick 
13952592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
14052592Smckusick 
14152592Smckusick /*
14252592Smckusick  * The given `aux' argument describes a device that has been found
14352592Smckusick  * on the given parent, but not necessarily configured.  Locate the
14452592Smckusick  * configuration data for that device (using the cd_match configuration
14552592Smckusick  * driver function) and attach it, and return true.  If the device was
14652592Smckusick  * not configured, call the given `print' function and return 0.
14752592Smckusick  */
14852592Smckusick int
14952592Smckusick config_found(parent, aux, print)
15052592Smckusick 	struct device *parent;
15152592Smckusick 	void *aux;
15252592Smckusick 	cfprint_t print;
15352592Smckusick {
15452592Smckusick 	struct cfdata *cf;
15552592Smckusick 
15652592Smckusick 	if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
15752592Smckusick 		config_attach(parent, cf, aux, print);
15852592Smckusick 		return (1);
15952592Smckusick 	}
16052592Smckusick 	printf(msgs[(*print)(aux, parent->dv_xname)]);
16152592Smckusick 	return (0);
16252592Smckusick }
16352592Smckusick 
16452592Smckusick /*
16552592Smckusick  * As above, but for root devices.
16652592Smckusick  */
16752592Smckusick int
16852592Smckusick config_rootfound(rootname, aux)
16952592Smckusick 	char *rootname;
17052592Smckusick 	void *aux;
17152592Smckusick {
17252592Smckusick 	struct cfdata *cf;
17352592Smckusick 
17452592Smckusick 	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
17552592Smckusick 		config_attach(ROOT, cf, aux, (cfprint_t)NULL);
17652592Smckusick 		return (1);
17752592Smckusick 	}
17852592Smckusick 	printf("root device %s not configured\n", rootname);
17952592Smckusick 	return (0);
18052592Smckusick }
18152592Smckusick 
18252592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */
18352592Smckusick static char *
18452592Smckusick number(ep, n)
18552592Smckusick 	register char *ep;
18652592Smckusick 	register int n;
18752592Smckusick {
18852592Smckusick 
18952592Smckusick 	*--ep = 0;
19052592Smckusick 	while (n >= 10) {
19152592Smckusick 		*--ep = (n % 10) + '0';
19252592Smckusick 		n /= 10;
19352592Smckusick 	}
19452592Smckusick 	*--ep = n + '0';
19552592Smckusick 	return (ep);
19652592Smckusick }
19752592Smckusick 
19852592Smckusick /*
19952592Smckusick  * Attach a found device.  Allocates memory for device variables.
20052592Smckusick  */
20152592Smckusick void
20252592Smckusick config_attach(parent, cf, aux, print)
20352592Smckusick 	register struct device *parent;
20452592Smckusick 	register struct cfdata *cf;
20552592Smckusick 	register void *aux;
20652592Smckusick 	cfprint_t print;
20752592Smckusick {
20852592Smckusick 	register struct device *dev;
20952592Smckusick 	register struct cfdriver *cd;
21052592Smckusick 	register size_t lname, lunit;
21152592Smckusick 	register char *xunit;
21254121Smckusick 	int myunit;
21352592Smckusick 	char num[10];
21454121Smckusick 	static struct device **nextp = &alldevs;
21552592Smckusick 
21652592Smckusick 	cd = cf->cf_driver;
21752592Smckusick 	if (cd->cd_devsize < sizeof(struct device))
21852592Smckusick 		panic("config_attach");
21954121Smckusick 	myunit = cf->cf_unit;
22052592Smckusick 	if (cf->cf_fstate == FSTATE_NOTFOUND)
22152592Smckusick 		cf->cf_fstate = FSTATE_FOUND;
22254121Smckusick 	else
22354121Smckusick 		cf->cf_unit++;
22452592Smckusick 
22552592Smckusick 	/* compute length of name and decimal expansion of unit number */
22652592Smckusick 	lname = strlen(cd->cd_name);
22754121Smckusick 	xunit = number(&num[sizeof num], myunit);
22852592Smckusick 	lunit = &num[sizeof num] - xunit;
229*56896Storek 	if (lname + lunit >= sizeof(dev->dv_xname))
230*56896Storek 		panic("config_attach: device name too long");
23152592Smckusick 
232*56896Storek 	/* get memory for all device vars */
233*56896Storek 	dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
234*56896Storek 					/* XXX cannot wait! */
23552592Smckusick 	bzero(dev, cd->cd_devsize);
23654121Smckusick 	*nextp = dev;			/* link up */
23754121Smckusick 	nextp = &dev->dv_next;
23854121Smckusick 	dev->dv_class = cd->cd_class;
23954121Smckusick 	dev->dv_cfdata = cf;
24054121Smckusick 	dev->dv_unit = myunit;
241*56896Storek 	bcopy(cd->cd_name, dev->dv_xname, lname);
24252592Smckusick 	bcopy(xunit, dev->dv_xname + lname, lunit);
24352592Smckusick 	dev->dv_parent = parent;
244*56896Storek 	bcopy(cd->cd_evnam, dev->dv_evnam, sizeof(cd->cd_evnam));
24552592Smckusick 	if (parent == ROOT)
24652592Smckusick 		printf("%s (root)", dev->dv_xname);
24752592Smckusick 	else {
24852592Smckusick 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
24952592Smckusick 		(void) (*print)(aux, (char *)0);
25052592Smckusick 	}
25152592Smckusick 
25252592Smckusick 	/* put this device in the devices array */
25352592Smckusick 	if (dev->dv_unit >= cd->cd_ndevs) {
25452592Smckusick 		/*
25552592Smckusick 		 * Need to expand the array.
25652592Smckusick 		 */
25752592Smckusick 		int old = cd->cd_ndevs, oldbytes, new, newbytes;
25852592Smckusick 		void **nsp;
25952592Smckusick 
26052592Smckusick 		if (old == 0) {
26152592Smckusick 			nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK);	/*XXX*/
26252592Smckusick 			bzero(nsp, MINALLOCSIZE);
26352592Smckusick 			cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
26452592Smckusick 		} else {
26552592Smckusick 			new = cd->cd_ndevs;
26652592Smckusick 			do {
26752592Smckusick 				new *= 2;
26852592Smckusick 			} while (new <= dev->dv_unit);
26952592Smckusick 			cd->cd_ndevs = new;
27052592Smckusick 			oldbytes = old * sizeof(void *);
27152592Smckusick 			newbytes = new * sizeof(void *);
27252592Smckusick 			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
27352592Smckusick 			bcopy(cd->cd_devs, nsp, oldbytes);
27452592Smckusick 			bzero(&nsp[old], newbytes - oldbytes);
27552592Smckusick 			free(cd->cd_devs, M_DEVBUF);
27652592Smckusick 		}
27752592Smckusick 		cd->cd_devs = nsp;
27852592Smckusick 	}
27954121Smckusick 	if (cd->cd_devs[dev->dv_unit])
28054121Smckusick 		panic("config_attach: duplicate %s", dev->dv_xname);
28152592Smckusick 	cd->cd_devs[dev->dv_unit] = dev;
282*56896Storek 
283*56896Storek 	/*
284*56896Storek 	 * Before attaching, clobber any unfound devices that are
285*56896Storek 	 * otherwise identical.
286*56896Storek 	 */
287*56896Storek 	for (cf = cfdata; cf->cf_driver; cf++)
288*56896Storek 		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
289*56896Storek 		    cf->cf_fstate == FSTATE_NOTFOUND)
290*56896Storek 			cf->cf_fstate = FSTATE_FOUND;
29152592Smckusick 	(*cd->cd_attach)(parent, dev, aux);
29252592Smckusick }
293