xref: /netbsd-src/sys/kern/subr_autoconf.c (revision 6ea46cb5e46c49111a6ecf3bcbe3c7e2730fe9f6)
1 /*	$NetBSD: subr_autoconf.c,v 1.9 1994/06/29 06:32:54 cgd Exp $	*/
2 
3 /*
4  * Copyright (c) 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This software was developed by the Computer Systems Engineering group
8  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
9  * contributed to Berkeley.
10  *
11  * All advertising materials mentioning features or use of this software
12  * must display the following acknowledgement:
13  *	This product includes software developed by the University of
14  *	California, Lawrence Berkeley Laboratories.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  * 3. All advertising materials mentioning features or use of this software
25  *    must display the following acknowledgement:
26  *	This product includes software developed by the University of
27  *	California, Berkeley and its contributors.
28  * 4. Neither the name of the University nor the names of its contributors
29  *    may be used to endorse or promote products derived from this software
30  *    without specific prior written permission.
31  *
32  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42  * SUCH DAMAGE.
43  *
44  * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp  (LBL)
45  *
46  *	@(#)subr_autoconf.c	8.1 (Berkeley) 6/10/93
47  */
48 
49 #include <sys/param.h>
50 #include <sys/device.h>
51 #include <sys/malloc.h>
52 
53 /*
54  * Autoconfiguration subroutines.
55  */
56 
57 /*
58  * ioconf.c exports exactly two names: cfdata and cfroots.  All system
59  * devices and drivers are found via these tables.
60  */
61 extern struct cfdata cfdata[];
62 extern short cfroots[];
63 
64 #define	ROOT ((struct device *)NULL)
65 
66 struct matchinfo {
67 	cfmatch_t fn;
68 	struct	device *parent;
69 	void	*aux;
70 	struct	cfdata *match;
71 	int	pri;
72 };
73 
74 /*
75  * Apply the matching function and choose the best.  This is used
76  * a few times and we want to keep the code small.
77  */
78 static void
79 mapply(m, cf)
80 	register struct matchinfo *m;
81 	register struct cfdata *cf;
82 {
83 	register int pri;
84 
85 	if (m->fn != NULL)
86 		pri = (*m->fn)(m->parent, cf, m->aux);
87 	else {
88 	        if (cf->cf_driver->cd_match == NULL) {
89 		    printf("mapply: no match function for '%s' device\n",
90 			   cf->cf_driver->cd_name);
91 		    panic("mapply: not match function\n");
92 		}
93 		pri = (*cf->cf_driver->cd_match)(m->parent, cf, m->aux);
94 	}
95 	if (pri > m->pri) {
96 		m->match = cf;
97 		m->pri = pri;
98 	}
99 }
100 
101 /*
102  * Iterate over all potential children of some device, calling the given
103  * function (default being the child's match function) for each one.
104  * Nonzero returns are matches; the highest value returned is considered
105  * the best match.  Return the `found child' if we got a match, or NULL
106  * otherwise.  The `aux' pointer is simply passed on through.
107  *
108  * Note that this function is designed so that it can be used to apply
109  * an arbitrary function to all potential children (its return value
110  * can be ignored).
111  */
112 struct cfdata *
113 config_search(fn, parent, aux)
114 	cfmatch_t fn;
115 	register struct device *parent;
116 	void *aux;
117 {
118 	register struct cfdata *cf;
119 	register short *p;
120 	struct matchinfo m;
121 
122 	m.fn = fn;
123 	m.parent = parent;
124 	m.aux = aux;
125 	m.match = NULL;
126 	m.pri = 0;
127 	for (cf = cfdata; cf->cf_driver; cf++) {
128 		/*
129 		 * Skip cf if no longer eligible, otherwise scan through
130 		 * parents for one matching `parent', and try match function.
131 		 */
132 		if (cf->cf_fstate == FSTATE_FOUND)
133 			continue;
134 		for (p = cf->cf_parents; *p >= 0; p++)
135 			if (parent->dv_cfdata == &cfdata[*p])
136 				mapply(&m, cf);
137 	}
138 	return (m.match);
139 }
140 
141 /*
142  * Find the given root device.
143  * This is much like config_search, but there is no parent.
144  */
145 struct cfdata *
146 config_rootsearch(fn, rootname, aux)
147 	register cfmatch_t fn;
148 	register char *rootname;
149 	register void *aux;
150 {
151 	register struct cfdata *cf;
152 	register short *p;
153 	struct matchinfo m;
154 
155 	m.fn = fn;
156 	m.parent = ROOT;
157 	m.aux = aux;
158 	m.match = NULL;
159 	m.pri = 0;
160 	/*
161 	 * Look at root entries for matching name.  We do not bother
162 	 * with found-state here since only one root should ever be
163 	 * searched (and it must be done first).
164 	 */
165 	for (p = cfroots; *p >= 0; p++) {
166 		cf = &cfdata[*p];
167 		if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
168 			mapply(&m, cf);
169 	}
170 	return (m.match);
171 }
172 
173 static char *msgs[3] = { "", " not configured\n", " unsupported\n" };
174 
175 /*
176  * The given `aux' argument describes a device that has been found
177  * on the given parent, but not necessarily configured.  Locate the
178  * configuration data for that device (using the cd_match configuration
179  * driver function) and attach it, and return true.  If the device was
180  * not configured, call the given `print' function and return 0.
181  */
182 int
183 config_found(parent, aux, print)
184 	struct device *parent;
185 	void *aux;
186 	cfprint_t print;
187 {
188 	struct cfdata *cf;
189 
190 	if ((cf = config_search((cfmatch_t)NULL, parent, aux)) != NULL) {
191 		config_attach(parent, cf, aux, print);
192 		return (1);
193 	}
194 	if (print)
195 	    printf(msgs[(*print)(aux, parent->dv_xname)]);
196 	return (0);
197 }
198 
199 /*
200  * As above, but for root devices.
201  */
202 int
203 config_rootfound(rootname, aux)
204 	char *rootname;
205 	void *aux;
206 {
207 	struct cfdata *cf;
208 
209 	if ((cf = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) {
210 		config_attach(ROOT, cf, aux, (cfprint_t)NULL);
211 		return (1);
212 	}
213 	printf("root device %s not configured\n", rootname);
214 	return (0);
215 }
216 
217 /* just like sprintf(buf, "%d") except that it works from the end */
218 static char *
219 number(ep, n)
220 	register char *ep;
221 	register int n;
222 {
223 
224 	*--ep = 0;
225 	while (n >= 10) {
226 		*--ep = (n % 10) + '0';
227 		n /= 10;
228 	}
229 	*--ep = n + '0';
230 	return (ep);
231 }
232 
233 /*
234  * Attach a found device.  Allocates memory for device variables.
235  */
236 void
237 config_attach(parent, cf, aux, print)
238 	register struct device *parent;
239 	register struct cfdata *cf;
240 	register void *aux;
241 	cfprint_t print;
242 {
243 	register struct device *dev;
244 	register struct cfdriver *cd;
245 	register size_t lname, lunit;
246 	register char *xunit;
247 	int myunit;
248 	char num[10];
249 	static struct device **nextp = &alldevs;
250 
251 	cd = cf->cf_driver;
252 	if (cd->cd_devsize < sizeof(struct device))
253 		panic("config_attach");
254 	myunit = cf->cf_unit;
255 	if (cf->cf_fstate == FSTATE_NOTFOUND)
256 		cf->cf_fstate = FSTATE_FOUND;
257 	else
258 		cf->cf_unit++;
259 
260 	/* compute length of name and decimal expansion of unit number */
261 	lname = strlen(cd->cd_name);
262 	xunit = number(&num[sizeof num], myunit);
263 	lunit = &num[sizeof num] - xunit;
264 	if (lname + lunit >= sizeof(dev->dv_xname))
265 		panic("config_attach: device name too long");
266 
267 	/* get memory for all device vars */
268 	dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_NOWAIT);
269 	if (!dev)
270 	    panic("config_attach: memory allocation for device softc failed");
271 	bzero(dev, cd->cd_devsize);
272 	*nextp = dev;			/* link up */
273 	nextp = &dev->dv_next;
274 	dev->dv_class = cd->cd_class;
275 	dev->dv_cfdata = cf;
276 	dev->dv_unit = myunit;
277 	bcopy(cd->cd_name, dev->dv_xname, lname);
278 	bcopy(xunit, dev->dv_xname + lname, lunit);
279 	dev->dv_parent = parent;
280 	if (parent == ROOT)
281 		printf("%s (root)", dev->dv_xname);
282 	else {
283 		printf("%s at %s", dev->dv_xname, parent->dv_xname);
284 		if (print)
285 		    (void) (*print)(aux, (char *)0);
286 	}
287 
288 	/* put this device in the devices array */
289 	if (dev->dv_unit >= cd->cd_ndevs) {
290 		/*
291 		 * Need to expand the array.
292 		 */
293 		int old = cd->cd_ndevs, oldbytes, new, newbytes;
294 		void **nsp;
295 
296 		if (old == 0) {
297 			new = max(MINALLOCSIZE / sizeof(void *),
298 				  dev->dv_unit + 1);
299 		        newbytes = new * sizeof(void *);
300 			nsp = malloc(newbytes, M_DEVBUF, M_NOWAIT);
301 			if (!nsp)
302 			    panic("config_attach: expanding dev array");
303 			bzero(nsp, newbytes);
304 		} else {
305 			new = cd->cd_ndevs;
306 			do {
307 				new *= 2;
308 			} while (new <= dev->dv_unit);
309 			oldbytes = old * sizeof(void *);
310 			newbytes = new * sizeof(void *);
311 			nsp = malloc(newbytes, M_DEVBUF, M_NOWAIT);
312 			if (!nsp)
313 			    panic("config_attach: expanding dev array (2)");
314 			bcopy(cd->cd_devs, nsp, oldbytes);
315 			bzero(&nsp[old], newbytes - oldbytes);
316 			free(cd->cd_devs, M_DEVBUF);
317 		}
318 		cd->cd_ndevs = new;
319 		cd->cd_devs = nsp;
320 	}
321 	if (cd->cd_devs[dev->dv_unit])
322 		panic("config_attach: duplicate %s", dev->dv_xname);
323 	cd->cd_devs[dev->dv_unit] = dev;
324 
325 	/*
326 	 * Before attaching, clobber any unfound devices that are
327 	 * otherwise identical.
328 	 */
329 	for (cf = cfdata; cf->cf_driver; cf++)
330 		if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit &&
331 		    cf->cf_fstate == FSTATE_NOTFOUND)
332 			cf->cf_fstate = FSTATE_FOUND;
333 	(*cd->cd_attach)(parent, dev, aux);
334 }
335 
336 /*
337  * Attach an event.  These must come from initially-zero space (see
338  * commented-out assignments below), but that occurs naturally for
339  * device instance variables.
340  */
341 void
342 evcnt_attach(dev, name, ev)
343 	struct device *dev;
344 	const char *name;
345 	struct evcnt *ev;
346 {
347 	static struct evcnt **nextp = &allevents;
348 
349 #ifdef DIAGNOSTIC
350 	if (strlen(name) >= sizeof(ev->ev_name))
351 		panic("evcnt_attach");
352 #endif
353 	/* ev->ev_next = NULL; */
354 	ev->ev_dev = dev;
355 	/* ev->ev_count = 0; */
356 	strcpy(ev->ev_name, name);
357 	*nextp = ev;
358 	nextp = &ev->ev_next;
359 }
360