xref: /csrg-svn/sys/kern/subr_autoconf.c (revision 52592)
1*52592Smckusick /*
2*52592Smckusick  * Copyright (c) 1992 Regents of the University of California.
3*52592Smckusick  * All rights reserved.
4*52592Smckusick  *
5*52592Smckusick  * This code is derived from software contributed to Berkeley by
6*52592Smckusick  * the Computer Systems Engineering group at Lawrence Berkeley
7*52592Smckusick  * Laboratory under DARPA contract BG 91-66.
8*52592Smckusick  *
9*52592Smckusick  * %sccs.include.redist.c%
10*52592Smckusick  *
11*52592Smckusick  *	@(#)subr_autoconf.c	7.1 (Berkeley) 02/19/92
12*52592Smckusick  *
13*52592Smckusick  * from: $Header: subr_autoconf.c,v 1.3 91/11/23 00:53:49 torek Exp $ (LBL)
14*52592Smckusick  */
15*52592Smckusick 
16*52592Smckusick #include "sys/param.h"
17*52592Smckusick #include "sys/device.h"
18*52592Smckusick #include "sys/malloc.h"
19*52592Smckusick 
20*52592Smckusick /*
21*52592Smckusick  * Autoconfiguration subroutines.
22*52592Smckusick  */
23*52592Smckusick 
24*52592Smckusick extern struct cfdata cfdata[];		/* from ioconf.c */
25*52592Smckusick 
26*52592Smckusick #define	ROOT ((struct device *)NULL)
27*52592Smckusick 
28*52592Smckusick /*
29*52592Smckusick  * Iterate over all potential children of some device, calling the given
30*52592Smckusick  * function (default being the child's match function) for each one.
31*52592Smckusick  * Nonzero returns are matches; the highest value returned is considered
32*52592Smckusick  * the best match.  Return the `found child' if we got a match, or NULL
33*52592Smckusick  * otherwise.  The `aux' pointer is simply passed on through.
34*52592Smckusick  *
35*52592Smckusick  * Note that this function is designed so that it can be used to apply
36*52592Smckusick  * an arbitrary function to all potential children (its return value
37*52592Smckusick  * can be ignored).
38*52592Smckusick  */
39*52592Smckusick struct cfdata *
40*52592Smckusick config_search(fn, dev, aux)
41*52592Smckusick 	register cfmatch_t fn;
42*52592Smckusick 	register struct device *dev;
43*52592Smckusick 	register void *aux;
44*52592Smckusick {
45*52592Smckusick 	register struct cfdata *cf, *pcf, *match = NULL;
46*52592Smckusick 	register short *p;
47*52592Smckusick 	register int pri, bestpri = 0;
48*52592Smckusick 
49*52592Smckusick 	for (cf = cfdata; cf->cf_driver; cf++) {
50*52592Smckusick 		/*
51*52592Smckusick 		 * Skip cf if no longer eligible, or if a root entry.
52*52592Smckusick 		 * Otherwise scan through parents for one matching `dev'
53*52592Smckusick 		 * (and alive), and try match function.
54*52592Smckusick 		 */
55*52592Smckusick 		if (cf->cf_fstate == FSTATE_FOUND ||
56*52592Smckusick 		    (p = cf->cf_parents) == NULL)
57*52592Smckusick 			continue;
58*52592Smckusick 		while (*p >= 0) {
59*52592Smckusick 			pcf = &cfdata[*p++];
60*52592Smckusick 			if (pcf->cf_fstate != FSTATE_FOUND ||
61*52592Smckusick 			    pcf->cf_driver->cd_name != dev->dv_name ||
62*52592Smckusick 			    pcf->cf_unit != dev->dv_unit)
63*52592Smckusick 				continue;
64*52592Smckusick 			if (fn != NULL)
65*52592Smckusick 				pri = (*fn)(dev, cf, aux);
66*52592Smckusick 			else
67*52592Smckusick 				pri = (*cf->cf_driver->cd_match)(dev, cf, aux);
68*52592Smckusick 			if (pri > bestpri) {
69*52592Smckusick 				match = cf;
70*52592Smckusick 				bestpri = pri;
71*52592Smckusick 			}
72*52592Smckusick 		}
73*52592Smckusick 	}
74*52592Smckusick 	return (match);
75*52592Smckusick }
76*52592Smckusick 
77*52592Smckusick /*
78*52592Smckusick  * Find the given root device.
79*52592Smckusick  * This is much like config_search, but there is no parent.
80*52592Smckusick  */
81*52592Smckusick struct cfdata *
82*52592Smckusick config_rootsearch(fn, rootname, aux)
83*52592Smckusick 	register cfmatch_t fn;
84*52592Smckusick 	register char *rootname;
85*52592Smckusick 	register void *aux;
86*52592Smckusick {
87*52592Smckusick 	register struct cfdata *cf, *match = NULL;
88*52592Smckusick 	register int pri, bestpri = 0;
89*52592Smckusick 
90*52592Smckusick 	for (cf = cfdata; cf->cf_driver; cf++) {
91*52592Smckusick 		/*
92*52592Smckusick 		 * Look at root entries for matching name.
93*52592Smckusick 		 * We do not bother with found-state here
94*52592Smckusick 		 * since only one root should ever be searched.
95*52592Smckusick 		 */
96*52592Smckusick 		if (cf->cf_parents != NULL || cf->cf_unit != 0 ||
97*52592Smckusick 		    strcmp(cf->cf_driver->cd_name, rootname) != 0)
98*52592Smckusick 			continue;
99*52592Smckusick 		if (fn != NULL)
100*52592Smckusick 			pri = (*fn)(ROOT, cf, aux);
101*52592Smckusick 		else
102*52592Smckusick 			pri = (*cf->cf_driver->cd_match)(ROOT, cf, aux);
103*52592Smckusick 		if (pri > bestpri) {
104*52592Smckusick 			match = cf;
105*52592Smckusick 			bestpri = pri;
106*52592Smckusick 		}
107*52592Smckusick 	}
108*52592Smckusick 	return (match);
109*52592Smckusick }
110*52592Smckusick 
111*52592Smckusick static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
112*52592Smckusick 
113*52592Smckusick /*
114*52592Smckusick  * The given `aux' argument describes a device that has been found
115*52592Smckusick  * on the given parent, but not necessarily configured.  Locate the
116*52592Smckusick  * configuration data for that device (using the cd_match configuration
117*52592Smckusick  * driver function) and attach it, and return true.  If the device was
118*52592Smckusick  * not configured, call the given `print' function and return 0.
119*52592Smckusick  */
120*52592Smckusick int
121*52592Smckusick config_found(parent, aux, print)
122*52592Smckusick 	struct device *parent;
123*52592Smckusick 	void *aux;
124*52592Smckusick 	cfprint_t print;
125*52592Smckusick {
126*52592Smckusick 	struct cfdata *cf;
127*52592Smckusick 
128*52592Smckusick 	if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
129*52592Smckusick 		config_attach(parent, cf, aux, print);
130*52592Smckusick 		return (1);
131*52592Smckusick 	}
132*52592Smckusick 	printf(msgs[(*print)(aux, parent->dv_xname)]);
133*52592Smckusick 	return (0);
134*52592Smckusick }
135*52592Smckusick 
136*52592Smckusick /*
137*52592Smckusick  * As above, but for root devices.
138*52592Smckusick  */
139*52592Smckusick int
140*52592Smckusick config_rootfound(rootname, aux)
141*52592Smckusick 	char *rootname;
142*52592Smckusick 	void *aux;
143*52592Smckusick {
144*52592Smckusick 	struct cfdata *cf;
145*52592Smckusick 
146*52592Smckusick 	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
147*52592Smckusick 		config_attach(ROOT, cf, aux, (cfprint_t)NULL);
148*52592Smckusick 		return (1);
149*52592Smckusick 	}
150*52592Smckusick 	printf("root device %s not configured\n", rootname);
151*52592Smckusick 	return (0);
152*52592Smckusick }
153*52592Smckusick 
154*52592Smckusick /* just like sprintf(buf, "%d") except that it works from the end */
155*52592Smckusick static char *
156*52592Smckusick number(ep, n)
157*52592Smckusick 	register char *ep;
158*52592Smckusick 	register int n;
159*52592Smckusick {
160*52592Smckusick 
161*52592Smckusick 	*--ep = 0;
162*52592Smckusick 	while (n >= 10) {
163*52592Smckusick 		*--ep = (n % 10) + '0';
164*52592Smckusick 		n /= 10;
165*52592Smckusick 	}
166*52592Smckusick 	*--ep = n + '0';
167*52592Smckusick 	return (ep);
168*52592Smckusick }
169*52592Smckusick 
170*52592Smckusick /*
171*52592Smckusick  * Attach a found device.  Allocates memory for device variables.
172*52592Smckusick  */
173*52592Smckusick void
174*52592Smckusick config_attach(parent, cf, aux, print)
175*52592Smckusick 	register struct device *parent;
176*52592Smckusick 	register struct cfdata *cf;
177*52592Smckusick 	register void *aux;
178*52592Smckusick 	cfprint_t print;
179*52592Smckusick {
180*52592Smckusick 	register struct device *dev;
181*52592Smckusick 	register struct cfdriver *cd;
182*52592Smckusick 	register size_t lname, lunit;
183*52592Smckusick 	register char *xunit;
184*52592Smckusick 	char num[10];
185*52592Smckusick 
186*52592Smckusick 	cd = cf->cf_driver;
187*52592Smckusick 	if (cd->cd_devsize < sizeof(struct device))
188*52592Smckusick 		panic("config_attach");
189*52592Smckusick 	if (cf->cf_fstate == FSTATE_NOTFOUND)
190*52592Smckusick 		cf->cf_fstate = FSTATE_FOUND;
191*52592Smckusick 
192*52592Smckusick 	/* compute length of name and decimal expansion of unit number */
193*52592Smckusick 	lname = strlen(cd->cd_name);
194*52592Smckusick 	xunit = number(&num[sizeof num], cf->cf_unit);
195*52592Smckusick 	lunit = &num[sizeof num] - xunit;
196*52592Smckusick 
197*52592Smckusick 	/* get memory for all device vars, plus expanded name */
198*52592Smckusick 	dev = (struct device *)malloc(cd->cd_devsize + lname + lunit,
199*52592Smckusick 	    M_DEVBUF, M_WAITOK);		/* XXX cannot wait! */
200*52592Smckusick 	bzero(dev, cd->cd_devsize);
201*52592Smckusick 	dev->dv_name = cd->cd_name;
202*52592Smckusick 	dev->dv_unit = cf->cf_unit;
203*52592Smckusick 	dev->dv_flags = cf->cf_flags;
204*52592Smckusick 	dev->dv_xname = (char *)dev + cd->cd_devsize;
205*52592Smckusick 	bcopy(dev->dv_name, dev->dv_xname, lname);
206*52592Smckusick 	bcopy(xunit, dev->dv_xname + lname, lunit);
207*52592Smckusick 	dev->dv_parent = parent;
208*52592Smckusick 	if (parent == ROOT)
209*52592Smckusick 		printf("%s (root)", dev->dv_xname);
210*52592Smckusick 	else {
211*52592Smckusick 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
212*52592Smckusick 		(void) (*print)(aux, (char *)0);
213*52592Smckusick 	}
214*52592Smckusick 
215*52592Smckusick 	/* put this device in the devices array */
216*52592Smckusick 	if (dev->dv_unit >= cd->cd_ndevs) {
217*52592Smckusick 		/*
218*52592Smckusick 		 * Need to expand the array.
219*52592Smckusick 		 */
220*52592Smckusick 		int old = cd->cd_ndevs, oldbytes, new, newbytes;
221*52592Smckusick 		void **nsp;
222*52592Smckusick 
223*52592Smckusick 		if (old == 0) {
224*52592Smckusick 			nsp = malloc(MINALLOCSIZE, M_DEVBUF, M_WAITOK);	/*XXX*/
225*52592Smckusick 			bzero(nsp, MINALLOCSIZE);
226*52592Smckusick 			cd->cd_ndevs = MINALLOCSIZE / sizeof(void *);
227*52592Smckusick 		} else {
228*52592Smckusick 			new = cd->cd_ndevs;
229*52592Smckusick 			do {
230*52592Smckusick 				new *= 2;
231*52592Smckusick 			} while (new <= dev->dv_unit);
232*52592Smckusick 			cd->cd_ndevs = new;
233*52592Smckusick 			oldbytes = old * sizeof(void *);
234*52592Smckusick 			newbytes = new * sizeof(void *);
235*52592Smckusick 			nsp = malloc(newbytes, M_DEVBUF, M_WAITOK);	/*XXX*/
236*52592Smckusick 			bcopy(cd->cd_devs, nsp, oldbytes);
237*52592Smckusick 			bzero(&nsp[old], newbytes - oldbytes);
238*52592Smckusick 			free(cd->cd_devs, M_DEVBUF);
239*52592Smckusick 		}
240*52592Smckusick 		cd->cd_devs = nsp;
241*52592Smckusick 	}
242*52592Smckusick 	cd->cd_devs[dev->dv_unit] = dev;
243*52592Smckusick 	(*cd->cd_attach)(parent, dev, aux);
244*52592Smckusick }
245