/*
* 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
* the information ethtool needs. We fall back to calling do_ioctl()
* for drivers which haven't been converted to ethtool_ops yet.
*
* It's GPL, stupid.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
/*
* 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)
{
return (dev->features & (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM)) != 0;
}
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;
}
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;
}
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;
}
int ethtool_op_get_perm_addr(struct net_device *dev, struct ethtool_perm_addr *addr, u8 *data)
{
unsigned char len = dev->addr_len;
if ( addr->size < len )
return -ETOOSMALL;
addr->size = len;
memcpy(data, dev->perm_addr, len);
return 0;
}
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;
}
/* 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;
struct ethtool_ops *ops = dev->ethtool_ops;
if (!ops->get_drvinfo)
return -EOPNOTSUPP;
memset(&info, 0, sizeof(info));
info.cmd = ETHTOOL_GDRVINFO;
ops->get_drvinfo(dev, &info);
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);
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;