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 KM |
8 | * |
9 | * %sccs.include.redist.c% | |
10 | * | |
d7821156 | 11 | * @(#)subr_autoconf.c 7.3 (Berkeley) %G% |
bad76f5e | 12 | * |
d7821156 | 13 | * from: $Header: subr_autoconf.c,v 1.6 92/06/11 17:56:19 torek Exp $ (LBL) |
bad76f5e KM |
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 | ||
d7821156 KM |
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 | ||
bad76f5e KM |
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 * | |
d7821156 KM |
69 | config_search(fn, parent, aux) |
70 | cfmatch_t fn; | |
71 | register struct device *parent; | |
72 | void *aux; | |
bad76f5e | 73 | { |
d7821156 | 74 | register struct cfdata *cf, *pcf; |
bad76f5e | 75 | register short *p; |
d7821156 | 76 | struct matchinfo m; |
bad76f5e | 77 | |
d7821156 KM |
78 | m.fn = fn; |
79 | m.parent = parent; | |
80 | m.aux = aux; | |
81 | m.match = NULL; | |
82 | m.pri = 0; | |
bad76f5e KM |
83 | for (cf = cfdata; cf->cf_driver; cf++) { |
84 | /* | |
85 | * Skip cf if no longer eligible, or if a root entry. | |
d7821156 | 86 | * Otherwise scan through parents for one matching `parent' |
bad76f5e KM |
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++]; | |
d7821156 KM |
94 | if (parent->dv_cfdata == pcf) |
95 | mapply(&m, cf); | |
bad76f5e KM |
96 | } |
97 | } | |
d7821156 | 98 | return (m.match); |
bad76f5e KM |
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 | { | |
d7821156 KM |
111 | register struct cfdata *cf; |
112 | struct matchinfo m; | |
bad76f5e | 113 | |
d7821156 KM |
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); | |
bad76f5e KM |
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; | |
d7821156 | 203 | int myunit; |
bad76f5e | 204 | char num[10]; |
d7821156 | 205 | static struct device **nextp = &alldevs; |
bad76f5e KM |
206 | |
207 | cd = cf->cf_driver; | |
208 | if (cd->cd_devsize < sizeof(struct device)) | |
209 | panic("config_attach"); | |
d7821156 | 210 | myunit = cf->cf_unit; |
bad76f5e KM |
211 | if (cf->cf_fstate == FSTATE_NOTFOUND) |
212 | cf->cf_fstate = FSTATE_FOUND; | |
d7821156 KM |
213 | else |
214 | cf->cf_unit++; | |
bad76f5e KM |
215 | |
216 | /* compute length of name and decimal expansion of unit number */ | |
217 | lname = strlen(cd->cd_name); | |
d7821156 | 218 | xunit = number(&num[sizeof num], myunit); |
bad76f5e KM |
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); | |
d7821156 KM |
225 | *nextp = dev; /* link up */ |
226 | nextp = &dev->dv_next; | |
227 | dev->dv_class = cd->cd_class; | |
228 | dev->dv_cfdata = cf; | |
bad76f5e | 229 | dev->dv_name = cd->cd_name; |
d7821156 | 230 | dev->dv_unit = myunit; |
bad76f5e KM |
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 | } | |
d7821156 KM |
269 | if (cd->cd_devs[dev->dv_unit]) |
270 | panic("config_attach: duplicate %s", dev->dv_xname); | |
bad76f5e KM |
271 | cd->cd_devs[dev->dv_unit] = dev; |
272 | (*cd->cd_attach)(parent, dev, aux); | |
273 | } |