2005-04-17 02:20:36 +04:00
/*
* net / core / ethtool . c - Ethtool ioctl handler
* Copyright ( c ) 2003 Matthew Wilcox < matthew @ wil . cx >
*
* This file is where we call all the ethtool_ops commands to get
2007-08-01 01:00:02 +04:00
* the information ethtool needs .
2005-04-17 02:20:36 +04:00
*
2007-08-01 01:00:02 +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 .
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/types.h>
2006-01-11 23:17:47 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <linux/ethtool.h>
# include <linux/netdevice.h>
# include <asm/uaccess.h>
2007-02-09 17:24:36 +03:00
/*
2005-04-17 02:20:36 +04:00
* Some useful ethtool_ops methods that ' re device independent .
* If we find that all drivers want to do the same thing here ,
* we can turn these into dev_ ( ) function calls .
*/
u32 ethtool_op_get_link ( struct net_device * dev )
{
return netif_carrier_ok ( dev ) ? 1 : 0 ;
}
u32 ethtool_op_get_tx_csum ( struct net_device * dev )
{
2006-06-18 09:06:05 +04:00
return ( dev - > features & NETIF_F_ALL_CSUM ) ! = 0 ;
2005-04-17 02:20:36 +04:00
}
int ethtool_op_set_tx_csum ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_IP_CSUM ;
else
dev - > features & = ~ NETIF_F_IP_CSUM ;
return 0 ;
}
2005-05-30 07:27:24 +04:00
int ethtool_op_set_tx_hw_csum ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_HW_CSUM ;
else
dev - > features & = ~ NETIF_F_HW_CSUM ;
return 0 ;
}
2007-07-15 06:07:52 +04:00
int ethtool_op_set_tx_ipv6_csum ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ;
else
dev - > features & = ~ ( NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
u32 ethtool_op_get_sg ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_SG ) ! = 0 ;
}
int ethtool_op_set_sg ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_SG ;
else
dev - > features & = ~ NETIF_F_SG ;
return 0 ;
}
u32 ethtool_op_get_tso ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_TSO ) ! = 0 ;
}
int ethtool_op_set_tso ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_TSO ;
else
dev - > features & = ~ NETIF_F_TSO ;
return 0 ;
}
2005-10-19 02:46:41 +04:00
u32 ethtool_op_get_ufo ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_UFO ) ! = 0 ;
}
int ethtool_op_set_ufo ( struct net_device * dev , u32 data )
{
if ( data )
dev - > features | = NETIF_F_UFO ;
else
dev - > features & = ~ NETIF_F_UFO ;
return 0 ;
}
2007-08-16 03:00:51 +04:00
/* the following list of flags are the same as their associated
* NETIF_F_xxx values in include / linux / netdevice . h
*/
static const u32 flags_dup_features =
ETH_FLAG_LRO ;
u32 ethtool_op_get_flags ( struct net_device * dev )
{
/* in the future, this function will probably contain additional
* handling for flags which are not so easily handled
* by a simple masking operation
*/
return dev - > features & flags_dup_features ;
}
int ethtool_op_set_flags ( struct net_device * dev , u32 data )
{
if ( data & ETH_FLAG_LRO )
dev - > features | = NETIF_F_LRO ;
else
dev - > features & = ~ NETIF_F_LRO ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/* Handlers for each ethtool command */
static int ethtool_get_settings ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_cmd cmd = { ETHTOOL_GSET } ;
int err ;
if ( ! dev - > ethtool_ops - > get_settings )
return - EOPNOTSUPP ;
err = dev - > ethtool_ops - > get_settings ( dev , & cmd ) ;
if ( err < 0 )
return err ;
if ( copy_to_user ( useraddr , & cmd , sizeof ( cmd ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_settings ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_cmd cmd ;
if ( ! dev - > ethtool_ops - > set_settings )
return - EOPNOTSUPP ;
if ( copy_from_user ( & cmd , useraddr , sizeof ( cmd ) ) )
return - EFAULT ;
return dev - > ethtool_ops - > set_settings ( dev , & cmd ) ;
}
static int ethtool_get_drvinfo ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_drvinfo info ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
if ( ! ops - > get_drvinfo )
return - EOPNOTSUPP ;
memset ( & info , 0 , sizeof ( info ) ) ;
info . cmd = ETHTOOL_GDRVINFO ;
ops - > get_drvinfo ( dev , & info ) ;
2007-08-16 03:01:08 +04:00
if ( ops - > get_sset_count ) {
int rc ;
rc = ops - > get_sset_count ( dev , ETH_SS_TEST ) ;
if ( rc > = 0 )
info . testinfo_len = rc ;
rc = ops - > get_sset_count ( dev , ETH_SS_STATS ) ;
if ( rc > = 0 )
info . n_stats = rc ;
2007-08-16 03:01:32 +04:00
rc = ops - > get_sset_count ( dev , ETH_SS_PRIV_FLAGS ) ;
if ( rc > = 0 )
info . n_priv_flags = rc ;
2007-08-16 03:01:08 +04:00
} else {
/* code path for obsolete hooks */
if ( ops - > self_test_count )
info . testinfo_len = ops - > self_test_count ( dev ) ;
if ( ops - > get_stats_count )
info . n_stats = ops - > get_stats_count ( dev ) ;
}
2005-04-17 02:20:36 +04:00
if ( ops - > get_regs_len )
info . regdump_len = ops - > get_regs_len ( dev ) ;
if ( ops - > get_eeprom_len )
info . eedump_len = ops - > get_eeprom_len ( dev ) ;
if ( copy_to_user ( useraddr , & info , sizeof ( info ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_get_regs ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_regs regs ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
void * regbuf ;
int reglen , ret ;
if ( ! ops - > get_regs | | ! ops - > get_regs_len )
return - EOPNOTSUPP ;
if ( copy_from_user ( & regs , useraddr , sizeof ( regs ) ) )
return - EFAULT ;
reglen = ops - > get_regs_len ( dev ) ;
if ( regs . len > reglen )
regs . len = reglen ;
regbuf = kmalloc ( reglen , GFP_USER ) ;
if ( ! regbuf )
return - ENOMEM ;
ops - > get_regs ( dev , & regs , regbuf ) ;
ret = - EFAULT ;
if ( copy_to_user ( useraddr , & regs , sizeof ( regs ) ) )
goto out ;
useraddr + = offsetof ( struct ethtool_regs , data ) ;
if ( copy_to_user ( useraddr , regbuf , regs . len ) )
goto out ;
ret = 0 ;
out :
kfree ( regbuf ) ;
return ret ;
}
static int ethtool_get_wol ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_wolinfo wol = { ETHTOOL_GWOL } ;
if ( ! dev - > ethtool_ops - > get_wol )
return - EOPNOTSUPP ;
dev - > ethtool_ops - > get_wol ( dev , & wol ) ;
if ( copy_to_user ( useraddr , & wol , sizeof ( wol ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_wol ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_wolinfo wol ;
if ( ! dev - > ethtool_ops - > set_wol )
return - EOPNOTSUPP ;
if ( copy_from_user ( & wol , useraddr , sizeof ( wol ) ) )
return - EFAULT ;
return dev - > ethtool_ops - > set_wol ( dev , & wol ) ;
}
static int ethtool_nway_reset ( struct net_device * dev )
{
if ( ! dev - > ethtool_ops - > nway_reset )
return - EOPNOTSUPP ;
return dev - > ethtool_ops - > nway_reset ( dev ) ;
}
static int ethtool_get_eeprom ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_eeprom eeprom ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
u8 * data ;
int ret ;
if ( ! ops - > get_eeprom | | ! ops - > get_eeprom_len )
return - EOPNOTSUPP ;
if ( copy_from_user ( & eeprom , useraddr , sizeof ( eeprom ) ) )
return - EFAULT ;
/* Check for wrap and zero */
if ( eeprom . offset + eeprom . len < = eeprom . offset )
return - EINVAL ;
/* Check for exceeding total eeprom len */
if ( eeprom . offset + eeprom . len > ops - > get_eeprom_len ( dev ) )
return - EINVAL ;
data = kmalloc ( eeprom . len , GFP_USER ) ;
if ( ! data )
return - ENOMEM ;
ret = - EFAULT ;
if ( copy_from_user ( data , useraddr + sizeof ( eeprom ) , eeprom . len ) )
goto out ;
ret = ops - > get_eeprom ( dev , & eeprom , data ) ;
if ( ret )
goto out ;
ret = - EFAULT ;
if ( copy_to_user ( useraddr , & eeprom , sizeof ( eeprom ) ) )
goto out ;
if ( copy_to_user ( useraddr + sizeof ( eeprom ) , data , eeprom . len ) )
goto out ;
ret = 0 ;
out :
kfree ( data ) ;
return ret ;
}
static int ethtool_set_eeprom ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_eeprom eeprom ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
u8 * data ;
int ret ;
if ( ! ops - > set_eeprom | | ! ops - > get_eeprom_len )
return - EOPNOTSUPP ;
if ( copy_from_user ( & eeprom , useraddr , sizeof ( eeprom ) ) )
return - EFAULT ;
/* Check for wrap and zero */
if ( eeprom . offset + eeprom . len < = eeprom . offset )
return - EINVAL ;
/* Check for exceeding total eeprom len */
if ( eeprom . offset + eeprom . len > ops - > get_eeprom_len ( dev ) )
return - EINVAL ;
data = kmalloc ( eeprom . len , GFP_USER ) ;
if ( ! data )
return - ENOMEM ;
ret = - EFAULT ;
if ( copy_from_user ( data , useraddr + sizeof ( eeprom ) , eeprom . len ) )
goto out ;
ret = ops - > set_eeprom ( dev , & eeprom , data ) ;
if ( ret )
goto out ;
if ( copy_to_user ( useraddr + sizeof ( eeprom ) , data , eeprom . len ) )
ret = - EFAULT ;
out :
kfree ( data ) ;
return ret ;
}
static int ethtool_get_coalesce ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_coalesce coalesce = { ETHTOOL_GCOALESCE } ;
if ( ! dev - > ethtool_ops - > get_coalesce )
return - EOPNOTSUPP ;
dev - > ethtool_ops - > get_coalesce ( dev , & coalesce ) ;
if ( copy_to_user ( useraddr , & coalesce , sizeof ( coalesce ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_coalesce ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_coalesce coalesce ;
2005-06-07 02:07:19 +04:00
if ( ! dev - > ethtool_ops - > set_coalesce )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & coalesce , useraddr , sizeof ( coalesce ) ) )
return - EFAULT ;
return dev - > ethtool_ops - > set_coalesce ( dev , & coalesce ) ;
}
static int ethtool_get_ringparam ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_ringparam ringparam = { ETHTOOL_GRINGPARAM } ;
if ( ! dev - > ethtool_ops - > get_ringparam )
return - EOPNOTSUPP ;
dev - > ethtool_ops - > get_ringparam ( dev , & ringparam ) ;
if ( copy_to_user ( useraddr , & ringparam , sizeof ( ringparam ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_ringparam ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_ringparam ringparam ;
if ( ! dev - > ethtool_ops - > set_ringparam )
return - EOPNOTSUPP ;
if ( copy_from_user ( & ringparam , useraddr , sizeof ( ringparam ) ) )
return - EFAULT ;
return dev - > ethtool_ops - > set_ringparam ( dev , & ringparam ) ;
}
static int ethtool_get_pauseparam ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM } ;
if ( ! dev - > ethtool_ops - > get_pauseparam )
return - EOPNOTSUPP ;
dev - > ethtool_ops - > get_pauseparam ( dev , & pauseparam ) ;
if ( copy_to_user ( useraddr , & pauseparam , sizeof ( pauseparam ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_pauseparam ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_pauseparam pauseparam ;
2006-07-17 20:54:40 +04:00
if ( ! dev - > ethtool_ops - > set_pauseparam )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & pauseparam , useraddr , sizeof ( pauseparam ) ) )
return - EFAULT ;
return dev - > ethtool_ops - > set_pauseparam ( dev , & pauseparam ) ;
}
static int __ethtool_set_sg ( struct net_device * dev , u32 data )
{
int err ;
if ( ! data & & dev - > ethtool_ops - > set_tso ) {
err = dev - > ethtool_ops - > set_tso ( dev , 0 ) ;
if ( err )
return err ;
}
2005-10-19 02:46:41 +04:00
if ( ! data & & dev - > ethtool_ops - > set_ufo ) {
err = dev - > ethtool_ops - > set_ufo ( dev , 0 ) ;
if ( err )
return err ;
}
2005-04-17 02:20:36 +04:00
return dev - > ethtool_ops - > set_sg ( dev , data ) ;
}
static int ethtool_set_tx_csum ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
int err ;
if ( ! dev - > ethtool_ops - > set_tx_csum )
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
if ( ! edata . data & & dev - > ethtool_ops - > set_sg ) {
err = __ethtool_set_sg ( dev , 0 ) ;
if ( err )
return err ;
}
return dev - > ethtool_ops - > set_tx_csum ( dev , edata . data ) ;
}
static int ethtool_set_sg ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
if ( ! dev - > ethtool_ops - > set_sg )
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
2007-02-09 17:24:36 +03:00
if ( edata . data & &
2006-06-18 09:06:05 +04:00
! ( dev - > features & NETIF_F_ALL_CSUM ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
return __ethtool_set_sg ( dev , edata . data ) ;
}
static int ethtool_set_tso ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
if ( ! dev - > ethtool_ops - > set_tso )
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
if ( edata . data & & ! ( dev - > features & NETIF_F_SG ) )
return - EINVAL ;
return dev - > ethtool_ops - > set_tso ( dev , edata . data ) ;
}
2005-10-19 02:46:41 +04:00
static int ethtool_set_ufo ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
if ( ! dev - > ethtool_ops - > set_ufo )
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
if ( edata . data & & ! ( dev - > features & NETIF_F_SG ) )
return - EINVAL ;
if ( edata . data & & ! ( dev - > features & NETIF_F_HW_CSUM ) )
return - EINVAL ;
return dev - > ethtool_ops - > set_ufo ( dev , edata . data ) ;
}
2006-06-22 14:07:29 +04:00
static int ethtool_get_gso ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata = { ETHTOOL_GGSO } ;
edata . data = dev - > features & NETIF_F_GSO ;
if ( copy_to_user ( useraddr , & edata , sizeof ( edata ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_gso ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
if ( edata . data )
dev - > features | = NETIF_F_GSO ;
else
dev - > features & = ~ NETIF_F_GSO ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int ethtool_self_test ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_test test ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
u64 * data ;
2007-08-16 03:01:08 +04:00
int ret , test_len ;
2005-04-17 02:20:36 +04:00
2007-08-16 03:01:08 +04:00
if ( ! ops - > self_test )
return - EOPNOTSUPP ;
if ( ! ops - > get_sset_count & & ! ops - > self_test_count )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
2007-08-16 03:01:08 +04:00
if ( ops - > get_sset_count )
test_len = ops - > get_sset_count ( dev , ETH_SS_TEST ) ;
else
/* code path for obsolete hook */
test_len = ops - > self_test_count ( dev ) ;
if ( test_len < 0 )
return test_len ;
WARN_ON ( test_len = = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & test , useraddr , sizeof ( test ) ) )
return - EFAULT ;
2007-08-16 03:01:08 +04:00
test . len = test_len ;
data = kmalloc ( test_len * sizeof ( u64 ) , GFP_USER ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - ENOMEM ;
ops - > self_test ( dev , & test , data ) ;
ret = - EFAULT ;
if ( copy_to_user ( useraddr , & test , sizeof ( test ) ) )
goto out ;
useraddr + = sizeof ( test ) ;
if ( copy_to_user ( useraddr , data , test . len * sizeof ( u64 ) ) )
goto out ;
ret = 0 ;
out :
kfree ( data ) ;
return ret ;
}
static int ethtool_get_strings ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_gstrings gstrings ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
u8 * data ;
int ret ;
if ( ! ops - > get_strings )
return - EOPNOTSUPP ;
if ( copy_from_user ( & gstrings , useraddr , sizeof ( gstrings ) ) )
return - EFAULT ;
2007-08-16 03:01:08 +04:00
if ( ops - > get_sset_count ) {
ret = ops - > get_sset_count ( dev , gstrings . string_set ) ;
if ( ret < 0 )
return ret ;
gstrings . len = ret ;
} else {
/* code path for obsolete hooks */
switch ( gstrings . string_set ) {
case ETH_SS_TEST :
if ( ! ops - > self_test_count )
return - EOPNOTSUPP ;
gstrings . len = ops - > self_test_count ( dev ) ;
break ;
case ETH_SS_STATS :
if ( ! ops - > get_stats_count )
return - EOPNOTSUPP ;
gstrings . len = ops - > get_stats_count ( dev ) ;
break ;
default :
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
}
data = kmalloc ( gstrings . len * ETH_GSTRING_LEN , GFP_USER ) ;
if ( ! data )
return - ENOMEM ;
ops - > get_strings ( dev , gstrings . string_set , data ) ;
ret = - EFAULT ;
if ( copy_to_user ( useraddr , & gstrings , sizeof ( gstrings ) ) )
goto out ;
useraddr + = sizeof ( gstrings ) ;
if ( copy_to_user ( useraddr , data , gstrings . len * ETH_GSTRING_LEN ) )
goto out ;
ret = 0 ;
out :
kfree ( data ) ;
return ret ;
}
static int ethtool_phys_id ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_value id ;
if ( ! dev - > ethtool_ops - > phys_id )
return - EOPNOTSUPP ;
if ( copy_from_user ( & id , useraddr , sizeof ( id ) ) )
return - EFAULT ;
return dev - > ethtool_ops - > phys_id ( dev , id . data ) ;
}
static int ethtool_get_stats ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_stats stats ;
2006-09-08 22:16:13 +04:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
2005-04-17 02:20:36 +04:00
u64 * data ;
2007-08-16 03:01:08 +04:00
int ret , n_stats ;
2005-04-17 02:20:36 +04:00
2007-08-16 03:01:08 +04:00
if ( ! ops - > get_ethtool_stats )
return - EOPNOTSUPP ;
if ( ! ops - > get_sset_count & & ! ops - > get_stats_count )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
2007-08-16 03:01:08 +04:00
if ( ops - > get_sset_count )
n_stats = ops - > get_sset_count ( dev , ETH_SS_STATS ) ;
else
/* code path for obsolete hook */
n_stats = ops - > get_stats_count ( dev ) ;
if ( n_stats < 0 )
return n_stats ;
WARN_ON ( n_stats = = 0 ) ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & stats , useraddr , sizeof ( stats ) ) )
return - EFAULT ;
2007-08-16 03:01:08 +04:00
stats . n_stats = n_stats ;
data = kmalloc ( n_stats * sizeof ( u64 ) , GFP_USER ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - ENOMEM ;
ops - > get_ethtool_stats ( dev , & stats , data ) ;
ret = - EFAULT ;
if ( copy_to_user ( useraddr , & stats , sizeof ( stats ) ) )
goto out ;
useraddr + = sizeof ( stats ) ;
if ( copy_to_user ( useraddr , data , stats . n_stats * sizeof ( u64 ) ) )
goto out ;
ret = 0 ;
out :
kfree ( data ) ;
return ret ;
}
2005-09-05 06:26:18 +04:00
static int ethtool_get_perm_addr ( struct net_device * dev , void __user * useraddr )
2005-08-21 04:15:54 +04:00
{
struct ethtool_perm_addr epaddr ;
2007-08-01 01:00:29 +04:00
if ( copy_from_user ( & epaddr , useraddr , sizeof ( epaddr ) ) )
2005-08-21 04:15:54 +04:00
return - EFAULT ;
2007-08-01 01:00:29 +04:00
if ( epaddr . size < dev - > addr_len )
return - ETOOSMALL ;
epaddr . size = dev - > addr_len ;
2005-08-21 04:15:54 +04:00
if ( copy_to_user ( useraddr , & epaddr , sizeof ( epaddr ) ) )
2007-08-01 01:00:29 +04:00
return - EFAULT ;
2005-08-21 04:15:54 +04:00
useraddr + = sizeof ( epaddr ) ;
2007-08-01 01:00:29 +04:00
if ( copy_to_user ( useraddr , dev - > perm_addr , epaddr . size ) )
return - EFAULT ;
return 0 ;
2005-08-21 04:15:54 +04:00
}
2007-08-16 03:01:56 +04:00
static int ethtool_get_value ( struct net_device * dev , char __user * useraddr ,
u32 cmd , u32 ( * actor ) ( struct net_device * ) )
2007-08-16 03:00:51 +04:00
{
2007-08-16 03:01:56 +04:00
struct ethtool_value edata = { cmd } ;
2007-08-16 03:00:51 +04:00
2007-08-16 03:01:56 +04:00
if ( ! actor )
2007-08-16 03:00:51 +04:00
return - EOPNOTSUPP ;
2007-08-16 03:01:56 +04:00
edata . data = actor ( dev ) ;
2007-08-16 03:00:51 +04:00
if ( copy_to_user ( useraddr , & edata , sizeof ( edata ) ) )
return - EFAULT ;
return 0 ;
}
2007-08-16 03:01:56 +04:00
static int ethtool_set_value_void ( struct net_device * dev , char __user * useraddr ,
void ( * actor ) ( struct net_device * , u32 ) )
2007-08-16 03:00:51 +04:00
{
struct ethtool_value edata ;
2007-08-16 03:01:56 +04:00
if ( ! actor )
2007-08-16 03:00:51 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
2007-08-16 03:01:56 +04:00
actor ( dev , edata . data ) ;
2007-08-16 03:01:32 +04:00
return 0 ;
}
2007-08-16 03:01:56 +04:00
static int ethtool_set_value ( struct net_device * dev , char __user * useraddr ,
int ( * actor ) ( struct net_device * , u32 ) )
2007-08-16 03:01:32 +04:00
{
struct ethtool_value edata ;
2007-08-16 03:01:56 +04:00
if ( ! actor )
2007-08-16 03:01:32 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
2007-08-16 03:01:56 +04:00
return actor ( dev , edata . data ) ;
2007-08-16 03:01:32 +04:00
}
2005-04-17 02:20:36 +04:00
/* The main entry point in this file. Called from net/core/dev.c */
2007-09-17 22:56:21 +04:00
int dev_ethtool ( struct net * net , struct ifreq * ifr )
2005-04-17 02:20:36 +04:00
{
2007-09-17 22:56:21 +04:00
struct net_device * dev = __dev_get_by_name ( net , ifr - > ifr_name ) ;
2005-04-17 02:20:36 +04:00
void __user * useraddr = ifr - > ifr_data ;
u32 ethcmd ;
int rc ;
2005-05-30 01:14:35 +04:00
unsigned long old_features ;
2005-04-17 02:20:36 +04:00
if ( ! dev | | ! netif_device_present ( dev ) )
return - ENODEV ;
if ( ! dev - > ethtool_ops )
2007-08-01 01:00:02 +04:00
return - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & ethcmd , useraddr , sizeof ( ethcmd ) ) )
return - EFAULT ;
2006-09-29 02:13:37 +04:00
/* Allow some commands to be done by anyone */
switch ( ethcmd ) {
case ETHTOOL_GDRVINFO :
case ETHTOOL_GMSGLVL :
case ETHTOOL_GCOALESCE :
case ETHTOOL_GRINGPARAM :
case ETHTOOL_GPAUSEPARAM :
case ETHTOOL_GRXCSUM :
case ETHTOOL_GTXCSUM :
case ETHTOOL_GSG :
case ETHTOOL_GSTRINGS :
case ETHTOOL_GTSO :
case ETHTOOL_GPERMADDR :
case ETHTOOL_GUFO :
case ETHTOOL_GGSO :
2007-08-16 03:01:32 +04:00
case ETHTOOL_GFLAGS :
case ETHTOOL_GPFLAGS :
2006-09-29 02:13:37 +04:00
break ;
default :
if ( ! capable ( CAP_NET_ADMIN ) )
return - EPERM ;
}
2007-04-11 07:10:33 +04:00
if ( dev - > ethtool_ops - > begin )
2005-04-17 02:20:36 +04:00
if ( ( rc = dev - > ethtool_ops - > begin ( dev ) ) < 0 )
return rc ;
2005-05-30 01:13:47 +04:00
old_features = dev - > features ;
2005-04-17 02:20:36 +04:00
switch ( ethcmd ) {
case ETHTOOL_GSET :
rc = ethtool_get_settings ( dev , useraddr ) ;
break ;
case ETHTOOL_SSET :
rc = ethtool_set_settings ( dev , useraddr ) ;
break ;
case ETHTOOL_GDRVINFO :
rc = ethtool_get_drvinfo ( dev , useraddr ) ;
break ;
case ETHTOOL_GREGS :
rc = ethtool_get_regs ( dev , useraddr ) ;
break ;
case ETHTOOL_GWOL :
rc = ethtool_get_wol ( dev , useraddr ) ;
break ;
case ETHTOOL_SWOL :
rc = ethtool_set_wol ( dev , useraddr ) ;
break ;
case ETHTOOL_GMSGLVL :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_msglevel ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_SMSGLVL :
2007-08-16 03:01:56 +04:00
rc = ethtool_set_value_void ( dev , useraddr ,
dev - > ethtool_ops - > set_msglevel ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_NWAY_RST :
rc = ethtool_nway_reset ( dev ) ;
break ;
case ETHTOOL_GLINK :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_link ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_GEEPROM :
rc = ethtool_get_eeprom ( dev , useraddr ) ;
break ;
case ETHTOOL_SEEPROM :
rc = ethtool_set_eeprom ( dev , useraddr ) ;
break ;
case ETHTOOL_GCOALESCE :
rc = ethtool_get_coalesce ( dev , useraddr ) ;
break ;
case ETHTOOL_SCOALESCE :
rc = ethtool_set_coalesce ( dev , useraddr ) ;
break ;
case ETHTOOL_GRINGPARAM :
rc = ethtool_get_ringparam ( dev , useraddr ) ;
break ;
case ETHTOOL_SRINGPARAM :
rc = ethtool_set_ringparam ( dev , useraddr ) ;
break ;
case ETHTOOL_GPAUSEPARAM :
rc = ethtool_get_pauseparam ( dev , useraddr ) ;
break ;
case ETHTOOL_SPAUSEPARAM :
rc = ethtool_set_pauseparam ( dev , useraddr ) ;
break ;
case ETHTOOL_GRXCSUM :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_rx_csum ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_SRXCSUM :
2007-08-16 03:01:56 +04:00
rc = ethtool_set_value ( dev , useraddr ,
dev - > ethtool_ops - > set_rx_csum ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_GTXCSUM :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_tx_csum ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_STXCSUM :
rc = ethtool_set_tx_csum ( dev , useraddr ) ;
break ;
case ETHTOOL_GSG :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_sg ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_SSG :
rc = ethtool_set_sg ( dev , useraddr ) ;
break ;
case ETHTOOL_GTSO :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_tso ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_STSO :
rc = ethtool_set_tso ( dev , useraddr ) ;
break ;
case ETHTOOL_TEST :
rc = ethtool_self_test ( dev , useraddr ) ;
break ;
case ETHTOOL_GSTRINGS :
rc = ethtool_get_strings ( dev , useraddr ) ;
break ;
case ETHTOOL_PHYS_ID :
rc = ethtool_phys_id ( dev , useraddr ) ;
break ;
case ETHTOOL_GSTATS :
rc = ethtool_get_stats ( dev , useraddr ) ;
break ;
2005-08-21 04:15:54 +04:00
case ETHTOOL_GPERMADDR :
rc = ethtool_get_perm_addr ( dev , useraddr ) ;
break ;
2005-10-19 02:46:41 +04:00
case ETHTOOL_GUFO :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_ufo ) ;
2005-10-19 02:46:41 +04:00
break ;
case ETHTOOL_SUFO :
rc = ethtool_set_ufo ( dev , useraddr ) ;
break ;
2006-06-22 14:07:29 +04:00
case ETHTOOL_GGSO :
rc = ethtool_get_gso ( dev , useraddr ) ;
break ;
case ETHTOOL_SGSO :
rc = ethtool_set_gso ( dev , useraddr ) ;
break ;
2007-08-16 03:00:51 +04:00
case ETHTOOL_GFLAGS :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_flags ) ;
2007-08-16 03:00:51 +04:00
break ;
case ETHTOOL_SFLAGS :
2007-08-16 03:01:56 +04:00
rc = ethtool_set_value ( dev , useraddr ,
dev - > ethtool_ops - > set_flags ) ;
2007-08-16 03:00:51 +04:00
break ;
2007-08-16 03:01:32 +04:00
case ETHTOOL_GPFLAGS :
2007-08-16 03:01:56 +04:00
rc = ethtool_get_value ( dev , useraddr , ethcmd ,
dev - > ethtool_ops - > get_priv_flags ) ;
2007-08-16 03:01:32 +04:00
break ;
case ETHTOOL_SPFLAGS :
2007-08-16 03:01:56 +04:00
rc = ethtool_set_value ( dev , useraddr ,
dev - > ethtool_ops - > set_priv_flags ) ;
2007-08-16 03:01:32 +04:00
break ;
2005-04-17 02:20:36 +04:00
default :
2007-08-01 01:00:02 +04:00
rc = - EOPNOTSUPP ;
2005-04-17 02:20:36 +04:00
}
2007-02-09 17:24:36 +03:00
2007-04-11 07:10:33 +04:00
if ( dev - > ethtool_ops - > complete )
2005-04-17 02:20:36 +04:00
dev - > ethtool_ops - > complete ( dev ) ;
2005-05-30 01:13:47 +04:00
if ( old_features ! = dev - > features )
netdev_features_change ( dev ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
EXPORT_SYMBOL ( ethtool_op_get_link ) ;
EXPORT_SYMBOL ( ethtool_op_get_sg ) ;
EXPORT_SYMBOL ( ethtool_op_get_tso ) ;
EXPORT_SYMBOL ( ethtool_op_get_tx_csum ) ;
EXPORT_SYMBOL ( ethtool_op_set_sg ) ;
EXPORT_SYMBOL ( ethtool_op_set_tso ) ;
EXPORT_SYMBOL ( ethtool_op_set_tx_csum ) ;
2005-05-30 07:27:24 +04:00
EXPORT_SYMBOL ( ethtool_op_set_tx_hw_csum ) ;
2007-07-15 06:07:52 +04:00
EXPORT_SYMBOL ( ethtool_op_set_tx_ipv6_csum ) ;
2005-10-19 02:46:41 +04:00
EXPORT_SYMBOL ( ethtool_op_set_ufo ) ;
EXPORT_SYMBOL ( ethtool_op_get_ufo ) ;
2007-08-16 03:00:51 +04:00
EXPORT_SYMBOL ( ethtool_op_set_flags ) ;
EXPORT_SYMBOL ( ethtool_op_get_flags ) ;