merge in changes for Sparc
[unix-history] / usr / src / sys / kern / subr_autoconf.c
CommitLineData
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
24extern struct cfdata cfdata[]; /* from ioconf.c */
25
26#define ROOT ((struct device *)NULL)
27
d7821156
KM
28struct 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 */
40static void
41mapply(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 */
68struct cfdata *
d7821156
KM
69config_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 */
105struct cfdata *
106config_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
130static 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 */
139int
140config_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 */
158int
159config_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 */
174static char *
175number(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 */
192void
193config_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}