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