2005-04-17 02:20:36 +04:00
/*
* proc_devtree . c - handles / proc / device - tree
*
* Copyright 1997 Paul Mackerras
*/
# include <linux/errno.h>
2008-10-17 05:07:44 +04:00
# include <linux/init.h>
2005-04-17 02:20:36 +04:00
# include <linux/time.h>
# include <linux/proc_fs.h>
2009-09-07 03:31:25 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/stat.h>
# include <linux/string.h>
# include <asm/prom.h>
# include <asm/uaccess.h>
2009-04-07 21:19:18 +04:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
# ifndef HAVE_ARCH_DEVTREE_FIXUPS
2005-06-01 11:07:27 +04:00
static inline void set_node_proc_entry ( struct device_node * np ,
struct proc_dir_entry * de )
2005-04-17 02:20:36 +04:00
{
}
# endif
static struct proc_dir_entry * proc_device_tree ;
/*
* Supply data on a read from / proc / device - tree / node / property .
*/
2009-09-07 03:31:25 +04:00
static int property_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
2009-09-07 03:31:25 +04:00
struct property * pp = m - > private ;
2005-04-17 02:20:36 +04:00
2009-09-07 03:31:25 +04:00
seq_write ( m , pp - > value , pp - > length ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-09-07 03:31:25 +04:00
static int property_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , property_proc_show , PDE ( inode ) - > data ) ;
}
static const struct file_operations property_proc_fops = {
. owner = THIS_MODULE ,
. open = property_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2005-04-17 02:20:36 +04:00
/*
* For a node with a name like " gc@10 " , we make symlinks called " gc "
* and " @10 " to it .
*/
2005-11-07 06:29:02 +03:00
/*
* Add a property to a node
*/
static struct proc_dir_entry *
2006-03-27 07:26:26 +04:00
__proc_device_tree_add_prop ( struct proc_dir_entry * de , struct property * pp ,
const char * name )
2005-11-07 06:29:02 +03:00
{
struct proc_dir_entry * ent ;
/*
* Unfortunately proc_register puts each new entry
* at the beginning of the list . So we rearrange them .
*/
2009-09-07 03:31:25 +04:00
ent = proc_create_data ( name ,
strncmp ( name , " security- " , 9 ) ? S_IRUGO : S_IRUSR ,
de , & property_proc_fops , pp ) ;
2005-11-07 06:29:02 +03:00
if ( ent = = NULL )
return NULL ;
2006-03-27 07:26:26 +04:00
if ( ! strncmp ( name , " security- " , 9 ) )
2005-11-07 06:29:02 +03:00
ent - > size = 0 ; /* don't leak number of password chars */
else
ent - > size = pp - > length ;
return ent ;
}
void proc_device_tree_add_prop ( struct proc_dir_entry * pde , struct property * prop )
{
2006-03-27 07:26:26 +04:00
__proc_device_tree_add_prop ( pde , prop , prop - > name ) ;
2005-11-07 06:29:02 +03:00
}
2006-01-13 01:07:17 +03:00
void proc_device_tree_remove_prop ( struct proc_dir_entry * pde ,
struct property * prop )
{
remove_proc_entry ( prop - > name , pde ) ;
}
void proc_device_tree_update_prop ( struct proc_dir_entry * pde ,
struct property * newprop ,
struct property * oldprop )
{
struct proc_dir_entry * ent ;
for ( ent = pde - > subdir ; ent ! = NULL ; ent = ent - > next )
if ( ent - > data = = oldprop )
break ;
if ( ent = = NULL ) {
printk ( KERN_WARNING " device-tree: property \" %s \" "
" does not exist \n " , oldprop - > name ) ;
} else {
ent - > data = newprop ;
ent - > size = newprop - > length ;
}
}
2006-03-27 07:26:26 +04:00
/*
* Various dodgy firmware might give us nodes and / or properties with
* conflicting names . That ' s generally ok , except for exporting via / proc ,
* so munge names here to ensure they ' re unique .
*/
static int duplicate_name ( struct proc_dir_entry * de , const char * name )
{
struct proc_dir_entry * ent ;
int found = 0 ;
spin_lock ( & proc_subdir_lock ) ;
for ( ent = de - > subdir ; ent ! = NULL ; ent = ent - > next ) {
if ( strcmp ( ent - > name , name ) = = 0 ) {
found = 1 ;
break ;
}
}
spin_unlock ( & proc_subdir_lock ) ;
return found ;
}
static const char * fixup_name ( struct device_node * np , struct proc_dir_entry * de ,
const char * name )
{
char * fixed_name ;
int fixup_len = strlen ( name ) + 2 + 1 ; /* name + #x + \0 */
int i = 1 , size ;
realloc :
fixed_name = kmalloc ( fixup_len , GFP_KERNEL ) ;
if ( fixed_name = = NULL ) {
printk ( KERN_ERR " device-tree: Out of memory trying to fixup "
" name \" %s \" \n " , name ) ;
return name ;
}
retry :
size = snprintf ( fixed_name , fixup_len , " %s#%d " , name , i ) ;
size + + ; /* account for NULL */
if ( size > fixup_len ) {
/* We ran out of space, free and reallocate. */
kfree ( fixed_name ) ;
fixup_len = size ;
goto realloc ;
}
if ( duplicate_name ( de , fixed_name ) ) {
/* Multiple duplicates. Retry with a different offset. */
i + + ;
goto retry ;
}
printk ( KERN_WARNING " device-tree: Duplicate name in %s, "
" renamed to \" %s \" \n " , np - > full_name , fixed_name ) ;
return fixed_name ;
}
2005-04-17 02:20:36 +04:00
/*
* Process a node , adding entries for its children and its properties .
*/
2005-06-01 11:07:27 +04:00
void proc_device_tree_add_node ( struct device_node * np ,
struct proc_dir_entry * de )
2005-04-17 02:20:36 +04:00
{
struct property * pp ;
struct proc_dir_entry * ent ;
2005-06-01 11:07:27 +04:00
struct device_node * child ;
const char * p ;
2005-04-17 02:20:36 +04:00
set_node_proc_entry ( np , de ) ;
2005-06-01 11:07:27 +04:00
for ( child = NULL ; ( child = of_get_next_child ( np , child ) ) ; ) {
2006-03-27 07:26:26 +04:00
/* Use everything after the last slash, or the full name */
2005-04-17 02:20:36 +04:00
p = strrchr ( child - > full_name , ' / ' ) ;
if ( ! p )
p = child - > full_name ;
else
+ + p ;
2006-03-27 07:26:26 +04:00
if ( duplicate_name ( de , p ) )
p = fixup_name ( np , de , p ) ;
2005-04-17 02:20:36 +04:00
ent = proc_mkdir ( p , de ) ;
2009-06-18 03:25:59 +04:00
if ( ent = = NULL )
2005-04-17 02:20:36 +04:00
break ;
proc_device_tree_add_node ( child , ent ) ;
2005-06-01 11:07:27 +04:00
}
of_node_put ( child ) ;
2006-03-27 07:26:26 +04:00
2009-06-18 03:25:59 +04:00
for ( pp = np - > properties ; pp ! = NULL ; pp = pp - > next ) {
2006-03-27 07:26:26 +04:00
p = pp - > name ;
if ( duplicate_name ( de , p ) )
p = fixup_name ( np , de , p ) ;
2005-06-01 11:07:27 +04:00
2006-03-27 07:26:26 +04:00
ent = __proc_device_tree_add_prop ( de , pp , p ) ;
2009-06-18 03:25:59 +04:00
if ( ent = = NULL )
2005-04-17 02:20:36 +04:00
break ;
}
}
/*
* Called on initialization to set up the / proc / device - tree subtree
*/
2008-10-17 05:07:44 +04:00
void __init proc_device_tree_init ( void )
2005-04-17 02:20:36 +04:00
{
struct device_node * root ;
2008-12-09 12:47:29 +03:00
2005-04-17 02:20:36 +04:00
proc_device_tree = proc_mkdir ( " device-tree " , NULL ) ;
2009-06-18 03:25:59 +04:00
if ( proc_device_tree = = NULL )
2005-04-17 02:20:36 +04:00
return ;
root = of_find_node_by_path ( " / " ) ;
2009-06-18 03:25:59 +04:00
if ( root = = NULL ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR " /proc/device-tree: can't find root \n " ) ;
return ;
}
proc_device_tree_add_node ( root , proc_device_tree ) ;
of_node_put ( root ) ;
}