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