subr_autoconf.c revision 1.13
144229Sdavidn/* $OpenBSD: subr_autoconf.c,v 1.13 1996/10/18 14:46:35 niklas Exp $ */ 244229Sdavidn/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */ 344229Sdavidn 444229Sdavidn/* 544229Sdavidn * Copyright (c) 1992, 1993 644229Sdavidn * The Regents of the University of California. All rights reserved. 744229Sdavidn * 844229Sdavidn * This software was developed by the Computer Systems Engineering group 944229Sdavidn * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 1044229Sdavidn * contributed to Berkeley. 1144229Sdavidn * 1244229Sdavidn * All advertising materials mentioning features or use of this software 1344229Sdavidn * must display the following acknowledgement: 1444229Sdavidn * This product includes software developed by the University of 1544229Sdavidn * California, Lawrence Berkeley Laboratories. 1644229Sdavidn * 1744229Sdavidn * Redistribution and use in source and binary forms, with or without 1844229Sdavidn * modification, are permitted provided that the following conditions 1944229Sdavidn * are met: 2044229Sdavidn * 1. Redistributions of source code must retain the above copyright 2144229Sdavidn * notice, this list of conditions and the following disclaimer. 2244229Sdavidn * 2. Redistributions in binary form must reproduce the above copyright 2344229Sdavidn * notice, this list of conditions and the following disclaimer in the 2444229Sdavidn * documentation and/or other materials provided with the distribution. 2544229Sdavidn * 3. All advertising materials mentioning features or use of this software 2644229Sdavidn * must display the following acknowledgement: 2744229Sdavidn * This product includes software developed by the University of 2844229Sdavidn * California, Berkeley and its contributors. 2944229Sdavidn * 4. Neither the name of the University nor the names of its contributors 3044229Sdavidn * may be used to endorse or promote products derived from this software 3144229Sdavidn * without specific prior written permission. 3244229Sdavidn * 3344229Sdavidn * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 3444229Sdavidn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3544229Sdavidn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3644229Sdavidn * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3744229Sdavidn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3844229Sdavidn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3944229Sdavidn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 4044229Sdavidn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 4144229Sdavidn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 4244229Sdavidn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 4344229Sdavidn * SUCH DAMAGE. 4444229Sdavidn * 4544229Sdavidn * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL) 4644229Sdavidn * 4744229Sdavidn * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93 4844229Sdavidn */ 4944229Sdavidn 5044229Sdavidn#include <sys/param.h> 5144229Sdavidn#include <sys/device.h> 5244229Sdavidn#include <sys/malloc.h> 5344229Sdavidn#include <sys/systm.h> 5444229Sdavidn#include <machine/limits.h> 5544229Sdavidn/* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> */ 5644229Sdavidn#include <sys/queue.h> 5744229Sdavidn 5844229Sdavidn/* Bleh! Need device_register proto */ 5944229Sdavidn#ifdef __alpha__ 6044229Sdavidn#include <machine/autoconf.h> 6144229Sdavidn#endif /* __alpha__ */ 6244229Sdavidn 6344229Sdavidn/* 6444229Sdavidn * Autoconfiguration subroutines. 6544229Sdavidn */ 6644229Sdavidn 6744229Sdavidn/* 6844229Sdavidn * ioconf.c exports exactly two names: cfdata and cfroots. All system 6944229Sdavidn * devices and drivers are found via these tables. 7044229Sdavidn */ 7144229Sdavidnextern struct cfdata cfdata[]; 7244229Sdavidnextern short cfroots[]; 7344229Sdavidn 7444229Sdavidn#define ROOT ((struct device *)NULL) 7544229Sdavidn 7644229Sdavidnstruct matchinfo { 7744229Sdavidn cfmatch_t fn; 7844229Sdavidn struct device *parent; 7944229Sdavidn void *match, *aux; 8044229Sdavidn int indirect, pri; 8144229Sdavidn}; 8244229Sdavidn 8344229Sdavidnstruct cftable_head allcftables; 8444229Sdavidn 8544229Sdavidnstatic struct cftable staticcftable = { 8644229Sdavidn cfdata 8744229Sdavidn}; 8844229Sdavidn 8944229Sdavidnstatic char *number __P((char *, int)); 9044229Sdavidnstatic void mapply __P((struct matchinfo *, struct cfdata *)); 9144229Sdavidn 9244229Sdavidnstruct devicelist alldevs; /* list of all devices */ 9344229Sdavidnstruct evcntlist allevents; /* list of all event counters */ 9444229Sdavidn 9544229Sdavidn/* 9644229Sdavidn * Initialize autoconfiguration data structures. This occurs before console 9744229Sdavidn * initialization as that might require use of this subsystem. Furthermore 9844229Sdavidn * this means that malloc et al. isn't yet available. 9944229Sdavidn */ 10044229Sdavidnvoid 10144229Sdavidnconfig_init() 10244229Sdavidn{ 10344229Sdavidn 10444229Sdavidn TAILQ_INIT(&alldevs); 10544229Sdavidn TAILQ_INIT(&allevents); 10644229Sdavidn TAILQ_INIT(&allcftables); 10744229Sdavidn TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list); 10844229Sdavidn} 10944229Sdavidn 11044229Sdavidn/* 11144229Sdavidn * Apply the matching function and choose the best. This is used 11244229Sdavidn * a few times and we want to keep the code small. 11344229Sdavidn */ 11444229Sdavidnstatic void 11544229Sdavidnmapply(m, cf) 11644229Sdavidn register struct matchinfo *m; 11744229Sdavidn register struct cfdata *cf; 11844229Sdavidn{ 11944229Sdavidn register int pri; 12044229Sdavidn void *match; 12144229Sdavidn 12244229Sdavidn if (m->indirect) 12344229Sdavidn match = config_make_softc(m->parent, cf); 12444229Sdavidn else 12544229Sdavidn match = cf; 12644229Sdavidn 12744229Sdavidn if (m->fn != NULL) 12844229Sdavidn pri = (*m->fn)(m->parent, match, m->aux); 12944229Sdavidn else { 13044229Sdavidn if (cf->cf_attach->ca_match == NULL) { 13144229Sdavidn panic("mapply: no match function for '%s' device\n", 13244229Sdavidn cf->cf_driver->cd_name); 13344229Sdavidn } 13444229Sdavidn pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux); 13544229Sdavidn } 13644229Sdavidn 13744229Sdavidn if (pri > m->pri) { 13844229Sdavidn if (m->indirect && m->match) 13944229Sdavidn free(m->match, M_DEVBUF); 14044229Sdavidn m->match = match; 14144229Sdavidn m->pri = pri; 14244229Sdavidn } else { 14344229Sdavidn if (m->indirect) 14444229Sdavidn free(match, M_DEVBUF); 14544229Sdavidn } 14644229Sdavidn} 14744229Sdavidn 14844229Sdavidn/* 14944229Sdavidn * Iterate over all potential children of some device, calling the given 15044229Sdavidn * function (default being the child's match function) for each one. 15144229Sdavidn * Nonzero returns are matches; the highest value returned is considered 15244229Sdavidn * the best match. Return the `found child' if we got a match, or NULL 15344229Sdavidn * otherwise. The `aux' pointer is simply passed on through. 15444229Sdavidn * 15544229Sdavidn * Note that this function is designed so that it can be used to apply 15644229Sdavidn * an arbitrary function to all potential children (its return value 15744229Sdavidn * can be ignored). 15844229Sdavidn */ 15944229Sdavidnvoid * 16044229Sdavidnconfig_search(fn, parent, aux) 16144229Sdavidn cfmatch_t fn; 16244229Sdavidn register struct device *parent; 16344229Sdavidn void *aux; 16444229Sdavidn{ 16544229Sdavidn register struct cfdata *cf; 16644229Sdavidn register short *p; 16744229Sdavidn struct matchinfo m; 16844229Sdavidn struct cftable *t; 16944229Sdavidn 17044229Sdavidn m.fn = fn; 17144229Sdavidn m.parent = parent; 17244229Sdavidn m.match = NULL; 17344229Sdavidn m.aux = aux; 17444229Sdavidn m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 17544229Sdavidn m.pri = 0; 17644229Sdavidn for(t = allcftables.tqh_first; t; t = t->list.tqe_next){ 17744229Sdavidn for (cf = t->tab; cf->cf_driver; cf++) { 17844229Sdavidn /* 17944229Sdavidn * Skip cf if no longer eligible, otherwise scan through 18044229Sdavidn * parents for one matching `parent', and try match function. 18144229Sdavidn */ 18244229Sdavidn if (cf->cf_fstate == FSTATE_FOUND) 18344229Sdavidn continue; 18444229Sdavidn if (cf->cf_fstate == FSTATE_DNOTFOUND || 18544229Sdavidn cf->cf_fstate == FSTATE_DSTAR) 18644229Sdavidn continue; 18744229Sdavidn for (p = cf->cf_parents; *p >= 0; p++) 18844229Sdavidn if (parent->dv_cfdata == &(t->tab)[*p]) 18944229Sdavidn mapply(&m, cf); 19044229Sdavidn } 19144229Sdavidn } 19244229Sdavidn return (m.match); 19344229Sdavidn} 19444229Sdavidn 19544229Sdavidn/* 19644229Sdavidn * Iterate over all potential children of some device, calling the given 19744229Sdavidn * function for each one. 19844229Sdavidn * 19944229Sdavidn * Note that this function is designed so that it can be used to apply 20044229Sdavidn * an arbitrary function to all potential children (its return value 20144229Sdavidn * can be ignored). 20244229Sdavidn */ 20344229Sdavidnvoid 20444229Sdavidnconfig_scan(fn, parent) 20544229Sdavidn cfscan_t fn; 20644229Sdavidn register struct device *parent; 20744229Sdavidn{ 20844229Sdavidn register struct cfdata *cf; 20944229Sdavidn register short *p; 21044229Sdavidn void *match; 21144229Sdavidn int indirect; 21244229Sdavidn struct cftable *t; 21344229Sdavidn 21444229Sdavidn indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; 21544229Sdavidn for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { 21644229Sdavidn for (cf = t->tab; cf->cf_driver; cf++) { 21744229Sdavidn /* 21844229Sdavidn * Skip cf if no longer eligible, otherwise scan through 21944229Sdavidn * parents for one matching `parent', and try match function. 22044229Sdavidn */ 22144229Sdavidn if (cf->cf_fstate == FSTATE_FOUND) 22244229Sdavidn continue; 22344229Sdavidn if (cf->cf_fstate == FSTATE_DNOTFOUND || 22444229Sdavidn cf->cf_fstate == FSTATE_DSTAR) 22544229Sdavidn continue; 22644229Sdavidn for (p = cf->cf_parents; *p >= 0; p++) 22744229Sdavidn if (parent->dv_cfdata == &(t->tab)[*p]) { 22844229Sdavidn if (indirect) 22944229Sdavidn match = config_make_softc(parent, cf); 23044229Sdavidn else 23144229Sdavidn match = cf; 23244229Sdavidn (*fn)(parent, match); 23344229Sdavidn } 23444229Sdavidn } 23544229Sdavidn } 23644229Sdavidn} 23744229Sdavidn 23844229Sdavidn/* 23944229Sdavidn * Find the given root device. 24044229Sdavidn * This is much like config_search, but there is no parent. 24144229Sdavidn */ 24244229Sdavidnvoid * 24344229Sdavidnconfig_rootsearch(fn, rootname, aux) 24444229Sdavidn register cfmatch_t fn; 24544229Sdavidn register char *rootname; 24644229Sdavidn register void *aux; 24744229Sdavidn{ 24844229Sdavidn register struct cfdata *cf; 24944229Sdavidn register short *p; 25044229Sdavidn struct matchinfo m; 25144229Sdavidn 25244229Sdavidn m.fn = fn; 25344229Sdavidn m.parent = ROOT; 25444229Sdavidn m.match = NULL; 25544229Sdavidn m.aux = aux; 25644229Sdavidn m.indirect = 0; 25744229Sdavidn m.pri = 0; 25844229Sdavidn /* 25944229Sdavidn * Look at root entries for matching name. We do not bother 26044229Sdavidn * with found-state here since only one root should ever be 26144229Sdavidn * searched (and it must be done first). 26244229Sdavidn */ 26344229Sdavidn for (p = cfroots; *p >= 0; p++) { 26444229Sdavidn cf = &cfdata[*p]; 26544229Sdavidn if (strcmp(cf->cf_driver->cd_name, rootname) == 0) 26644229Sdavidn mapply(&m, cf); 26744229Sdavidn } 26844229Sdavidn return (m.match); 26944229Sdavidn} 27044229Sdavidn 27144229Sdavidnchar *msgs[3] = { "", " not configured\n", " unsupported\n" }; 27244229Sdavidn 27344229Sdavidn/* 27444229Sdavidn * The given `aux' argument describes a device that has been found 27544229Sdavidn * on the given parent, but not necessarily configured. Locate the 27644229Sdavidn * configuration data for that device (using the submatch function 27744229Sdavidn * provided, or using candidates' cd_match configuration driver 27844229Sdavidn * functions) and attach it, and return true. If the device was 27944229Sdavidn * not configured, call the given `print' function and return 0. 28044229Sdavidn */ 28144229Sdavidnstruct device * 28244229Sdavidnconfig_found_sm(parent, aux, print, submatch) 28344229Sdavidn struct device *parent; 28444229Sdavidn void *aux; 28544229Sdavidn cfprint_t print; 28644229Sdavidn cfmatch_t submatch; 28744229Sdavidn{ 28844229Sdavidn void *match; 28944229Sdavidn 29044229Sdavidn if ((match = config_search(submatch, parent, aux)) != NULL) 29144229Sdavidn return (config_attach(parent, match, aux, print)); 29244229Sdavidn if (print) 29344229Sdavidn printf(msgs[(*print)(aux, parent->dv_xname)]); 29444229Sdavidn return (NULL); 29544229Sdavidn} 29644229Sdavidn 29744229Sdavidn/* 29844229Sdavidn * As above, but for root devices. 29944229Sdavidn */ 30044229Sdavidnstruct device * 30144229Sdavidnconfig_rootfound(rootname, aux) 30244229Sdavidn char *rootname; 30344229Sdavidn void *aux; 30444229Sdavidn{ 30544229Sdavidn void *match; 30644229Sdavidn 30744229Sdavidn if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL) 30844229Sdavidn return (config_attach(ROOT, match, aux, (cfprint_t)NULL)); 30944229Sdavidn printf("root device %s not configured\n", rootname); 31044229Sdavidn return (NULL); 31144229Sdavidn} 31244229Sdavidn 31344229Sdavidn/* just like sprintf(buf, "%d") except that it works from the end */ 31444229Sdavidnstatic char * 31544229Sdavidnnumber(ep, n) 31644229Sdavidn register char *ep; 317 register int n; 318{ 319 320 *--ep = 0; 321 while (n >= 10) { 322 *--ep = (n % 10) + '0'; 323 n /= 10; 324 } 325 *--ep = n + '0'; 326 return (ep); 327} 328 329/* 330 * Attach a found device. Allocates memory for device variables. 331 */ 332struct device * 333config_attach(parent, match, aux, print) 334 register struct device *parent; 335 void *match; 336 register void *aux; 337 cfprint_t print; 338{ 339 register struct cfdata *cf; 340 register struct device *dev; 341 register struct cfdriver *cd; 342 register struct cfattach *ca; 343 struct cftable *t; 344 345 if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { 346 dev = match; 347 cf = dev->dv_cfdata; 348 } else { 349 cf = match; 350 dev = config_make_softc(parent, cf); 351 } 352 353 cd = cf->cf_driver; 354 ca = cf->cf_attach; 355 cd->cd_devs[cf->cf_unit] = dev; 356 357 if (cf->cf_fstate == FSTATE_STAR) 358 cf->cf_unit++; 359 else 360 cf->cf_fstate = FSTATE_FOUND; 361 362 TAILQ_INSERT_TAIL(&alldevs, dev, dv_list); 363 364 if (parent == ROOT) 365 printf("%s (root)", dev->dv_xname); 366 else { 367 printf("%s at %s", dev->dv_xname, parent->dv_xname); 368 if (print) 369 (void) (*print)(aux, (char *)0); 370 } 371 372 /* 373 * Before attaching, clobber any unfound devices that are 374 * otherwise identical, or bump the unit number on all starred 375 * cfdata for this device. 376 */ 377 for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { 378 for (cf = t->tab; cf->cf_driver; cf++) 379 if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { 380 if (cf->cf_fstate == FSTATE_NOTFOUND) 381 cf->cf_fstate = FSTATE_FOUND; 382 if (cf->cf_fstate == FSTATE_STAR) 383 cf->cf_unit++; 384 } 385 } 386#ifdef __alpha__ 387 device_register(dev, aux); 388#endif 389 (*ca->ca_attach)(parent, dev, aux); 390 return (dev); 391} 392 393struct device * 394config_make_softc(parent, cf) 395 struct device *parent; 396 struct cfdata *cf; 397{ 398 register struct device *dev; 399 register struct cfdriver *cd; 400 register struct cfattach *ca; 401 register size_t lname, lunit; 402 register char *xunit; 403 char num[10]; 404 405 cd = cf->cf_driver; 406 ca = cf->cf_attach; 407 if (ca->ca_devsize < sizeof(struct device)) 408 panic("config_make_softc"); 409 410 /* compute length of name and decimal expansion of unit number */ 411 lname = strlen(cd->cd_name); 412 xunit = number(&num[sizeof num], cf->cf_unit); 413 lunit = &num[sizeof num] - xunit; 414 if (lname + lunit >= sizeof(dev->dv_xname)) 415 panic("config_attach: device name too long"); 416 417 /* get memory for all device vars */ 418 dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT); 419 if (!dev) 420 panic("config_attach: memory allocation for device softc failed"); 421 bzero(dev, ca->ca_devsize); 422 dev->dv_class = cd->cd_class; 423 dev->dv_cfdata = cf; 424 dev->dv_unit = cf->cf_unit; 425 bcopy(cd->cd_name, dev->dv_xname, lname); 426 bcopy(xunit, dev->dv_xname + lname, lunit); 427 dev->dv_parent = parent; 428 429 /* put this device in the devices array */ 430 if (dev->dv_unit >= cd->cd_ndevs) { 431 /* 432 * Need to expand the array. 433 */ 434 int old = cd->cd_ndevs, new; 435 void **nsp; 436 437 if (old == 0) 438 new = MINALLOCSIZE / sizeof(void *); 439 else 440 new = old * 2; 441 while (new <= dev->dv_unit) 442 new *= 2; 443 cd->cd_ndevs = new; 444 nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT); 445 if (nsp == 0) 446 panic("config_attach: %sing dev array", 447 old != 0 ? "expand" : "creat"); 448 bzero(nsp + old, (new - old) * sizeof(void *)); 449 if (old != 0) { 450 bcopy(cd->cd_devs, nsp, old * sizeof(void *)); 451 free(cd->cd_devs, M_DEVBUF); 452 } 453 cd->cd_devs = nsp; 454 } 455 if (cd->cd_devs[dev->dv_unit]) 456 panic("config_attach: duplicate %s", dev->dv_xname); 457 458 return (dev); 459} 460 461/* 462 * Attach an event. These must come from initially-zero space (see 463 * commented-out assignments below), but that occurs naturally for 464 * device instance variables. 465 */ 466void 467evcnt_attach(dev, name, ev) 468 struct device *dev; 469 const char *name; 470 struct evcnt *ev; 471{ 472 473#ifdef DIAGNOSTIC 474 if (strlen(name) >= sizeof(ev->ev_name)) 475 panic("evcnt_attach"); 476#endif 477 /* ev->ev_next = NULL; */ 478 ev->ev_dev = dev; 479 /* ev->ev_count = 0; */ 480 strcpy(ev->ev_name, name); 481 TAILQ_INSERT_TAIL(&allevents, ev, ev_list); 482} 483 484typedef int (*cond_predicate_t) __P((struct device*, void*)); 485 486static int haschild __P((struct device *)); 487static int detach_devices __P((cond_predicate_t, void *, 488 config_detach_callback_t, void *)); 489 490static int 491haschild(dev) 492 struct device *dev; 493{ 494 struct device *d; 495 496 for (d = alldevs.tqh_first; 497 d != NULL; 498 d = d->dv_list.tqe_next) { 499 if (d->dv_parent == dev) 500 return(1); 501 } 502 return(0); 503} 504 505static int 506detach_devices(cond, condarg, callback, arg) 507 cond_predicate_t cond; 508 void *condarg; 509 config_detach_callback_t callback; 510 void *arg; 511{ 512 struct device *d; 513 int alldone = 1; 514 515 /* 516 * XXX should use circleq and run around the list backwards 517 * to allow for predicates to match children. 518 */ 519 d = alldevs.tqh_first; 520 while (d != NULL) { 521 if ((*cond)(d, condarg)) { 522 struct cfdriver *drv = d->dv_cfdata->cf_driver; 523 524 /* device not busy? */ 525 /* driver's detach routine decides, upper 526 layer (eg bus dependent code) is notified 527 via callback */ 528#ifdef DEBUG 529 printf("trying to detach device %s (%p)\n", 530 d->dv_xname, d); 531#endif 532 if (!haschild(d) && 533 d->dv_cfdata->cf_attach->ca_detach && 534 ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) { 535 int needit, i; 536 struct device *help; 537 538 if (callback) 539 (*callback)(d, arg); 540 541 /* remove reference in driver's devicelist */ 542 if ((d->dv_unit >= drv->cd_ndevs) || 543 (drv->cd_devs[d->dv_unit]!=d)) 544 panic("bad unit in detach_devices"); 545 drv->cd_devs[d->dv_unit] = NULL; 546 547 /* driver is not needed anymore? */ 548 needit = 0; 549 for(i = 0; i<drv->cd_ndevs; i++) 550 if (drv->cd_devs[i]) 551 needit = 1; 552 553 if (!needit) { 554 /* free devices array (alloc'd 555 in config_make_softc) */ 556 free(drv->cd_devs, M_DEVBUF); 557 drv->cd_ndevs = 0; 558 } 559 560 /* remove entry in global device list */ 561 help = d->dv_list.tqe_next; 562 TAILQ_REMOVE(&alldevs, d, dv_list); 563#ifdef DEBUG 564 printf("%s removed\n", d->dv_xname); 565#endif 566 d->dv_cfdata->cf_fstate = FSTATE_NOTFOUND; 567 /* free memory for dev data (alloc'd 568 in config_make_softc) */ 569 free(d, M_DEVBUF); 570 d = help; 571 continue; 572 } else 573 alldone = 0; 574 } 575 d = d->dv_list.tqe_next; 576 } 577 return(!alldone); 578} 579 580int dev_matches_cfdata __P((struct device *dev, void *)); 581 582int 583dev_matches_cfdata(dev, arg) 584 struct device *dev; 585 void *arg; 586{ 587 struct cfdata *cfdata = arg; 588 return(/* device uses same driver ? */ 589 (dev->dv_cfdata->cf_driver == cfdata->cf_driver) 590 /* device instance described by this cfdata? */ 591 && ((cfdata->cf_fstate == FSTATE_STAR) 592 || ((cfdata->cf_fstate == FSTATE_FOUND) 593 && (dev->dv_unit == cfdata->cf_unit))) 594 ); 595} 596 597int 598config_detach(cf, callback, arg) 599 struct cfdata *cf; 600 config_detach_callback_t callback; 601 void *arg; 602{ 603 return(detach_devices(dev_matches_cfdata, cf, callback, arg)); 604} 605 606int 607attach_loadable(parentname, parentunit, cftable) 608 char *parentname; 609 int parentunit; 610 struct cftable *cftable; 611{ 612 int found = 0; 613 struct device *d; 614 615 TAILQ_INSERT_TAIL(&allcftables, cftable, list); 616 617 for(d = alldevs.tqh_first; d != NULL; d = d->dv_list.tqe_next) { 618 struct cfdriver *drv = d->dv_cfdata->cf_driver; 619 620 if (strcmp(parentname, drv->cd_name) == NULL && 621 (parentunit == -1 || parentunit == d->dv_unit)) { 622 int s; 623 624 s = splhigh(); /* ??? */ 625 found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d, 626 &(cftable->tab[0])); 627 splx(s); 628 } 629 } 630 if (!found) 631 TAILQ_REMOVE(&allcftables, cftable, list); 632 return(found); 633} 634 635static int 636devcf_intable __P((struct device *, void *)); 637 638static int 639devcf_intable(dev, arg) 640 struct device *dev; 641 void *arg; 642{ 643 struct cftable *tbl = arg; 644 struct cfdata *cf; 645 646 for(cf = tbl->tab; cf->cf_driver; cf++) { 647 if (dev->dv_cfdata == cf) 648 return(1); 649 } 650 return(0); 651} 652 653int 654detach_loadable(cftable) 655 struct cftable *cftable; 656{ 657 if (!detach_devices(devcf_intable, cftable, 0, 0)) 658 return(0); 659 TAILQ_REMOVE(&allcftables, cftable, list); 660 return(1); 661} 662