* 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.
+ * the information ethtool needs.
*
- * It's GPL, stupid.
+ * 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.
*/
#include <linux/module.h>
return 0;
}
+
+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;
+}
+
u32 ethtool_op_get_sg(struct net_device *dev)
{
return (dev->features & NETIF_F_SG) != 0;
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;
return 0;
}
+/* 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;
+}
+
/* Handlers for each ethtool command */
static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
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_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;
+ rc = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS);
+ if (rc >= 0)
+ info.n_priv_flags = rc;
+ } 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);
+ }
if (ops->get_regs_len)
info.regdump_len = ops->get_regs_len(dev);
if (ops->get_eeprom_len)
struct ethtool_test test;
const struct ethtool_ops *ops = dev->ethtool_ops;
u64 *data;
- int ret;
+ int ret, test_len;
- if (!ops->self_test || !ops->self_test_count)
+ if (!ops->self_test)
+ return -EOPNOTSUPP;
+ if (!ops->get_sset_count && !ops->self_test_count)
return -EOPNOTSUPP;
+ 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);
+
if (copy_from_user(&test, useraddr, sizeof(test)))
return -EFAULT;
- test.len = ops->self_test_count(dev);
- data = kmalloc(test.len * sizeof(u64), GFP_USER);
+ test.len = test_len;
+ data = kmalloc(test_len * sizeof(u64), GFP_USER);
if (!data)
return -ENOMEM;
if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
return -EFAULT;
- 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;
+ 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;
+ }
}
data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
struct ethtool_stats stats;
const struct ethtool_ops *ops = dev->ethtool_ops;
u64 *data;
- int ret;
+ int ret, n_stats;
- if (!ops->get_ethtool_stats || !ops->get_stats_count)
+ if (!ops->get_ethtool_stats)
+ return -EOPNOTSUPP;
+ if (!ops->get_sset_count && !ops->get_stats_count)
return -EOPNOTSUPP;
+ 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);
+
if (copy_from_user(&stats, useraddr, sizeof(stats)))
return -EFAULT;
- stats.n_stats = ops->get_stats_count(dev);
- data = kmalloc(stats.n_stats * sizeof(u64), GFP_USER);
+ stats.n_stats = n_stats;
+ data = kmalloc(n_stats * sizeof(u64), GFP_USER);
if (!data)
return -ENOMEM;
static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
{
struct ethtool_perm_addr epaddr;
- u8 *data;
- int ret;
- if (!dev->ethtool_ops->get_perm_addr)
+ if (copy_from_user(&epaddr, useraddr, sizeof(epaddr)))
+ return -EFAULT;
+
+ if (epaddr.size < dev->addr_len)
+ return -ETOOSMALL;
+ epaddr.size = dev->addr_len;
+
+ if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
+ return -EFAULT;
+ useraddr += sizeof(epaddr);
+ if (copy_to_user(useraddr, dev->perm_addr, epaddr.size))
+ return -EFAULT;
+ return 0;
+}
+
+static int ethtool_get_flags(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata = { ETHTOOL_GFLAGS };
+
+ if (!dev->ethtool_ops->get_flags)
return -EOPNOTSUPP;
- if (copy_from_user(&epaddr,useraddr,sizeof(epaddr)))
+ edata.data = dev->ethtool_ops->get_flags(dev);
+
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
+ return 0;
+}
- data = kmalloc(epaddr.size, GFP_USER);
- if (!data)
- return -ENOMEM;
+static int ethtool_set_flags(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata;
- ret = dev->ethtool_ops->get_perm_addr(dev,&epaddr,data);
- if (ret)
- return ret;
+ if (!dev->ethtool_ops->set_flags)
+ return -EOPNOTSUPP;
- ret = -EFAULT;
- if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
- goto out;
- useraddr += sizeof(epaddr);
- if (copy_to_user(useraddr, data, epaddr.size))
- goto out;
- ret = 0;
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
- out:
- kfree(data);
- return ret;
+ return dev->ethtool_ops->set_flags(dev, edata.data);
+}
+
+static int ethtool_get_priv_flags(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata = { ETHTOOL_GPFLAGS };
+
+ if (!dev->ethtool_ops->get_priv_flags)
+ return -EOPNOTSUPP;
+
+ edata.data = dev->ethtool_ops->get_priv_flags(dev);
+
+ if (copy_to_user(useraddr, &edata, sizeof(edata)))
+ return -EFAULT;
+ return 0;
+}
+
+static int ethtool_set_priv_flags(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_value edata;
+
+ if (!dev->ethtool_ops->set_priv_flags)
+ return -EOPNOTSUPP;
+
+ if (copy_from_user(&edata, useraddr, sizeof(edata)))
+ return -EFAULT;
+
+ return dev->ethtool_ops->set_priv_flags(dev, edata.data);
}
/* The main entry point in this file. Called from net/core/dev.c */
return -ENODEV;
if (!dev->ethtool_ops)
- goto ioctl;
+ return -EOPNOTSUPP;
if (copy_from_user(ðcmd, useraddr, sizeof (ethcmd)))
return -EFAULT;
case ETHTOOL_GPERMADDR:
case ETHTOOL_GUFO:
case ETHTOOL_GGSO:
+ case ETHTOOL_GFLAGS:
+ case ETHTOOL_GPFLAGS:
break;
default:
if (!capable(CAP_NET_ADMIN))
case ETHTOOL_SGSO:
rc = ethtool_set_gso(dev, useraddr);
break;
+ case ETHTOOL_GFLAGS:
+ rc = ethtool_get_flags(dev, useraddr);
+ break;
+ case ETHTOOL_SFLAGS:
+ rc = ethtool_set_flags(dev, useraddr);
+ break;
+ case ETHTOOL_GPFLAGS:
+ rc = ethtool_get_priv_flags(dev, useraddr);
+ break;
+ case ETHTOOL_SPFLAGS:
+ rc = ethtool_set_priv_flags(dev, useraddr);
+ break;
default:
- rc = -EOPNOTSUPP;
+ rc = -EOPNOTSUPP;
}
if (dev->ethtool_ops->complete)
netdev_features_change(dev);
return rc;
-
- ioctl:
- /* Keep existing behaviour for the moment. */
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if (dev->do_ioctl)
- return dev->do_ioctl(dev, ifr, SIOCETHTOOL);
- return -EOPNOTSUPP;
}
-EXPORT_SYMBOL(dev_ethtool);
EXPORT_SYMBOL(ethtool_op_get_link);
-EXPORT_SYMBOL_GPL(ethtool_op_get_perm_addr);
EXPORT_SYMBOL(ethtool_op_get_sg);
EXPORT_SYMBOL(ethtool_op_get_tso);
EXPORT_SYMBOL(ethtool_op_get_tx_csum);
EXPORT_SYMBOL(ethtool_op_set_tso);
EXPORT_SYMBOL(ethtool_op_set_tx_csum);
EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
+EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum);
EXPORT_SYMBOL(ethtool_op_set_ufo);
EXPORT_SYMBOL(ethtool_op_get_ufo);
+EXPORT_SYMBOL(ethtool_op_set_flags);
+EXPORT_SYMBOL(ethtool_op_get_flags);