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