diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/subr_bus.c | 150 | ||||
-rw-r--r-- | sys/sys/bus.h | 71 |
2 files changed, 218 insertions, 3 deletions
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 2007c09cec73..4315a5da4d38 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.24 1999/05/21 08:23:58 dfr Exp $ + * $Id: subr_bus.c,v 1.25 1999/05/22 09:52:21 peter Exp $ */ #include <sys/param.h> @@ -34,6 +34,8 @@ #include <sys/sysctl.h> #include <sys/bus_private.h> #include <sys/systm.h> +#include <machine/bus.h> +#include <sys/rman.h> #include <machine/stdarg.h> /* for device_printf() */ #include "opt_bus.h" @@ -1607,6 +1609,152 @@ SYSINIT(cfgload, SI_SUB_KMEM, SI_ORDER_ANY + 50, resource_cfgload, 0) * Some useful method implementations to make life easier for bus drivers. */ +void +resource_list_init(struct resource_list *rl) +{ + SLIST_INIT(rl); +} + +void +resource_list_free(struct resource_list *rl) +{ + struct resource_list_entry *rle; + + while ((rle = SLIST_FIRST(rl)) != NULL) { + if (rle->res) + panic("resource_list_free: resource entry is busy"); + SLIST_REMOVE_HEAD(rl, link); + free(rle, M_DEVBUF); + } +} + +void +resource_list_add(struct resource_list *rl, + int type, int rid, + u_long start, u_long end, u_long count) +{ + struct resource_list_entry *rle; + + rle = resource_list_find(rl, type, rid); + if (!rle) { + rle = malloc(sizeof(struct resource_list_entry), M_DEVBUF, M_NOWAIT); + if (!rle) + panic("resource_list_add: can't record entry"); + SLIST_INSERT_HEAD(rl, rle, link); + rle->type = type; + rle->rid = rid; + rle->res = NULL; + } + + if (rle->res) + panic("resource_list_add: resource entry is busy"); + + rle->start = start; + rle->end = end; + rle->count = count; +} + +struct resource_list_entry* +resource_list_find(struct resource_list *rl, + int type, int rid) +{ + struct resource_list_entry *rle; + + SLIST_FOREACH(rle, rl, link) + if (rle->type == type && rle->rid == rid) + return rle; + return NULL; +} + +void +resource_list_remove(struct resource_list *rl, + int type, int rid) +{ + struct resource_list_entry *rle = resource_list_find(rl, type, rid); + + if (rle) { + SLIST_REMOVE(rl, rle, resource_list_entry, link); + free(rle, M_DEVBUF); + } +} + +struct resource * +resource_list_alloc(device_t bus, device_t child, + int type, int *rid, + u_long start, u_long end, + u_long count, u_int flags) +{ + struct resource_list *rl; + struct resource_list_entry *rle = 0; + int passthrough = (device_get_parent(child) != bus); + int isdefault = (start == 0UL && end == ~0UL); + + if (passthrough) { + return BUS_ALLOC_RESOURCE(device_get_parent(bus), child, + type, rid, + start, end, count, flags); + } + + rl = device_get_ivars(child); + rle = resource_list_find(rl, type, *rid); + + if (!rle) + return 0; /* no resource of that type/rid */ + if (rle->res) + panic("resource_list_alloc: resource entry is busy"); + + if (isdefault) { + start = rle->start; + count = max(count, rle->count); + end = max(rle->end, start + count - 1); + } + + rle->res = BUS_ALLOC_RESOURCE(device_get_parent(bus), child, + type, rid, start, end, count, flags); + + /* + * Record the new range. + */ + if (rle->res) { + rle->start = rman_get_start(rle->res); + rle->end = rman_get_end(rle->res); + rle->count = count; + } + + return rle->res; +} + +int +resource_list_release(device_t bus, device_t child, + int type, int rid, struct resource *res) +{ + struct resource_list *rl; + struct resource_list_entry *rle = 0; + int passthrough = (device_get_parent(child) != bus); + int error; + + if (passthrough) { + return BUS_RELEASE_RESOURCE(device_get_parent(bus), child, + type, rid, res); + } + + rl = device_get_ivars(child); + rle = resource_list_find(rl, type, rid); + + if (!rle) + panic("resource_list_release: can't find resource"); + if (!rle->res) + panic("resource_list_release: resource entry is not busy"); + + error = BUS_RELEASE_RESOURCE(device_get_parent(bus), child, + type, rid, res); + if (error) + return error; + + rle->res = NULL; + return 0; +} + /* * Call DEVICE_IDENTIFY for each driver. */ diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 7ee1530c9ba2..d416b6101324 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.15 1999/05/09 13:00:49 phk Exp $ + * $Id: bus.h,v 1.16 1999/05/14 11:22:47 dfr Exp $ */ #ifndef _SYS_BUS_H_ @@ -85,6 +85,74 @@ typedef enum device_state { } device_state_t; /* + * Definitions for drivers which need to keep simple lists of resources + * for their child devices. + */ +struct resource; + +struct resource_list_entry { + SLIST_ENTRY(resource_list_entry) link; + int type; /* type argument to alloc_resource */ + int rid; /* resource identifier */ + struct resource *res; /* the real resource when allocated */ + u_long start; /* start of resource range */ + u_long end; /* end of resource range */ + u_long count; /* count within range */ +}; +SLIST_HEAD(resource_list, resource_list_entry); + +/* + * Initialise a resource list. + */ +void resource_list_init(struct resource_list *rl); + +/* + * Reclaim memory used by a resource list. + */ +void resource_list_free(struct resource_list *rl); + +/* + * Add a resource entry or modify an existing entry if one exists with + * the same type and rid. + */ +void resource_list_add(struct resource_list *rl, + int type, int rid, + u_long start, u_long end, u_long count); + +/* + * Find a resource entry by type and rid. + */ +struct resource_list_entry* + resource_list_find(struct resource_list *rl, + int type, int rid); + +/* + * Remove a resource entry. + */ +void resource_list_remove(struct resource_list *rl, + int type, int rid); + +/* + * Implement BUS_ALLOC_RESOURCE by looking up a resource from the list + * and passing the allocation up to the parent of bus. This assumes + * that the first entry of device_get_ivars(child) is a struct + * resource_list. This also handles 'passthrough' allocations where a + * child is a remote descendant of bus by passing the allocation up to + * the parent of bus. + */ +struct resource * + resource_list_alloc(device_t bus, device_t child, + int type, int *rid, + u_long start, u_long end, + u_long count, u_int flags); + +/* + * Implement BUS_RELEASE_RESOURCE. + */ +int resource_list_release(device_t bus, device_t child, + int type, int rid, struct resource *res); + +/* * The root bus, to which all top-level busses are attached. */ extern device_t root_bus; @@ -94,7 +162,6 @@ void root_bus_configure(void); /* * Useful functions for implementing busses. */ -struct resource; int bus_generic_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *r); |