Commit | Line | Data |
---|---|---|
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 | */ | |
33 | extern struct cfdata cfdata[]; | |
34 | extern short cfroots[]; | |
bad76f5e KM |
35 | |
36 | #define ROOT ((struct device *)NULL) | |
37 | ||
d7821156 KM |
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 | 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 | */ | |
78 | struct cfdata * | |
d7821156 KM |
79 | config_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 | */ | |
111 | struct cfdata * | |
112 | config_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 | ||
139 | static 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 | */ | |
148 | int | |
149 | config_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 | */ | |
167 | int | |
168 | config_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 */ | |
183 | static char * | |
184 | number(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 | */ | |
201 | void | |
202 | config_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 | */ | |
298 | void | |
299 | evcnt_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 | } |