/*
* linux/kernel/resource.c
*
* Copyright (C) 1999 Linus Torvalds
* Copyright (C) 1999 Martin Mares <mj@ucw.cz>
*
* Arbitrary resource management.
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/malloc.h>
#include <linux/spinlock.h>
#include <asm/io.h>
struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO };
struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM };
static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
/*
* This generates reports for /proc/ioports and /proc/iomem
*/
26 static char * do_resource_list(struct resource *entry, const char *fmt, int offset, char *buf, char *end)
{
28 if (offset < 0)
offset = 0;
31 while (entry) {
const char *name = entry->name;
unsigned long from, to;
35 if ((int) (end-buf) < 80)
36 return buf;
from = entry->start;
to = entry->end;
40 if (!name)
name = "<BAD>";
buf += sprintf(buf, fmt + offset, from, to, name);
44 if (entry->child)
buf = do_resource_list(entry->child, fmt, offset-2, buf, end);
entry = entry->sibling;
}
49 return buf;
}
52 int get_resource_list(struct resource *root, char *buf, int size)
{
char *fmt;
int retval;
fmt = " %08lx-%08lx : %s\n";
58 if (root->end < 0x10000)
fmt = " %04lx-%04lx : %s\n";
read_lock(&resource_lock);
retval = do_resource_list(root->child, fmt, 8, buf, buf + size) - buf;
62 read_unlock(&resource_lock);
63 return retval;
}
/* Return the conflict entry if you can't request it */
67 static struct resource * __request_resource(struct resource *root, struct resource *new)
{
unsigned long start = new->start;
unsigned long end = new->end;
struct resource *tmp, **p;
73 if (end < start)
74 return root;
75 if (start < root->start)
76 return root;
77 if (end > root->end)
78 return root;
p = &root->child;
80 for (;;) {
tmp = *p;
82 if (!tmp || tmp->start > end) {
new->sibling = tmp;
*p = new;
new->parent = root;
86 return NULL;
}
p = &tmp->sibling;
89 if (tmp->end < start)
90 continue;
91 return tmp;
}
}
95 static int __release_resource(struct resource *old)
{
struct resource *tmp, **p;
p = &old->parent->child;
100 for (;;) {
tmp = *p;
102 if (!tmp)
103 break;
104 if (tmp == old) {
*p = tmp->sibling;
old->parent = NULL;
107 return 0;
}
p = &tmp->sibling;
}
111 return -EINVAL;
}
114 int request_resource(struct resource *root, struct resource *new)
{
struct resource *conflict;
write_lock(&resource_lock);
conflict = __request_resource(root, new);
120 write_unlock(&resource_lock);
121 return conflict ? -EBUSY : 0;
}
124 int release_resource(struct resource *old)
{
int retval;
write_lock(&resource_lock);
retval = __release_resource(old);
130 write_unlock(&resource_lock);
131 return retval;
}
134 int check_resource(struct resource *root, unsigned long start, unsigned long len)
{
struct resource *conflict, tmp;
tmp.start = start;
tmp.end = start + len - 1;
write_lock(&resource_lock);
conflict = __request_resource(root, &tmp);
142 if (!conflict)
__release_resource(&tmp);
144 write_unlock(&resource_lock);
145 return conflict ? -EBUSY : 0;
}
/*
* Find empty slot in the resource tree given range and alignment.
*/
151 static int find_resource(struct resource *root, struct resource *new,
unsigned long size,
unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void *, struct resource *, unsigned long),
void *alignf_data)
{
struct resource *this = root->child;
new->start = root->start;
161 for(;;) {
162 if (this)
new->end = this->start;
164 else
new->end = root->end;
166 if (new->start < min)
new->start = min;
168 if (new->end > max)
new->end = max;
new->start = (new->start + align - 1) & ~(align - 1);
171 if (alignf)
alignf(alignf_data, new, size);
173 if (new->start < new->end && new->end - new->start + 1 >= size) {
new->end = new->start + size - 1;
175 return 0;
}
177 if (!this)
178 break;
new->start = this->end + 1;
this = this->sibling;
}
182 return -EBUSY;
}
/*
* Allocate empty slot in the resource tree given range and alignment.
*/
188 int allocate_resource(struct resource *root, struct resource *new,
unsigned long size,
unsigned long min, unsigned long max,
unsigned long align,
void (*alignf)(void *, struct resource *, unsigned long),
void *alignf_data)
{
int err;
write_lock(&resource_lock);
err = find_resource(root, new, size, min, max, align, alignf, alignf_data);
199 if (err >= 0 && __request_resource(root, new))
err = -EBUSY;
201 write_unlock(&resource_lock);
202 return err;
}
/*
* This is compatibility stuff for IO resources.
*
* Note how this, unlike the above, knows about
* the IO flag meanings (busy etc).
*
* Request-region creates a new busy region.
*
* Check-region returns non-zero if the area is already busy
*
* Release-region releases a matching busy region.
*/
217 struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
{
struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
221 if (res) {
memset(res, 0, sizeof(*res));
res->name = name;
res->start = start;
res->end = start + n - 1;
res->flags = IORESOURCE_BUSY;
write_lock(&resource_lock);
230 for (;;) {
struct resource *conflict;
conflict = __request_resource(parent, res);
234 if (!conflict)
235 break;
236 if (conflict != parent) {
parent = conflict;
238 if (!(conflict->flags & IORESOURCE_BUSY))
239 continue;
}
/* Uhhuh, that didn't work out.. */
kfree(res);
res = NULL;
245 break;
}
247 write_unlock(&resource_lock);
}
249 return res;
}
252 int __check_region(struct resource *parent, unsigned long start, unsigned long n)
{
struct resource * res;
res = __request_region(parent, start, n, "check-region");
257 if (!res)
258 return -EBUSY;
release_resource(res);
kfree(res);
262 return 0;
}
265 void __release_region(struct resource *parent, unsigned long start, unsigned long n)
{
struct resource **p;
unsigned long end;
p = &parent->child;
end = start + n - 1;
273 for (;;) {
struct resource *res = *p;
276 if (!res)
277 break;
278 if (res->start <= start && res->end >= end) {
279 if (!(res->flags & IORESOURCE_BUSY)) {
p = &res->child;
281 continue;
}
283 if (res->start != start || res->end != end)
284 break;
*p = res->sibling;
kfree(res);
287 return;
}
p = &res->sibling;
}
printk("Trying to free nonexistent resource <%08lx-%08lx>\n", start, end);
}
/*
* Called from init/main.c to reserve IO ports.
*/
#define MAXRESERVE 4
298 static int __init reserve_setup(char *str)
{
int opt = 2, io_start, io_num;
static int reserved = 0;
static struct resource reserve[MAXRESERVE];
304 while (opt==2) {
int x = reserved;
307 if (get_option (&str, &io_start) != 2) break;
308 if (get_option (&str, &io_num) == 0) break;
309 if (x < MAXRESERVE) {
struct resource *res = reserve + x;
res->name = "reserved";
res->start = io_start;
res->end = io_start + io_num - 1;
res->child = NULL;
315 if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0)
reserved = x+1;
}
}
319 return 1;
}
__setup("reserve=", reserve_setup);