aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/subr_bus.c150
-rw-r--r--sys/sys/bus.h71
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);