subr_autoconf.c revision 1.1 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.1 1993/08/13 13:19:59 glass Exp $ (LBL)
19 */
20
21 #include <sys/param.h>
22 #include <sys/device.h>
23 #include <sys/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 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
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 *
79 config_search(fn, parent, aux)
80 cfmatch_t fn;
81 register struct device *parent;
82 void *aux;
83 {
84 register struct cfdata *cf;
85 register short *p;
86 struct matchinfo m;
87
88 m.fn = fn;
89 m.parent = parent;
90 m.aux = aux;
91 m.match = NULL;
92 m.pri = 0;
93 for (cf = cfdata; cf->cf_driver; cf++) {
94 /*
95 * Skip cf if no longer eligible, otherwise scan through
96 * parents for one matching `parent', and try match function.
97 */
98 if (cf->cf_fstate == FSTATE_FOUND)
99 continue;
100 for (p = cf->cf_parents; *p >= 0; p++)
101 if (parent->dv_cfdata == &cfdata[*p])
102 mapply(&m, cf);
103 }
104 return (m.match);
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 {
117 register struct cfdata *cf;
118 register short *p;
119 struct matchinfo m;
120
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
128 * with found-state here since only one root should ever be
129 * searched (and it must be done first).
130 */
131 for (p = cfroots; *p >= 0; p++) {
132 cf = &cfdata[*p];
133 if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
134 mapply(&m, cf);
135 }
136 return (m.match);
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;
212 int myunit;
213 char num[10];
214 static struct device **nextp = &alldevs;
215
216 cd = cf->cf_driver;
217 if (cd->cd_devsize < sizeof(struct device))
218 panic("config_attach");
219 myunit = cf->cf_unit;
220 if (cf->cf_fstate == FSTATE_NOTFOUND)
221 cf->cf_fstate = FSTATE_FOUND;
222 else
223 cf->cf_unit++;
224
225 /* compute length of name and decimal expansion of unit number */
226 lname = strlen(cd->cd_name);
227 xunit = number(&num[sizeof num], myunit);
228 lunit = &num[sizeof num] - xunit;
229 if (lname + lunit >= sizeof(dev->dv_xname))
230 panic("config_attach: device name too long");
231
232 /* get memory for all device vars */
233 dev = (struct device *)malloc(cd->cd_devsize, M_DEVBUF, M_WAITOK);
234 /* XXX cannot wait! */
235 bzero(dev, cd->cd_devsize);
236 *nextp = dev; /* link up */
237 nextp = &dev->dv_next;
238 dev->dv_class = cd->cd_class;
239 dev->dv_cfdata = cf;
240 dev->dv_unit = myunit;
241 bcopy(cd->cd_name, dev->dv_xname, lname);
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 }
278 if (cd->cd_devs[dev->dv_unit])
279 panic("config_attach: duplicate %s", dev->dv_xname);
280 cd->cd_devs[dev->dv_unit] = dev;
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;
290 (*cd->cd_attach)(parent, dev, aux);
291 }
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 }
317