diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1999-08-08 00:37:10 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1999-08-08 00:37:10 +0000 |
commit | 8caf3f08d55dcea309e251d36eca66a7159efe22 (patch) | |
tree | 0b028ee174ed5db9e0dbfb21a0d9aa6e77da27c1 /sys | |
parent | a54bf8de952ccdc7011b84e89eeb6f39a7f54b81 (diff) |
From NetBSD; new device detaching infrastructure.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/subr_autoconf.c | 164 | ||||
-rw-r--r-- | sys/sys/device.h | 26 |
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 *))); |