2005-04-17 02:20:36 +04:00
/*
* linux / kernel / resource . c
*
* Copyright ( C ) 1999 Linus Torvalds
* Copyright ( C ) 1999 Martin Mares < mj @ ucw . cz >
*
* Arbitrary resource management .
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/fs.h>
# include <linux/proc_fs.h>
# include <linux/seq_file.h>
# include <asm/io.h>
struct resource ioport_resource = {
. name = " PCI IO " ,
. start = 0x0000 ,
. end = IO_SPACE_LIMIT ,
. flags = IORESOURCE_IO ,
} ;
EXPORT_SYMBOL ( ioport_resource ) ;
struct resource iomem_resource = {
. name = " PCI mem " ,
. start = 0UL ,
. end = ~ 0UL ,
. flags = IORESOURCE_MEM ,
} ;
EXPORT_SYMBOL ( iomem_resource ) ;
static DEFINE_RWLOCK ( resource_lock ) ;
# ifdef CONFIG_PROC_FS
enum { MAX_IORES_LEVEL = 5 } ;
static void * r_next ( struct seq_file * m , void * v , loff_t * pos )
{
struct resource * p = v ;
( * pos ) + + ;
if ( p - > child )
return p - > child ;
while ( ! p - > sibling & & p - > parent )
p = p - > parent ;
return p - > sibling ;
}
static void * r_start ( struct seq_file * m , loff_t * pos )
__acquires ( resource_lock )
{
struct resource * p = m - > private ;
loff_t l = 0 ;
read_lock ( & resource_lock ) ;
for ( p = p - > child ; p & & l < * pos ; p = r_next ( m , p , & l ) )
;
return p ;
}
static void r_stop ( struct seq_file * m , void * v )
__releases ( resource_lock )
{
read_unlock ( & resource_lock ) ;
}
static int r_show ( struct seq_file * m , void * v )
{
struct resource * root = m - > private ;
struct resource * r = v , * p ;
int width = root - > end < 0x10000 ? 4 : 8 ;
int depth ;
for ( depth = 0 , p = r ; depth < MAX_IORES_LEVEL ; depth + + , p = p - > parent )
if ( p - > parent = = root )
break ;
seq_printf ( m , " %*s%0*lx-%0*lx : %s \n " ,
depth * 2 , " " ,
width , r - > start ,
width , r - > end ,
r - > name ? r - > name : " <BAD> " ) ;
return 0 ;
}
static struct seq_operations resource_op = {
. start = r_start ,
. next = r_next ,
. stop = r_stop ,
. show = r_show ,
} ;
static int ioports_open ( struct inode * inode , struct file * file )
{
int res = seq_open ( file , & resource_op ) ;
if ( ! res ) {
struct seq_file * m = file - > private_data ;
m - > private = & ioport_resource ;
}
return res ;
}
static int iomem_open ( struct inode * inode , struct file * file )
{
int res = seq_open ( file , & resource_op ) ;
if ( ! res ) {
struct seq_file * m = file - > private_data ;
m - > private = & iomem_resource ;
}
return res ;
}
static struct file_operations proc_ioports_operations = {
. open = ioports_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static struct file_operations proc_iomem_operations = {
. open = iomem_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = seq_release ,
} ;
static int __init ioresources_init ( void )
{
struct proc_dir_entry * entry ;
entry = create_proc_entry ( " ioports " , 0 , NULL ) ;
if ( entry )
entry - > proc_fops = & proc_ioports_operations ;
entry = create_proc_entry ( " iomem " , 0 , NULL ) ;
if ( entry )
entry - > proc_fops = & proc_iomem_operations ;
return 0 ;
}
__initcall ( ioresources_init ) ;
# endif /* CONFIG_PROC_FS */
/* Return the conflict entry if you can't request it */
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 ;
if ( end < start )
return root ;
if ( start < root - > start )
return root ;
if ( end > root - > end )
return root ;
p = & root - > child ;
for ( ; ; ) {
tmp = * p ;
if ( ! tmp | | tmp - > start > end ) {
new - > sibling = tmp ;
* p = new ;
new - > parent = root ;
return NULL ;
}
p = & tmp - > sibling ;
if ( tmp - > end < start )
continue ;
return tmp ;
}
}
static int __release_resource ( struct resource * old )
{
struct resource * tmp , * * p ;
p = & old - > parent - > child ;
for ( ; ; ) {
tmp = * p ;
if ( ! tmp )
break ;
if ( tmp = = old ) {
* p = tmp - > sibling ;
old - > parent = NULL ;
return 0 ;
}
p = & tmp - > sibling ;
}
return - EINVAL ;
}
int request_resource ( struct resource * root , struct resource * new )
{
struct resource * conflict ;
write_lock ( & resource_lock ) ;
conflict = __request_resource ( root , new ) ;
write_unlock ( & resource_lock ) ;
return conflict ? - EBUSY : 0 ;
}
EXPORT_SYMBOL ( request_resource ) ;
struct resource * ____request_resource ( struct resource * root , struct resource * new )
{
struct resource * conflict ;
write_lock ( & resource_lock ) ;
conflict = __request_resource ( root , new ) ;
write_unlock ( & resource_lock ) ;
return conflict ;
}
EXPORT_SYMBOL ( ____request_resource ) ;
int release_resource ( struct resource * old )
{
int retval ;
write_lock ( & resource_lock ) ;
retval = __release_resource ( old ) ;
write_unlock ( & resource_lock ) ;
return retval ;
}
EXPORT_SYMBOL ( release_resource ) ;
/*
* Find empty slot in the resource tree given range and alignment .
*/
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 , unsigned long ) ,
void * alignf_data )
{
struct resource * this = root - > child ;
new - > start = root - > start ;
/*
* Skip past an allocated resource that starts at 0 , since the assignment
* of this - > start - 1 to new - > end below would cause an underflow .
*/
if ( this & & this - > start = = 0 ) {
new - > start = this - > end + 1 ;
this = this - > sibling ;
}
for ( ; ; ) {
if ( this )
new - > end = this - > start - 1 ;
else
new - > end = root - > end ;
if ( new - > start < min )
new - > start = min ;
if ( new - > end > max )
new - > end = max ;
2005-06-26 01:59:00 +04:00
new - > start = ALIGN ( new - > start , align ) ;
2005-04-17 02:20:36 +04:00
if ( alignf )
alignf ( alignf_data , new , size , align ) ;
2005-04-17 02:25:58 +04:00
if ( new - > start < new - > end & & new - > end - new - > start > = size - 1 ) {
2005-04-17 02:20:36 +04:00
new - > end = new - > start + size - 1 ;
return 0 ;
}
if ( ! this )
break ;
new - > start = this - > end + 1 ;
this = this - > sibling ;
}
return - EBUSY ;
}
/*
* Allocate empty slot in the resource tree given range and alignment .
*/
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 , unsigned long ) ,
void * alignf_data )
{
int err ;
write_lock ( & resource_lock ) ;
err = find_resource ( root , new , size , min , max , align , alignf , alignf_data ) ;
if ( err > = 0 & & __request_resource ( root , new ) )
err = - EBUSY ;
write_unlock ( & resource_lock ) ;
return err ;
}
EXPORT_SYMBOL ( allocate_resource ) ;
/**
* insert_resource - Inserts a resource in the resource tree
* @ parent : parent of the new resource
* @ new : new resource to insert
*
* Returns 0 on success , - EBUSY if the resource can ' t be inserted .
*
* This function is equivalent of request_resource when no conflict
* happens . If a conflict happens , and the conflicting resources
* entirely fit within the range of the new resource , then the new
* resource is inserted and the conflicting resources become childs of
* the new resource . Otherwise the new resource becomes the child of
* the conflicting resource
*/
int insert_resource ( struct resource * parent , struct resource * new )
{
int result ;
struct resource * first , * next ;
write_lock ( & resource_lock ) ;
begin :
result = 0 ;
first = __request_resource ( parent , new ) ;
if ( ! first )
goto out ;
result = - EBUSY ;
if ( first = = parent )
goto out ;
/* Resource fully contained by the clashing resource? Recurse into it */
if ( first - > start < = new - > start & & first - > end > = new - > end ) {
parent = first ;
goto begin ;
}
for ( next = first ; ; next = next - > sibling ) {
/* Partial overlap? Bad, and unfixable */
if ( next - > start < new - > start | | next - > end > new - > end )
goto out ;
if ( ! next - > sibling )
break ;
if ( next - > sibling - > start > new - > end )
break ;
}
result = 0 ;
new - > parent = parent ;
new - > sibling = next - > sibling ;
new - > child = first ;
next - > sibling = NULL ;
for ( next = first ; next ; next = next - > sibling )
next - > parent = new ;
if ( parent - > child = = first ) {
parent - > child = new ;
} else {
next = parent - > child ;
while ( next - > sibling ! = first )
next = next - > sibling ;
next - > sibling = new ;
}
out :
write_unlock ( & resource_lock ) ;
return result ;
}
EXPORT_SYMBOL ( insert_resource ) ;
/*
* Given an existing resource , change its start and size to match the
* arguments . Returns - EBUSY if it can ' t fit . Existing children of
* the resource are assumed to be immutable .
*/
int adjust_resource ( struct resource * res , unsigned long start , unsigned long size )
{
struct resource * tmp , * parent = res - > parent ;
unsigned long end = start + size - 1 ;
int result = - EBUSY ;
write_lock ( & resource_lock ) ;
if ( ( start < parent - > start ) | | ( end > parent - > end ) )
goto out ;
for ( tmp = res - > child ; tmp ; tmp = tmp - > sibling ) {
if ( ( tmp - > start < start ) | | ( tmp - > end > end ) )
goto out ;
}
if ( res - > sibling & & ( res - > sibling - > start < = end ) )
goto out ;
tmp = parent - > child ;
if ( tmp ! = res ) {
while ( tmp - > sibling ! = res )
tmp = tmp - > sibling ;
if ( start < = tmp - > end )
goto out ;
}
res - > start = start ;
res - > end = end ;
result = 0 ;
out :
write_unlock ( & resource_lock ) ;
return result ;
}
EXPORT_SYMBOL ( adjust_resource ) ;
/*
* 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 .
*/
struct resource * __request_region ( struct resource * parent , unsigned long start , unsigned long n , const char * name )
{
struct resource * res = kmalloc ( sizeof ( * res ) , GFP_KERNEL ) ;
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 ) ;
for ( ; ; ) {
struct resource * conflict ;
conflict = __request_resource ( parent , res ) ;
if ( ! conflict )
break ;
if ( conflict ! = parent ) {
parent = conflict ;
if ( ! ( conflict - > flags & IORESOURCE_BUSY ) )
continue ;
}
/* Uhhuh, that didn't work out.. */
kfree ( res ) ;
res = NULL ;
break ;
}
write_unlock ( & resource_lock ) ;
}
return res ;
}
EXPORT_SYMBOL ( __request_region ) ;
int __deprecated __check_region ( struct resource * parent , unsigned long start , unsigned long n )
{
struct resource * res ;
res = __request_region ( parent , start , n , " check-region " ) ;
if ( ! res )
return - EBUSY ;
release_resource ( res ) ;
kfree ( res ) ;
return 0 ;
}
EXPORT_SYMBOL ( __check_region ) ;
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 ;
write_lock ( & resource_lock ) ;
for ( ; ; ) {
struct resource * res = * p ;
if ( ! res )
break ;
if ( res - > start < = start & & res - > end > = end ) {
if ( ! ( res - > flags & IORESOURCE_BUSY ) ) {
p = & res - > child ;
continue ;
}
if ( res - > start ! = start | | res - > end ! = end )
break ;
* p = res - > sibling ;
write_unlock ( & resource_lock ) ;
kfree ( res ) ;
return ;
}
p = & res - > sibling ;
}
write_unlock ( & resource_lock ) ;
printk ( KERN_WARNING " Trying to free nonexistent resource <%08lx-%08lx> \n " , start , end ) ;
}
EXPORT_SYMBOL ( __release_region ) ;
/*
* Called from init / main . c to reserve IO ports .
*/
# define MAXRESERVE 4
static int __init reserve_setup ( char * str )
{
static int reserved ;
static struct resource reserve [ MAXRESERVE ] ;
for ( ; ; ) {
int io_start , io_num ;
int x = reserved ;
if ( get_option ( & str , & io_start ) ! = 2 )
break ;
if ( get_option ( & str , & io_num ) = = 0 )
break ;
if ( x < MAXRESERVE ) {
struct resource * res = reserve + x ;
res - > name = " reserved " ;
res - > start = io_start ;
res - > end = io_start + io_num - 1 ;
res - > flags = IORESOURCE_BUSY ;
res - > child = NULL ;
if ( request_resource ( res - > start > = 0x10000 ? & iomem_resource : & ioport_resource , res ) = = 0 )
reserved = x + 1 ;
}
}
return 1 ;
}
__setup ( " reserve= " , reserve_setup ) ;