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 */ 71 extern short cfroots[]; 72 73 #define ROOT ((struct device *)NULL) 74 75 struct matchinfo { 76 cfmatch_t fn; 77 struct device *parent; 78 void *match, *aux; 79 int indirect, pri; 80 }; 81 82 struct cftable_head allcftables; 83 84 static struct cftable staticcftable = { 85 cfdata 86 }; 87 88 static char *number __P((char *, int)); 89 static void mapply __P((struct matchinfo *, struct cfdata *)); 90 91 struct devicelist alldevs; /* list of all devices */ 92 struct 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 */ 99 void 100 config_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 */ 113 static void 114 mapply(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 */ 158 void * 159 config_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 */ 202 void 203 config_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 */ 241 void * 242 config_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 270 char *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 */ 280 struct device * 281 config_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 */ 299 struct device * 300 config_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 */ 313 static char * 314 number(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 */ 331 struct device * 332 config_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 392 struct device * 393 config_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 */ 465 void 466 evcnt_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 483 typedef int (*cond_predicate_t) __P((struct device*, void*)); 484 485 static int haschild __P((struct device *)); 486 static int detach_devices __P((cond_predicate_t, void *, 487 config_detach_callback_t, void *)); 488 489 static int 490 haschild(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 504 static int 505 detach_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 579 int dev_matches_cfdata __P((struct device *dev, void *)); 580 581 int 582 dev_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 596 int 597 config_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 605 int 606 attach_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 634 static int 635 devcf_intable __P((struct device *, void *)); 636 637 static int 638 devcf_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 652 int 653 detach_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