xref: /csrg-svn/sys/kern/subr_autoconf.c (revision 57761)
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  *
956896Storek  * All advertising materials mentioning features or use of this software
1056896Storek  * must display the following acknowledgement:
1156896Storek  *	This product includes software developed by the University of
1256896Storek  *	California, Lawrence Berkeley Laboratories.
1356896Storek  *
1452592Smckusick  * %sccs.include.redist.c%
1552592Smckusick  *
16*57761Storek  *	@(#)subr_autoconf.c	7.6 (Berkeley) 02/01/93
1752592Smckusick  *
18*57761Storek  * from: $Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 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 
2956896Storek /*
3056896Storek  * ioconf.c exports exactly two names: cfdata and cfroots.  All system
3156896Storek  * devices and drivers are found via these tables.
3256896Storek  */
3356896Storek extern struct cfdata cfdata[];
3456896Storek 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 {
8456896Storek 	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 		/*
9556896Storek 		 * Skip cf if no longer eligible, otherwise scan through
9656896Storek 		 * parents for one matching `parent', and try match function.
9752592Smckusick 		 */
9856896Storek 		if (cf->cf_fstate == FSTATE_FOUND)
9952592Smckusick 			continue;
10056896Storek 		for (p = cf->cf_parents; *p >= 0; p++)
10156896Storek 			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;
11856896Storek 	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
12856896Storek 	 * with found-state here since only one root should ever be
12956896Storek 	 * searched (and it must be done first).
13054121Smckusick 	 */
13156896Storek 	for (p = cfroots; *p >= 0; p++) {
13256896Storek 		cf = &cfdata[*p];
13356896Storek 		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
13454121Smckusick 			mapply(&m, cf);
13556896Storek 	}
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;
22956896Storek 	if (lname + lunit >= sizeof(dev->dv_xname))
23056896Storek 		panic("config_attach: device name too long");
23152592Smckusick 
23256896Storek 	/* get memory for all device vars */
23356896Storek 	dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
23456896Storek 					/* 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;
24156896Storek 	bcopy(cd->cd_name, dev->dv_xname, lname);
24252592Smckusick 	bcopy(xunit, dev->dv_xname + lname, lunit);
24352592Smckusick 	dev->dv_parent = parent;
24452592Smckusick 	if (parent == ROOT)
24552592Smckusick 		printf("%s (root)", dev->dv_xname);
24652592Smckusick 	else {
24752592Smckusick 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
24852592Smckusick 		(void) (*print)(aux, (char *)0);
24952592Smckusick 	}
25052592Smckusick 
25152592Smckusick 	/* put this device in the devices array */
25252592Smckusick 	if (dev->dv_unit >= cd->cd_ndevs) {
25352592Smckusick 		/*
25452592Smckusick 		 * Need to expand the array.
25552592Smckusick 		 */
25652592Smckusick 		int old = cd->cd_ndevs, oldbytes, new, newbytes;
25752592Smckusick 		void **nsp;
25852592Smckusick 
25952592Smckusick 		if (old == 0) {
26052592Smckusick 			nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK);	/*XXX*/
26152592Smckusick 			bzero(nsp, MINALLOCSIZE);
26252592Smckusick 			cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
26352592Smckusick 		} else {
26452592Smckusick 			new = cd->cd_ndevs;
26552592Smckusick 			do {
26652592Smckusick 				new *= 2;
26752592Smckusick 			} while (new <= dev->dv_unit);
26852592Smckusick 			cd->cd_ndevs = new;
26952592Smckusick 			oldbytes = old * sizeof(void *);
27052592Smckusick 			newbytes = new * sizeof(void *);
27152592Smckusick 			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
27252592Smckusick 			bcopy(cd->cd_devs, nsp, oldbytes);
27352592Smckusick 			bzero(&nsp[old], newbytes - oldbytes);
27452592Smckusick 			free(cd->cd_devs, M_DEVBUF);
27552592Smckusick 		}
27652592Smckusick 		cd->cd_devs = nsp;
27752592Smckusick 	}
27854121Smckusick 	if (cd->cd_devs[dev->dv_unit])
27954121Smckusick 		panic("config_attach: duplicate %s", dev->dv_xname);
28052592Smckusick 	cd->cd_devs[dev->dv_unit] = dev;
28156896Storek 
28256896Storek 	/*
28356896Storek 	 * Before attaching, clobber any unfound devices that are
28456896Storek 	 * otherwise identical.
28556896Storek 	 */
28656896Storek 	for (cf = cfdata; cf->cf_driver; cf++)
28756896Storek 		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
28856896Storek 		    cf->cf_fstate == FSTATE_NOTFOUND)
28956896Storek 			cf->cf_fstate = FSTATE_FOUND;
29052592Smckusick 	(*cd->cd_attach)(parent, dev, aux);
29152592Smckusick }
292*57761Storek 
293*57761Storek /*
294*57761Storek  * Attach an event.  These must come from initially-zero space (see
295*57761Storek  * commented-out assignments below), but that occurs naturally for
296*57761Storek  * device instance variables.
297*57761Storek  */
298*57761Storek void
299*57761Storek evcnt_attach(dev, name, ev)
300*57761Storek 	struct device *dev;
301*57761Storek 	const char *name;
302*57761Storek 	struct evcnt *ev;
303*57761Storek {
304*57761Storek 	static struct evcnt **nextp = &allevents;
305*57761Storek 
306*57761Storek #ifdef DIAGNOSTIC
307*57761Storek 	if (strlen(name) >= sizeof(ev->ev_name))
308*57761Storek 		panic("evcnt_attach");
309*57761Storek #endif
310*57761Storek 	/* ev->ev_next = NULL; */
311*57761Storek 	ev->ev_dev = dev;
312*57761Storek 	/* ev->ev_count = 0; */
313*57761Storek 	strcpy(ev->ev_name, name);
314*57761Storek 	*nextp = ev;
315*57761Storek 	nextp = &ev->ev_next;
316*57761Storek }
317