1 /* $NetBSD: subr_autoconf.c,v 1.22 1996/06/13 04:50:29 cgd Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratories. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. All advertising materials mentioning features or use of this software 25 * must display the following acknowledgement: 26 * This product includes software developed by the University of 27 * California, Berkeley and its contributors. 28 * 4. Neither the name of the University nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 * 44 * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL) 45 * 46 * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 47 */ 48 49 #include <sys/param.h> 50 #include <sys/device.h> 51 #include <sys/malloc.h> 52 #include <sys/systm.h> 53 #include <machine/limits.h> 54 55 /* 56 * Autoconfiguration subroutines. 57 */ 58 59 /* 60 * ioconf.c exports exactly two names: cfdata and cfroots. All system 61 * devices and drivers are found via these tables. 62 */ 63 extern struct cfdata cfdata[]; 64 extern short cfroots[]; 65 66 #define ROOT ((struct device *)NULL) 67 68 struct device *config_make_softc __P((struct device *, struct cfdata *)); 69 70 struct matchinfo { 71 cfmatch_t fn; 72 struct device *parent; 73 void *match, *aux; 74 int indirect, pri; 75 }; 76 77 static char *number __P((char *, int)); 78 static void mapply __P((struct matchinfo *, struct cfdata *)); 79 80 struct devicelist alldevs; /* list of all devices */ 81 struct evcntlist allevents; /* list of all event counters */ 82 83 /* 84 * Initialize autoconfiguration data structures. 85 */ 86 void 87 config_init() 88 { 89 90 TAILQ_INIT(&alldevs); 91 TAILQ_INIT(&allevents); 92 } 93 94 /* 95 * Apply the matching function and choose the best. This is used 96 * a few times and we want to keep the code small. 97 */ 98 static void 99 mapply(m, cf) 100 register struct matchinfo *m; 101 register struct cfdata *cf; 102 { 103 register int pri; 104 void *match; 105 106 if (m->indirect) 107 match = config_make_softc(m->parent, cf); 108 else 109 match = cf; 110 111 if (m->fn != NULL) 112 pri = (*m->fn)(m->parent, match, m->aux); 113 else { 114 if (cf->cf_attach->ca_match == NULL) { 115 panic("mapply: no match function for '%s' device\n", 116 cf->cf_driver->cd_name); 117 } 118 pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux); 119 } 120 121 if (pri > m->pri) { 122 if (m->indirect && m->match) 123 free(m->match, M_DEVBUF); 124 m->match = match; 125 m->pri = pri; 126 } else { 127 if (m->indirect) 128 free(match, M_DEVBUF); 129 } 130 } 131 132 /* 133 * Iterate over all potential children of some device, calling the given 134 * function (default being the child's match function) for each one. 135 * Nonzero returns are matches; the highest value returned is considered 136 * the best match. Return the `found child' if we got a match, or NULL 137 * otherwise. The `aux' pointer is simply passed on through. 138 * 139 * Note that this function is designed so that it can be used to apply 140 * an arbitrary function to all potential children (its return value 141 * can be ignored). 142 */ 143 void * 144 config_search(fn, parent, aux) 145 cfmatch_t fn; 146 register struct device *parent; 147 void *aux; 148 { 149 register struct cfdata *cf; 150 register short *p; 151 struct matchinfo m; 152 153 m.fn = fn; 154 m.parent = parent; 155 m.match = NULL; 156 m.aux = aux; 157 m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 158 m.pri = 0; 159 for (cf = cfdata; cf->cf_driver; cf++) { 160 /* 161 * Skip cf if no longer eligible, otherwise scan through 162 * parents for one matching `parent', and try match function. 163 */ 164 if (cf->cf_fstate == FSTATE_FOUND) 165 continue; 166 for (p = cf->cf_parents; *p >= 0; p++) 167 if (parent->dv_cfdata == &cfdata[*p]) 168 mapply(&m, cf); 169 } 170 return (m.match); 171 } 172 173 /* 174 * Iterate over all potential children of some device, calling the given 175 * function for each one. 176 * 177 * Note that this function is designed so that it can be used to apply 178 * an arbitrary function to all potential children (its return value 179 * can be ignored). 180 */ 181 void 182 config_scan(fn, parent) 183 cfscan_t fn; 184 register struct device *parent; 185 { 186 register struct cfdata *cf; 187 register short *p; 188 void *match; 189 int indirect; 190 191 indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 192 for (cf = cfdata; cf->cf_driver; cf++) { 193 /* 194 * Skip cf if no longer eligible, otherwise scan through 195 * parents for one matching `parent', and try match function. 196 */ 197 if (cf->cf_fstate == FSTATE_FOUND) 198 continue; 199 for (p = cf->cf_parents; *p >= 0; p++) 200 if (parent->dv_cfdata == &cfdata[*p]) { 201 if (indirect) 202 match = config_make_softc(parent, cf); 203 else 204 match = cf; 205 (*fn)(parent, match); 206 } 207 } 208 } 209 210 /* 211 * Find the given root device. 212 * This is much like config_search, but there is no parent. 213 */ 214 void * 215 config_rootsearch(fn, rootname, aux) 216 register cfmatch_t fn; 217 register char *rootname; 218 register void *aux; 219 { 220 register struct cfdata *cf; 221 register short *p; 222 struct matchinfo m; 223 224 m.fn = fn; 225 m.parent = ROOT; 226 m.match = NULL; 227 m.aux = aux; 228 m.indirect = 0; 229 m.pri = 0; 230 /* 231 * Look at root entries for matching name. We do not bother 232 * with found-state here since only one root should ever be 233 * searched (and it must be done first). 234 */ 235 for (p = cfroots; *p >= 0; p++) { 236 cf = &cfdata[*p]; 237 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 238 mapply(&m, cf); 239 } 240 return (m.match); 241 } 242 243 static char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 244 245 /* 246 * The given `aux' argument describes a device that has been found 247 * on the given parent, but not necessarily configured. Locate the 248 * configuration data for that device (using the submatch function 249 * provided, or using candidates' cd_match configuration driver 250 * functions) and attach it, and return true. If the device was 251 * not configured, call the given `print' function and return 0. 252 */ 253 struct device * 254 config_found_sm(parent, aux, print, submatch) 255 struct device *parent; 256 void *aux; 257 cfprint_t print; 258 cfmatch_t submatch; 259 { 260 void *match; 261 262 if ((match = config_search(submatch, parent, aux)) != NULL) 263 return (config_attach(parent, match, aux, print)); 264 if (print) 265 printf(msgs[(*print)(aux, parent->dv_xname)]); 266 return (NULL); 267 } 268 269 /* 270 * As above, but for root devices. 271 */ 272 struct device * 273 config_rootfound(rootname, aux) 274 char *rootname; 275 void *aux; 276 { 277 void *match; 278 279 if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 280 return (config_attach(ROOT, match, aux, (cfprint_t)NULL)); 281 printf("root device %s not configured\n", rootname); 282 return (NULL); 283 } 284 285 /* just like sprintf(buf, "%d") except that it works from the end */ 286 static char * 287 number(ep, n) 288 register char *ep; 289 register int n; 290 { 291 292 *--ep = 0; 293 while (n >= 10) { 294 *--ep = (n % 10) + '0'; 295 n /= 10; 296 } 297 *--ep = n + '0'; 298 return (ep); 299 } 300 301 /* 302 * Attach a found device. Allocates memory for device variables. 303 */ 304 struct device * 305 config_attach(parent, match, aux, print) 306 register struct device *parent; 307 void *match; 308 register void *aux; 309 cfprint_t print; 310 { 311 register struct cfdata *cf; 312 register struct device *dev; 313 register struct cfdriver *cd; 314 register struct cfattach *ca; 315 316 if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { 317 dev = match; 318 cf = dev->dv_cfdata; 319 } else { 320 cf = match; 321 dev = config_make_softc(parent, cf); 322 } 323 324 cd = cf->cf_driver; 325 ca = cf->cf_attach; 326 cd->cd_devs[cf->cf_unit] = dev; 327 328 if (cf->cf_fstate == FSTATE_STAR) 329 cf->cf_unit++; 330 else 331 cf->cf_fstate = FSTATE_FOUND; 332 333 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); 334 335 if (parent == ROOT) 336 printf("%s (root)", dev->dv_xname); 337 else { 338 printf("%s at %s", dev->dv_xname, parent->dv_xname); 339 if (print) 340 (void) (*print)(aux, (char *)0); 341 } 342 343 /* 344 * Before attaching, clobber any unfound devices that are 345 * otherwise identical, or bump the unit number on all starred 346 * cfdata for this device. 347 */ 348 for (cf = cfdata; cf->cf_driver; cf++) 349 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { 350 if (cf->cf_fstate == FSTATE_NOTFOUND) 351 cf->cf_fstate = FSTATE_FOUND; 352 if (cf->cf_fstate == FSTATE_STAR) 353 cf->cf_unit++; 354 } 355 #ifdef __alpha__ 356 device_register(dev, aux); 357 #endif 358 (*ca->ca_attach)(parent, dev, aux); 359 return (dev); 360 } 361 362 struct device * 363 config_make_softc(parent, cf) 364 struct device *parent; 365 struct cfdata *cf; 366 { 367 register struct device *dev; 368 register struct cfdriver *cd; 369 register struct cfattach *ca; 370 register size_t lname, lunit; 371 register char *xunit; 372 char num[10]; 373 374 cd = cf->cf_driver; 375 ca = cf->cf_attach; 376 if (ca->ca_devsize < sizeof(struct device)) 377 panic("config_make_softc"); 378 379 /* compute length of name and decimal expansion of unit number */ 380 lname = strlen(cd->cd_name); 381 xunit = number(&num[sizeof num], cf->cf_unit); 382 lunit = &num[sizeof num] - xunit; 383 if (lname + lunit >= sizeof(dev->dv_xname)) 384 panic("config_attach: device name too long"); 385 386 /* get memory for all device vars */ 387 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT); 388 if (!dev) 389 panic("config_attach: memory allocation for device softc failed"); 390 bzero(dev, ca->ca_devsize); 391 dev->dv_class = cd->cd_class; 392 dev->dv_cfdata = cf; 393 dev->dv_unit = cf->cf_unit; 394 bcopy(cd->cd_name, dev->dv_xname, lname); 395 bcopy(xunit, dev->dv_xname + lname, lunit); 396 dev->dv_parent = parent; 397 398 /* put this device in the devices array */ 399 if (dev->dv_unit >= cd->cd_ndevs) { 400 /* 401 * Need to expand the array. 402 */ 403 int old = cd->cd_ndevs, new; 404 void **nsp; 405 406 if (old == 0) 407 new = MINALLOCSIZE / sizeof(void *); 408 else 409 new = old * 2; 410 while (new <= dev->dv_unit) 411 new *= 2; 412 cd->cd_ndevs = new; 413 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT); 414 if (nsp == 0) 415 panic("config_attach: %sing dev array", 416 old != 0 ? "expand" : "creat"); 417 bzero(nsp + old, (new - old) * sizeof(void *)); 418 if (old != 0) { 419 bcopy(cd->cd_devs, nsp, old * sizeof(void *)); 420 free(cd->cd_devs, M_DEVBUF); 421 } 422 cd->cd_devs = nsp; 423 } 424 if (cd->cd_devs[dev->dv_unit]) 425 panic("config_attach: duplicate %s", dev->dv_xname); 426 427 return (dev); 428 } 429 430 /* 431 * Attach an event. These must come from initially-zero space (see 432 * commented-out assignments below), but that occurs naturally for 433 * device instance variables. 434 */ 435 void 436 evcnt_attach(dev, name, ev) 437 struct device *dev; 438 const char *name; 439 struct evcnt *ev; 440 { 441 442 #ifdef DIAGNOSTIC 443 if (strlen(name) >= sizeof(ev->ev_name)) 444 panic("evcnt_attach"); 445 #endif 446 /* ev->ev_next = NULL; */ 447 ev->ev_dev = dev; 448 /* ev->ev_count = 0; */ 449 strcpy(ev->ev_name, name); 450 TAILQ_INSERT_TAIL(&allevents, ev, ev_list); 451 } 452