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