xref: /csrg-svn/sys/kern/subr_autoconf.c (revision 54121)
152592Smckusick /*
252592Smckusick  * Copyright (c) 1992 Regents of the University of California.
352592Smckusick  * All rights reserved.
452592Smckusick  *
5*54121Smckusick  * This software was developed by the Computer Systems Engineering group
6*54121Smckusick  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7*54121Smckusick  * contributed to Berkeley.
852592Smckusick  *
952592Smckusick  * %sccs.include.redist.c%
1052592Smckusick  *
11*54121Smckusick  *	@(#)subr_autoconf.c	7.3 (Berkeley) 06/20/92
1252592Smckusick  *
13*54121Smckusick  * from: $Header: subr_autoconf.c,v 1.6 92/06/11 17:56:19 torek Exp $ (LBL)
1452592Smckusick  */
1552592Smckusick 
1652592Smckusick #include "sys/param.h"
1752592Smckusick #include "sys/device.h"
1852592Smckusick #include "sys/malloc.h"
1952592Smckusick 
2052592Smckusick /*
2152592Smckusick  * Autoconfiguration subroutines.
2252592Smckusick  */
2352592Smckusick 
2452592Smckusick extern struct cfdata cfdata[];		/* from ioconf.c */
2552592Smckusick 
2652592Smckusick #define	ROOT ((struct device *)NULL)
2752592Smckusick 
28*54121Smckusick struct matchinfo {
29*54121Smckusick 	cfmatch_t fn;
30*54121Smckusick 	struct	device *parent;
31*54121Smckusick 	void	*aux;
32*54121Smckusick 	struct	cfdata *match;
33*54121Smckusick 	int	pri;
34*54121Smckusick };
35*54121Smckusick 
3652592Smckusick /*
37*54121Smckusick  * Apply the matching function and choose the best.  This is used
38*54121Smckusick  * a few times and we want to keep the code small.
39*54121Smckusick  */
40*54121Smckusick static void
41*54121Smckusick mapply(m, cf)
42*54121Smckusick 	register struct matchinfo *m;
43*54121Smckusick 	register struct cfdata *cf;
44*54121Smckusick {
45*54121Smckusick 	register int pri;
46*54121Smckusick 
47*54121Smckusick 	if (m->fn != NULL)
48*54121Smckusick 		pri = (*m->fn)(m->parent, cf, m->aux);
49*54121Smckusick 	else
50*54121Smckusick 		pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
51*54121Smckusick 	if (pri > m->pri) {
52*54121Smckusick 		m->match = cf;
53*54121Smckusick 		m->pri = pri;
54*54121Smckusick 	}
55*54121Smckusick }
56*54121Smckusick 
57*54121Smckusick /*
5852592Smckusick  * Iterate over all potential children of some device, calling the given
5952592Smckusick  * function (default being the child's match function) for each one.
6052592Smckusick  * Nonzero returns are matches; the highest value returned is considered
6152592Smckusick  * the best match.  Return the `found child' if we got a match, or NULL
6252592Smckusick  * otherwise.  The `aux' pointer is simply passed on through.
6352592Smckusick  *
6452592Smckusick  * Note that this function is designed so that it can be used to apply
6552592Smckusick  * an arbitrary function to all potential children (its return value
6652592Smckusick  * can be ignored).
6752592Smckusick  */
6852592Smckusick struct cfdata *
69*54121Smckusick config_search(fn, parent, aux)
70*54121Smckusick 	cfmatch_t fn;
71*54121Smckusick 	register struct device *parent;
72*54121Smckusick 	void *aux;
7352592Smckusick {
74*54121Smckusick 	register struct cfdata *cf, *pcf;
7552592Smckusick 	register short *p;
76*54121Smckusick 	struct matchinfo m;
7752592Smckusick 
78*54121Smckusick 	m.fn = fn;
79*54121Smckusick 	m.parent = parent;
80*54121Smckusick 	m.aux = aux;
81*54121Smckusick 	m.match = NULL;
82*54121Smckusick 	m.pri = 0;
8352592Smckusick 	for (cf = cfdata; cf->cf_driver; cf++) {
8452592Smckusick 		/*
8552592Smckusick 		 * Skip cf if no longer eligible, or if a root entry.
86*54121Smckusick 		 * Otherwise scan through parents for one matching `parent'
8752592Smckusick 		 * (and alive), and try match function.
8852592Smckusick 		 */
8952592Smckusick 		if (cf->cf_fstate == FSTATE_FOUND ||
9052592Smckusick 		    (p = cf->cf_parents) == NULL)
9152592Smckusick 			continue;
9252592Smckusick 		while (*p >= 0) {
9352592Smckusick 			pcf = &cfdata[*p++];
94*54121Smckusick 			if (parent->dv_cfdata == pcf)
95*54121Smckusick 				mapply(&m, cf);
9652592Smckusick 		}
9752592Smckusick 	}
98*54121Smckusick 	return (m.match);
9952592Smckusick }
10052592Smckusick 
10152592Smckusick /*
10252592Smckusick  * Find the given root device.
10352592Smckusick  * This is much like config_search, but there is no parent.
10452592Smckusick  */
10552592Smckusick struct cfdata *
10652592Smckusick config_rootsearch(fn, rootname, aux)
10752592Smckusick 	register cfmatch_t fn;
10852592Smckusick 	register char *rootname;
10952592Smckusick 	register void *aux;
11052592Smckusick {
111*54121Smckusick 	register struct cfdata *cf;
112*54121Smckusick 	struct matchinfo m;
11352592Smckusick 
114*54121Smckusick 	m.fn = fn;
115*54121Smckusick 	m.parent = ROOT;
116*54121Smckusick 	m.aux = aux;
117*54121Smckusick 	m.match = NULL;
118*54121Smckusick 	m.pri = 0;
119*54121Smckusick 	/*
120*54121Smckusick 	 * Look at root entries for matching name.  We do not bother
121*54121Smckusick 	 * with found-state here since only one root should ever be searched.
122*54121Smckusick 	 */
123*54121Smckusick 	for (cf = cfdata; cf->cf_driver; cf++)
124*54121Smckusick 		if (cf->cf_parents == NULL && cf->cf_unit == 0 &&
125*54121Smckusick 		    strcmp(cf->cf_driver->cd_name, rootname) == 0)
126*54121Smckusick 			mapply(&m, cf);
127*54121Smckusick 	return (m.match);
12852592Smckusick }
12952592Smckusick 
13052592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
13152592Smckusick 
13252592Smckusick /*
13352592Smckusick  * The given `aux' argument describes a device that has been found
13452592Smckusick  * on the given parent, but not necessarily configured.  Locate the
13552592Smckusick  * configuration data for that device (using the cd_match configuration
13652592Smckusick  * driver function) and attach it, and return true.  If the device was
13752592Smckusick  * not configured, call the given `print' function and return 0.
13852592Smckusick  */
13952592Smckusick int
14052592Smckusick config_found(parent, aux, print)
14152592Smckusick 	struct device *parent;
14252592Smckusick 	void *aux;
14352592Smckusick 	cfprint_t print;
14452592Smckusick {
14552592Smckusick 	struct cfdata *cf;
14652592Smckusick 
14752592Smckusick 	if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
14852592Smckusick 		config_attach(parent, cf, aux, print);
14952592Smckusick 		return (1);
15052592Smckusick 	}
15152592Smckusick 	printf(msgs[(*print)(aux, parent->dv_xname)]);
15252592Smckusick 	return (0);
15352592Smckusick }
15452592Smckusick 
15552592Smckusick /*
15652592Smckusick  * As above, but for root devices.
15752592Smckusick  */
15852592Smckusick int
15952592Smckusick config_rootfound(rootname, aux)
16052592Smckusick 	char *rootname;
16152592Smckusick 	void *aux;
16252592Smckusick {
16352592Smckusick 	struct cfdata *cf;
16452592Smckusick 
16552592Smckusick 	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
16652592Smckusick 		config_attach(ROOT, cf, aux, (cfprint_t)NULL);
16752592Smckusick 		return (1);
16852592Smckusick 	}
16952592Smckusick 	printf("root device %s not configured\n", rootname);
17052592Smckusick 	return (0);
17152592Smckusick }
17252592Smckusick 
17352592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */
17452592Smckusick static char *
17552592Smckusick number(ep, n)
17652592Smckusick 	register char *ep;
17752592Smckusick 	register int n;
17852592Smckusick {
17952592Smckusick 
18052592Smckusick 	*--ep = 0;
18152592Smckusick 	while (n >= 10) {
18252592Smckusick 		*--ep = (n % 10) + '0';
18352592Smckusick 		n /= 10;
18452592Smckusick 	}
18552592Smckusick 	*--ep = n + '0';
18652592Smckusick 	return (ep);
18752592Smckusick }
18852592Smckusick 
18952592Smckusick /*
19052592Smckusick  * Attach a found device.  Allocates memory for device variables.
19152592Smckusick  */
19252592Smckusick void
19352592Smckusick config_attach(parent, cf, aux, print)
19452592Smckusick 	register struct device *parent;
19552592Smckusick 	register struct cfdata *cf;
19652592Smckusick 	register void *aux;
19752592Smckusick 	cfprint_t print;
19852592Smckusick {
19952592Smckusick 	register struct device *dev;
20052592Smckusick 	register struct cfdriver *cd;
20152592Smckusick 	register size_t lname, lunit;
20252592Smckusick 	register char *xunit;
203*54121Smckusick 	int myunit;
20452592Smckusick 	char num[10];
205*54121Smckusick 	static struct device **nextp = &alldevs;
20652592Smckusick 
20752592Smckusick 	cd = cf->cf_driver;
20852592Smckusick 	if (cd->cd_devsize < sizeof(struct device))
20952592Smckusick 		panic("config_attach");
210*54121Smckusick 	myunit = cf->cf_unit;
21152592Smckusick 	if (cf->cf_fstate == FSTATE_NOTFOUND)
21252592Smckusick 		cf->cf_fstate = FSTATE_FOUND;
213*54121Smckusick 	else
214*54121Smckusick 		cf->cf_unit++;
21552592Smckusick 
21652592Smckusick 	/* compute length of name and decimal expansion of unit number */
21752592Smckusick 	lname = strlen(cd->cd_name);
218*54121Smckusick 	xunit = number(&num[sizeof num], myunit);
21952592Smckusick 	lunit = &num[sizeof num] - xunit;
22052592Smckusick 
22152592Smckusick 	/* get memory for all device vars, plus expanded name */
22252592Smckusick 	dev = (struct device *)malloc(cd->cd_devsize + lname + lunit,
22352592Smckusick 	    M_DEVBUF, M_WAITOK);		/* XXX cannot wait! */
22452592Smckusick 	bzero(dev, cd->cd_devsize);
225*54121Smckusick 	*nextp = dev;			/* link up */
226*54121Smckusick 	nextp = &dev->dv_next;
227*54121Smckusick 	dev->dv_class = cd->cd_class;
228*54121Smckusick 	dev->dv_cfdata = cf;
22952592Smckusick 	dev->dv_name = cd->cd_name;
230*54121Smckusick 	dev->dv_unit = myunit;
23152592Smckusick 	dev->dv_xname = (char *)dev + cd->cd_devsize;
23252592Smckusick 	bcopy(dev->dv_name, dev->dv_xname, lname);
23352592Smckusick 	bcopy(xunit, dev->dv_xname + lname, lunit);
23452592Smckusick 	dev->dv_parent = parent;
23552592Smckusick 	if (parent == ROOT)
23652592Smckusick 		printf("%s (root)", dev->dv_xname);
23752592Smckusick 	else {
23852592Smckusick 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
23952592Smckusick 		(void) (*print)(aux, (char *)0);
24052592Smckusick 	}
24152592Smckusick 
24252592Smckusick 	/* put this device in the devices array */
24352592Smckusick 	if (dev->dv_unit >= cd->cd_ndevs) {
24452592Smckusick 		/*
24552592Smckusick 		 * Need to expand the array.
24652592Smckusick 		 */
24752592Smckusick 		int old = cd->cd_ndevs, oldbytes, new, newbytes;
24852592Smckusick 		void **nsp;
24952592Smckusick 
25052592Smckusick 		if (old == 0) {
25152592Smckusick 			nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK);	/*XXX*/
25252592Smckusick 			bzero(nsp, MINALLOCSIZE);
25352592Smckusick 			cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
25452592Smckusick 		} else {
25552592Smckusick 			new = cd->cd_ndevs;
25652592Smckusick 			do {
25752592Smckusick 				new *= 2;
25852592Smckusick 			} while (new <= dev->dv_unit);
25952592Smckusick 			cd->cd_ndevs = new;
26052592Smckusick 			oldbytes = old * sizeof(void *);
26152592Smckusick 			newbytes = new * sizeof(void *);
26252592Smckusick 			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
26352592Smckusick 			bcopy(cd->cd_devs, nsp, oldbytes);
26452592Smckusick 			bzero(&nsp[old], newbytes - oldbytes);
26552592Smckusick 			free(cd->cd_devs, M_DEVBUF);
26652592Smckusick 		}
26752592Smckusick 		cd->cd_devs = nsp;
26852592Smckusick 	}
269*54121Smckusick 	if (cd->cd_devs[dev->dv_unit])
270*54121Smckusick 		panic("config_attach: duplicate %s", dev->dv_xname);
27152592Smckusick 	cd->cd_devs[dev->dv_unit] = dev;
27252592Smckusick 	(*cd->cd_attach)(parent, dev, aux);
27352592Smckusick }
274