summaryrefslogtreecommitdiff
path: root/sys/kern/subr_autoconf.c
diff options
context:
space:
mode:
authorConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2000-04-09 19:23:19 +0000
committerConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2000-04-09 19:23:19 +0000
commit12ece17dccce8d44b4fddd3e91f7a76015763f1c (patch)
tree913ba369b20ad72ec511003f201899ce5dbbafc4 /sys/kern/subr_autoconf.c
parent9e11ee26538ad57dbcd4af6608bac6a2dac123ab (diff)
Added config_detach_children, config_activate_children.
Added ref counting to devices: device_ref, device_unref and a new method for devices : ca_zeroref, called when the ref count hits zero. Note that ca_zeroref may be called from interrupt context.
Diffstat (limited to 'sys/kern/subr_autoconf.c')
-rw-r--r--sys/kern/subr_autoconf.c141
1 files changed, 139 insertions, 2 deletions
diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c
index 12f8234d317..1dcdcacf061 100644
--- a/sys/kern/subr_autoconf.c
+++ b/sys/kern/subr_autoconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_autoconf.c,v 1.25 1999/08/08 00:37:09 niklas Exp $ */
+/* $OpenBSD: subr_autoconf.c,v 1.26 2000/04/09 19:23:18 csapuntz Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */
/*
@@ -400,6 +400,7 @@ config_attach(parent, match, aux, print)
cf->cf_fstate = FSTATE_FOUND;
TAILQ_INSERT_TAIL(&alldevs, dev, dv_list);
+ device_ref(dev);
if (parent == ROOT)
printf("%s (root)", dev->dv_xname);
@@ -508,6 +509,8 @@ config_make_softc(parent, cf)
if (cd->cd_devs[dev->dv_unit])
panic("config_make_softc: duplicate %s", dev->dv_xname);
+ dev->dv_ref = 1;
+
return (dev);
}
@@ -607,6 +610,7 @@ config_detach(dev, flags)
* Unlink from device list.
*/
TAILQ_REMOVE(&alldevs, dev, dv_list);
+ device_unref(dev);
/*
* Remove from cfdriver's array, tell the world, and free softc.
@@ -614,8 +618,8 @@ config_detach(dev, flags)
cd->cd_devs[dev->dv_unit] = NULL;
if ((flags & DETACH_QUIET) == 0)
printf("%s detached\n", dev->dv_xname);
- free(dev, M_DEVBUF);
+ device_unref(dev);
/*
* If the device now has no units in use, deallocate its softc array.
*/
@@ -722,6 +726,139 @@ config_process_deferred_children(parent)
}
}
+int
+config_detach_children(parent, flags)
+ struct device *parent;
+ int flags;
+{
+ struct device *dev, *next_dev;
+ int rv = 0;
+
+ /* The config_detach routine may sleep, meaning devices
+ may be added to the queue. However, all devices will
+ be added to the tail of the queue, the queue won't
+ be re-organized, and the subtree of parent here should be locked
+ for purposes of adding/removing children.
+ */
+ for (dev = TAILQ_FIRST(&alldevs);
+ dev != NULL; dev = next_dev) {
+ next_dev = TAILQ_NEXT(dev, dv_list);
+ if (dev->dv_parent == parent &&
+ (rv = config_detach(dev, flags)))
+ return (rv);
+ }
+
+ return (rv);
+}
+
+int
+config_activate_children(parent, act)
+ struct device *parent;
+ enum devact act;
+{
+ struct device *dev, *next_dev;
+ int rv = 0;
+
+ /* The config_deactivate routine may sleep, meaning devices
+ may be added to the queue. However, all devices will
+ be added to the tail of the queue, the queue won't
+ be re-organized, and the subtree of parent here should be locked
+ for purposes of adding/removing children.
+ */
+ for (dev = TAILQ_FIRST(&alldevs);
+ dev != NULL; dev = next_dev) {
+ next_dev = TAILQ_NEXT(dev, dv_list);
+ if (dev->dv_parent == parent) {
+ switch (act) {
+ case DVACT_ACTIVATE:
+ rv = config_activate(dev);
+ break;
+ case DVACT_DEACTIVATE:
+ rv = config_deactivate(dev);
+ break;
+ default:
+#ifdef DIAGNOSTIC
+ printf ("config_activate_children: shouldn't get here");
+#endif
+ rv = EOPNOTSUPP;
+ break;
+
+ }
+
+ if (rv)
+ break;
+ }
+ }
+
+ return (rv);
+}
+
+/*
+ * Lookup a device in the cfdriver device array. Does not return a
+ * device if it is not active.
+ *
+ * Increments ref count on the device by one, reflecting the
+ * new reference created on the stack.
+ *
+ * Context: process only
+ */
+struct device *
+device_lookup(cd, unit)
+ struct cfdriver *cd;
+ int unit;
+{
+ struct device *dv = NULL;
+
+ if (unit >= 0 && unit <= cd->cd_ndevs)
+ dv = (struct device *)(cd->cd_devs[unit]);
+
+ if (!dv)
+ return (NULL);
+
+ if (!(dv->dv_flags & DVF_ACTIVE))
+ dv = NULL;
+
+ if (dv != NULL)
+ device_ref(dv);
+
+ return (dv);
+}
+
+
+/*
+ * Increments the ref count on the device structure. The device
+ * structure is freed when the ref count hits 0.
+ *
+ * Context: process or interrupt
+ */
+void
+device_ref(dv)
+ struct device *dv;
+{
+ dv->dv_ref++;
+}
+
+/*
+ * Decrement the ref count on the device structure.
+ *
+ * free's the structure when the ref count hits zero and calls the zeroref
+ * function.
+ *
+ * Context: process or interrupt
+ */
+void
+device_unref(dv)
+ struct device *dv;
+{
+ dv->dv_ref--;
+ if (dv->dv_ref == 0) {
+ if (dv->dv_cfdata->cf_attach->ca_zeroref)
+ (*dv->dv_cfdata->cf_attach->ca_zeroref)(dv);
+
+ free(dv, M_DEVBUF);
+ }
+}
+
/*
* Attach an event. These must come from initially-zero space (see
* commented-out assignments below), but that occurs naturally for