diff options
author | dfr <dfr@FreeBSD.org> | 1999-03-29 08:54:20 +0000 |
---|---|---|
committer | dfr <dfr@FreeBSD.org> | 1999-03-29 08:54:20 +0000 |
commit | e92e9c75bfcb1d32e9a6da832e0288caef0abd4a (patch) | |
tree | d56b9573f9c1710544ea3b0ac9249a916228c0ed /sys | |
parent | 21b8cdbbc44555e6e72a62f039cb74083fa94385 (diff) | |
download | FreeBSD-src-e92e9c75bfcb1d32e9a6da832e0288caef0abd4a.zip FreeBSD-src-e92e9c75bfcb1d32e9a6da832e0288caef0abd4a.tar.gz |
Add some useful functions to the device framework:
* bus_setup_intr() as a wrapper for BUS_SETUP_INTR
* bus_teardown_intr() as a wrapper for BUS_TEARDOWN_INTR
* device_get_nameunit() which returns e.g. "foo0" for name "foo" and unit 0.
* device_set_desc_copy() malloc a copy of the description string.
* device_quiet(), device_is_quiet(), device_verbose() suppress probe message.
Add one method to the BUS interface, BUS_CHILD_DETACHED() which is called
after the child has been detached to allow the bus to clean up any memory
which it allocated on behalf of the child.
I also fixed a bug which corrupted the list of drivers in a devclass if
a driver was added to more than one devclass.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/bus_if.m | 11 | ||||
-rw-r--r-- | sys/kern/makedevops.pl | 1 | ||||
-rw-r--r-- | sys/kern/subr_bus.c | 345 | ||||
-rw-r--r-- | sys/sys/bus.h | 13 | ||||
-rw-r--r-- | sys/sys/bus_private.h | 22 |
5 files changed, 343 insertions, 49 deletions
diff --git a/sys/kern/bus_if.m b/sys/kern/bus_if.m index fd4f648..8c48082 100644 --- a/sys/kern/bus_if.m +++ b/sys/kern/bus_if.m @@ -23,7 +23,7 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $Id: bus_if.m,v 1.4 1998/11/08 18:51:38 nsouch Exp $ +# $Id: bus_if.m,v 1.5 1998/11/14 21:58:51 wollman Exp $ # INTERFACE bus; @@ -71,6 +71,15 @@ METHOD int write_ivar { }; # +# Called after the child's DEVICE_DETACH method to allow the parent +# to reclaim any resources allocated on behalf of the child. +# +METHOD void child_detached { + device_t dev; + device_t child; +}; + +# # Allocate a system resource attached to `dev' on behalf of `child'. # The types are defined in <machine/resource.h>; the meaning of the # resource-ID field varies from bus to bus (but *rid == 0 is always diff --git a/sys/kern/makedevops.pl b/sys/kern/makedevops.pl index a542dcd..1c434d5 100644 --- a/sys/kern/makedevops.pl +++ b/sys/kern/makedevops.pl @@ -153,6 +153,7 @@ foreach $src ( @filenames ) { print CFILE "\n"; print CFILE "#include <sys/param.h>\n"; print CFILE "#include <sys/queue.h>\n"; + print CFILE "#include <sys/sysctl.h>\n"; print CFILE "#include <sys/bus_private.h>\n"; } diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 283dd60..4469b70 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: subr_bus.c,v 1.14 1999/01/16 17:44:09 dfr Exp $ + * $Id: subr_bus.c,v 1.15 1999/01/27 21:49:57 dillon Exp $ */ #include <sys/param.h> @@ -31,6 +31,7 @@ #include <sys/malloc.h> #include <sys/kernel.h> #include <sys/module.h> +#include <sys/sysctl.h> #include <sys/bus_private.h> #include <sys/systm.h> #include <machine/stdarg.h> /* for device_printf() */ @@ -84,6 +85,10 @@ void print_devclass_list(void); #define print_devclass_list() /* nop */ #endif +#ifdef DEVICE_SYSCTLS +static void device_register_oids(device_t dev); +static void device_unregister_oids(device_t dev); +#endif /* * Method table handling @@ -227,7 +232,14 @@ devclass_find(const char *classname) int devclass_add_driver(devclass_t dc, driver_t *driver) { + driverlink_t dl; + PDEBUG(("%s", DRIVERNAME(driver))); + + dl = malloc(sizeof *dl, M_DEVBUF, M_NOWAIT); + if (!dl) + return ENOMEM; + /* * Compile the drivers methods. */ @@ -238,7 +250,8 @@ devclass_add_driver(devclass_t dc, driver_t *driver) */ devclass_find_internal(driver->name, TRUE); - TAILQ_INSERT_TAIL(&dc->drivers, driver, link); + dl->driver = driver; + TAILQ_INSERT_TAIL(&dc->drivers, dl, link); return 0; } @@ -247,6 +260,7 @@ int devclass_delete_driver(devclass_t busclass, driver_t *driver) { devclass_t dc = devclass_find(driver->name); + driverlink_t dl; device_t dev; int i; int error; @@ -257,14 +271,34 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver) return 0; /* + * Find the link structure in the bus' list of drivers. + */ + for (dl = TAILQ_FIRST(&busclass->drivers); dl; + dl = TAILQ_NEXT(dl, link)) { + if (dl->driver == driver) + break; + } + + if (!dl) { + PDEBUG(("%s not found in %s list", driver->name, busclass->name)); + return ENOENT; + } + + /* * Disassociate from any devices. We iterate through all the * devices in the devclass of the driver and detach any which are - * using the driver. + * using the driver and which have a parent in the devclass which + * we are deleting from. + * + * Note that since a driver can be in multiple devclasses, we + * should not detach devices which are not children of devices in + * the affected devclass. */ for (i = 0; i < dc->maxunit; i++) { if (dc->devices[i]) { dev = dc->devices[i]; - if (dev->driver == driver) { + if (dev->driver == driver + && dev->parent && dev->parent->devclass == busclass) { if ((error = device_detach(dev)) != 0) return error; device_set_driver(dev, NULL); @@ -272,27 +306,40 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver) } } - TAILQ_REMOVE(&busclass->drivers, driver, link); + TAILQ_REMOVE(&busclass->drivers, dl, link); + free(dl, M_DEVBUF); + return 0; } -driver_t * -devclass_find_driver(devclass_t dc, const char *classname) +static driverlink_t +devclass_find_driver_internal(devclass_t dc, const char *classname) { - driver_t *driver; + driverlink_t dl; PDEBUG(("%s in devclass %s", classname, DEVCLANAME(dc))); - for (driver = TAILQ_FIRST(&dc->drivers); driver; - driver = TAILQ_NEXT(driver, link)) { - if (!strcmp(driver->name, classname)) - return driver; + for (dl = TAILQ_FIRST(&dc->drivers); dl; dl = TAILQ_NEXT(dl, link)) { + if (!strcmp(dl->driver->name, classname)) + return dl; } PDEBUG(("not found")); return NULL; } +driver_t * +devclass_find_driver(devclass_t dc, const char *classname) +{ + driverlink_t dl; + + dl = devclass_find_driver_internal(dc, classname); + if (dl) + return dl->driver; + else + return NULL; +} + const char * devclass_get_name(devclass_t dc) { @@ -407,14 +454,28 @@ devclass_alloc_unit(devclass_t dc, int *unitp) static int devclass_add_device(devclass_t dc, device_t dev) { - int error; + int buflen, error; PDEBUG(("%s in devclass %s", DEVICENAME(dev), DEVCLANAME(dc))); - if ((error = devclass_alloc_unit(dc, &dev->unit)) != 0) + buflen = strlen(dc->name) + 5; + dev->nameunit = malloc(buflen, M_DEVBUF, M_NOWAIT); + if (!dev->nameunit) + return ENOMEM; + + if ((error = devclass_alloc_unit(dc, &dev->unit)) != 0) { + free(dev->nameunit, M_DEVBUF); + dev->nameunit = NULL; return error; + } dc->devices[dev->unit] = dev; dev->devclass = dc; + snprintf(dev->nameunit, buflen, "%s%d", dc->name, dev->unit); + +#ifdef DEVICE_SYSCTLS + device_register_oids(dev); +#endif + return 0; } @@ -433,8 +494,15 @@ devclass_delete_device(devclass_t dc, device_t dev) if (dev->flags & DF_WILDCARD) dev->unit = -1; dev->devclass = NULL; + free(dev->nameunit, M_DEVBUF); + dev->nameunit = NULL; while (dc->nextunit > 0 && dc->devices[dc->nextunit - 1] == NULL) dc->nextunit--; + +#ifdef DEVICE_SYSCTLS + device_unregister_oids(dev); +#endif + return 0; } @@ -444,7 +512,6 @@ make_device(device_t parent, const char *name, { device_t dev; devclass_t dc; - int error; PDEBUG(("%s at %s as unit %d with%s ivars", name, DEVICENAME(parent), unit, (ivars? "":"out"))); @@ -455,9 +522,6 @@ make_device(device_t parent, const char *name, printf("make_device: can't find device class %s\n", name); return NULL; } - - if ((error = devclass_alloc_unit(dc, &unit)) != 0) - return NULL; } else dc = NULL; @@ -469,21 +533,21 @@ make_device(device_t parent, const char *name, TAILQ_INIT(&dev->children); dev->ops = &null_ops; dev->driver = NULL; - dev->devclass = dc; + dev->devclass = NULL; dev->unit = unit; + dev->nameunit = NULL; dev->desc = NULL; dev->busy = 0; dev->flags = DF_ENABLED; if (unit == -1) dev->flags |= DF_WILDCARD; - if (name) + if (name) { dev->flags |= DF_FIXEDCLASS; + devclass_add_device(dc, dev); + } dev->ivars = ivars; dev->softc = NULL; - if (dc) - dc->devices[unit] = dev; - dev->state = DS_NOTPRESENT; return dev; @@ -560,6 +624,7 @@ device_delete_child(device_t dev, device_t child) if (child->devclass) devclass_delete_device(child->devclass, child); TAILQ_REMOVE(&dev->children, child, link); + device_set_desc(child, NULL); free(child, M_DEVBUF); return 0; @@ -584,24 +649,23 @@ device_find_child(device_t dev, const char *classname, int unit) return NULL; } -static driver_t * +static driverlink_t first_matching_driver(devclass_t dc, device_t dev) { if (dev->devclass) - return devclass_find_driver(dc, dev->devclass->name); + return devclass_find_driver_internal(dc, dev->devclass->name); else return TAILQ_FIRST(&dc->drivers); } -static driver_t * -next_matching_driver(devclass_t dc, device_t dev, driver_t *last) +static driverlink_t +next_matching_driver(devclass_t dc, device_t dev, driverlink_t last) { if (dev->devclass) { - driver_t *driver; - for (driver = TAILQ_NEXT(last, link); driver; - driver = TAILQ_NEXT(driver, link)) - if (!strcmp(dev->devclass->name, driver->name)) - return driver; + driverlink_t dl; + for (dl = TAILQ_NEXT(last, link); dl; dl = TAILQ_NEXT(dl, link)) + if (!strcmp(dev->devclass->name, dl->driver->name)) + return dl; return NULL; } else return TAILQ_NEXT(last, link); @@ -611,7 +675,7 @@ static int device_probe_child(device_t dev, device_t child) { devclass_t dc; - driver_t *driver; + driverlink_t dl; dc = dev->devclass; if (dc == NULL) @@ -620,14 +684,14 @@ device_probe_child(device_t dev, device_t child) if (child->state == DS_ALIVE) return 0; - for (driver = first_matching_driver(dc, child); - driver; - driver = next_matching_driver(dc, child, driver)) { - PDEBUG(("Trying %s", DRIVERNAME(driver))); - device_set_driver(child, driver); + for (dl = first_matching_driver(dc, child); + dl; + dl = next_matching_driver(dc, child, dl)) { + PDEBUG(("Trying %s", DRIVERNAME(dl->driver))); + device_set_driver(child, dl->driver); if (DEVICE_PROBE(child) == 0) { if (!child->devclass) - device_set_devclass(child, driver->name); + device_set_devclass(child, dl->driver->name); child->state = DS_ALIVE; return 0; } @@ -691,6 +755,12 @@ device_get_name(device_t dev) return NULL; } +const char * +device_get_nameunit(device_t dev) +{ + return dev->nameunit; +} + int device_get_unit(device_t dev) { @@ -724,10 +794,44 @@ device_printf(device_t dev, const char * fmt, ...) va_end(ap); } +static void +device_set_desc_internal(device_t dev, const char* desc, int copy) +{ + if (dev->desc && (dev->flags & DF_DESCMALLOCED)) { + free(dev->desc, M_DEVBUF); + dev->flags &= ~DF_DESCMALLOCED; + dev->desc = NULL; + } + + if (copy && desc) { + dev->desc = malloc(strlen(desc) + 1, M_DEVBUF, M_NOWAIT); + if (dev->desc) { + strcpy(dev->desc, desc); + dev->flags |= DF_DESCMALLOCED; + } + } else + /* Avoid a -Wcast-qual warning */ + dev->desc = (char *)(uintptr_t) desc; + +#ifdef DEVICE_SYSCTLS + { + struct sysctl_oid *oid = &dev->oid[1]; + oid->oid_arg1 = dev->desc ? dev->desc : ""; + oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0; + } +#endif +} + void device_set_desc(device_t dev, const char* desc) { - dev->desc = desc; + device_set_desc_internal(dev, desc, FALSE); +} + +void +device_set_desc_copy(device_t dev, const char* desc) +{ + device_set_desc_internal(dev, desc, TRUE); } void * @@ -784,6 +888,24 @@ device_unbusy(device_t dev) } } +void +device_quiet(device_t dev) +{ + dev->flags |= DF_QUIET; +} + +void +device_verbose(device_t dev) +{ + dev->flags &= ~DF_QUIET; +} + +int +device_is_quiet(device_t dev) +{ + return (dev->flags & DF_QUIET) != 0; +} + int device_is_enabled(device_t dev) { @@ -853,7 +975,8 @@ device_probe_and_attach(device_t dev) if (dev->flags & DF_ENABLED) { error = device_probe_child(bus, dev); if (!error) { - device_print_child(bus, dev); + if (!device_is_quiet(dev)) + device_print_child(bus, dev); error = DEVICE_ATTACH(dev); if (!error) dev->state = DS_ATTACHED; @@ -884,7 +1007,9 @@ device_detach(device_t dev) return 0; if ((error = DEVICE_DETACH(dev)) != 0) - return error; + return error; + if (dev->parent) + BUS_CHILD_DETACHED(dev->parent, dev); if (!(dev->flags & DF_FIXEDCLASS)) devclass_delete_device(dev->devclass, dev); @@ -903,6 +1028,124 @@ device_shutdown(device_t dev) return DEVICE_SHUTDOWN(dev); } +#ifdef DEVICE_SYSCTLS + +/* + * Sysctl nodes for devices. + */ + +SYSCTL_NODE(_hw, OID_AUTO, devices, CTLFLAG_RW, 0, "A list of all devices"); + +static int +sysctl_handle_children SYSCTL_HANDLER_ARGS +{ + device_t dev = arg1; + device_t child; + int first = 1, error = 0; + + for (child = TAILQ_FIRST(&dev->children); child; + child = TAILQ_NEXT(child, link)) { + if (child->nameunit) { + if (!first) { + error = SYSCTL_OUT(req, ",", 1); + if (error) return error; + } else { + first = 0; + } + error = SYSCTL_OUT(req, child->nameunit, strlen(child->nameunit)); + if (error) return error; + } + } + + error = SYSCTL_OUT(req, "", 1); + + return error; +} + +static int +sysctl_handle_state SYSCTL_HANDLER_ARGS +{ + device_t dev = arg1; + + switch (dev->state) { + case DS_NOTPRESENT: + return SYSCTL_OUT(req, "notpresent", sizeof("notpresent")); + case DS_ALIVE: + return SYSCTL_OUT(req, "alive", sizeof("alive")); + case DS_ATTACHED: + return SYSCTL_OUT(req, "attached", sizeof("attached")); + case DS_BUSY: + return SYSCTL_OUT(req, "busy", sizeof("busy")); + } + + return 0; +} + +static void +device_register_oids(device_t dev) +{ + struct sysctl_oid* oid; + + oid = &dev->oid[0]; + bzero(oid, sizeof(*oid)); + oid->oid_parent = &sysctl__hw_devices_children; + oid->oid_number = OID_AUTO; + oid->oid_kind = CTLTYPE_NODE | CTLFLAG_RW; + oid->oid_arg1 = &dev->oidlist[0]; + oid->oid_arg2 = 0; + oid->oid_name = dev->nameunit; + oid->oid_handler = 0; + oid->oid_fmt = "N"; + SLIST_INIT(&dev->oidlist[0]); + sysctl_register_oid(oid); + + oid = &dev->oid[1]; + bzero(oid, sizeof(*oid)); + oid->oid_parent = &dev->oidlist[0]; + oid->oid_number = OID_AUTO; + oid->oid_kind = CTLTYPE_STRING | CTLFLAG_RD; + oid->oid_arg1 = dev->desc ? dev->desc : ""; + oid->oid_arg2 = dev->desc ? strlen(dev->desc) : 0; + oid->oid_name = "desc"; + oid->oid_handler = sysctl_handle_string; + oid->oid_fmt = "A"; + sysctl_register_oid(oid); + + oid = &dev->oid[2]; + bzero(oid, sizeof(*oid)); + oid->oid_parent = &dev->oidlist[0]; + oid->oid_number = OID_AUTO; + oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD; + oid->oid_arg1 = dev; + oid->oid_arg2 = 0; + oid->oid_name = "children"; + oid->oid_handler = sysctl_handle_children; + oid->oid_fmt = "A"; + sysctl_register_oid(oid); + + oid = &dev->oid[3]; + bzero(oid, sizeof(*oid)); + oid->oid_parent = &dev->oidlist[0]; + oid->oid_number = OID_AUTO; + oid->oid_kind = CTLTYPE_INT | CTLFLAG_RD; + oid->oid_arg1 = dev; + oid->oid_arg2 = 0; + oid->oid_name = "state"; + oid->oid_handler = sysctl_handle_state; + oid->oid_fmt = "A"; + sysctl_register_oid(oid); +} + +static void +device_unregister_oids(device_t dev) +{ + sysctl_unregister_oid(&dev->oid[0]); + sysctl_unregister_oid(&dev->oid[1]); + sysctl_unregister_oid(&dev->oid[2]); +} + +#endif + /* * Access functions for device resources. */ @@ -1236,6 +1479,23 @@ bus_release_resource(device_t dev, int type, int rid, struct resource *r) type, rid, r)); } +int +bus_setup_intr(device_t dev, struct resource *r, + driver_intr_t handler, void *arg, void **cookiep) +{ + if (dev->parent == 0) + return (EINVAL); + return (BUS_SETUP_INTR(dev->parent, dev, r, handler, arg, cookiep)); +} + +int +bus_teardown_intr(device_t dev, struct resource *r, void *cookie) +{ + if (dev->parent == 0) + return (EINVAL); + return (BUS_TEARDOWN_INTR(dev->parent, dev, r, cookie)); +} + static void root_print_child(device_t dev, device_t child) { @@ -1423,6 +1683,7 @@ print_device_short(device_t dev, int indent) (dev->flags&DF_ENABLED? "enabled,":"disabled,"), (dev->flags&DF_FIXEDCLASS? "fixed,":""), (dev->flags&DF_WILDCARD? "wildcard,":""), + (dev->flags&DF_DESCMALLOCED? "descmalloced,":""), (dev->ivars? "":"no "), (dev->softc? "":"no "), dev->busy)); diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 86316a8..3c7f9de 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bus.h,v 1.8 1999/01/16 17:44:08 dfr Exp $ + * $Id: bus.h,v 1.9 1999/03/06 16:52:04 bde Exp $ */ #ifndef _SYS_BUS_H_ @@ -74,7 +74,6 @@ struct driver { driver_type_t type; size_t softc; /* size of device softc struct */ void *priv; /* driver private data */ - TAILQ_ENTRY(driver) link; /* list of devices on bus */ device_ops_t ops; /* compiled method table */ }; @@ -136,6 +135,9 @@ int bus_deactivate_resource(device_t dev, int type, int rid, struct resource *r); int bus_release_resource(device_t dev, int type, int rid, struct resource *r); +int bus_setup_intr(device_t dev, struct resource *r, + driver_intr_t handler, void *arg, void **cookiep); +int bus_teardown_intr(device_t dev, struct resource *r, void *cookie); /* * Access functions for device. @@ -158,19 +160,24 @@ device_t device_get_parent(device_t dev); int device_get_children(device_t dev, device_t **listp, int *countp); void *device_get_ivars(device_t dev); const char *device_get_name(device_t dev); +const char *device_get_nameunit(device_t dev); void *device_get_softc(device_t dev); device_state_t device_get_state(device_t dev); int device_get_unit(device_t dev); -int device_is_enabled(device_t dev); int device_is_alive(device_t dev); /* did probe succeed? */ +int device_is_enabled(device_t dev); +int device_is_quiet(device_t dev); void device_print_prettyname(device_t dev); void device_printf(device_t dev, const char *, ...) __printflike(2, 3); int device_probe_and_attach(device_t dev); +void device_quiet(device_t dev); void device_set_desc(device_t dev, const char* desc); +void device_set_desc_copy(device_t dev, const char* desc); int device_set_devclass(device_t dev, const char *classname); int device_set_driver(device_t dev, driver_t *driver); int device_shutdown(device_t dev); void device_unbusy(device_t dev); +void device_verbose(device_t dev); /* * Access functions for devclass. diff --git a/sys/sys/bus_private.h b/sys/sys/bus_private.h index 8dfa87c..8ac9944 100644 --- a/sys/sys/bus_private.h +++ b/sys/sys/bus_private.h @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bus_private.h,v 1.3 1998/07/22 08:35:50 dfr Exp $ + * $Id: bus_private.h,v 1.4 1998/11/14 21:58:41 wollman Exp $ */ #ifndef _SYS_BUS_PRIVATE_H_ @@ -32,10 +32,19 @@ #include <sys/bus.h> /* + * Used to attach drivers to devclasses. + */ +typedef struct driverlink *driverlink_t; +struct driverlink { + driver_t *driver; + TAILQ_ENTRY(driverlink) link; /* list of drivers in devclass */ +}; + +/* * Forward declarations */ typedef TAILQ_HEAD(devclass_list, devclass) devclass_list_t; -typedef TAILQ_HEAD(driver_list, driver) driver_list_t; +typedef TAILQ_HEAD(driver_list, driverlink) driver_list_t; typedef TAILQ_HEAD(device_list, device) device_list_t; struct devclass { @@ -109,13 +118,20 @@ struct device { driver_t *driver; devclass_t devclass; /* device class which we are in */ int unit; - const char* desc; /* driver specific description */ + char* nameunit; /* name+unit e.g. foodev0 */ + char* desc; /* driver specific description */ int busy; /* count of calls to device_busy() */ device_state_t state; int flags; +#ifdef DEVICE_SYSCTLS + struct sysctl_oid oid[4]; + struct sysctl_oid_list oidlist[1]; +#endif #define DF_ENABLED 1 /* device should be probed/attached */ #define DF_FIXEDCLASS 2 /* devclass specified at create time */ #define DF_WILDCARD 4 /* unit was originally wildcard */ +#define DF_DESCMALLOCED 8 /* description was malloced */ +#define DF_QUIET 16 /* don't print verbose attach message */ void *ivars; void *softc; }; |