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