summaryrefslogtreecommitdiff
path: root/sys/kern/subr_autoconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/subr_autoconf.c')
-rw-r--r--sys/kern/subr_autoconf.c260
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);
+}