subr_autoconf.c revision 1.14
1/* $OpenBSD: subr_autoconf.c,v 1.14 1996/11/21 12:47:15 mickey Exp $ */ 2/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */ 3 4/* 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This software was developed by the Computer Systems Engineering group 9 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 10 * contributed to Berkeley. 11 * 12 * All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by the University of 15 * California, Lawrence Berkeley Laboratories. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. All advertising materials mentioning features or use of this software 26 * must display the following acknowledgement: 27 * This product includes software developed by the University of 28 * California, Berkeley and its contributors. 29 * 4. Neither the name of the University nor the names of its contributors 30 * may be used to endorse or promote products derived from this software 31 * without specific prior written permission. 32 * 33 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 34 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 35 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 37 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 38 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 39 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 40 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 41 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 42 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 43 * SUCH DAMAGE. 44 * 45 * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL) 46 * 47 * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 48 */ 49 50#include <sys/param.h> 51#include <sys/device.h> 52#include <sys/malloc.h> 53#include <sys/systm.h> 54#include <machine/limits.h> 55/* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> */ 56#include <sys/queue.h> 57 58/* Bleh! Need device_register proto */ 59#ifdef __alpha__ 60#include <machine/autoconf.h> 61#endif /* __alpha__ */ 62 63/* 64 * Autoconfiguration subroutines. 65 */ 66 67/* 68 * ioconf.c exports exactly two names: cfdata and cfroots. All system 69 * devices and drivers are found via these tables. 70 */ 71extern short cfroots[]; 72 73#define ROOT ((struct device *)NULL) 74 75struct matchinfo { 76 cfmatch_t fn; 77 struct device *parent; 78 void *match, *aux; 79 int indirect, pri; 80}; 81 82struct cftable_head allcftables; 83 84static struct cftable staticcftable = { 85 cfdata 86}; 87 88static char *number __P((char *, int)); 89static void mapply __P((struct matchinfo *, struct cfdata *)); 90 91struct devicelist alldevs; /* list of all devices */ 92struct evcntlist allevents; /* list of all event counters */ 93 94/* 95 * Initialize autoconfiguration data structures. This occurs before console 96 * initialization as that might require use of this subsystem. Furthermore 97 * this means that malloc et al. isn't yet available. 98 */ 99void 100config_init() 101{ 102 103 TAILQ_INIT(&alldevs); 104 TAILQ_INIT(&allevents); 105 TAILQ_INIT(&allcftables); 106 TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list); 107} 108 109/* 110 * Apply the matching function and choose the best. This is used 111 * a few times and we want to keep the code small. 112 */ 113static void 114mapply(m, cf) 115 register struct matchinfo *m; 116 register struct cfdata *cf; 117{ 118 register int pri; 119 void *match; 120 121 if (m->indirect) 122 match = config_make_softc(m->parent, cf); 123 else 124 match = cf; 125 126 if (m->fn != NULL) 127 pri = (*m->fn)(m->parent, match, m->aux); 128 else { 129 if (cf->cf_attach->ca_match == NULL) { 130 panic("mapply: no match function for '%s' device\n", 131 cf->cf_driver->cd_name); 132 } 133 pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux); 134 } 135 136 if (pri > m->pri) { 137 if (m->indirect && m->match) 138 free(m->match, M_DEVBUF); 139 m->match = match; 140 m->pri = pri; 141 } else { 142 if (m->indirect) 143 free(match, M_DEVBUF); 144 } 145} 146 147/* 148 * Iterate over all potential children of some device, calling the given 149 * function (default being the child's match function) for each one. 150 * Nonzero returns are matches; the highest value returned is considered 151 * the best match. Return the `found child' if we got a match, or NULL 152 * otherwise. The `aux' pointer is simply passed on through. 153 * 154 * Note that this function is designed so that it can be used to apply 155 * an arbitrary function to all potential children (its return value 156 * can be ignored). 157 */ 158void * 159config_search(fn, parent, aux) 160 cfmatch_t fn; 161 register struct device *parent; 162 void *aux; 163{ 164 register struct cfdata *cf; 165 register short *p; 166 struct matchinfo m; 167 struct cftable *t; 168 169 m.fn = fn; 170 m.parent = parent; 171 m.match = NULL; 172 m.aux = aux; 173 m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 174 m.pri = 0; 175 for(t = allcftables.tqh_first; t; t = t->list.tqe_next){ 176 for (cf = t->tab; cf->cf_driver; cf++) { 177 /* 178 * Skip cf if no longer eligible, otherwise scan through 179 * parents for one matching `parent', and try match function. 180 */ 181 if (cf->cf_fstate == FSTATE_FOUND) 182 continue; 183 if (cf->cf_fstate == FSTATE_DNOTFOUND || 184 cf->cf_fstate == FSTATE_DSTAR) 185 continue; 186 for (p = cf->cf_parents; *p >= 0; p++) 187 if (parent->dv_cfdata == &(t->tab)[*p]) 188 mapply(&m, cf); 189 } 190 } 191 return (m.match); 192} 193 194/* 195 * Iterate over all potential children of some device, calling the given 196 * function for each one. 197 * 198 * Note that this function is designed so that it can be used to apply 199 * an arbitrary function to all potential children (its return value 200 * can be ignored). 201 */ 202void 203config_scan(fn, parent) 204 cfscan_t fn; 205 register struct device *parent; 206{ 207 register struct cfdata *cf; 208 register short *p; 209 void *match; 210 int indirect; 211 struct cftable *t; 212 213 indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 214 for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { 215 for (cf = t->tab; cf->cf_driver; cf++) { 216 /* 217 * Skip cf if no longer eligible, otherwise scan through 218 * parents for one matching `parent', and try match function. 219 */ 220 if (cf->cf_fstate == FSTATE_FOUND) 221 continue; 222 if (cf->cf_fstate == FSTATE_DNOTFOUND || 223 cf->cf_fstate == FSTATE_DSTAR) 224 continue; 225 for (p = cf->cf_parents; *p >= 0; p++) 226 if (parent->dv_cfdata == &(t->tab)[*p]) { 227 if (indirect) 228 match = config_make_softc(parent, cf); 229 else 230 match = cf; 231 (*fn)(parent, match); 232 } 233 } 234 } 235} 236 237/* 238 * Find the given root device. 239 * This is much like config_search, but there is no parent. 240 */ 241void * 242config_rootsearch(fn, rootname, aux) 243 register cfmatch_t fn; 244 register char *rootname; 245 register void *aux; 246{ 247 register struct cfdata *cf; 248 register short *p; 249 struct matchinfo m; 250 251 m.fn = fn; 252 m.parent = ROOT; 253 m.match = NULL; 254 m.aux = aux; 255 m.indirect = 0; 256 m.pri = 0; 257 /* 258 * Look at root entries for matching name. We do not bother 259 * with found-state here since only one root should ever be 260 * searched (and it must be done first). 261 */ 262 for (p = cfroots; *p >= 0; p++) { 263 cf = &cfdata[*p]; 264 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 265 mapply(&m, cf); 266 } 267 return (m.match); 268} 269 270char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 271 272/* 273 * The given `aux' argument describes a device that has been found 274 * on the given parent, but not necessarily configured. Locate the 275 * configuration data for that device (using the submatch function 276 * provided, or using candidates' cd_match configuration driver 277 * functions) and attach it, and return true. If the device was 278 * not configured, call the given `print' function and return 0. 279 */ 280struct device * 281config_found_sm(parent, aux, print, submatch) 282 struct device *parent; 283 void *aux; 284 cfprint_t print; 285 cfmatch_t submatch; 286{ 287 void *match; 288 289 if ((match = config_search(submatch, parent, aux)) != NULL) 290 return (config_attach(parent, match, aux, print)); 291 if (print) 292 printf(msgs[(*print)(aux, parent->dv_xname)]); 293 return (NULL); 294} 295 296/* 297 * As above, but for root devices. 298 */ 299struct device * 300config_rootfound(rootname, aux) 301 char *rootname; 302 void *aux; 303{ 304 void *match; 305 306 if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 307 return (config_attach(ROOT, match, aux, (cfprint_t)NULL)); 308 printf("root device %s not configured\n", rootname); 309 return (NULL); 310} 311 312/* just like sprintf(buf, "%d") except that it works from the end */ 313static char * 314number(ep, n) 315 register char *ep; 316 register int n; 317{ 318 319 *--ep = 0; 320 while (n >= 10) { 321 *--ep = (n % 10) + '0'; 322 n /= 10; 323 } 324 *--ep = n + '0'; 325 return (ep); 326} 327 328/* 329 * Attach a found device. Allocates memory for device variables. 330 */ 331struct device * 332config_attach(parent, match, aux, print) 333 register struct device *parent; 334 void *match; 335 register void *aux; 336 cfprint_t print; 337{ 338 register struct cfdata *cf; 339 register struct device *dev; 340 register struct cfdriver *cd; 341 register struct cfattach *ca; 342 struct cftable *t; 343 344 if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { 345 dev = match; 346 cf = dev->dv_cfdata; 347 } else { 348 cf = match; 349 dev = config_make_softc(parent, cf); 350 } 351 352 cd = cf->cf_driver; 353 ca = cf->cf_attach; 354 cd->cd_devs[cf->cf_unit] = dev; 355 356 if (cf->cf_fstate == FSTATE_STAR) 357 cf->cf_unit++; 358 else 359 cf->cf_fstate = FSTATE_FOUND; 360 361 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); 362 363 if (parent == ROOT) 364 printf("%s (root)", dev->dv_xname); 365 else { 366 printf("%s at %s", dev->dv_xname, parent->dv_xname); 367 if (print) 368 (void) (*print)(aux, (char *)0); 369 } 370 371 /* 372 * Before attaching, clobber any unfound devices that are 373 * otherwise identical, or bump the unit number on all starred 374 * cfdata for this device. 375 */ 376 for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { 377 for (cf = t->tab; cf->cf_driver; cf++) 378 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { 379 if (cf->cf_fstate == FSTATE_NOTFOUND) 380 cf->cf_fstate = FSTATE_FOUND; 381 if (cf->cf_fstate == FSTATE_STAR) 382 cf->cf_unit++; 383 } 384 } 385#ifdef __alpha__ 386 device_register(dev, aux); 387#endif 388 (*ca->ca_attach)(parent, dev, aux); 389 return (dev); 390} 391 392struct device * 393config_make_softc(parent, cf) 394 struct device *parent; 395 struct cfdata *cf; 396{ 397 register struct device *dev; 398 register struct cfdriver *cd; 399 register struct cfattach *ca; 400 register size_t lname, lunit; 401 register char *xunit; 402 char num[10]; 403 404 cd = cf->cf_driver; 405 ca = cf->cf_attach; 406 if (ca->ca_devsize < sizeof(struct device)) 407 panic("config_make_softc"); 408 409 /* compute length of name and decimal expansion of unit number */ 410 lname = strlen(cd->cd_name); 411 xunit = number(&num[sizeof num], cf->cf_unit); 412 lunit = &num[sizeof num] - xunit; 413 if (lname + lunit >= sizeof(dev->dv_xname)) 414 panic("config_attach: device name too long"); 415 416 /* get memory for all device vars */ 417 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT); 418 if (!dev) 419 panic("config_attach: memory allocation for device softc failed"); 420 bzero(dev, ca->ca_devsize); 421 dev->dv_class = cd->cd_class; 422 dev->dv_cfdata = cf; 423 dev->dv_unit = cf->cf_unit; 424 bcopy(cd->cd_name, dev->dv_xname, lname); 425 bcopy(xunit, dev->dv_xname + lname, lunit); 426 dev->dv_parent = parent; 427 428 /* put this device in the devices array */ 429 if (dev->dv_unit >= cd->cd_ndevs) { 430 /* 431 * Need to expand the array. 432 */ 433 int old = cd->cd_ndevs, new; 434 void **nsp; 435 436 if (old == 0) 437 new = MINALLOCSIZE / sizeof(void *); 438 else 439 new = old * 2; 440 while (new <= dev->dv_unit) 441 new *= 2; 442 cd->cd_ndevs = new; 443 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT); 444 if (nsp == 0) 445 panic("config_attach: %sing dev array", 446 old != 0 ? "expand" : "creat"); 447 bzero(nsp + old, (new - old) * sizeof(void *)); 448 if (old != 0) { 449 bcopy(cd->cd_devs, nsp, old * sizeof(void *)); 450 free(cd->cd_devs, M_DEVBUF); 451 } 452 cd->cd_devs = nsp; 453 } 454 if (cd->cd_devs[dev->dv_unit]) 455 panic("config_attach: duplicate %s", dev->dv_xname); 456 457 return (dev); 458} 459 460/* 461 * Attach an event. These must come from initially-zero space (see 462 * commented-out assignments below), but that occurs naturally for 463 * device instance variables. 464 */ 465void 466evcnt_attach(dev, name, ev) 467 struct device *dev; 468 const char *name; 469 struct evcnt *ev; 470{ 471 472#ifdef DIAGNOSTIC 473 if (strlen(name) >= sizeof(ev->ev_name)) 474 panic("evcnt_attach"); 475#endif 476 /* ev->ev_next = NULL; */ 477 ev->ev_dev = dev; 478 /* ev->ev_count = 0; */ 479 strcpy(ev->ev_name, name); 480 TAILQ_INSERT_TAIL(&allevents, ev, ev_list); 481} 482 483typedef int (*cond_predicate_t) __P((struct device*, void*)); 484 485static int haschild __P((struct device *)); 486static int detach_devices __P((cond_predicate_t, void *, 487 config_detach_callback_t, void *)); 488 489static int 490haschild(dev) 491 struct device *dev; 492{ 493 struct device *d; 494 495 for (d = alldevs.tqh_first; 496 d != NULL; 497 d = d->dv_list.tqe_next) { 498 if (d->dv_parent == dev) 499 return(1); 500 } 501 return(0); 502} 503 504static int 505detach_devices(cond, condarg, callback, arg) 506 cond_predicate_t cond; 507 void *condarg; 508 config_detach_callback_t callback; 509 void *arg; 510{ 511 struct device *d; 512 int alldone = 1; 513 514 /* 515 * XXX should use circleq and run around the list backwards 516 * to allow for predicates to match children. 517 */ 518 d = alldevs.tqh_first; 519 while (d != NULL) { 520 if ((*cond)(d, condarg)) { 521 struct cfdriver *drv = d->dv_cfdata->cf_driver; 522 523 /* device not busy? */ 524 /* driver's detach routine decides, upper 525 layer (eg bus dependent code) is notified 526 via callback */ 527#ifdef DEBUG 528 printf("trying to detach device %s (%p)\n", 529 d->dv_xname, d); 530#endif 531 if (!haschild(d) && 532 d->dv_cfdata->cf_attach->ca_detach && 533 ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) { 534 int needit, i; 535 struct device *help; 536 537 if (callback) 538 (*callback)(d, arg); 539 540 /* remove reference in driver's devicelist */ 541 if ((d->dv_unit >= drv->cd_ndevs) || 542 (drv->cd_devs[d->dv_unit]!=d)) 543 panic("bad unit in detach_devices"); 544 drv->cd_devs[d->dv_unit] = NULL; 545 546 /* driver is not needed anymore? */ 547 needit = 0; 548 for(i = 0; i<drv->cd_ndevs; i++) 549 if (drv->cd_devs[i]) 550 needit = 1; 551 552 if (!needit) { 553 /* free devices array (alloc'd 554 in config_make_softc) */ 555 free(drv->cd_devs, M_DEVBUF); 556 drv->cd_ndevs = 0; 557 } 558 559 /* remove entry in global device list */ 560 help = d->dv_list.tqe_next; 561 TAILQ_REMOVE(&alldevs, d, dv_list); 562#ifdef DEBUG 563 printf("%s removed\n", d->dv_xname); 564#endif 565 d->dv_cfdata->cf_fstate = FSTATE_NOTFOUND; 566 /* free memory for dev data (alloc'd 567 in config_make_softc) */ 568 free(d, M_DEVBUF); 569 d = help; 570 continue; 571 } else 572 alldone = 0; 573 } 574 d = d->dv_list.tqe_next; 575 } 576 return(!alldone); 577} 578 579int dev_matches_cfdata __P((struct device *dev, void *)); 580 581int 582dev_matches_cfdata(dev, arg) 583 struct device *dev; 584 void *arg; 585{ 586 struct cfdata *cfdata = arg; 587 return(/* device uses same driver ? */ 588 (dev->dv_cfdata->cf_driver == cfdata->cf_driver) 589 /* device instance described by this cfdata? */ 590 && ((cfdata->cf_fstate == FSTATE_STAR) 591 || ((cfdata->cf_fstate == FSTATE_FOUND) 592 && (dev->dv_unit == cfdata->cf_unit))) 593 ); 594} 595 596int 597config_detach(cf, callback, arg) 598 struct cfdata *cf; 599 config_detach_callback_t callback; 600 void *arg; 601{ 602 return(detach_devices(dev_matches_cfdata, cf, callback, arg)); 603} 604 605int 606attach_loadable(parentname, parentunit, cftable) 607 char *parentname; 608 int parentunit; 609 struct cftable *cftable; 610{ 611 int found = 0; 612 struct device *d; 613 614 TAILQ_INSERT_TAIL(&allcftables, cftable, list); 615 616 for(d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) { 617 struct cfdriver *drv = d->dv_cfdata->cf_driver; 618 619 if (strcmp(parentname, drv->cd_name) == NULL && 620 (parentunit == -1 || parentunit == d->dv_unit)) { 621 int s; 622 623 s = splhigh(); /* ??? */ 624 found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d, 625 &(cftable->tab[0])); 626 splx(s); 627 } 628 } 629 if (!found) 630 TAILQ_REMOVE(&allcftables, cftable, list); 631 return(found); 632} 633 634static int 635devcf_intable __P((struct device *, void *)); 636 637static int 638devcf_intable(dev, arg) 639 struct device *dev; 640 void *arg; 641{ 642 struct cftable *tbl = arg; 643 struct cfdata *cf; 644 645 for(cf = tbl->tab; cf->cf_driver; cf++) { 646 if (dev->dv_cfdata == cf) 647 return(1); 648 } 649 return(0); 650} 651 652int 653detach_loadable(cftable) 654 struct cftable *cftable; 655{ 656 if (!detach_devices(devcf_intable, cftable, 0, 0)) 657 return(0); 658 TAILQ_REMOVE(&allcftables, cftable, list); 659 return(1); 660} 661