2005-04-17 02:20:36 +04:00
/*
* net - sysfs . c - network device class and attributes
*
* Copyright ( c ) 2003 Stephen Hemminger < shemminger @ osdl . org >
2007-02-09 17:24:36 +03:00
*
2005-04-17 02:20:36 +04:00
* 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 .
*/
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/netdevice.h>
# include <linux/if_arp.h>
# include <net/sock.h>
# include <linux/rtnetlink.h>
# include <linux/wireless.h>
2006-01-10 07:51:28 +03:00
# include <net/iw_handler.h>
2005-04-17 02:20:36 +04:00
2007-10-24 08:14:45 +04:00
# include "net-sysfs.h"
2007-09-27 09:02:53 +04:00
# ifdef CONFIG_SYSFS
2005-04-17 02:20:36 +04:00
static const char fmt_hex [ ] = " %#x \n " ;
2005-05-30 07:28:25 +04:00
static const char fmt_long_hex [ ] = " %#lx \n " ;
2005-04-17 02:20:36 +04:00
static const char fmt_dec [ ] = " %d \n " ;
static const char fmt_ulong [ ] = " %lu \n " ;
2007-02-09 17:24:36 +03:00
static inline int dev_isalive ( const struct net_device * dev )
2005-04-17 02:20:36 +04:00
{
2006-05-07 04:56:03 +04:00
return dev - > reg_state < = NETREG_REGISTERED ;
2005-04-17 02:20:36 +04:00
}
/* use same locking rules as GIF* ioctl's */
2002-04-09 23:14:34 +04:00
static ssize_t netdev_show ( const struct device * dev ,
struct device_attribute * attr , char * buf ,
2005-04-17 02:20:36 +04:00
ssize_t ( * format ) ( const struct net_device * , char * ) )
{
2002-04-09 23:14:34 +04:00
struct net_device * net = to_net_dev ( dev ) ;
2005-04-17 02:20:36 +04:00
ssize_t ret = - EINVAL ;
read_lock ( & dev_base_lock ) ;
if ( dev_isalive ( net ) )
ret = ( * format ) ( net , buf ) ;
read_unlock ( & dev_base_lock ) ;
return ret ;
}
/* generate a show function for simple field */
# define NETDEVICE_SHOW(field, format_string) \
static ssize_t format_ # # field ( const struct net_device * net , char * buf ) \
{ \
return sprintf ( buf , format_string , net - > field ) ; \
} \
2002-04-09 23:14:34 +04:00
static ssize_t show_ # # field ( struct device * dev , \
struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
2002-04-09 23:14:34 +04:00
return netdev_show ( dev , attr , buf , format_ # # field ) ; \
2005-04-17 02:20:36 +04:00
}
/* use same locking and permission rules as SIF* ioctl's */
2002-04-09 23:14:34 +04:00
static ssize_t netdev_store ( struct device * dev , struct device_attribute * attr ,
2005-04-17 02:20:36 +04:00
const char * buf , size_t len ,
int ( * set ) ( struct net_device * , unsigned long ) )
{
struct net_device * net = to_net_dev ( dev ) ;
char * endp ;
unsigned long new ;
int ret = - EINVAL ;
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
new = simple_strtoul ( buf , & endp , 0 ) ;
if ( endp = = buf )
goto err ;
rtnl_lock ( ) ;
if ( dev_isalive ( net ) ) {
if ( ( ret = ( * set ) ( net , new ) ) = = 0 )
ret = len ;
}
rtnl_unlock ( ) ;
err :
return ret ;
}
2005-12-19 03:42:56 +03:00
NETDEVICE_SHOW ( addr_len , fmt_dec ) ;
NETDEVICE_SHOW ( iflink , fmt_dec ) ;
NETDEVICE_SHOW ( ifindex , fmt_dec ) ;
NETDEVICE_SHOW ( features , fmt_long_hex ) ;
NETDEVICE_SHOW ( type , fmt_dec ) ;
2006-03-21 04:09:11 +03:00
NETDEVICE_SHOW ( link_mode , fmt_dec ) ;
2005-04-17 02:20:36 +04:00
/* use same locking rules as GIFHWADDR ioctl's */
static ssize_t format_addr ( char * buf , const unsigned char * addr , int len )
{
int i ;
char * cp = buf ;
for ( i = 0 ; i < len ; i + + )
cp + = sprintf ( cp , " %02x%c " , addr [ i ] ,
i = = ( len - 1 ) ? ' \n ' : ' : ' ) ;
return cp - buf ;
}
2002-04-09 23:14:34 +04:00
static ssize_t show_address ( struct device * dev , struct device_attribute * attr ,
char * buf )
2005-04-17 02:20:36 +04:00
{
struct net_device * net = to_net_dev ( dev ) ;
ssize_t ret = - EINVAL ;
read_lock ( & dev_base_lock ) ;
if ( dev_isalive ( net ) )
ret = format_addr ( buf , net - > dev_addr , net - > addr_len ) ;
read_unlock ( & dev_base_lock ) ;
return ret ;
}
2002-04-09 23:14:34 +04:00
static ssize_t show_broadcast ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct net_device * net = to_net_dev ( dev ) ;
if ( dev_isalive ( net ) )
return format_addr ( buf , net - > broadcast , net - > addr_len ) ;
return - EINVAL ;
}
2002-04-09 23:14:34 +04:00
static ssize_t show_carrier ( struct device * dev ,
struct device_attribute * attr , char * buf )
2005-04-17 02:20:36 +04:00
{
struct net_device * netdev = to_net_dev ( dev ) ;
if ( netif_running ( netdev ) ) {
return sprintf ( buf , fmt_dec , ! ! netif_carrier_ok ( netdev ) ) ;
}
return - EINVAL ;
}
2002-04-09 23:14:34 +04:00
static ssize_t show_dormant ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-03-21 04:09:11 +03:00
{
struct net_device * netdev = to_net_dev ( dev ) ;
if ( netif_running ( netdev ) )
return sprintf ( buf , fmt_dec , ! ! netif_dormant ( netdev ) ) ;
return - EINVAL ;
}
static const char * operstates [ ] = {
" unknown " ,
" notpresent " , /* currently unused */
" down " ,
" lowerlayerdown " ,
" testing " , /* currently unused */
" dormant " ,
" up "
} ;
2002-04-09 23:14:34 +04:00
static ssize_t show_operstate ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-03-21 04:09:11 +03:00
{
const struct net_device * netdev = to_net_dev ( dev ) ;
unsigned char operstate ;
read_lock ( & dev_base_lock ) ;
operstate = netdev - > operstate ;
if ( ! netif_running ( netdev ) )
operstate = IF_OPER_DOWN ;
read_unlock ( & dev_base_lock ) ;
2006-04-06 09:19:47 +04:00
if ( operstate > = ARRAY_SIZE ( operstates ) )
2006-03-21 04:09:11 +03:00
return - EINVAL ; /* should not happen */
return sprintf ( buf , " %s \n " , operstates [ operstate ] ) ;
}
2005-04-17 02:20:36 +04:00
/* read-write attributes */
NETDEVICE_SHOW ( mtu , fmt_dec ) ;
static int change_mtu ( struct net_device * net , unsigned long new_mtu )
{
return dev_set_mtu ( net , ( int ) new_mtu ) ;
}
2002-04-09 23:14:34 +04:00
static ssize_t store_mtu ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t len )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
return netdev_store ( dev , attr , buf , len , change_mtu ) ;
2005-04-17 02:20:36 +04:00
}
NETDEVICE_SHOW ( flags , fmt_hex ) ;
static int change_flags ( struct net_device * net , unsigned long new_flags )
{
return dev_change_flags ( net , ( unsigned ) new_flags ) ;
}
2002-04-09 23:14:34 +04:00
static ssize_t store_flags ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t len )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
return netdev_store ( dev , attr , buf , len , change_flags ) ;
2005-04-17 02:20:36 +04:00
}
NETDEVICE_SHOW ( tx_queue_len , fmt_ulong ) ;
static int change_tx_queue_len ( struct net_device * net , unsigned long new_len )
{
net - > tx_queue_len = new_len ;
return 0 ;
}
2002-04-09 23:14:34 +04:00
static ssize_t store_tx_queue_len ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
return netdev_store ( dev , attr , buf , len , change_tx_queue_len ) ;
2005-04-17 02:20:36 +04:00
}
2002-04-09 23:14:34 +04:00
static struct device_attribute net_class_attributes [ ] = {
2005-12-19 03:42:56 +03:00
__ATTR ( addr_len , S_IRUGO , show_addr_len , NULL ) ,
__ATTR ( iflink , S_IRUGO , show_iflink , NULL ) ,
__ATTR ( ifindex , S_IRUGO , show_ifindex , NULL ) ,
__ATTR ( features , S_IRUGO , show_features , NULL ) ,
__ATTR ( type , S_IRUGO , show_type , NULL ) ,
2006-03-21 04:09:11 +03:00
__ATTR ( link_mode , S_IRUGO , show_link_mode , NULL ) ,
2005-12-19 03:42:56 +03:00
__ATTR ( address , S_IRUGO , show_address , NULL ) ,
__ATTR ( broadcast , S_IRUGO , show_broadcast , NULL ) ,
__ATTR ( carrier , S_IRUGO , show_carrier , NULL ) ,
2006-03-21 04:09:11 +03:00
__ATTR ( dormant , S_IRUGO , show_dormant , NULL ) ,
__ATTR ( operstate , S_IRUGO , show_operstate , NULL ) ,
2005-12-19 03:42:56 +03:00
__ATTR ( mtu , S_IRUGO | S_IWUSR , show_mtu , store_mtu ) ,
__ATTR ( flags , S_IRUGO | S_IWUSR , show_flags , store_flags ) ,
__ATTR ( tx_queue_len , S_IRUGO | S_IWUSR , show_tx_queue_len ,
store_tx_queue_len ) ,
{ }
2005-04-17 02:20:36 +04:00
} ;
/* Show a given an attribute in the statistics group */
2002-04-09 23:14:34 +04:00
static ssize_t netstat_show ( const struct device * d ,
struct device_attribute * attr , char * buf ,
2005-04-17 02:20:36 +04:00
unsigned long offset )
{
2002-04-09 23:14:34 +04:00
struct net_device * dev = to_net_dev ( d ) ;
2005-04-17 02:20:36 +04:00
struct net_device_stats * stats ;
ssize_t ret = - EINVAL ;
2007-11-29 16:42:42 +03:00
WARN_ON ( offset > sizeof ( struct net_device_stats ) | |
offset % sizeof ( unsigned long ) ! = 0 ) ;
2005-04-17 02:20:36 +04:00
read_lock ( & dev_base_lock ) ;
if ( dev_isalive ( dev ) & & dev - > get_stats & &
2007-02-09 17:24:36 +03:00
( stats = ( * dev - > get_stats ) ( dev ) ) )
2005-04-17 02:20:36 +04:00
ret = sprintf ( buf , fmt_ulong ,
* ( unsigned long * ) ( ( ( u8 * ) stats ) + offset ) ) ;
read_unlock ( & dev_base_lock ) ;
return ret ;
}
/* generate a read-only statistics attribute */
# define NETSTAT_ENTRY(name) \
2002-04-09 23:14:34 +04:00
static ssize_t show_ # # name ( struct device * d , \
struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
2002-04-09 23:14:34 +04:00
return netstat_show ( d , attr , buf , \
2005-04-17 02:20:36 +04:00
offsetof ( struct net_device_stats , name ) ) ; \
} \
2002-04-09 23:14:34 +04:00
static DEVICE_ATTR ( name , S_IRUGO , show_ # # name , NULL )
2005-04-17 02:20:36 +04:00
NETSTAT_ENTRY ( rx_packets ) ;
NETSTAT_ENTRY ( tx_packets ) ;
NETSTAT_ENTRY ( rx_bytes ) ;
NETSTAT_ENTRY ( tx_bytes ) ;
NETSTAT_ENTRY ( rx_errors ) ;
NETSTAT_ENTRY ( tx_errors ) ;
NETSTAT_ENTRY ( rx_dropped ) ;
NETSTAT_ENTRY ( tx_dropped ) ;
NETSTAT_ENTRY ( multicast ) ;
NETSTAT_ENTRY ( collisions ) ;
NETSTAT_ENTRY ( rx_length_errors ) ;
NETSTAT_ENTRY ( rx_over_errors ) ;
NETSTAT_ENTRY ( rx_crc_errors ) ;
NETSTAT_ENTRY ( rx_frame_errors ) ;
NETSTAT_ENTRY ( rx_fifo_errors ) ;
NETSTAT_ENTRY ( rx_missed_errors ) ;
NETSTAT_ENTRY ( tx_aborted_errors ) ;
NETSTAT_ENTRY ( tx_carrier_errors ) ;
NETSTAT_ENTRY ( tx_fifo_errors ) ;
NETSTAT_ENTRY ( tx_heartbeat_errors ) ;
NETSTAT_ENTRY ( tx_window_errors ) ;
NETSTAT_ENTRY ( rx_compressed ) ;
NETSTAT_ENTRY ( tx_compressed ) ;
static struct attribute * netstat_attrs [ ] = {
2002-04-09 23:14:34 +04:00
& dev_attr_rx_packets . attr ,
& dev_attr_tx_packets . attr ,
& dev_attr_rx_bytes . attr ,
& dev_attr_tx_bytes . attr ,
& dev_attr_rx_errors . attr ,
& dev_attr_tx_errors . attr ,
& dev_attr_rx_dropped . attr ,
& dev_attr_tx_dropped . attr ,
& dev_attr_multicast . attr ,
& dev_attr_collisions . attr ,
& dev_attr_rx_length_errors . attr ,
& dev_attr_rx_over_errors . attr ,
& dev_attr_rx_crc_errors . attr ,
& dev_attr_rx_frame_errors . attr ,
& dev_attr_rx_fifo_errors . attr ,
& dev_attr_rx_missed_errors . attr ,
& dev_attr_tx_aborted_errors . attr ,
& dev_attr_tx_carrier_errors . attr ,
& dev_attr_tx_fifo_errors . attr ,
& dev_attr_tx_heartbeat_errors . attr ,
& dev_attr_tx_window_errors . attr ,
& dev_attr_rx_compressed . attr ,
& dev_attr_tx_compressed . attr ,
2005-04-17 02:20:36 +04:00
NULL
} ;
static struct attribute_group netstat_group = {
. name = " statistics " ,
. attrs = netstat_attrs ,
} ;
2007-02-06 03:41:36 +03:00
# ifdef CONFIG_WIRELESS_EXT
2005-04-17 02:20:36 +04:00
/* helper function that does all the locking etc for wireless stats */
2002-04-09 23:14:34 +04:00
static ssize_t wireless_show ( struct device * d , char * buf ,
2005-04-17 02:20:36 +04:00
ssize_t ( * format ) ( const struct iw_statistics * ,
char * ) )
{
2002-04-09 23:14:34 +04:00
struct net_device * dev = to_net_dev ( d ) ;
2006-01-10 07:51:28 +03:00
const struct iw_statistics * iw = NULL ;
2005-04-17 02:20:36 +04:00
ssize_t ret = - EINVAL ;
2007-02-09 17:24:36 +03:00
2005-04-17 02:20:36 +04:00
read_lock ( & dev_base_lock ) ;
2006-01-10 07:51:28 +03:00
if ( dev_isalive ( dev ) ) {
2007-04-11 07:10:33 +04:00
if ( dev - > wireless_handlers & &
dev - > wireless_handlers - > get_wireless_stats )
2006-01-10 07:51:28 +03:00
iw = dev - > wireless_handlers - > get_wireless_stats ( dev ) ;
if ( iw ! = NULL )
ret = ( * format ) ( iw , buf ) ;
}
2005-04-17 02:20:36 +04:00
read_unlock ( & dev_base_lock ) ;
return ret ;
}
/* show function template for wireless fields */
# define WIRELESS_SHOW(name, field, format_string) \
static ssize_t format_iw_ # # name ( const struct iw_statistics * iw , char * buf ) \
{ \
return sprintf ( buf , format_string , iw - > field ) ; \
} \
2002-04-09 23:14:34 +04:00
static ssize_t show_iw_ # # name ( struct device * d , \
struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
2002-04-09 23:14:34 +04:00
return wireless_show ( d , buf , format_iw_ # # name ) ; \
2005-04-17 02:20:36 +04:00
} \
2002-04-09 23:14:34 +04:00
static DEVICE_ATTR ( name , S_IRUGO , show_iw_ # # name , NULL )
2005-04-17 02:20:36 +04:00
WIRELESS_SHOW ( status , status , fmt_hex ) ;
WIRELESS_SHOW ( link , qual . qual , fmt_dec ) ;
WIRELESS_SHOW ( level , qual . level , fmt_dec ) ;
WIRELESS_SHOW ( noise , qual . noise , fmt_dec ) ;
WIRELESS_SHOW ( nwid , discard . nwid , fmt_dec ) ;
WIRELESS_SHOW ( crypt , discard . code , fmt_dec ) ;
WIRELESS_SHOW ( fragment , discard . fragment , fmt_dec ) ;
WIRELESS_SHOW ( misc , discard . misc , fmt_dec ) ;
WIRELESS_SHOW ( retries , discard . retries , fmt_dec ) ;
WIRELESS_SHOW ( beacon , miss . beacon , fmt_dec ) ;
static struct attribute * wireless_attrs [ ] = {
2002-04-09 23:14:34 +04:00
& dev_attr_status . attr ,
& dev_attr_link . attr ,
& dev_attr_level . attr ,
& dev_attr_noise . attr ,
& dev_attr_nwid . attr ,
& dev_attr_crypt . attr ,
& dev_attr_fragment . attr ,
& dev_attr_retries . attr ,
& dev_attr_misc . attr ,
& dev_attr_beacon . attr ,
2005-04-17 02:20:36 +04:00
NULL
} ;
static struct attribute_group wireless_group = {
. name = " wireless " ,
. attrs = wireless_attrs ,
} ;
# endif
2007-09-27 09:02:53 +04:00
# endif /* CONFIG_SYSFS */
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_HOTPLUG
2007-08-14 17:15:12 +04:00
static int netdev_uevent ( struct device * d , struct kobj_uevent_env * env )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
struct net_device * dev = to_net_dev ( d ) ;
2007-08-14 17:15:12 +04:00
int retval ;
2005-04-17 02:20:36 +04:00
2005-11-16 11:00:00 +03:00
/* pass interface to uevent. */
2007-08-14 17:15:12 +04:00
retval = add_uevent_var ( env , " INTERFACE=%s " , dev - > name ) ;
2007-03-31 09:23:12 +04:00
if ( retval )
goto exit ;
2007-03-07 21:49:30 +03:00
/* pass ifindex to uevent.
* ifindex is useful as it won ' t change ( interface name may change )
* and is what RtNetlink uses natively . */
2007-08-14 17:15:12 +04:00
retval = add_uevent_var ( env , " IFINDEX=%d " , dev - > ifindex ) ;
2005-04-17 02:20:36 +04:00
2007-03-31 09:23:12 +04:00
exit :
return retval ;
2005-04-17 02:20:36 +04:00
}
# endif
/*
2007-02-09 17:24:36 +03:00
* netdev_release - - destroy and free a dead device .
2002-04-09 23:14:34 +04:00
* Called when last reference to device kobject is gone .
2005-04-17 02:20:36 +04:00
*/
2002-04-09 23:14:34 +04:00
static void netdev_release ( struct device * d )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
struct net_device * dev = to_net_dev ( d ) ;
2005-04-17 02:20:36 +04:00
BUG_ON ( dev - > reg_state ! = NETREG_RELEASED ) ;
kfree ( ( char * ) dev - dev - > padded ) ;
}
static struct class net_class = {
. name = " net " ,
2002-04-09 23:14:34 +04:00
. dev_release = netdev_release ,
2007-09-27 09:02:53 +04:00
# ifdef CONFIG_SYSFS
2002-04-09 23:14:34 +04:00
. dev_attrs = net_class_attributes ,
2007-09-27 09:02:53 +04:00
# endif /* CONFIG_SYSFS */
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_HOTPLUG
2002-04-09 23:14:34 +04:00
. dev_uevent = netdev_uevent ,
2005-04-17 02:20:36 +04:00
# endif
} ;
2007-05-20 02:39:25 +04:00
/* Delete sysfs entries but hold kobject reference until after all
* netdev references are gone .
*/
2007-09-27 09:02:53 +04:00
void netdev_unregister_kobject ( struct net_device * net )
2005-04-17 02:20:36 +04:00
{
2007-05-20 02:39:25 +04:00
struct device * dev = & ( net - > dev ) ;
kobject_get ( & dev - > kobj ) ;
device_del ( dev ) ;
2005-04-17 02:20:36 +04:00
}
/* Create sysfs entries for network device. */
2007-09-27 09:02:53 +04:00
int netdev_register_kobject ( struct net_device * net )
2005-04-17 02:20:36 +04:00
{
2002-04-09 23:14:34 +04:00
struct device * dev = & ( net - > dev ) ;
2006-05-07 04:56:03 +04:00
struct attribute_group * * groups = net - > sysfs_groups ;
2005-04-17 02:20:36 +04:00
2002-04-09 23:14:34 +04:00
device_initialize ( dev ) ;
dev - > class = & net_class ;
dev - > platform_data = net ;
dev - > groups = groups ;
2005-04-17 02:20:36 +04:00
2006-05-07 04:56:03 +04:00
BUILD_BUG_ON ( BUS_ID_SIZE < IFNAMSIZ ) ;
2002-04-09 23:14:34 +04:00
strlcpy ( dev - > bus_id , net - > name , BUS_ID_SIZE ) ;
2005-04-17 02:20:36 +04:00
2007-09-27 09:02:53 +04:00
# ifdef CONFIG_SYSFS
2006-05-07 04:56:03 +04:00
if ( net - > get_stats )
* groups + + = & netstat_group ;
2005-04-17 02:20:36 +04:00
2007-02-06 03:41:36 +03:00
# ifdef CONFIG_WIRELESS_EXT
2006-09-09 00:04:05 +04:00
if ( net - > wireless_handlers & & net - > wireless_handlers - > get_wireless_stats )
2006-05-07 04:56:03 +04:00
* groups + + = & wireless_group ;
2005-04-17 02:20:36 +04:00
# endif
2007-09-27 09:02:53 +04:00
# endif /* CONFIG_SYSFS */
2005-04-17 02:20:36 +04:00
2002-04-09 23:14:34 +04:00
return device_add ( dev ) ;
2005-04-17 02:20:36 +04:00
}
2007-09-27 09:02:53 +04:00
int netdev_kobject_init ( void )
2005-04-17 02:20:36 +04:00
{
return class_register ( & net_class ) ;
}