summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>1999-08-08 00:37:10 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>1999-08-08 00:37:10 +0000
commit8caf3f08d55dcea309e251d36eca66a7159efe22 (patch)
tree0b028ee174ed5db9e0dbfb21a0d9aa6e77da27c1 /sys
parenta54bf8de952ccdc7011b84e89eeb6f39a7f54b81 (diff)
From NetBSD; new device detaching infrastructure.
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/subr_autoconf.c164
-rw-r--r--sys/sys/device.h26
2 files changed, 186 insertions, 4 deletions
diff --git a/sys/kern/subr_autoconf.c b/sys/kern/subr_autoconf.c
index 93613bb63df..12f8234d317 100644
--- a/sys/kern/subr_autoconf.c
+++ b/sys/kern/subr_autoconf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_autoconf.c,v 1.24 1999/08/05 17:41:44 niklas Exp $ */
+/* $OpenBSD: subr_autoconf.c,v 1.25 1999/08/08 00:37:09 niklas Exp $ */
/* $NetBSD: subr_autoconf.c,v 1.21 1996/04/04 06:06:18 cgd Exp $ */
/*
@@ -456,6 +456,7 @@ config_make_softc(parent, cf)
bzero(dev, ca->ca_devsize);
dev->dv_class = cd->cd_class;
dev->dv_cfdata = cf;
+ dev->dv_flags = DVF_ACTIVE; /* always initially active */
/* If this is a STAR device, search for a free unit number */
if (cf->cf_fstate == FSTATE_STAR) {
@@ -511,6 +512,167 @@ config_make_softc(parent, cf)
}
/*
+ * Detach a device. Optionally forced (e.g. because of hardware
+ * removal) and quiet. Returns zero if successful, non-zero
+ * (an error code) otherwise.
+ *
+ * Note that this code wants to be run from a process context, so
+ * that the detach can sleep to allow processes which have a device
+ * open to run and unwind their stacks.
+ */
+int
+config_detach(dev, flags)
+ struct device *dev;
+ int flags;
+{
+ struct cfdata *cf;
+ struct cfattach *ca;
+ struct cfdriver *cd;
+#ifdef DIAGNOSTIC
+ struct device *d;
+#endif
+ int rv = 0, i;
+
+ cf = dev->dv_cfdata;
+#ifdef DIAGNOSTIC
+ if (cf->cf_fstate != FSTATE_FOUND && cf->cf_fstate != FSTATE_STAR)
+ panic("config_detach: bad device fstate");
+#endif
+ ca = cf->cf_attach;
+ cd = cf->cf_driver;
+
+ /*
+ * Ensure the device is deactivated. If the device doesn't
+ * have an activation entry point, we allow DVF_ACTIVE to
+ * remain set. Otherwise, if DVF_ACTIVE is still set, the
+ * device is busy, and the detach fails.
+ */
+ if (ca->ca_activate != NULL)
+ rv = config_deactivate(dev);
+
+ /*
+ * Try to detach the device. If that's not possible, then
+ * we either panic() (for the forced but failed case), or
+ * return an error.
+ */
+ if (rv == 0) {
+ if (ca->ca_detach != NULL)
+ rv = (*ca->ca_detach)(dev, flags);
+ else
+ rv = EOPNOTSUPP;
+ }
+ if (rv != 0) {
+ if ((flags & DETACH_FORCE) == 0)
+ return (rv);
+ else
+ panic("config_detach: forced detach of %s failed (%d)",
+ dev->dv_xname, rv);
+ }
+
+ /*
+ * The device has now been successfully detached.
+ */
+
+#ifdef DIAGNOSTIC
+ /*
+ * Sanity: If you're successfully detached, you should have no
+ * children. (Note that because children must be attached
+ * after parents, we only need to search the latter part of
+ * the list.)
+ */
+ for (d = TAILQ_NEXT(dev, dv_list); d != NULL;
+ d = TAILQ_NEXT(d, dv_list)) {
+ if (d->dv_parent == dev)
+ panic("config_detach: detached device has children");
+ }
+#endif
+
+ /*
+ * Mark cfdata to show that the unit can be reused, if possible.
+ * Note that we can only re-use a starred unit number if the unit
+ * being detached had the last assigned unit number.
+ */
+ for (cf = cfdata; cf->cf_driver; cf++) {
+ if (cf->cf_driver == cd) {
+ if (cf->cf_fstate == FSTATE_FOUND &&
+ cf->cf_unit == dev->dv_unit)
+ cf->cf_fstate = FSTATE_NOTFOUND;
+ if (cf->cf_fstate == FSTATE_STAR &&
+ cf->cf_unit == dev->dv_unit + 1)
+ cf->cf_unit--;
+ }
+ }
+
+ /*
+ * Unlink from device list.
+ */
+ TAILQ_REMOVE(&alldevs, dev, dv_list);
+
+ /*
+ * Remove from cfdriver's array, tell the world, and free softc.
+ */
+ cd->cd_devs[dev->dv_unit] = NULL;
+ if ((flags & DETACH_QUIET) == 0)
+ printf("%s detached\n", dev->dv_xname);
+ free(dev, M_DEVBUF);
+
+ /*
+ * If the device now has no units in use, deallocate its softc array.
+ */
+ for (i = 0; i < cd->cd_ndevs; i++)
+ if (cd->cd_devs[i] != NULL)
+ break;
+ if (i == cd->cd_ndevs) { /* nothing found; deallocate */
+ free(cd->cd_devs, M_DEVBUF);
+ cd->cd_devs = NULL;
+ cd->cd_ndevs = 0;
+ }
+
+ /*
+ * Return success.
+ */
+ return (0);
+}
+
+int
+config_activate(dev)
+ struct device *dev;
+{
+ struct cfattach *ca = dev->dv_cfdata->cf_attach;
+ int rv = 0, oflags = dev->dv_flags;
+
+ if (ca->ca_activate == NULL)
+ return (EOPNOTSUPP);
+
+ if ((dev->dv_flags & DVF_ACTIVE) == 0) {
+ dev->dv_flags |= DVF_ACTIVE;
+ rv = (*ca->ca_activate)(dev, DVACT_ACTIVATE);
+ if (rv)
+ dev->dv_flags = oflags;
+ }
+ return (rv);
+}
+
+int
+config_deactivate(dev)
+ struct device *dev;
+{
+ struct cfattach *ca = dev->dv_cfdata->cf_attach;
+ int rv = 0, oflags = dev->dv_flags;
+
+ if (ca->ca_activate == NULL)
+ return (EOPNOTSUPP);
+
+ if (dev->dv_flags & DVF_ACTIVE) {
+ dev->dv_flags &= ~DVF_ACTIVE;
+ rv = (*ca->ca_activate)(dev, DVACT_DEACTIVATE);
+ if (rv)
+ dev->dv_flags = oflags;
+ }
+ return (rv);
+}
+
+/*
* Defer the configuration of the specified device until all
* of its parent's devices have been attached.
*/
diff --git a/sys/sys/device.h b/sys/sys/device.h
index bb241eeba60..5db3220c683 100644
--- a/sys/sys/device.h
+++ b/sys/sys/device.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: device.h,v 1.16 1999/08/05 17:41:43 niklas Exp $ */
+/* $OpenBSD: device.h,v 1.17 1999/08/08 00:37:09 niklas Exp $ */
/* $NetBSD: device.h,v 1.15 1996/04/09 20:55:24 cgd Exp $ */
/*
@@ -63,6 +63,14 @@ enum devclass {
DV_TTY /* serial line interface (???) */
};
+/*
+ * Actions for ca_activate.
+ */
+enum devact {
+ DVACT_ACTIVATE, /* activate the device */
+ DVACT_DEACTIVATE, /* deactivate the device */
+};
+
struct device {
enum devclass dv_class; /* this device's classification */
TAILQ_ENTRY(device) dv_list; /* entry on list of all devices */
@@ -70,7 +78,12 @@ struct device {
int dv_unit; /* device unit number */
char dv_xname[16]; /* external name (name + unit) */
struct device *dv_parent; /* pointer to parent device */
+ int dv_flags; /* misc. flags; see below */
};
+
+/* dv_flags */
+#define DVF_ACTIVE 0x0001 /* device is activated */
+
TAILQ_HEAD(devicelist, device);
/* `event' counters (use zero or more per device instance, as needed) */
@@ -127,10 +140,14 @@ struct cfattach {
size_t ca_devsize; /* size of dev data (for malloc) */
cfmatch_t ca_match; /* returns a match level */
void (*ca_attach) __P((struct device *, struct device *, void *));
- int (*ca_detach) __P((struct device*));
- int (*ca_reprobe) __P((struct device*, struct cfdata*));
+ int (*ca_detach) __P((struct device *, int));
+ int (*ca_activate) __P((struct device *, enum devact));
};
+/* Flags given to config_detach(), and the ca_detach function. */
+#define DETACH_FORCE 0x01 /* force detachment; hardware gone */
+#define DETACH_QUIET 0x02 /* don't print a notice */
+
struct cfdriver {
void **cd_devs; /* devices found */
char *cd_name; /* device name */
@@ -179,6 +196,9 @@ struct device *config_found_sm __P((struct device *, void *, cfprint_t,
struct device *config_rootfound __P((char *, void *));
void config_scan __P((cfscan_t, struct device *));
struct device *config_attach __P((struct device *, void *, void *, cfprint_t));
+int config_detach __P((struct device *, int));
+int config_activate __P((struct device *));
+int config_deactivate __P((struct device *));
struct device *config_make_softc __P((struct device *parent,
struct cfdata *cf));
void config_defer __P((struct device *, void (*)(struct device *)));