diff options
Diffstat (limited to 'sys/kern/subr_autoconf.c')
-rw-r--r-- | sys/kern/subr_autoconf.c | 260 |
1 files changed, 231 insertions, 29 deletions
diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c index 1e9ab0ae6ba..ab73c7d2416 100644 --- a/sys/kern/subr_autoconf.c +++ b/sys/kern/subr_autoconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_autoconf.c,v 1.4 1996/04/21 22:27:13 deraadt Exp $ */ +/* $OpenBSD: subr_autoconf.c,v 1.5 1996/04/29 14:17:45 hvozda Exp $ */ /* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */ /* @@ -52,6 +52,9 @@ #include <sys/malloc.h> #include <sys/systm.h> #include <machine/limits.h> +/* Extra stuff from Matthias Drochner <drochner@zelux6.zel.kfa-juelich.de> + */ +#include <sys/queue.h> /* * Autoconfiguration subroutines. @@ -75,6 +78,12 @@ struct matchinfo { int indirect, pri; }; +struct cftable_head allcftables; + +static struct cftable staticcftable = { + cfdata +}; + static char *number __P((char *, int)); static void mapply __P((struct matchinfo *, struct cfdata *)); @@ -90,6 +99,8 @@ config_init() TAILQ_INIT(&alldevs); TAILQ_INIT(&allevents); + TAILQ_INIT(&allcftables); + TAILQ_INSERT_TAIL(&allcftables, &staticcftable, list); } /* @@ -150,6 +161,7 @@ config_search(fn, parent, aux) register struct cfdata *cf; register short *p; struct matchinfo m; + struct cftable *t; m.fn = fn; m.parent = parent; @@ -157,16 +169,18 @@ config_search(fn, parent, aux) m.aux = aux; m.indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; m.pri = 0; - for (cf = cfdata; cf->cf_driver; cf++) { - /* - * Skip cf if no longer eligible, otherwise scan through - * parents for one matching `parent', and try match function. - */ - if (cf->cf_fstate == FSTATE_FOUND) - continue; - for (p = cf->cf_parents; *p >= 0; p++) - if (parent->dv_cfdata == &cfdata[*p]) - mapply(&m, cf); + for(t = allcftables.tqh_first; t; t = t->list.tqe_next){ + for (cf = t->tab; cf->cf_driver; cf++) { + /* + * Skip cf if no longer eligible, otherwise scan through + * parents for one matching `parent', and try match function. + */ + if (cf->cf_fstate == FSTATE_FOUND) + continue; + for (p = cf->cf_parents; *p >= 0; p++) + if (parent->dv_cfdata == &(t->tab)[*p]) + mapply(&m, cf); + } } return (m.match); } @@ -188,23 +202,26 @@ config_scan(fn, parent) register short *p; void *match; int indirect; + struct cftable *t; indirect = parent && parent->dv_cfdata->cf_driver->cd_indirect; - for (cf = cfdata; cf->cf_driver; cf++) { - /* - * Skip cf if no longer eligible, otherwise scan through - * parents for one matching `parent', and try match function. - */ - if (cf->cf_fstate == FSTATE_FOUND) - continue; - for (p = cf->cf_parents; *p >= 0; p++) - if (parent->dv_cfdata == &cfdata[*p]) { - if (indirect) - match = config_make_softc(parent, cf); - else - match = cf; - (*fn)(parent, match); - } + for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { + for (cf = t->tab; cf->cf_driver; cf++) { + /* + * Skip cf if no longer eligible, otherwise scan through + * parents for one matching `parent', and try match function. + */ + if (cf->cf_fstate == FSTATE_FOUND) + continue; + for (p = cf->cf_parents; *p >= 0; p++) + if (parent->dv_cfdata == &(t->tab)[*p]) { + if (indirect) + match = config_make_softc(parent, cf); + else + match = cf; + (*fn)(parent, match); + } + } } } @@ -313,6 +330,7 @@ config_attach(parent, match, aux, print) register struct device *dev; register struct cfdriver *cd; register struct cfattach *ca; + struct cftable *t; if (parent && parent->dv_cfdata->cf_driver->cd_indirect) { dev = match; @@ -346,13 +364,15 @@ config_attach(parent, match, aux, print) * otherwise identical, or bump the unit number on all starred * cfdata for this device. */ - for (cf = cfdata; cf->cf_driver; cf++) - if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { + for (t = allcftables.tqh_first; t; t = t->list.tqe_next) { + for (cf = t->tab; cf->cf_driver; cf++) + if (cf->cf_driver == cd && cf->cf_unit == dev->dv_unit) { if (cf->cf_fstate == FSTATE_NOTFOUND) cf->cf_fstate = FSTATE_FOUND; if (cf->cf_fstate == FSTATE_STAR) cf->cf_unit++; - } + } + } (*ca->ca_attach)(parent, dev, aux); return (dev); } @@ -447,3 +467,185 @@ evcnt_attach(dev, name, ev) strcpy(ev->ev_name, name); TAILQ_INSERT_TAIL(&allevents, ev, ev_list); } + +typedef int (*cond_predicate_t) __P((struct device*, void*)); + +static int haschild __P((struct device *)); +static int detach_devices __P((cond_predicate_t, void *, + config_detach_callback_t, void *)); + +static int +haschild(dev) + struct device *dev; +{ + struct device *d; + + for (d = alldevs.tqh_first; + d != NULL; + d = d->dv_list.tqe_next) { + if (d->dv_parent == dev) + return(1); + } + return(0); +} + +static int +detach_devices(cond, condarg, callback, arg) + cond_predicate_t cond; + void *condarg; + config_detach_callback_t callback; + void *arg; +{ + struct device *d; + int alldone = 1; + + /* + * XXX should use circleq and run around the list backwards + * to allow for predicates to match children. + */ + d = alldevs.tqh_first; + while (d != NULL) { + if ((*cond)(d, condarg)) { + struct cfdriver *drv = d->dv_cfdata->cf_driver; + + /* device not busy? */ + /* driver's detach routine decides, upper + layer (eg bus dependent code) is notified + via callback */ +#ifdef DEBUG + printf("trying to detach device %s (%p)\n", + d->dv_xname, d); +#endif + if (!haschild(d) && + d->dv_cfdata->cf_attach->ca_detach && + ((*(d->dv_cfdata->cf_attach->ca_detach))(d)) == 0) { + int needit, i; + struct device *help; + + if (callback) + (*callback)(d, arg); + + /* remove reference in driver's devicelist */ + if ((d->dv_unit >= drv->cd_ndevs) || + (drv->cd_devs[d->dv_unit]!=d)) + panic("bad unit in detach_devices"); + drv->cd_devs[d->dv_unit] = NULL; + + /* driver is not needed anymore? */ + needit = 0; + for(i = 0; i<drv->cd_ndevs; i++) + if (drv->cd_devs[i]) + needit = 1; + + if (!needit) { + /* free devices array (alloc'd + in config_make_softc) */ + free(drv->cd_devs, M_DEVBUF); + drv->cd_ndevs = 0; + } + + /* remove entry in global device list */ + help = d->dv_list.tqe_next; + TAILQ_REMOVE(&alldevs, d, dv_list); +#ifdef DEBUG + printf("%s removed\n", d->dv_xname); +#endif + d->dv_cfdata->cf_fstate = FSTATE_NOTFOUND; + /* free memory for dev data (alloc'd + in config_make_softc) */ + free(d, M_DEVBUF); + d = help; + continue; + } else + alldone = 0; + } + d = d->dv_list.tqe_next; + } + return(!alldone); +} + +int dev_matches_cfdata __P((struct device *dev, void *)); + +int +dev_matches_cfdata(dev, arg) + struct device *dev; + void *arg; +{ + struct cfdata *cfdata = arg; + return(/* device uses same driver ? */ + (dev->dv_cfdata->cf_driver == cfdata->cf_driver) + /* device instance described by this cfdata? */ + && ((cfdata->cf_fstate == FSTATE_STAR) + || ((cfdata->cf_fstate == FSTATE_FOUND) + && (dev->dv_unit == cfdata->cf_unit))) + ); +} + +int +config_detach(cf, callback, arg) + struct cfdata *cf; + config_detach_callback_t callback; + void *arg; +{ + return(detach_devices(dev_matches_cfdata, cf, callback, arg)); +} + +int +attach_loadable(parentname, parentunit, cftable) + char *parentname; + int parentunit; + struct cftable *cftable; +{ + int found = 0; + struct device *d; + + TAILQ_INSERT_TAIL(&allcftables, cftable, list); + + for(d = alldevs.tqh_first; + d != NULL; + d = d->dv_list.tqe_next) { + struct cfdriver *drv = d->dv_cfdata->cf_driver; + + if ((!strcmp(parentname, drv->cd_name)) + && ((parentunit == -1) || (parentunit == d->dv_unit))) { + int s; + + s = splhigh(); /* ??? */ + found |= (*d->dv_cfdata->cf_attach->ca_reprobe)(d, &(cftable->tab[0])); + splx(s); + } + } + + if (!found) + TAILQ_REMOVE(&allcftables, cftable, list); + + return(found); +} + +static int +devcf_intable __P((struct device *, void *)); + +static int +devcf_intable(dev, arg) + struct device *dev; + void *arg; +{ + struct cftable *tbl = arg; + struct cfdata *cf; + + for(cf = tbl->tab; cf->cf_driver; cf++) { + if (dev->dv_cfdata == cf) + return(1); + } + return(0); +} + +int +detach_loadable(cftable) + struct cftable *cftable; +{ + if (!detach_devices(devcf_intable, cftable, 0, 0)) + return(0); + TAILQ_REMOVE(&allcftables, cftable, list); + return(1); +} |