Annotation of sys/kern/subr_autoconf.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: subr_autoconf.c,v 1.52 2007/05/30 05:36:36 deraadt 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. Neither the name of the University nor the names of its contributors
! 26: * may be used to endorse or promote products derived from this software
! 27: * without specific prior written permission.
! 28: *
! 29: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 30: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 31: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 32: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 33: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 34: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 35: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 36: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 37: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 38: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 39: * SUCH DAMAGE.
! 40: *
! 41: * from: Header: subr_autoconf.c,v 1.12 93/02/01 19:31:48 torek Exp (LBL)
! 42: *
! 43: * @(#)subr_autoconf.c 8.1 (Berkeley) 6/10/93
! 44: */
! 45:
! 46: #include <sys/param.h>
! 47: #include <sys/device.h>
! 48: #include <sys/hotplug.h>
! 49: #include <sys/limits.h>
! 50: #include <sys/malloc.h>
! 51: #include <sys/systm.h>
! 52: /* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> */
! 53: #include <sys/queue.h>
! 54: #include <sys/proc.h>
! 55:
! 56: #include "hotplug.h"
! 57:
! 58: /*
! 59: * Autoconfiguration subroutines.
! 60: */
! 61:
! 62: typedef int (*cond_predicate_t)(struct device *, void *);
! 63:
! 64: /*
! 65: * ioconf.c exports exactly two names: cfdata and cfroots. All system
! 66: * devices and drivers are found via these tables.
! 67: */
! 68: extern short cfroots[];
! 69:
! 70: #define ROOT ((struct device *)NULL)
! 71:
! 72: struct matchinfo {
! 73: cfmatch_t fn;
! 74: struct device *parent;
! 75: void *match, *aux;
! 76: int indirect, pri;
! 77: };
! 78:
! 79: struct cftable_head allcftables;
! 80:
! 81: static struct cftable staticcftable = {
! 82: cfdata
! 83: };
! 84:
! 85: #ifndef AUTOCONF_VERBOSE
! 86: #define AUTOCONF_VERBOSE 0
! 87: #endif /* AUTOCONF_VERBOSE */
! 88: int autoconf_verbose = AUTOCONF_VERBOSE; /* trace probe calls */
! 89:
! 90: static void mapply(struct matchinfo *, struct cfdata *);
! 91:
! 92: struct deferred_config {
! 93: TAILQ_ENTRY(deferred_config) dc_queue;
! 94: struct device *dc_dev;
! 95: void (*dc_func)(struct device *);
! 96: };
! 97:
! 98: TAILQ_HEAD(, deferred_config) deferred_config_queue;
! 99:
! 100: void config_process_deferred_children(struct device *);
! 101:
! 102: struct devicelist alldevs; /* list of all devices */
! 103:
! 104: __volatile int config_pending; /* semaphore for mountroot */
! 105:
! 106: /*
! 107: * Initialize autoconfiguration data structures. This occurs before console
! 108: * initialization as that might require use of this subsystem. Furthermore
! 109: * this means that malloc et al. isn't yet available.
! 110: */
! 111: void
! 112: config_init(void)
! 113: {
! 114: TAILQ_INIT(&deferred_config_queue);
! 115: TAILQ_INIT(&alldevs);
! 116: TAILQ_INIT(&allcftables);
! 117: TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list);
! 118: }
! 119:
! 120: /*
! 121: * Apply the matching function and choose the best. This is used
! 122: * a few times and we want to keep the code small.
! 123: */
! 124: void
! 125: mapply(struct matchinfo *m, struct cfdata *cf)
! 126: {
! 127: int pri;
! 128: void *match;
! 129:
! 130: if (m->indirect)
! 131: match = config_make_softc(m->parent, cf);
! 132: else
! 133: match = cf;
! 134:
! 135: if (autoconf_verbose) {
! 136: printf(">>> probing for %s", cf->cf_driver->cd_name);
! 137: if (cf->cf_fstate == FSTATE_STAR)
! 138: printf("*\n");
! 139: else
! 140: printf("%d\n", cf->cf_unit);
! 141: }
! 142: if (m->fn != NULL)
! 143: pri = (*m->fn)(m->parent, match, m->aux);
! 144: else {
! 145: if (cf->cf_attach->ca_match == NULL) {
! 146: panic("mapply: no match function for '%s' device",
! 147: cf->cf_driver->cd_name);
! 148: }
! 149: pri = (*cf->cf_attach->ca_match)(m->parent, match, m->aux);
! 150: }
! 151: if (autoconf_verbose)
! 152: printf(">>> %s probe returned %d\n", cf->cf_driver->cd_name,
! 153: pri);
! 154:
! 155: if (pri > m->pri) {
! 156: if (m->indirect && m->match)
! 157: free(m->match, M_DEVBUF);
! 158: m->match = match;
! 159: m->pri = pri;
! 160: } else {
! 161: if (m->indirect)
! 162: free(match, M_DEVBUF);
! 163: }
! 164: }
! 165:
! 166: /*
! 167: * Iterate over all potential children of some device, calling the given
! 168: * function (default being the child's match function) for each one.
! 169: * Nonzero returns are matches; the highest value returned is considered
! 170: * the best match. Return the `found child' if we got a match, or NULL
! 171: * otherwise. The `aux' pointer is simply passed on through.
! 172: *
! 173: * Note that this function is designed so that it can be used to apply
! 174: * an arbitrary function to all potential children (its return value
! 175: * can be ignored).
! 176: */
! 177: void *
! 178: config_search(cfmatch_t fn, struct device *parent, void *aux)
! 179: {
! 180: struct cfdata *cf;
! 181: short *p;
! 182: struct matchinfo m;
! 183: struct cftable *t;
! 184:
! 185: m.fn = fn;
! 186: m.parent = parent;
! 187: m.match = NULL;
! 188: m.aux = aux;
! 189: m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
! 190: m.pri = 0;
! 191: TAILQ_FOREACH(t, &allcftables, list) {
! 192: for (cf = t->tab; cf->cf_driver; cf++) {
! 193: /*
! 194: * Skip cf if no longer eligible, otherwise scan
! 195: * through parents for one matching `parent',
! 196: * and try match function.
! 197: */
! 198: if (cf->cf_fstate == FSTATE_FOUND)
! 199: continue;
! 200: if (cf->cf_fstate == FSTATE_DNOTFOUND ||
! 201: cf->cf_fstate == FSTATE_DSTAR)
! 202: continue;
! 203: for (p = cf->cf_parents; *p >= 0; p++)
! 204: if (parent->dv_cfdata == &(t->tab)[*p])
! 205: mapply(&m, cf);
! 206: }
! 207: }
! 208: if (autoconf_verbose) {
! 209: if (m.match) {
! 210: if (m.indirect)
! 211: cf = ((struct device *)m.match)->dv_cfdata;
! 212: else
! 213: cf = (struct cfdata *)m.match;
! 214: printf(">>> %s probe won\n",
! 215: cf->cf_driver->cd_name);
! 216: } else
! 217: printf(">>> no winning probe\n");
! 218: }
! 219: return (m.match);
! 220: }
! 221:
! 222: /*
! 223: * Iterate over all potential children of some device, calling the given
! 224: * function for each one.
! 225: *
! 226: * Note that this function is designed so that it can be used to apply
! 227: * an arbitrary function to all potential children (its return value
! 228: * can be ignored).
! 229: */
! 230: void
! 231: config_scan(cfscan_t fn, struct device *parent)
! 232: {
! 233: struct cfdata *cf;
! 234: short *p;
! 235: void *match;
! 236: int indirect;
! 237: struct cftable *t;
! 238:
! 239: indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect;
! 240: TAILQ_FOREACH(t, &allcftables, list) {
! 241: for (cf = t->tab; cf->cf_driver; cf++) {
! 242: /*
! 243: * Skip cf if no longer eligible, otherwise scan
! 244: * through parents for one matching `parent',
! 245: * and try match function.
! 246: */
! 247: if (cf->cf_fstate == FSTATE_FOUND)
! 248: continue;
! 249: if (cf->cf_fstate == FSTATE_DNOTFOUND ||
! 250: cf->cf_fstate == FSTATE_DSTAR)
! 251: continue;
! 252: for (p = cf->cf_parents; *p >= 0; p++)
! 253: if (parent->dv_cfdata == &(t->tab)[*p]) {
! 254: match = indirect?
! 255: config_make_softc(parent, cf) :
! 256: (void *)cf;
! 257: (*fn)(parent, match);
! 258: }
! 259: }
! 260: }
! 261: }
! 262:
! 263: /*
! 264: * Find the given root device.
! 265: * This is much like config_search, but there is no parent.
! 266: */
! 267: void *
! 268: config_rootsearch(cfmatch_t fn, char *rootname, void *aux)
! 269: {
! 270: struct cfdata *cf;
! 271: short *p;
! 272: struct matchinfo m;
! 273:
! 274: m.fn = fn;
! 275: m.parent = ROOT;
! 276: m.match = NULL;
! 277: m.aux = aux;
! 278: m.indirect = 0;
! 279: m.pri = 0;
! 280: /*
! 281: * Look at root entries for matching name. We do not bother
! 282: * with found-state here since only one root should ever be
! 283: * searched (and it must be done first).
! 284: */
! 285: for (p = cfroots; *p >= 0; p++) {
! 286: cf = &cfdata[*p];
! 287: if (strcmp(cf->cf_driver->cd_name, rootname) == 0)
! 288: mapply(&m, cf);
! 289: }
! 290: return (m.match);
! 291: }
! 292:
! 293: char *msgs[3] = { "", " not configured\n", " unsupported\n" };
! 294:
! 295: /*
! 296: * The given `aux' argument describes a device that has been found
! 297: * on the given parent, but not necessarily configured. Locate the
! 298: * configuration data for that device (using the submatch function
! 299: * provided, or using candidates' cd_match configuration driver
! 300: * functions) and attach it, and return true. If the device was
! 301: * not configured, call the given `print' function and return 0.
! 302: */
! 303: struct device *
! 304: config_found_sm(struct device *parent, void *aux, cfprint_t print,
! 305: cfmatch_t submatch)
! 306: {
! 307: void *match;
! 308:
! 309: if ((match = config_search(submatch, parent, aux)) != NULL)
! 310: return (config_attach(parent, match, aux, print));
! 311: if (print)
! 312: printf(msgs[(*print)(aux, parent->dv_xname)]);
! 313: return (NULL);
! 314: }
! 315:
! 316: /*
! 317: * As above, but for root devices.
! 318: */
! 319: struct device *
! 320: config_rootfound(char *rootname, void *aux)
! 321: {
! 322: void *match;
! 323:
! 324: if ((match = config_rootsearch((cfmatch_t)NULL, rootname, aux)) != NULL)
! 325: return (config_attach(ROOT, match, aux, (cfprint_t)NULL));
! 326: printf("root device %s not configured\n", rootname);
! 327: return (NULL);
! 328: }
! 329:
! 330: /*
! 331: * Attach a found device. Allocates memory for device variables.
! 332: */
! 333: struct device *
! 334: config_attach(struct device *parent, void *match, void *aux, cfprint_t print)
! 335: {
! 336: struct cfdata *cf;
! 337: struct device *dev;
! 338: struct cfdriver *cd;
! 339: struct cfattach *ca;
! 340: struct cftable *t;
! 341:
! 342: if (parent && parent->dv_cfdata->cf_driver->cd_indirect) {
! 343: dev = match;
! 344: cf = dev->dv_cfdata;
! 345: } else {
! 346: cf = match;
! 347: dev = config_make_softc(parent, cf);
! 348: }
! 349:
! 350: cd = cf->cf_driver;
! 351: ca = cf->cf_attach;
! 352:
! 353: cd->cd_devs[dev->dv_unit] = dev;
! 354:
! 355: /*
! 356: * If this is a "STAR" device and we used the last unit, prepare for
! 357: * another one.
! 358: */
! 359: if (cf->cf_fstate == FSTATE_STAR) {
! 360: if (dev->dv_unit == cf->cf_unit)
! 361: cf->cf_unit++;
! 362: } else
! 363: cf->cf_fstate = FSTATE_FOUND;
! 364:
! 365: TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
! 366: device_ref(dev);
! 367:
! 368: if (parent == ROOT)
! 369: printf("%s at root", dev->dv_xname);
! 370: else {
! 371: printf("%s at %s", dev->dv_xname, parent->dv_xname);
! 372: if (print)
! 373: (void) (*print)(aux, (char *)0);
! 374: }
! 375:
! 376: /*
! 377: * Before attaching, clobber any unfound devices that are
! 378: * otherwise identical, or bump the unit number on all starred
! 379: * cfdata for this device.
! 380: */
! 381: TAILQ_FOREACH(t, &allcftables, list) {
! 382: for (cf = t->tab; cf->cf_driver; cf++)
! 383: if (cf->cf_driver == cd &&
! 384: cf->cf_unit == dev->dv_unit) {
! 385: if (cf->cf_fstate == FSTATE_NOTFOUND)
! 386: cf->cf_fstate = FSTATE_FOUND;
! 387: if (cf->cf_fstate == FSTATE_STAR)
! 388: cf->cf_unit++;
! 389: }
! 390: }
! 391: device_register(dev, aux);
! 392: (*ca->ca_attach)(parent, dev, aux);
! 393: config_process_deferred_children(dev);
! 394: #if NHOTPLUG > 0
! 395: if (!cold)
! 396: hotplug_device_attach(cd->cd_class, dev->dv_xname);
! 397: #endif
! 398: return (dev);
! 399: }
! 400:
! 401: struct device *
! 402: config_make_softc(struct device *parent, struct cfdata *cf)
! 403: {
! 404: struct device *dev;
! 405: struct cfdriver *cd;
! 406: struct cfattach *ca;
! 407:
! 408: cd = cf->cf_driver;
! 409: ca = cf->cf_attach;
! 410: if (ca->ca_devsize < sizeof(struct device))
! 411: panic("config_make_softc");
! 412:
! 413: /* get memory for all device vars */
! 414: dev = (struct device *)malloc(ca->ca_devsize, M_DEVBUF, M_NOWAIT);
! 415: if (!dev)
! 416: panic("config_make_softc: allocation for device softc failed");
! 417: bzero(dev, ca->ca_devsize);
! 418: dev->dv_class = cd->cd_class;
! 419: dev->dv_cfdata = cf;
! 420: dev->dv_flags = DVF_ACTIVE; /* always initially active */
! 421:
! 422: /* If this is a STAR device, search for a free unit number */
! 423: if (cf->cf_fstate == FSTATE_STAR) {
! 424: for (dev->dv_unit = cf->cf_starunit1;
! 425: dev->dv_unit < cf->cf_unit; dev->dv_unit++)
! 426: if (cd->cd_ndevs == 0 ||
! 427: dev->dv_unit >= cd->cd_ndevs ||
! 428: cd->cd_devs[dev->dv_unit] == NULL)
! 429: break;
! 430: } else
! 431: dev->dv_unit = cf->cf_unit;
! 432:
! 433: /* Build the device name into dv_xname. */
! 434: if (snprintf(dev->dv_xname, sizeof(dev->dv_xname), "%s%d",
! 435: cd->cd_name, dev->dv_unit) >= sizeof(dev->dv_xname))
! 436: panic("config_make_softc: device name too long");
! 437: dev->dv_parent = parent;
! 438:
! 439: /* put this device in the devices array */
! 440: if (dev->dv_unit >= cd->cd_ndevs) {
! 441: /*
! 442: * Need to expand the array.
! 443: */
! 444: int old = cd->cd_ndevs, new;
! 445: void **nsp;
! 446:
! 447: if (old == 0)
! 448: new = MINALLOCSIZE / sizeof(void *);
! 449: else
! 450: new = old * 2;
! 451: while (new <= dev->dv_unit)
! 452: new *= 2;
! 453: cd->cd_ndevs = new;
! 454: nsp = malloc(new * sizeof(void *), M_DEVBUF, M_NOWAIT);
! 455: if (nsp == 0)
! 456: panic("config_make_softc: %sing dev array",
! 457: old != 0 ? "expand" : "creat");
! 458: bzero(nsp + old, (new - old) * sizeof(void *));
! 459: if (old != 0) {
! 460: bcopy(cd->cd_devs, nsp, old * sizeof(void *));
! 461: free(cd->cd_devs, M_DEVBUF);
! 462: }
! 463: cd->cd_devs = nsp;
! 464: }
! 465: if (cd->cd_devs[dev->dv_unit])
! 466: panic("config_make_softc: duplicate %s", dev->dv_xname);
! 467:
! 468: dev->dv_ref = 1;
! 469:
! 470: return (dev);
! 471: }
! 472:
! 473: /*
! 474: * Detach a device. Optionally forced (e.g. because of hardware
! 475: * removal) and quiet. Returns zero if successful, non-zero
! 476: * (an error code) otherwise.
! 477: *
! 478: * Note that this code wants to be run from a process context, so
! 479: * that the detach can sleep to allow processes which have a device
! 480: * open to run and unwind their stacks.
! 481: */
! 482: int
! 483: config_detach(struct device *dev, int flags)
! 484: {
! 485: struct cfdata *cf;
! 486: struct cfattach *ca;
! 487: struct cfdriver *cd;
! 488: int rv = 0, i;
! 489: #ifdef DIAGNOSTIC
! 490: struct device *d;
! 491: #endif
! 492: #if NHOTPLUG > 0
! 493: char devname[16];
! 494: #endif
! 495:
! 496: #if NHOTPLUG > 0
! 497: strlcpy(devname, dev->dv_xname, sizeof(devname));
! 498: #endif
! 499:
! 500: cf = dev->dv_cfdata;
! 501: #ifdef DIAGNOSTIC
! 502: if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR)
! 503: panic("config_detach: bad device fstate");
! 504: #endif
! 505: ca = cf->cf_attach;
! 506: cd = cf->cf_driver;
! 507:
! 508: /*
! 509: * Ensure the device is deactivated. If the device doesn't
! 510: * have an activation entry point, we allow DVF_ACTIVE to
! 511: * remain set. Otherwise, if DVF_ACTIVE is still set, the
! 512: * device is busy, and the detach fails.
! 513: */
! 514: if (ca->ca_activate != NULL)
! 515: rv = config_deactivate(dev);
! 516:
! 517: /*
! 518: * Try to detach the device. If that's not possible, then
! 519: * we either panic() (for the forced but failed case), or
! 520: * return an error.
! 521: */
! 522: if (rv == 0) {
! 523: if (ca->ca_detach != NULL)
! 524: rv = (*ca->ca_detach)(dev, flags);
! 525: else
! 526: rv = EOPNOTSUPP;
! 527: }
! 528: if (rv != 0) {
! 529: if ((flags & DETACH_FORCE) == 0)
! 530: return (rv);
! 531: else
! 532: panic("config_detach: forced detach of %s failed (%d)",
! 533: dev->dv_xname, rv);
! 534: }
! 535:
! 536: /*
! 537: * The device has now been successfully detached.
! 538: */
! 539:
! 540: #ifdef DIAGNOSTIC
! 541: /*
! 542: * Sanity: If you're successfully detached, you should have no
! 543: * children. (Note that because children must be attached
! 544: * after parents, we only need to search the latter part of
! 545: * the list.)
! 546: */
! 547: for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
! 548: d = TAILQ_NEXT(d, dv_list)) {
! 549: if (d->dv_parent == dev)
! 550: panic("config_detach: detached device has children");
! 551: }
! 552: #endif
! 553:
! 554: /*
! 555: * Mark cfdata to show that the unit can be reused, if possible.
! 556: * Note that we can only re-use a starred unit number if the unit
! 557: * being detached had the last assigned unit number.
! 558: */
! 559: for (cf = cfdata; cf->cf_driver; cf++) {
! 560: if (cf->cf_driver == cd) {
! 561: if (cf->cf_fstate == FSTATE_FOUND &&
! 562: cf->cf_unit == dev->dv_unit)
! 563: cf->cf_fstate = FSTATE_NOTFOUND;
! 564: if (cf->cf_fstate == FSTATE_STAR &&
! 565: cf->cf_unit == dev->dv_unit + 1)
! 566: cf->cf_unit--;
! 567: }
! 568: }
! 569:
! 570: /*
! 571: * Unlink from device list.
! 572: */
! 573: TAILQ_REMOVE(&alldevs, dev, dv_list);
! 574: device_unref(dev);
! 575:
! 576: /*
! 577: * Remove from cfdriver's array, tell the world, and free softc.
! 578: */
! 579: cd->cd_devs[dev->dv_unit] = NULL;
! 580: if ((flags & DETACH_QUIET) == 0)
! 581: printf("%s detached\n", dev->dv_xname);
! 582:
! 583: device_unref(dev);
! 584: /*
! 585: * If the device now has no units in use, deallocate its softc array.
! 586: */
! 587: for (i = 0; i < cd->cd_ndevs; i++)
! 588: if (cd->cd_devs[i] != NULL)
! 589: break;
! 590: if (i == cd->cd_ndevs) { /* nothing found; deallocate */
! 591: free(cd->cd_devs, M_DEVBUF);
! 592: cd->cd_devs = NULL;
! 593: cd->cd_ndevs = 0;
! 594: cf->cf_unit = 0;
! 595: }
! 596:
! 597: #if NHOTPLUG > 0
! 598: if (!cold)
! 599: hotplug_device_detach(cd->cd_class, devname);
! 600: #endif
! 601:
! 602: /*
! 603: * Return success.
! 604: */
! 605: return (0);
! 606: }
! 607:
! 608: int
! 609: config_activate(struct device *dev)
! 610: {
! 611: struct cfattach *ca = dev->dv_cfdata->cf_attach;
! 612: int rv = 0, oflags = dev->dv_flags;
! 613:
! 614: if (ca->ca_activate == NULL)
! 615: return (EOPNOTSUPP);
! 616:
! 617: if ((dev->dv_flags & DVF_ACTIVE) == 0) {
! 618: dev->dv_flags |= DVF_ACTIVE;
! 619: rv = (*ca->ca_activate)(dev, DVACT_ACTIVATE);
! 620: if (rv)
! 621: dev->dv_flags = oflags;
! 622: }
! 623: return (rv);
! 624: }
! 625:
! 626: int
! 627: config_deactivate(struct device *dev)
! 628: {
! 629: struct cfattach *ca = dev->dv_cfdata->cf_attach;
! 630: int rv = 0, oflags = dev->dv_flags;
! 631:
! 632: if (ca->ca_activate == NULL)
! 633: return (EOPNOTSUPP);
! 634:
! 635: if (dev->dv_flags & DVF_ACTIVE) {
! 636: dev->dv_flags &= ~DVF_ACTIVE;
! 637: rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE);
! 638: if (rv)
! 639: dev->dv_flags = oflags;
! 640: }
! 641: return (rv);
! 642: }
! 643:
! 644: /*
! 645: * Defer the configuration of the specified device until all
! 646: * of its parent's devices have been attached.
! 647: */
! 648: void
! 649: config_defer(struct device *dev, void (*func)(struct device *))
! 650: {
! 651: struct deferred_config *dc;
! 652:
! 653: if (dev->dv_parent == NULL)
! 654: panic("config_defer: can't defer config of a root device");
! 655:
! 656: #ifdef DIAGNOSTIC
! 657: for (dc = TAILQ_FIRST(&deferred_config_queue); dc != NULL;
! 658: dc = TAILQ_NEXT(dc, dc_queue)) {
! 659: if (dc->dc_dev == dev)
! 660: panic("config_defer: deferred twice");
! 661: }
! 662: #endif
! 663:
! 664: if ((dc = malloc(sizeof(*dc), M_DEVBUF, M_NOWAIT)) == NULL)
! 665: panic("config_defer: can't allocate defer structure");
! 666:
! 667: dc->dc_dev = dev;
! 668: dc->dc_func = func;
! 669: TAILQ_INSERT_TAIL(&deferred_config_queue, dc, dc_queue);
! 670: config_pending_incr();
! 671: }
! 672:
! 673: /*
! 674: * Process the deferred configuration queue for a device.
! 675: */
! 676: void
! 677: config_process_deferred_children(struct device *parent)
! 678: {
! 679: struct deferred_config *dc, *ndc;
! 680:
! 681: for (dc = TAILQ_FIRST(&deferred_config_queue);
! 682: dc != NULL; dc = ndc) {
! 683: ndc = TAILQ_NEXT(dc, dc_queue);
! 684: if (dc->dc_dev->dv_parent == parent) {
! 685: TAILQ_REMOVE(&deferred_config_queue, dc, dc_queue);
! 686: (*dc->dc_func)(dc->dc_dev);
! 687: free(dc, M_DEVBUF);
! 688: config_pending_decr();
! 689: }
! 690: }
! 691: }
! 692:
! 693: /*
! 694: * Manipulate the config_pending semaphore.
! 695: */
! 696: void
! 697: config_pending_incr(void)
! 698: {
! 699:
! 700: config_pending++;
! 701: }
! 702:
! 703: void
! 704: config_pending_decr(void)
! 705: {
! 706:
! 707: #ifdef DIAGNOSTIC
! 708: if (config_pending == 0)
! 709: panic("config_pending_decr: config_pending == 0");
! 710: #endif
! 711: config_pending--;
! 712: if (config_pending == 0)
! 713: wakeup((void *)&config_pending);
! 714: }
! 715:
! 716: int
! 717: config_detach_children(struct device *parent, int flags)
! 718: {
! 719: struct device *dev, *next_dev, *prev_dev;
! 720: int rv = 0;
! 721:
! 722: /*
! 723: * The config_detach routine may sleep, meaning devices
! 724: * may be added to the queue. However, all devices will
! 725: * be added to the tail of the queue, the queue won't
! 726: * be re-organized, and the subtree of parent here should be locked
! 727: * for purposes of adding/removing children.
! 728: *
! 729: * Note that we can not afford trying to walk the device list
! 730: * once - our ``next'' device might be a child of the device
! 731: * we are about to detach, so it would disappear.
! 732: * Just play it safe and restart from the parent.
! 733: */
! 734: for (prev_dev = NULL, dev = TAILQ_LAST(&alldevs, devicelist);
! 735: dev != NULL; dev = next_dev) {
! 736: if (dev->dv_parent == parent) {
! 737: if ((rv = config_detach(dev, flags)) != 0)
! 738: return (rv);
! 739: next_dev = prev_dev ? prev_dev : TAILQ_LAST(&alldevs,
! 740: devicelist);
! 741: } else {
! 742: prev_dev = dev;
! 743: next_dev = TAILQ_PREV(dev, devicelist, dv_list);
! 744: }
! 745: }
! 746:
! 747: return (0);
! 748: }
! 749:
! 750: int
! 751: config_activate_children(struct device *parent, enum devact act)
! 752: {
! 753: struct device *dev, *next_dev;
! 754: int rv = 0;
! 755:
! 756: /* The config_deactivate routine may sleep, meaning devices
! 757: may be added to the queue. However, all devices will
! 758: be added to the tail of the queue, the queue won't
! 759: be re-organized, and the subtree of parent here should be locked
! 760: for purposes of adding/removing children.
! 761: */
! 762: for (dev = TAILQ_FIRST(&alldevs);
! 763: dev != NULL; dev = next_dev) {
! 764: next_dev = TAILQ_NEXT(dev, dv_list);
! 765: if (dev->dv_parent == parent) {
! 766: switch (act) {
! 767: case DVACT_ACTIVATE:
! 768: rv = config_activate(dev);
! 769: break;
! 770: case DVACT_DEACTIVATE:
! 771: rv = config_deactivate(dev);
! 772: break;
! 773: default:
! 774: #ifdef DIAGNOSTIC
! 775: printf ("config_activate_children: shouldn't get here");
! 776: #endif
! 777: rv = EOPNOTSUPP;
! 778: break;
! 779:
! 780: }
! 781:
! 782: if (rv)
! 783: break;
! 784: }
! 785: }
! 786:
! 787: return (rv);
! 788: }
! 789:
! 790: /*
! 791: * Lookup a device in the cfdriver device array. Does not return a
! 792: * device if it is not active.
! 793: *
! 794: * Increments ref count on the device by one, reflecting the
! 795: * new reference created on the stack.
! 796: *
! 797: * Context: process only
! 798: */
! 799: struct device *
! 800: device_lookup(struct cfdriver *cd, int unit)
! 801: {
! 802: struct device *dv = NULL;
! 803:
! 804: if (unit >= 0 && unit < cd->cd_ndevs)
! 805: dv = (struct device *)(cd->cd_devs[unit]);
! 806:
! 807: if (!dv)
! 808: return (NULL);
! 809:
! 810: if (!(dv->dv_flags & DVF_ACTIVE))
! 811: dv = NULL;
! 812:
! 813: if (dv != NULL)
! 814: device_ref(dv);
! 815:
! 816: return (dv);
! 817: }
! 818:
! 819:
! 820: /*
! 821: * Increments the ref count on the device structure. The device
! 822: * structure is freed when the ref count hits 0.
! 823: *
! 824: * Context: process or interrupt
! 825: */
! 826: void
! 827: device_ref(struct device *dv)
! 828: {
! 829: dv->dv_ref++;
! 830: }
! 831:
! 832: /*
! 833: * Decrement the ref count on the device structure.
! 834: *
! 835: * free's the structure when the ref count hits zero.
! 836: *
! 837: * Context: process or interrupt
! 838: */
! 839: void
! 840: device_unref(struct device *dv)
! 841: {
! 842: dv->dv_ref--;
! 843: if (dv->dv_ref == 0) {
! 844: free(dv, M_DEVBUF);
! 845: }
! 846: }
CVSweb