2007-09-25 17:57:13 -07:00
/*
Copyright ( C ) 2004 - 2007 rt2x00 SourceForge Project
< http : //rt2x00.serialmonkey.com>
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the
Free Software Foundation , Inc . ,
59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
/*
Module : rt2x00lib
Abstract : rt2x00 debugfs specific routines .
*/
# include <linux/debugfs.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/uaccess.h>
# include "rt2x00.h"
# include "rt2x00lib.h"
# define PRINT_LINE_LEN_MAX 32
struct rt2x00debug_intf {
/*
* Pointer to driver structure where
* this debugfs entry belongs to .
*/
struct rt2x00_dev * rt2x00dev ;
/*
* Reference to the rt2x00debug structure
* which can be used to communicate with
* the registers .
*/
const struct rt2x00debug * debug ;
/*
* Debugfs entries for :
* - driver folder
2007-11-27 21:48:16 +01:00
* - driver file
* - chipset file
* - device flags file
* - register folder
* - csr offset / value files
* - eeprom offset / value files
* - bbp offset / value files
* - rf offset / value files
2007-09-25 17:57:13 -07:00
*/
struct dentry * driver_folder ;
struct dentry * driver_entry ;
struct dentry * chipset_entry ;
2007-09-25 20:13:51 +02:00
struct dentry * dev_flags ;
2007-11-27 21:48:16 +01:00
struct dentry * register_folder ;
2007-09-25 17:57:13 -07:00
struct dentry * csr_off_entry ;
struct dentry * csr_val_entry ;
struct dentry * eeprom_off_entry ;
struct dentry * eeprom_val_entry ;
struct dentry * bbp_off_entry ;
struct dentry * bbp_val_entry ;
struct dentry * rf_off_entry ;
struct dentry * rf_val_entry ;
/*
* Driver and chipset files will use a data buffer
* that has been created in advance . This will simplify
* the code since we can use the debugfs functions .
*/
struct debugfs_blob_wrapper driver_blob ;
struct debugfs_blob_wrapper chipset_blob ;
/*
* Requested offset for each register type .
*/
unsigned int offset_csr ;
unsigned int offset_eeprom ;
unsigned int offset_bbp ;
unsigned int offset_rf ;
} ;
static int rt2x00debug_file_open ( struct inode * inode , struct file * file )
{
struct rt2x00debug_intf * intf = inode - > i_private ;
file - > private_data = inode - > i_private ;
if ( ! try_module_get ( intf - > debug - > owner ) )
return - EBUSY ;
return 0 ;
}
static int rt2x00debug_file_release ( struct inode * inode , struct file * file )
{
struct rt2x00debug_intf * intf = file - > private_data ;
module_put ( intf - > debug - > owner ) ;
return 0 ;
}
# define RT2X00DEBUGFS_OPS_READ(__name, __format, __type) \
static ssize_t rt2x00debug_read_ # # __name ( struct file * file , \
char __user * buf , \
size_t length , \
loff_t * offset ) \
{ \
2007-11-27 21:48:16 +01:00
struct rt2x00debug_intf * intf = file - > private_data ; \
2007-09-25 17:57:13 -07:00
const struct rt2x00debug * debug = intf - > debug ; \
char line [ 16 ] ; \
size_t size ; \
__type value ; \
\
if ( * offset ) \
return 0 ; \
\
if ( intf - > offset_ # # __name > = debug - > __name . word_count ) \
return - EINVAL ; \
\
debug - > __name . read ( intf - > rt2x00dev , \
intf - > offset_ # # __name , & value ) ; \
\
size = sprintf ( line , __format , value ) ; \
\
if ( copy_to_user ( buf , line , size ) ) \
return - EFAULT ; \
\
* offset + = size ; \
return size ; \
}
# define RT2X00DEBUGFS_OPS_WRITE(__name, __type) \
static ssize_t rt2x00debug_write_ # # __name ( struct file * file , \
const char __user * buf , \
size_t length , \
loff_t * offset ) \
{ \
2007-11-27 21:48:16 +01:00
struct rt2x00debug_intf * intf = file - > private_data ; \
2007-09-25 17:57:13 -07:00
const struct rt2x00debug * debug = intf - > debug ; \
char line [ 16 ] ; \
size_t size ; \
__type value ; \
\
if ( * offset ) \
return 0 ; \
\
if ( ! capable ( CAP_NET_ADMIN ) ) \
return - EPERM ; \
\
if ( intf - > offset_ # # __name > = debug - > __name . word_count ) \
return - EINVAL ; \
\
if ( copy_from_user ( line , buf , length ) ) \
return - EFAULT ; \
\
size = strlen ( line ) ; \
value = simple_strtoul ( line , NULL , 0 ) ; \
\
debug - > __name . write ( intf - > rt2x00dev , \
intf - > offset_ # # __name , value ) ; \
\
* offset + = size ; \
return size ; \
}
# define RT2X00DEBUGFS_OPS(__name, __format, __type) \
RT2X00DEBUGFS_OPS_READ ( __name , __format , __type ) ; \
RT2X00DEBUGFS_OPS_WRITE ( __name , __type ) ; \
\
static const struct file_operations rt2x00debug_fop_ # # __name = { \
. owner = THIS_MODULE , \
. read = rt2x00debug_read_ # # __name , \
. write = rt2x00debug_write_ # # __name , \
. open = rt2x00debug_file_open , \
. release = rt2x00debug_file_release , \
} ;
RT2X00DEBUGFS_OPS ( csr , " 0x%.8x \n " , u32 ) ;
RT2X00DEBUGFS_OPS ( eeprom , " 0x%.4x \n " , u16 ) ;
RT2X00DEBUGFS_OPS ( bbp , " 0x%.2x \n " , u8 ) ;
RT2X00DEBUGFS_OPS ( rf , " 0x%.8x \n " , u32 ) ;
2007-09-25 20:13:51 +02:00
static ssize_t rt2x00debug_read_dev_flags ( struct file * file ,
char __user * buf ,
size_t length ,
loff_t * offset )
{
struct rt2x00debug_intf * intf = file - > private_data ;
char line [ 16 ] ;
size_t size ;
if ( * offset )
return 0 ;
size = sprintf ( line , " 0x%.8x \n " , ( unsigned int ) intf - > rt2x00dev - > flags ) ;
if ( copy_to_user ( buf , line , size ) )
return - EFAULT ;
* offset + = size ;
return size ;
}
static const struct file_operations rt2x00debug_fop_dev_flags = {
. owner = THIS_MODULE ,
. read = rt2x00debug_read_dev_flags ,
. open = rt2x00debug_file_open ,
. release = rt2x00debug_file_release ,
} ;
2007-09-25 17:57:13 -07:00
static struct dentry * rt2x00debug_create_file_driver ( const char * name ,
struct rt2x00debug_intf
* intf ,
struct debugfs_blob_wrapper
* blob )
{
char * data ;
data = kzalloc ( 3 * PRINT_LINE_LEN_MAX , GFP_KERNEL ) ;
if ( ! data )
return NULL ;
blob - > data = data ;
data + = sprintf ( data , " driver: %s \n " , intf - > rt2x00dev - > ops - > name ) ;
data + = sprintf ( data , " version: %s \n " , DRV_VERSION ) ;
data + = sprintf ( data , " compiled: %s %s \n " , __DATE__ , __TIME__ ) ;
blob - > size = strlen ( blob - > data ) ;
return debugfs_create_blob ( name , S_IRUGO , intf - > driver_folder , blob ) ;
}
static struct dentry * rt2x00debug_create_file_chipset ( const char * name ,
struct rt2x00debug_intf
* intf ,
struct
debugfs_blob_wrapper
* blob )
{
const struct rt2x00debug * debug = intf - > debug ;
char * data ;
data = kzalloc ( 4 * PRINT_LINE_LEN_MAX , GFP_KERNEL ) ;
if ( ! data )
return NULL ;
blob - > data = data ;
data + = sprintf ( data , " csr length: %d \n " , debug - > csr . word_count ) ;
data + = sprintf ( data , " eeprom length: %d \n " , debug - > eeprom . word_count ) ;
data + = sprintf ( data , " bbp length: %d \n " , debug - > bbp . word_count ) ;
data + = sprintf ( data , " rf length: %d \n " , debug - > rf . word_count ) ;
blob - > size = strlen ( blob - > data ) ;
return debugfs_create_blob ( name , S_IRUGO , intf - > driver_folder , blob ) ;
}
void rt2x00debug_register ( struct rt2x00_dev * rt2x00dev )
{
const struct rt2x00debug * debug = rt2x00dev - > ops - > debugfs ;
struct rt2x00debug_intf * intf ;
intf = kzalloc ( sizeof ( struct rt2x00debug_intf ) , GFP_KERNEL ) ;
if ( ! intf ) {
ERROR ( rt2x00dev , " Failed to allocate debug handler. \n " ) ;
return ;
}
intf - > debug = debug ;
intf - > rt2x00dev = rt2x00dev ;
rt2x00dev - > debugfs_intf = intf ;
intf - > driver_folder =
debugfs_create_dir ( intf - > rt2x00dev - > ops - > name ,
rt2x00dev - > hw - > wiphy - > debugfsdir ) ;
if ( IS_ERR ( intf - > driver_folder ) )
goto exit ;
intf - > driver_entry =
rt2x00debug_create_file_driver ( " driver " , intf , & intf - > driver_blob ) ;
if ( IS_ERR ( intf - > driver_entry ) )
goto exit ;
intf - > chipset_entry =
rt2x00debug_create_file_chipset ( " chipset " ,
intf , & intf - > chipset_blob ) ;
if ( IS_ERR ( intf - > chipset_entry ) )
goto exit ;
2007-09-25 20:13:51 +02:00
intf - > dev_flags = debugfs_create_file ( " dev_flags " , S_IRUGO ,
intf - > driver_folder , intf ,
& rt2x00debug_fop_dev_flags ) ;
if ( IS_ERR ( intf - > dev_flags ) )
goto exit ;
2007-11-27 21:48:16 +01:00
intf - > register_folder =
debugfs_create_dir ( " register " , intf - > driver_folder ) ;
if ( IS_ERR ( intf - > register_folder ) )
goto exit ;
# define RT2X00DEBUGFS_CREATE_REGISTER_ENTRY(__intf, __name) \
2007-09-25 17:57:13 -07:00
( { \
( __intf ) - > __name # # _off_entry = \
debugfs_create_u32 ( __stringify ( __name ) " _offset " , \
S_IRUGO | S_IWUSR , \
2007-11-27 21:48:16 +01:00
( __intf ) - > register_folder , \
2007-09-25 17:57:13 -07:00
& ( __intf ) - > offset_ # # __name ) ; \
if ( IS_ERR ( ( __intf ) - > __name # # _off_entry ) ) \
goto exit ; \
\
( __intf ) - > __name # # _val_entry = \
debugfs_create_file ( __stringify ( __name ) " _value " , \
S_IRUGO | S_IWUSR , \
2007-11-27 21:48:16 +01:00
( __intf ) - > register_folder , \
2007-09-25 17:57:13 -07:00
( __intf ) , & rt2x00debug_fop_ # # __name ) ; \
if ( IS_ERR ( ( __intf ) - > __name # # _val_entry ) ) \
goto exit ; \
} )
2007-11-27 21:48:16 +01:00
RT2X00DEBUGFS_CREATE_REGISTER_ENTRY ( intf , csr ) ;
RT2X00DEBUGFS_CREATE_REGISTER_ENTRY ( intf , eeprom ) ;
RT2X00DEBUGFS_CREATE_REGISTER_ENTRY ( intf , bbp ) ;
RT2X00DEBUGFS_CREATE_REGISTER_ENTRY ( intf , rf ) ;
2007-09-25 17:57:13 -07:00
2007-11-27 21:48:16 +01:00
# undef RT2X00DEBUGFS_CREATE_REGISTER_ENTRY
2007-09-25 17:57:13 -07:00
return ;
exit :
rt2x00debug_deregister ( rt2x00dev ) ;
ERROR ( rt2x00dev , " Failed to register debug handler. \n " ) ;
return ;
}
void rt2x00debug_deregister ( struct rt2x00_dev * rt2x00dev )
{
const struct rt2x00debug_intf * intf = rt2x00dev - > debugfs_intf ;
if ( unlikely ( ! intf ) )
return ;
debugfs_remove ( intf - > rf_val_entry ) ;
debugfs_remove ( intf - > rf_off_entry ) ;
debugfs_remove ( intf - > bbp_val_entry ) ;
debugfs_remove ( intf - > bbp_off_entry ) ;
debugfs_remove ( intf - > eeprom_val_entry ) ;
debugfs_remove ( intf - > eeprom_off_entry ) ;
debugfs_remove ( intf - > csr_val_entry ) ;
debugfs_remove ( intf - > csr_off_entry ) ;
2007-11-27 21:48:16 +01:00
debugfs_remove ( intf - > register_folder ) ;
2007-09-25 20:13:51 +02:00
debugfs_remove ( intf - > dev_flags ) ;
2007-09-25 17:57:13 -07:00
debugfs_remove ( intf - > chipset_entry ) ;
debugfs_remove ( intf - > driver_entry ) ;
debugfs_remove ( intf - > driver_folder ) ;
kfree ( intf - > chipset_blob . data ) ;
kfree ( intf - > driver_blob . data ) ;
kfree ( intf ) ;
rt2x00dev - > debugfs_intf = NULL ;
}