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 ;
}
2009-07-22 17:38:22 +04:00
u32 ethtool_op_get_rx_csum ( struct net_device * dev )
{
return ( dev - > features & NETIF_F_ALL_CSUM ) ! = 0 ;
}
2009-07-27 03:18:11 +04:00
EXPORT_SYMBOL ( ethtool_op_get_rx_csum ) ;
2009-07-22 17:38:22 +04:00
2005-04-17 02:20:36 +04:00
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
}
2009-07-27 03:18:11 +04:00
EXPORT_SYMBOL ( ethtool_op_get_tx_csum ) ;
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 =
2010-02-11 07:03:05 +03:00
( ETH_FLAG_LRO | ETH_FLAG_NTUPLE ) ;
2007-08-16 03:00:51 +04:00
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 ;
2010-02-11 07:03:05 +03:00
if ( data & ETH_FLAG_NTUPLE )
dev - > features | = NETIF_F_NTUPLE ;
else
dev - > features & = ~ NETIF_F_NTUPLE ;
2007-08-16 03:00:51 +04:00
return 0 ;
}
2010-02-11 07:03:05 +03:00
void ethtool_ntuple_flush ( struct net_device * dev )
{
struct ethtool_rx_ntuple_flow_spec_container * fsc , * f ;
list_for_each_entry_safe ( fsc , f , & dev - > ethtool_ntuple_list . list , list ) {
list_del ( & fsc - > list ) ;
kfree ( fsc ) ;
}
dev - > ethtool_ntuple_list . count = 0 ;
}
EXPORT_SYMBOL ( ethtool_ntuple_flush ) ;
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 )
{
2010-02-11 23:14:23 +03:00
struct ethtool_cmd cmd = { . cmd = ETHTOOL_GSET } ;
2005-04-17 02:20:36 +04:00
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
}
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 ;
}
2009-02-20 11:58:13 +03:00
static int ethtool_set_rxnfc ( struct net_device * dev , void __user * useraddr )
2008-07-02 14:47:41 +04:00
{
struct ethtool_rxnfc cmd ;
2009-02-20 11:58:13 +03:00
if ( ! dev - > ethtool_ops - > set_rxnfc )
2008-07-02 14:47:41 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & cmd , useraddr , sizeof ( cmd ) ) )
return - EFAULT ;
2009-02-20 11:58:13 +03:00
return dev - > ethtool_ops - > set_rxnfc ( dev , & cmd ) ;
2008-07-02 14:47:41 +04:00
}
2009-02-20 11:58:13 +03:00
static int ethtool_get_rxnfc ( struct net_device * dev , void __user * useraddr )
2008-07-02 14:47:41 +04:00
{
struct ethtool_rxnfc info ;
2009-02-20 11:58:13 +03:00
const struct ethtool_ops * ops = dev - > ethtool_ops ;
int ret ;
void * rule_buf = NULL ;
2008-07-02 14:47:41 +04:00
2009-02-20 11:58:13 +03:00
if ( ! ops - > get_rxnfc )
2008-07-02 14:47:41 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & info , useraddr , sizeof ( info ) ) )
return - EFAULT ;
2009-02-20 11:58:13 +03:00
if ( info . cmd = = ETHTOOL_GRXCLSRLALL ) {
if ( info . rule_cnt > 0 ) {
rule_buf = kmalloc ( info . rule_cnt * sizeof ( u32 ) ,
GFP_USER ) ;
if ( ! rule_buf )
return - ENOMEM ;
}
}
2008-07-02 14:47:41 +04:00
2009-02-20 11:58:13 +03:00
ret = ops - > get_rxnfc ( dev , & info , rule_buf ) ;
if ( ret < 0 )
goto err_out ;
ret = - EFAULT ;
2008-07-02 14:47:41 +04:00
if ( copy_to_user ( useraddr , & info , sizeof ( info ) ) )
2009-02-20 11:58:13 +03:00
goto err_out ;
if ( rule_buf ) {
useraddr + = offsetof ( struct ethtool_rxnfc , rule_locs ) ;
if ( copy_to_user ( useraddr , rule_buf ,
info . rule_cnt * sizeof ( u32 ) ) )
goto err_out ;
}
ret = 0 ;
err_out :
2009-04-01 02:06:26 +04:00
kfree ( rule_buf ) ;
2009-02-20 11:58:13 +03:00
return ret ;
2008-07-02 14:47:41 +04:00
}
2010-02-11 07:03:05 +03:00
static int __rx_ntuple_filter_add ( struct ethtool_rx_ntuple_list * list ,
struct ethtool_rx_ntuple_flow_spec * spec )
{
struct ethtool_rx_ntuple_flow_spec_container * fsc ;
/* don't add filters forever */
if ( list - > count > = ETHTOOL_MAX_NTUPLE_LIST_ENTRY )
return 0 ;
fsc = kmalloc ( sizeof ( * fsc ) , GFP_ATOMIC ) ;
if ( ! fsc )
return - ENOMEM ;
/* Copy the whole filter over */
fsc - > fs . flow_type = spec - > flow_type ;
memcpy ( & fsc - > fs . h_u , & spec - > h_u , sizeof ( spec - > h_u ) ) ;
memcpy ( & fsc - > fs . m_u , & spec - > m_u , sizeof ( spec - > m_u ) ) ;
fsc - > fs . vlan_tag = spec - > vlan_tag ;
fsc - > fs . vlan_tag_mask = spec - > vlan_tag_mask ;
fsc - > fs . data = spec - > data ;
fsc - > fs . data_mask = spec - > data_mask ;
fsc - > fs . action = spec - > action ;
/* add to the list */
list_add_tail_rcu ( & fsc - > list , & list - > list ) ;
list - > count + + ;
return 0 ;
}
static int ethtool_set_rx_ntuple ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_rx_ntuple cmd ;
const struct ethtool_ops * ops = dev - > ethtool_ops ;
int ret ;
if ( ! ops - > set_rx_ntuple )
return - EOPNOTSUPP ;
if ( ! ( dev - > features & NETIF_F_NTUPLE ) )
return - EINVAL ;
if ( copy_from_user ( & cmd , useraddr , sizeof ( cmd ) ) )
return - EFAULT ;
ret = ops - > set_rx_ntuple ( dev , & cmd ) ;
/*
* Cache filter in dev struct for GET operation only if
* the underlying driver doesn ' t have its own GET operation , and
* only if the filter was added successfully .
*/
if ( ! ops - > get_rx_ntuple & & ! ret )
if ( __rx_ntuple_filter_add ( & dev - > ethtool_ntuple_list , & cmd . fs ) )
return - ENOMEM ;
return ret ;
}
static int ethtool_get_rx_ntuple ( struct net_device * dev , void __user * useraddr )
{
struct ethtool_gstrings gstrings ;
const struct ethtool_ops * ops = dev - > ethtool_ops ;
struct ethtool_rx_ntuple_flow_spec_container * fsc ;
u8 * data ;
char * p ;
int ret , i , num_strings = 0 ;
if ( ! ops - > get_sset_count )
return - EOPNOTSUPP ;
if ( copy_from_user ( & gstrings , useraddr , sizeof ( gstrings ) ) )
return - EFAULT ;
ret = ops - > get_sset_count ( dev , gstrings . string_set ) ;
if ( ret < 0 )
return ret ;
gstrings . len = ret ;
data = kmalloc ( gstrings . len * ETH_GSTRING_LEN , GFP_USER ) ;
if ( ! data )
return - ENOMEM ;
if ( ops - > get_rx_ntuple ) {
/* driver-specific filter grab */
ret = ops - > get_rx_ntuple ( dev , gstrings . string_set , data ) ;
goto copy ;
}
/* default ethtool filter grab */
i = 0 ;
p = ( char * ) data ;
list_for_each_entry ( fsc , & dev - > ethtool_ntuple_list . list , list ) {
sprintf ( p , " Filter %d: \n " , i ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
switch ( fsc - > fs . flow_type ) {
case TCP_V4_FLOW :
sprintf ( p , " \t Flow Type: TCP \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case UDP_V4_FLOW :
sprintf ( p , " \t Flow Type: UDP \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case SCTP_V4_FLOW :
sprintf ( p , " \t Flow Type: SCTP \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case AH_ESP_V4_FLOW :
sprintf ( p , " \t Flow Type: AH ESP \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case ESP_V4_FLOW :
sprintf ( p , " \t Flow Type: ESP \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case IP_USER_FLOW :
sprintf ( p , " \t Flow Type: Raw IP \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case IPV4_FLOW :
sprintf ( p , " \t Flow Type: IPv4 \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
default :
sprintf ( p , " \t Flow Type: Unknown \n " ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
goto unknown_filter ;
} ;
/* now the rest of the filters */
switch ( fsc - > fs . flow_type ) {
case TCP_V4_FLOW :
case UDP_V4_FLOW :
case SCTP_V4_FLOW :
sprintf ( p , " \t Src IP addr: 0x%x \n " ,
fsc - > fs . h_u . tcp_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Src IP mask: 0x%x \n " ,
fsc - > fs . m_u . tcp_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP addr: 0x%x \n " ,
fsc - > fs . h_u . tcp_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP mask: 0x%x \n " ,
fsc - > fs . m_u . tcp_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Src Port: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . tcp_ip4_spec . psrc ,
fsc - > fs . m_u . tcp_ip4_spec . psrc ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest Port: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . tcp_ip4_spec . pdst ,
fsc - > fs . m_u . tcp_ip4_spec . pdst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t TOS: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . tcp_ip4_spec . tos ,
fsc - > fs . m_u . tcp_ip4_spec . tos ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case AH_ESP_V4_FLOW :
case ESP_V4_FLOW :
sprintf ( p , " \t Src IP addr: 0x%x \n " ,
fsc - > fs . h_u . ah_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Src IP mask: 0x%x \n " ,
fsc - > fs . m_u . ah_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP addr: 0x%x \n " ,
fsc - > fs . h_u . ah_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP mask: 0x%x \n " ,
fsc - > fs . m_u . ah_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t SPI: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . ah_ip4_spec . spi ,
fsc - > fs . m_u . ah_ip4_spec . spi ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t TOS: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . ah_ip4_spec . tos ,
fsc - > fs . m_u . ah_ip4_spec . tos ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case IP_USER_FLOW :
sprintf ( p , " \t Src IP addr: 0x%x \n " ,
fsc - > fs . h_u . raw_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Src IP mask: 0x%x \n " ,
fsc - > fs . m_u . raw_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP addr: 0x%x \n " ,
fsc - > fs . h_u . raw_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP mask: 0x%x \n " ,
fsc - > fs . m_u . raw_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
case IPV4_FLOW :
sprintf ( p , " \t Src IP addr: 0x%x \n " ,
fsc - > fs . h_u . usr_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Src IP mask: 0x%x \n " ,
fsc - > fs . m_u . usr_ip4_spec . ip4src ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP addr: 0x%x \n " ,
fsc - > fs . h_u . usr_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Dest IP mask: 0x%x \n " ,
fsc - > fs . m_u . usr_ip4_spec . ip4dst ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t L4 bytes: 0x%x, mask: 0x%x \n " ,
fsc - > fs . h_u . usr_ip4_spec . l4_4_bytes ,
fsc - > fs . m_u . usr_ip4_spec . l4_4_bytes ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t TOS: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . usr_ip4_spec . tos ,
fsc - > fs . m_u . usr_ip4_spec . tos ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t IP Version: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . usr_ip4_spec . ip_ver ,
fsc - > fs . m_u . usr_ip4_spec . ip_ver ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t Protocol: %d, mask: 0x%x \n " ,
fsc - > fs . h_u . usr_ip4_spec . proto ,
fsc - > fs . m_u . usr_ip4_spec . proto ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
break ;
} ;
sprintf ( p , " \t VLAN: %d, mask: 0x%x \n " ,
fsc - > fs . vlan_tag , fsc - > fs . vlan_tag_mask ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t User-defined: 0x%Lx \n " , fsc - > fs . data ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
sprintf ( p , " \t User-defined mask: 0x%Lx \n " , fsc - > fs . data_mask ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
if ( fsc - > fs . action = = ETHTOOL_RXNTUPLE_ACTION_DROP )
sprintf ( p , " \t Action: Drop \n " ) ;
else
sprintf ( p , " \t Action: Direct to queue %d \n " ,
fsc - > fs . action ) ;
p + = ETH_GSTRING_LEN ;
num_strings + + ;
unknown_filter :
i + + ;
}
copy :
/* indicate to userspace how many strings we actually have */
gstrings . len = num_strings ;
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 ;
}
2005-04-17 02:20:36 +04:00
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 ;
}
2009-10-05 14:59:58 +04:00
static int ethtool_reset ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value reset ;
int ret ;
if ( ! dev - > ethtool_ops - > reset )
return - EOPNOTSUPP ;
if ( copy_from_user ( & reset , useraddr , sizeof ( reset ) ) )
return - EFAULT ;
2010-02-11 07:03:05 +03:00
/* Clear ethtool n-tuple list */
ethtool_ntuple_flush ( dev ) ;
2009-10-05 14:59:58 +04:00
ret = dev - > ethtool_ops - > reset ( dev , & reset . data ) ;
if ( ret )
return ret ;
if ( copy_to_user ( useraddr , & reset , sizeof ( reset ) ) )
return - EFAULT ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int ethtool_get_wol ( struct net_device * dev , char __user * useraddr )
{
2010-02-11 23:14:23 +03:00
struct ethtool_wolinfo wol = { . cmd = ETHTOOL_GWOL } ;
2005-04-17 02:20:36 +04:00
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 ;
2008-04-16 06:24:17 +04:00
void __user * userbuf = useraddr + sizeof ( eeprom ) ;
u32 bytes_remaining ;
2005-04-17 02:20:36 +04:00
u8 * data ;
2008-04-16 06:24:17 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
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 ;
2008-04-16 06:24:17 +04:00
data = kmalloc ( PAGE_SIZE , GFP_USER ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - ENOMEM ;
2008-04-16 06:24:17 +04:00
bytes_remaining = eeprom . len ;
while ( bytes_remaining > 0 ) {
eeprom . len = min ( bytes_remaining , ( u32 ) PAGE_SIZE ) ;
ret = ops - > get_eeprom ( dev , & eeprom , data ) ;
if ( ret )
break ;
if ( copy_to_user ( userbuf , data , eeprom . len ) ) {
ret = - EFAULT ;
break ;
}
userbuf + = eeprom . len ;
eeprom . offset + = eeprom . len ;
bytes_remaining - = eeprom . len ;
}
2005-04-17 02:20:36 +04:00
2008-04-25 07:55:56 +04:00
eeprom . len = userbuf - ( useraddr + sizeof ( eeprom ) ) ;
eeprom . offset - = eeprom . len ;
if ( copy_to_user ( useraddr , & eeprom , sizeof ( eeprom ) ) )
ret = - EFAULT ;
2005-04-17 02:20:36 +04:00
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 ;
2008-04-16 06:24:17 +04:00
void __user * userbuf = useraddr + sizeof ( eeprom ) ;
u32 bytes_remaining ;
2005-04-17 02:20:36 +04:00
u8 * data ;
2008-04-16 06:24:17 +04:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
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 ;
2008-04-16 06:24:17 +04:00
data = kmalloc ( PAGE_SIZE , GFP_USER ) ;
2005-04-17 02:20:36 +04:00
if ( ! data )
return - ENOMEM ;
2008-04-16 06:24:17 +04:00
bytes_remaining = eeprom . len ;
while ( bytes_remaining > 0 ) {
eeprom . len = min ( bytes_remaining , ( u32 ) PAGE_SIZE ) ;
if ( copy_from_user ( data , userbuf , eeprom . len ) ) {
ret = - EFAULT ;
break ;
}
ret = ops - > set_eeprom ( dev , & eeprom , data ) ;
if ( ret )
break ;
userbuf + = eeprom . len ;
eeprom . offset + = eeprom . len ;
bytes_remaining - = eeprom . len ;
}
2005-04-17 02:20:36 +04:00
kfree ( data ) ;
return ret ;
}
static int ethtool_get_coalesce ( struct net_device * dev , void __user * useraddr )
{
2010-02-11 23:14:23 +03:00
struct ethtool_coalesce coalesce = { . cmd = ETHTOOL_GCOALESCE } ;
2005-04-17 02:20:36 +04:00
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 )
{
2010-02-11 23:14:23 +03:00
struct ethtool_ringparam ringparam = { . cmd = ETHTOOL_GRINGPARAM } ;
2005-04-17 02:20:36 +04:00
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 ) ;
}
2008-12-16 10:44:31 +03:00
static int ethtool_set_rx_csum ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
if ( ! dev - > ethtool_ops - > set_rx_csum )
return - EOPNOTSUPP ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
if ( ! edata . data & & dev - > ethtool_ops - > set_sg )
dev - > features & = ~ NETIF_F_GRO ;
return dev - > ethtool_ops - > set_rx_csum ( dev , edata . data ) ;
}
2005-04-17 02:20:36 +04:00
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 ;
}
2008-12-16 10:44:31 +03:00
static int ethtool_get_gro ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata = { ETHTOOL_GGRO } ;
edata . data = dev - > features & NETIF_F_GRO ;
if ( copy_to_user ( useraddr , & edata , sizeof ( edata ) ) )
return - EFAULT ;
return 0 ;
}
static int ethtool_set_gro ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_value edata ;
if ( copy_from_user ( & edata , useraddr , sizeof ( edata ) ) )
return - EFAULT ;
if ( edata . data ) {
if ( ! dev - > ethtool_ops - > get_rx_csum | |
! dev - > ethtool_ops - > get_rx_csum ( dev ) )
return - EINVAL ;
dev - > features | = NETIF_F_GRO ;
} else
dev - > features & = ~ NETIF_F_GRO ;
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
2009-10-01 15:33:03 +04:00
if ( ! ops - > self_test | | ! ops - > get_sset_count )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
2009-10-01 15:33:03 +04:00
test_len = ops - > get_sset_count ( dev , ETH_SS_TEST ) ;
2007-08-16 03:01:08 +04:00
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 ;
2009-10-01 15:33:03 +04:00
if ( ! ops - > get_strings | | ! ops - > get_sset_count )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
if ( copy_from_user ( & gstrings , useraddr , sizeof ( gstrings ) ) )
return - EFAULT ;
2009-10-01 15:33:03 +04:00
ret = ops - > get_sset_count ( dev , gstrings . string_set ) ;
if ( ret < 0 )
return ret ;
gstrings . len = ret ;
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
2009-10-01 15:33:03 +04:00
if ( ! ops - > get_ethtool_stats | | ! ops - > get_sset_count )
2005-04-17 02:20:36 +04:00
return - EOPNOTSUPP ;
2009-10-01 15:33:03 +04:00
n_stats = ops - > get_sset_count ( dev , ETH_SS_STATS ) ;
2007-08-16 03:01:08 +04:00
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
{
2010-02-11 23:14:23 +03:00
struct ethtool_value edata = { . cmd = 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
}
2009-09-02 21:02:55 +04:00
static int ethtool_flash_device ( struct net_device * dev , char __user * useraddr )
{
struct ethtool_flash efl ;
if ( copy_from_user ( & efl , useraddr , sizeof ( efl ) ) )
return - EFAULT ;
if ( ! dev - > ethtool_ops - > flash_device )
return - EOPNOTSUPP ;
return dev - > ethtool_ops - > flash_device ( dev , & efl ) ;
}
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 :
2008-07-02 14:47:41 +04:00
case ETHTOOL_GRXFH :
2009-02-20 11:58:13 +03:00
case ETHTOOL_GRXRINGS :
case ETHTOOL_GRXCLSRLCNT :
case ETHTOOL_GRXCLSRULE :
case ETHTOOL_GRXCLSRLALL :
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 ,
2009-07-22 17:38:22 +04:00
( dev - > ethtool_ops - > get_rx_csum ?
dev - > ethtool_ops - > get_rx_csum :
ethtool_op_get_rx_csum ) ) ;
2005-04-17 02:20:36 +04:00
break ;
case ETHTOOL_SRXCSUM :
2008-12-16 10:44:31 +03:00
rc = ethtool_set_rx_csum ( dev , useraddr ) ;
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 ,
2007-09-16 01:41:06 +04:00
( dev - > ethtool_ops - > get_tx_csum ?
dev - > ethtool_ops - > get_tx_csum :
ethtool_op_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 ,
2007-09-16 01:41:06 +04:00
( dev - > ethtool_ops - > get_sg ?
dev - > ethtool_ops - > get_sg :
ethtool_op_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 ,
2007-09-16 01:41:06 +04:00
( dev - > ethtool_ops - > get_tso ?
dev - > ethtool_ops - > get_tso :
ethtool_op_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 ,
2007-09-16 01:41:06 +04:00
( dev - > ethtool_ops - > get_ufo ?
dev - > ethtool_ops - > get_ufo :
ethtool_op_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 ,
2009-07-22 17:38:22 +04:00
( dev - > ethtool_ops - > get_flags ?
dev - > ethtool_ops - > get_flags :
ethtool_op_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 ;
2008-07-02 14:47:41 +04:00
case ETHTOOL_GRXFH :
2009-02-20 11:58:13 +03:00
case ETHTOOL_GRXRINGS :
case ETHTOOL_GRXCLSRLCNT :
case ETHTOOL_GRXCLSRULE :
case ETHTOOL_GRXCLSRLALL :
rc = ethtool_get_rxnfc ( dev , useraddr ) ;
2008-07-02 14:47:41 +04:00
break ;
case ETHTOOL_SRXFH :
2009-02-20 11:58:13 +03:00
case ETHTOOL_SRXCLSRLDEL :
case ETHTOOL_SRXCLSRLINS :
rc = ethtool_set_rxnfc ( dev , useraddr ) ;
2008-07-02 14:47:41 +04:00
break ;
2008-12-16 10:44:31 +03:00
case ETHTOOL_GGRO :
rc = ethtool_get_gro ( dev , useraddr ) ;
break ;
case ETHTOOL_SGRO :
rc = ethtool_set_gro ( dev , useraddr ) ;
break ;
2009-09-02 21:02:55 +04:00
case ETHTOOL_FLASHDEV :
rc = ethtool_flash_device ( dev , useraddr ) ;
break ;
2009-10-05 14:59:58 +04:00
case ETHTOOL_RESET :
rc = ethtool_reset ( dev , useraddr ) ;
break ;
2010-02-11 07:03:05 +03:00
case ETHTOOL_SRXNTUPLE :
rc = ethtool_set_rx_ntuple ( dev , useraddr ) ;
break ;
case ETHTOOL_GRXNTUPLE :
rc = ethtool_get_rx_ntuple ( dev , useraddr ) ;
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_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 ) ;