/*
       *	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);