xref: /csrg-svn/sys/kern/subr_autoconf.c (revision 67162)
152592Smckusick /*
263176Sbostic  * Copyright (c) 1992, 1993
363176Sbostic  *	The Regents of the University of California.  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*67162Smckusick  *	@(#)subr_autoconf.c	8.3 (Berkeley) 05/17/94
1752592Smckusick  *
1857761Storek  * 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>
24*67162Smckusick #include <libkern/libkern.h>
2552592Smckusick 
2652592Smckusick /*
2752592Smckusick  * Autoconfiguration subroutines.
2852592Smckusick  */
2952592Smckusick 
3056896Storek /*
3156896Storek  * ioconf.c exports exactly two names: cfdata and cfroots.  All system
3256896Storek  * devices and drivers are found via these tables.
3356896Storek  */
3456896Storek extern struct cfdata cfdata[];
3556896Storek extern short cfroots[];
3652592Smckusick 
3752592Smckusick #define	ROOT ((struct device *)NULL)
3852592Smckusick 
3954121Smckusick struct matchinfo {
4054121Smckusick 	cfmatch_t fn;
4154121Smckusick 	struct	device *parent;
4254121Smckusick 	void	*aux;
4354121Smckusick 	struct	cfdata *match;
4454121Smckusick 	int	pri;
4554121Smckusick };
4654121Smckusick 
4752592Smckusick /*
4854121Smckusick  * Apply the matching function and choose the best.  This is used
4954121Smckusick  * a few times and we want to keep the code small.
5054121Smckusick  */
5154121Smckusick static void
mapply(m,cf)5254121Smckusick mapply(m, cf)
5354121Smckusick 	register struct matchinfo *m;
5454121Smckusick 	register struct cfdata *cf;
5554121Smckusick {
5654121Smckusick 	register int pri;
5754121Smckusick 
5854121Smckusick 	if (m->fn != NULL)
5954121Smckusick 		pri = (*m->fn)(m->parent, cf, m->aux);
6054121Smckusick 	else
6154121Smckusick 		pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
6254121Smckusick 	if (pri > m->pri) {
6354121Smckusick 		m->match = cf;
6454121Smckusick 		m->pri = pri;
6554121Smckusick 	}
6654121Smckusick }
6754121Smckusick 
6854121Smckusick /*
6952592Smckusick  * Iterate over all potential children of some device, calling the given
7052592Smckusick  * function (default being the child's match function) for each one.
7152592Smckusick  * Nonzero returns are matches; the highest value returned is considered
7252592Smckusick  * the best match.  Return the `found child' if we got a match, or NULL
7352592Smckusick  * otherwise.  The `aux' pointer is simply passed on through.
7452592Smckusick  *
7552592Smckusick  * Note that this function is designed so that it can be used to apply
7652592Smckusick  * an arbitrary function to all potential children (its return value
7752592Smckusick  * can be ignored).
7852592Smckusick  */
7952592Smckusick struct cfdata *
config_search(fn,parent,aux)8054121Smckusick config_search(fn, parent, aux)
8154121Smckusick 	cfmatch_t fn;
8254121Smckusick 	register struct device *parent;
8354121Smckusick 	void *aux;
8452592Smckusick {
8556896Storek 	register struct cfdata *cf;
8652592Smckusick 	register short *p;
8754121Smckusick 	struct matchinfo m;
8852592Smckusick 
8954121Smckusick 	m.fn = fn;
9054121Smckusick 	m.parent = parent;
9154121Smckusick 	m.aux = aux;
9254121Smckusick 	m.match = NULL;
9354121Smckusick 	m.pri = 0;
9452592Smckusick 	for (cf = cfdata; cf->cf_driver; cf++) {
9552592Smckusick 		/*
9656896Storek 		 * Skip cf if no longer eligible, otherwise scan through
9756896Storek 		 * parents for one matching `parent', and try match function.
9852592Smckusick 		 */
9956896Storek 		if (cf->cf_fstate == FSTATE_FOUND)
10052592Smckusick 			continue;
10156896Storek 		for (p = cf->cf_parents; *p >= 0; p++)
10256896Storek 			if (parent->dv_cfdata == &cfdata[*p])
10354121Smckusick 				mapply(&m, cf);
10452592Smckusick 	}
10554121Smckusick 	return (m.match);
10652592Smckusick }
10752592Smckusick 
10852592Smckusick /*
10952592Smckusick  * Find the given root device.
11052592Smckusick  * This is much like config_search, but there is no parent.
11152592Smckusick  */
11252592Smckusick struct cfdata *
config_rootsearch(fn,rootname,aux)11352592Smckusick config_rootsearch(fn, rootname, aux)
11452592Smckusick 	register cfmatch_t fn;
11552592Smckusick 	register char *rootname;
11652592Smckusick 	register void *aux;
11752592Smckusick {
11854121Smckusick 	register struct cfdata *cf;
11956896Storek 	register short *p;
12054121Smckusick 	struct matchinfo m;
12152592Smckusick 
12254121Smckusick 	m.fn = fn;
12354121Smckusick 	m.parent = ROOT;
12454121Smckusick 	m.aux = aux;
12554121Smckusick 	m.match = NULL;
12654121Smckusick 	m.pri = 0;
12754121Smckusick 	/*
12854121Smckusick 	 * Look at root entries for matching name.  We do not bother
12956896Storek 	 * with found-state here since only one root should ever be
13056896Storek 	 * searched (and it must be done first).
13154121Smckusick 	 */
13256896Storek 	for (p = cfroots; *p >= 0; p++) {
13356896Storek 		cf = &cfdata[*p];
13456896Storek 		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
13554121Smckusick 			mapply(&m, cf);
13656896Storek 	}
13754121Smckusick 	return (m.match);
13852592Smckusick }
13952592Smckusick 
14052592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
14152592Smckusick 
14252592Smckusick /*
14352592Smckusick  * The given `aux' argument describes a device that has been found
14452592Smckusick  * on the given parent, but not necessarily configured.  Locate the
14552592Smckusick  * configuration data for that device (using the cd_match configuration
14652592Smckusick  * driver function) and attach it, and return true.  If the device was
14752592Smckusick  * not configured, call the given `print' function and return 0.
14852592Smckusick  */
14952592Smckusick int
config_found(parent,aux,print)15052592Smckusick config_found(parent, aux, print)
15152592Smckusick 	struct device *parent;
15252592Smckusick 	void *aux;
15352592Smckusick 	cfprint_t print;
15452592Smckusick {
15552592Smckusick 	struct cfdata *cf;
15652592Smckusick 
15752592Smckusick 	if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
15852592Smckusick 		config_attach(parent, cf, aux, print);
15952592Smckusick 		return (1);
16052592Smckusick 	}
16152592Smckusick 	printf(msgs[(*print)(aux, parent->dv_xname)]);
16252592Smckusick 	return (0);
16352592Smckusick }
16452592Smckusick 
16552592Smckusick /*
16652592Smckusick  * As above, but for root devices.
16752592Smckusick  */
16852592Smckusick int
config_rootfound(rootname,aux)16952592Smckusick config_rootfound(rootname, aux)
17052592Smckusick 	char *rootname;
17152592Smckusick 	void *aux;
17252592Smckusick {
17352592Smckusick 	struct cfdata *cf;
17452592Smckusick 
17552592Smckusick 	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
17652592Smckusick 		config_attach(ROOT, cf, aux, (cfprint_t)NULL);
17752592Smckusick 		return (1);
17852592Smckusick 	}
17952592Smckusick 	printf("root device %s not configured\n", rootname);
18052592Smckusick 	return (0);
18152592Smckusick }
18252592Smckusick 
18352592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */
18452592Smckusick static char *
number(ep,n)18552592Smckusick number(ep, n)
18652592Smckusick 	register char *ep;
18752592Smckusick 	register int n;
18852592Smckusick {
18952592Smckusick 
19052592Smckusick 	*--ep = 0;
19152592Smckusick 	while (n >= 10) {
19252592Smckusick 		*--ep = (n % 10) + '0';
19352592Smckusick 		n /= 10;
19452592Smckusick 	}
19552592Smckusick 	*--ep = n + '0';
19652592Smckusick 	return (ep);
19752592Smckusick }
19852592Smckusick 
19952592Smckusick /*
20052592Smckusick  * Attach a found device.  Allocates memory for device variables.
20152592Smckusick  */
20252592Smckusick void
config_attach(parent,cf,aux,print)20352592Smckusick config_attach(parent, cf, aux, print)
20452592Smckusick 	register struct device *parent;
20552592Smckusick 	register struct cfdata *cf;
20652592Smckusick 	register void *aux;
20752592Smckusick 	cfprint_t print;
20852592Smckusick {
20952592Smckusick 	register struct device *dev;
21052592Smckusick 	register struct cfdriver *cd;
21152592Smckusick 	register size_t lname, lunit;
21252592Smckusick 	register char *xunit;
21354121Smckusick 	int myunit;
21452592Smckusick 	char num[10];
21554121Smckusick 	static struct device **nextp = &alldevs;
21652592Smckusick 
21752592Smckusick 	cd = cf->cf_driver;
21852592Smckusick 	if (cd->cd_devsize < sizeof(struct device))
21952592Smckusick 		panic("config_attach");
22054121Smckusick 	myunit = cf->cf_unit;
22152592Smckusick 	if (cf->cf_fstate == FSTATE_NOTFOUND)
22252592Smckusick 		cf->cf_fstate = FSTATE_FOUND;
22354121Smckusick 	else
22454121Smckusick 		cf->cf_unit++;
22552592Smckusick 
22652592Smckusick 	/* compute length of name and decimal expansion of unit number */
22752592Smckusick 	lname = strlen(cd->cd_name);
22854121Smckusick 	xunit = number(&num[sizeof num], myunit);
22952592Smckusick 	lunit = &num[sizeof num] - xunit;
23056896Storek 	if (lname + lunit >= sizeof(dev->dv_xname))
23156896Storek 		panic("config_attach: device name too long");
23252592Smckusick 
23356896Storek 	/* get memory for all device vars */
23456896Storek 	dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
23556896Storek 					/* XXX cannot wait! */
23652592Smckusick 	bzero(dev, cd->cd_devsize);
23754121Smckusick 	*nextp = dev;			/* link up */
23854121Smckusick 	nextp = &dev->dv_next;
23954121Smckusick 	dev->dv_class = cd->cd_class;
24054121Smckusick 	dev->dv_cfdata = cf;
24154121Smckusick 	dev->dv_unit = myunit;
24256896Storek 	bcopy(cd->cd_name, dev->dv_xname, lname);
24352592Smckusick 	bcopy(xunit, dev->dv_xname + lname, lunit);
24452592Smckusick 	dev->dv_parent = parent;
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) {
26167130Smckusick 			new = max(MINALLOCSIZE / sizeof(void *),
26267130Smckusick 			    dev->dv_unit + 1);
26367130Smckusick 			newbytes = new * sizeof(void *);
26467130Smckusick 			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
26567130Smckusick 			bzero(nsp, newbytes);
26652592Smckusick 		} else {
26752592Smckusick 			new = cd->cd_ndevs;
26852592Smckusick 			do {
26952592Smckusick 				new *= 2;
27052592Smckusick 			} while (new <= dev->dv_unit);
27152592Smckusick 			oldbytes = old * sizeof(void *);
27252592Smckusick 			newbytes = new * sizeof(void *);
27352592Smckusick 			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
27452592Smckusick 			bcopy(cd->cd_devs, nsp, oldbytes);
27552592Smckusick 			bzero(&nsp[old], newbytes - oldbytes);
27652592Smckusick 			free(cd->cd_devs, M_DEVBUF);
27752592Smckusick 		}
27867130Smckusick 		cd->cd_ndevs = new;
27952592Smckusick 		cd->cd_devs = nsp;
28052592Smckusick 	}
28154121Smckusick 	if (cd->cd_devs[dev->dv_unit])
28254121Smckusick 		panic("config_attach: duplicate %s", dev->dv_xname);
28352592Smckusick 	cd->cd_devs[dev->dv_unit] = dev;
28456896Storek 
28556896Storek 	/*
28656896Storek 	 * Before attaching, clobber any unfound devices that are
28756896Storek 	 * otherwise identical.
28856896Storek 	 */
28956896Storek 	for (cf = cfdata; cf->cf_driver; cf++)
29056896Storek 		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
29156896Storek 		    cf->cf_fstate == FSTATE_NOTFOUND)
29256896Storek 			cf->cf_fstate = FSTATE_FOUND;
29352592Smckusick 	(*cd->cd_attach)(parent, dev, aux);
29452592Smckusick }
29557761Storek 
29657761Storek /*
29757761Storek  * Attach an event.  These must come from initially-zero space (see
29857761Storek  * commented-out assignments below), but that occurs naturally for
29957761Storek  * device instance variables.
30057761Storek  */
30157761Storek void
evcnt_attach(dev,name,ev)30257761Storek evcnt_attach(dev, name, ev)
30357761Storek 	struct device *dev;
30457761Storek 	const char *name;
30557761Storek 	struct evcnt *ev;
30657761Storek {
30757761Storek 	static struct evcnt **nextp = &allevents;
30857761Storek 
30957761Storek #ifdef DIAGNOSTIC
31057761Storek 	if (strlen(name) >= sizeof(ev->ev_name))
31157761Storek 		panic("evcnt_attach");
31257761Storek #endif
31357761Storek 	/* ev->ev_next = NULL; */
31457761Storek 	ev->ev_dev = dev;
31557761Storek 	/* ev->ev_count = 0; */
31657761Storek 	strcpy(ev->ev_name, name);
31757761Storek 	*nextp = ev;
31857761Storek 	nextp = &ev->ev_next;
31957761Storek }
320