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