subr_autoconf.c revision 1.21
1/* $OpenBSD: subr_autoconf.c,v 1.21 1998/05/11 09:59:39 niklas 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#if defined(__alpha__) || defined(hp300) 60#include <machine/autoconf.h> 61#endif /* __alpha__ || hp300 */ 62 63/* 64 * Autoconfiguration subroutines. 65 */ 66 67typedef int (*cond_predicate_t) __P((struct device *, void *)); 68 69/* 70 * ioconf.c exports exactly two names: cfdata and cfroots. All system 71 * devices and drivers are found via these tables. 72 */ 73extern short cfroots[]; 74 75#define ROOT ((struct device *)NULL) 76 77struct matchinfo { 78 cfmatch_t fn; 79 struct device *parent; 80 void *match, *aux; 81 int indirect, pri; 82}; 83 84struct cftable_head allcftables; 85 86static struct cftable staticcftable = { 87 cfdata 88}; 89 90#ifndef AUTOCONF_VERBOSE 91#define AUTOCONF_VERBOSE 0 92#endif /* AUTOCONF_VERBOSE */ 93int autoconf_verbose = AUTOCONF_VERBOSE; /* trace probe calls */ 94 95static char *number __P((char *, int)); 96static void mapply __P((struct matchinfo *, struct cfdata *)); 97static int haschild __P((struct device *)); 98int detach_devices __P((cond_predicate_t, void *, 99 config_detach_callback_t, void *)); 100int dev_matches_cfdata __P((struct device *dev, void *)); 101int parentdev_matches_cfdata __P((struct device *dev, void *)); 102 103 104struct devicelist alldevs; /* list of all devices */ 105struct evcntlist allevents; /* list of all event counters */ 106 107/* 108 * Initialize autoconfiguration data structures. This occurs before console 109 * initialization as that might require use of this subsystem. Furthermore 110 * this means that malloc et al. isn't yet available. 111 */ 112void 113config_init() 114{ 115 116 TAILQ_INIT(&alldevs); 117 TAILQ_INIT(&allevents); 118 TAILQ_INIT(&allcftables); 119 TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list); 120} 121 122/* 123 * Apply the matching function and choose the best. This is used 124 * a few times and we want to keep the code small. 125 */ 126static void 127mapply(m, cf) 128 register struct matchinfo *m; 129 register struct cfdata *cf; 130{ 131 register int pri; 132 void *match; 133 134 if (m->indirect) 135 match = config_make_softc(m->parent, cf); 136 else 137 match = cf; 138 139 if (autoconf_verbose) { 140 printf(">>> probing for %s", cf->cf_driver->cd_name); 141 if (cf->cf_fstate == FSTATE_STAR) 142 printf("*\n"); 143 else 144 printf("%d\n", cf->cf_unit); 145 } 146 if (m->fn != NULL) 147 pri = (*m->fn)(m->parent, match, m->aux); 148 else { 149 if (cf->cf_attach->ca_match == NULL) { 150 panic("mapply: no match function for '%s' device\n", 151 cf->cf_driver->cd_name); 152 } 153 pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux); 154 } 155 if (autoconf_verbose) 156 printf(">>> %s probe returned %d\n", cf->cf_driver->cd_name, 157 pri); 158 159 if (pri > m->pri) { 160 if (m->indirect && m->match) 161 free(m->match, M_DEVBUF); 162 m->match = match; 163 m->pri = pri; 164 } else { 165 if (m->indirect) 166 free(match, M_DEVBUF); 167 } 168} 169 170/* 171 * Iterate over all potential children of some device, calling the given 172 * function (default being the child's match function) for each one. 173 * Nonzero returns are matches; the highest value returned is considered 174 * the best match. Return the `found child' if we got a match, or NULL 175 * otherwise. The `aux' pointer is simply passed on through. 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 */ 181void * 182config_search(fn, parent, aux) 183 cfmatch_t fn; 184 register struct device *parent; 185 void *aux; 186{ 187 register struct cfdata *cf; 188 register short *p; 189 struct matchinfo m; 190 struct cftable *t; 191 192 m.fn = fn; 193 m.parent = parent; 194 m.match = NULL; 195 m.aux = aux; 196 m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 197 m.pri = 0; 198 for(t = allcftables.tqh_first; t; t = t->list.tqe_next) { 199 for (cf = t->tab; cf->cf_driver; cf++) { 200 /* 201 * Skip cf if no longer eligible, otherwise scan 202 * through parents for one matching `parent', 203 * and try match function. 204 */ 205 if (cf->cf_fstate == FSTATE_FOUND) 206 continue; 207 if (cf->cf_fstate == FSTATE_DNOTFOUND || 208 cf->cf_fstate == FSTATE_DSTAR) 209 continue; 210 for (p = cf->cf_parents; *p >= 0; p++) 211 if (parent->dv_cfdata == &(t->tab)[*p]) 212 mapply(&m, cf); 213 } 214 } 215 if (autoconf_verbose) { 216 if (m.match) 217 printf(">>> %s probe won\n", 218 ((struct cfdata *)m.match)->cf_driver->cd_name); 219 else 220 printf(">>> no winning probe\n"); 221 } 222 return (m.match); 223} 224 225/* 226 * Iterate over all potential children of some device, calling the given 227 * function for each one. 228 * 229 * Note that this function is designed so that it can be used to apply 230 * an arbitrary function to all potential children (its return value 231 * can be ignored). 232 */ 233void 234config_scan(fn, parent) 235 cfscan_t fn; 236 register struct device *parent; 237{ 238 register struct cfdata *cf; 239 register short *p; 240 void *match; 241 int indirect; 242 struct cftable *t; 243 244 indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 245 for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { 246 for (cf = t->tab; cf->cf_driver; cf++) { 247 /* 248 * Skip cf if no longer eligible, otherwise scan 249 * through parents for one matching `parent', 250 * and try match function. 251 */ 252 if (cf->cf_fstate == FSTATE_FOUND) 253 continue; 254 if (cf->cf_fstate == FSTATE_DNOTFOUND || 255 cf->cf_fstate == FSTATE_DSTAR) 256 continue; 257 for (p = cf->cf_parents; *p >= 0; p++) 258 if (parent->dv_cfdata == &(t->tab)[*p]) { 259 match = indirect? 260 config_make_softc(parent, cf) : 261 (void *)cf; 262 (*fn)(parent, match); 263 } 264 } 265 } 266} 267 268/* 269 * Find the given root device. 270 * This is much like config_search, but there is no parent. 271 */ 272void * 273config_rootsearch(fn, rootname, aux) 274 register cfmatch_t fn; 275 register char *rootname; 276 register void *aux; 277{ 278 register struct cfdata *cf; 279 register short *p; 280 struct matchinfo m; 281 282 m.fn = fn; 283 m.parent = ROOT; 284 m.match = NULL; 285 m.aux = aux; 286 m.indirect = 0; 287 m.pri = 0; 288 /* 289 * Look at root entries for matching name. We do not bother 290 * with found-state here since only one root should ever be 291 * searched (and it must be done first). 292 */ 293 for (p = cfroots; *p >= 0; p++) { 294 cf = &cfdata[*p]; 295 if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 296 mapply(&m, cf); 297 } 298 return (m.match); 299} 300 301char *msgs[3] = { "", " not configured\n", " unsupported\n" }; 302 303/* 304 * The given `aux' argument describes a device that has been found 305 * on the given parent, but not necessarily configured. Locate the 306 * configuration data for that device (using the submatch function 307 * provided, or using candidates' cd_match configuration driver 308 * functions) and attach it, and return true. If the device was 309 * not configured, call the given `print' function and return 0. 310 */ 311struct device * 312config_found_sm(parent, aux, print, submatch) 313 struct device *parent; 314 void *aux; 315 cfprint_t print; 316 cfmatch_t submatch; 317{ 318 void *match; 319 320 if ((match = config_search(submatch, parent, aux)) != NULL) 321 return (config_attach(parent, match, aux, print)); 322 if (print) 323 printf(msgs[(*print)(aux, parent->dv_xname)]); 324 return (NULL); 325} 326 327/* 328 * As above, but for root devices. 329 */ 330struct device * 331config_rootfound(rootname, aux) 332 char *rootname; 333 void *aux; 334{ 335 void *match; 336 337 if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 338 return (config_attach(ROOT, match, aux, (cfprint_t)NULL)); 339 printf("root device %s not configured\n", rootname); 340 return (NULL); 341} 342 343/* just like sprintf(buf, "%d") except that it works from the end */ 344static char * 345number(ep, n) 346 register char *ep; 347 register int n; 348{ 349 350 *--ep = 0; 351 while (n >= 10) { 352 *--ep = (n % 10) + '0'; 353 n /= 10; 354 } 355 *--ep = n + '0'; 356 return (ep); 357} 358 359/* 360 * Attach a found device. Allocates memory for device variables. 361 */ 362struct device * 363config_attach(parent, match, aux, print) 364 register struct device *parent; 365 void *match; 366 register void *aux; 367 cfprint_t print; 368{ 369 register struct cfdata *cf; 370 register struct device *dev; 371 register struct cfdriver *cd; 372 register struct cfattach *ca; 373 struct cftable *t; 374 375 if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { 376 dev = match; 377 cf = dev->dv_cfdata; 378 } else { 379 cf = match; 380 dev = config_make_softc(parent, cf); 381 } 382 383 cd = cf->cf_driver; 384 ca = cf->cf_attach; 385 386 cd->cd_devs[dev->dv_unit] = dev; 387 388 /* 389 * If this is a "STAR" device and we used the last unit, prepare for 390 * another one. 391 */ 392 if (cf->cf_fstate == FSTATE_STAR) { 393 if (dev->dv_unit == cf->cf_unit) 394 cf->cf_unit++; 395 } else 396 cf->cf_fstate = FSTATE_FOUND; 397 398 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); 399 400 if (parent == ROOT) 401 printf("%s (root)", dev->dv_xname); 402 else { 403 printf("%s at %s", dev->dv_xname, parent->dv_xname); 404 if (print) 405 (void) (*print)(aux, (char *)0); 406 } 407 408 /* 409 * Before attaching, clobber any unfound devices that are 410 * otherwise identical, or bump the unit number on all starred 411 * cfdata for this device. 412 */ 413 for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { 414 for (cf = t->tab; cf->cf_driver; cf++) 415 if (cf->cf_driver == cd && 416 cf->cf_unit == dev->dv_unit) { 417 if (cf->cf_fstate == FSTATE_NOTFOUND) 418 cf->cf_fstate = FSTATE_FOUND; 419 if (cf->cf_fstate == FSTATE_STAR) 420 cf->cf_unit++; 421 } 422 } 423#if defined(__alpha__) || defined(hp300) 424 device_register(dev, aux); 425#endif 426 (*ca->ca_attach)(parent, dev, aux); 427 return (dev); 428} 429 430struct device * 431config_make_softc(parent, cf) 432 struct device *parent; 433 struct cfdata *cf; 434{ 435 register struct device *dev; 436 register struct cfdriver *cd; 437 register struct cfattach *ca; 438 register size_t lname, lunit; 439 register char *xunit; 440 char num[10]; 441 442 cd = cf->cf_driver; 443 ca = cf->cf_attach; 444 if (ca->ca_devsize < sizeof(struct device)) 445 panic("config_make_softc"); 446 447 /* get memory for all device vars */ 448 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT); 449 if (!dev) 450 panic("config_make_softc: allocation for device softc failed"); 451 bzero(dev, ca->ca_devsize); 452 dev->dv_class = cd->cd_class; 453 dev->dv_cfdata = cf; 454 455 /* If this is a STAR device, search for a free unit number */ 456 if (cf->cf_fstate == FSTATE_STAR) { 457 for (dev->dv_unit = cf->cf_starunit1; 458 dev->dv_unit < cf->cf_unit; dev->dv_unit++) 459 if (cd->cd_ndevs == 0 || 460 cd->cd_devs[dev->dv_unit] == NULL) 461 break; 462 } else 463 dev->dv_unit = cf->cf_unit; 464 465 /* compute length of name and decimal expansion of unit number */ 466 lname = strlen(cd->cd_name); 467 xunit = number(&num[sizeof num], dev->dv_unit); 468 lunit = &num[sizeof num] - xunit; 469 if (lname + lunit >= sizeof(dev->dv_xname)) 470 panic("config_make_softc: device name too long"); 471 472 bcopy(cd->cd_name, dev->dv_xname, lname); 473 bcopy(xunit, dev->dv_xname + lname, lunit); 474 dev->dv_parent = parent; 475 476 /* put this device in the devices array */ 477 if (dev->dv_unit >= cd->cd_ndevs) { 478 /* 479 * Need to expand the array. 480 */ 481 int old = cd->cd_ndevs, new; 482 void **nsp; 483 484 if (old == 0) 485 new = MINALLOCSIZE / sizeof(void *); 486 else 487 new = old * 2; 488 while (new <= dev->dv_unit) 489 new *= 2; 490 cd->cd_ndevs = new; 491 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT); 492 if (nsp == 0) 493 panic("config_make_softc: %sing dev array", 494 old != 0 ? "expand" : "creat"); 495 bzero(nsp + old, (new - old) * sizeof(void *)); 496 if (old != 0) { 497 bcopy(cd->cd_devs, nsp, old * sizeof(void *)); 498 free(cd->cd_devs, M_DEVBUF); 499 } 500 cd->cd_devs = nsp; 501 } 502 if (cd->cd_devs[dev->dv_unit]) 503 panic("config_make_softc: duplicate %s", dev->dv_xname); 504 505 return (dev); 506} 507 508/* 509 * Attach an event. These must come from initially-zero space (see 510 * commented-out assignments below), but that occurs naturally for 511 * device instance variables. 512 */ 513void 514evcnt_attach(dev, name, ev) 515 struct device *dev; 516 const char *name; 517 struct evcnt *ev; 518{ 519 520#ifdef DIAGNOSTIC 521 if (strlen(name) >= sizeof(ev->ev_name)) 522 panic("evcnt_attach"); 523#endif 524 /* ev->ev_next = NULL; */ 525 ev->ev_dev = dev; 526 /* ev->ev_count = 0; */ 527 strcpy(ev->ev_name, name); 528 TAILQ_INSERT_TAIL(&allevents, ev, ev_list); 529} 530 531static int 532haschild(dev) 533 struct device *dev; 534{ 535 struct device *d; 536 537 for (d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) { 538 if (d->dv_parent == dev) 539 return(1); 540 } 541 return(0); 542} 543 544int 545detach_devices(cond, condarg, callback, arg) 546 cond_predicate_t cond; 547 void *condarg; 548 config_detach_callback_t callback; 549 void *arg; 550{ 551 struct device *d; 552 int alldone = 1; 553 554 /* 555 * XXX should use circleq and run around the list backwards 556 * to allow for predicates to match children. 557 */ 558 d = alldevs.tqh_first; 559 while (d != NULL) { 560 if ((*cond)(d, condarg)) { 561 struct cfdriver *drv = d->dv_cfdata->cf_driver; 562 563 /* device not busy? */ 564 /* driver's detach routine decides, upper 565 layer (eg bus dependent code) is notified 566 via callback */ 567#ifdef DEBUG 568 printf("trying to detach device %s (%p)\n", 569 d->dv_xname, d); 570#endif 571 if (!haschild(d) && 572 d->dv_cfdata->cf_attach->ca_detach && 573 ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) { 574 int needit, i; 575 struct device *help; 576 577 if (callback) 578 (*callback)(d, arg); 579 580 /* remove reference in driver's devicelist */ 581 if ((d->dv_unit >= drv->cd_ndevs) || 582 (drv->cd_devs[d->dv_unit]!=d)) 583 panic("bad unit in detach_devices"); 584 drv->cd_devs[d->dv_unit] = NULL; 585 586 /* driver is not needed anymore? */ 587 needit = 0; 588 for(i = 0; i<drv->cd_ndevs; i++) 589 if (drv->cd_devs[i]) 590 needit = 1; 591 592 if (!needit) { 593 /* free devices array (alloc'd 594 in config_make_softc) */ 595 free(drv->cd_devs, M_DEVBUF); 596 drv->cd_ndevs = 0; 597 } 598 599 /* remove entry in global device list */ 600 help = d->dv_list.tqe_next; 601 TAILQ_REMOVE(&alldevs, d, dv_list); 602#ifdef DEBUG 603 printf("%s removed\n", d->dv_xname); 604#endif 605 if (d->dv_cfdata->cf_fstate == FSTATE_FOUND) 606 d->dv_cfdata->cf_fstate = 607 FSTATE_NOTFOUND; 608 /* free memory for dev data (alloc'd 609 in config_make_softc) */ 610 free(d, M_DEVBUF); 611 d = help; 612 continue; 613 } else 614 alldone = 0; 615 } 616 d = d->dv_list.tqe_next; 617 } 618 return (!alldone); 619} 620 621int 622dev_matches_cfdata(dev, arg) 623 struct device *dev; 624 void *arg; 625{ 626 struct cfdata *cfdata = arg; 627 return(/* device uses same driver ? */ 628 (dev->dv_cfdata->cf_driver == cfdata->cf_driver) 629 /* device instance described by this cfdata? */ 630 && ((cfdata->cf_fstate == FSTATE_STAR) 631 || ((cfdata->cf_fstate == FSTATE_FOUND) 632 && (dev->dv_unit == cfdata->cf_unit))) 633 ); 634} 635 636int 637parentdev_matches_cfdata(dev, arg) 638 struct device *dev; 639 void *arg; 640{ 641 return (dev->dv_parent ? dev_matches_cfdata(dev->dv_parent, arg) : 0); 642} 643 644int 645config_detach(cf, callback, arg) 646 struct cfdata *cf; 647 config_detach_callback_t callback; 648 void *arg; 649{ 650 return (detach_devices(dev_matches_cfdata, cf, callback, arg)); 651} 652 653int 654config_detach_children(cf, callback, arg) 655 struct cfdata *cf; 656 config_detach_callback_t callback; 657 void *arg; 658{ 659 return (detach_devices(parentdev_matches_cfdata, cf, callback, arg)); 660} 661 662int 663attach_loadable(parentname, parentunit, cftable) 664 char *parentname; 665 int parentunit; 666 struct cftable *cftable; 667{ 668 int found = 0; 669 struct device *d; 670 671 TAILQ_INSERT_TAIL(&allcftables, cftable, list); 672 673 for(d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) { 674 struct cfdriver *drv = d->dv_cfdata->cf_driver; 675 676 if (strcmp(parentname, drv->cd_name) == NULL && 677 (parentunit == -1 || parentunit == d->dv_unit)) { 678 int s; 679 680 s = splhigh(); /* ??? */ 681 found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d, 682 &(cftable->tab[0])); 683 splx(s); 684 } 685 } 686 if (!found) 687 TAILQ_REMOVE(&allcftables, cftable, list); 688 return(found); 689} 690 691static int 692devcf_intable __P((struct device *, void *)); 693 694static int 695devcf_intable(dev, arg) 696 struct device *dev; 697 void *arg; 698{ 699 struct cftable *tbl = arg; 700 struct cfdata *cf; 701 702 for(cf = tbl->tab; cf->cf_driver; cf++) { 703 if (dev->dv_cfdata == cf) 704 return(1); 705 } 706 return(0); 707} 708 709int 710detach_loadable(cftable) 711 struct cftable *cftable; 712{ 713 if (!detach_devices(devcf_intable, cftable, 0, 0)) 714 return(0); 715 TAILQ_REMOVE(&allcftables, cftable, list); 716 return(1); 717} 718