]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/core/ethtool.c
Merge tag 'for-3.5' of git://openrisc.net/jonas/linux
[karo-tx-linux.git] / net / core / ethtool.c
index 6d6d7d25caaa30319870c6b1d62b38eb8972e543..9c2afb480270d0c8651e5f16d0b41dd58c694c51 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/errno.h>
 #include <linux/ethtool.h>
 #include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
+#include <linux/phy.h>
 #include <linux/bitops.h>
 #include <linux/uaccess.h>
 #include <linux/vmalloc.h>
@@ -36,6 +38,17 @@ u32 ethtool_op_get_link(struct net_device *dev)
 }
 EXPORT_SYMBOL(ethtool_op_get_link);
 
+int ethtool_op_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info)
+{
+       info->so_timestamping =
+               SOF_TIMESTAMPING_TX_SOFTWARE |
+               SOF_TIMESTAMPING_RX_SOFTWARE |
+               SOF_TIMESTAMPING_SOFTWARE;
+       info->phc_index = -1;
+       return 0;
+}
+EXPORT_SYMBOL(ethtool_op_get_ts_info);
+
 /* Handlers for each ethtool command */
 
 #define ETHTOOL_DEV_FEATURE_WORDS      ((NETDEV_FEATURE_COUNT + 31) / 32)
@@ -738,18 +751,17 @@ static int ethtool_get_link(struct net_device *dev, char __user *useraddr)
        return 0;
 }
 
-static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
+static int ethtool_get_any_eeprom(struct net_device *dev, void __user *useraddr,
+                                 int (*getter)(struct net_device *,
+                                               struct ethtool_eeprom *, u8 *),
+                                 u32 total_len)
 {
        struct ethtool_eeprom eeprom;
-       const struct ethtool_ops *ops = dev->ethtool_ops;
        void __user *userbuf = useraddr + sizeof(eeprom);
        u32 bytes_remaining;
        u8 *data;
        int ret = 0;
 
-       if (!ops->get_eeprom || !ops->get_eeprom_len)
-               return -EOPNOTSUPP;
-
        if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
                return -EFAULT;
 
@@ -758,7 +770,7 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
                return -EINVAL;
 
        /* Check for exceeding total eeprom len */
-       if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
+       if (eeprom.offset + eeprom.len > total_len)
                return -EINVAL;
 
        data = kmalloc(PAGE_SIZE, GFP_USER);
@@ -769,7 +781,7 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
        while (bytes_remaining > 0) {
                eeprom.len = min(bytes_remaining, (u32)PAGE_SIZE);
 
-               ret = ops->get_eeprom(dev, &eeprom, data);
+               ret = getter(dev, &eeprom, data);
                if (ret)
                        break;
                if (copy_to_user(userbuf, data, eeprom.len)) {
@@ -790,6 +802,17 @@ static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
        return ret;
 }
 
+static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
+{
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+
+       if (!ops->get_eeprom || !ops->get_eeprom_len)
+               return -EOPNOTSUPP;
+
+       return ethtool_get_any_eeprom(dev, useraddr, ops->get_eeprom,
+                                     ops->get_eeprom_len(dev));
+}
+
 static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_eeprom eeprom;
@@ -1278,6 +1301,81 @@ out:
        return ret;
 }
 
+static int ethtool_get_ts_info(struct net_device *dev, void __user *useraddr)
+{
+       int err = 0;
+       struct ethtool_ts_info info;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+       struct phy_device *phydev = dev->phydev;
+
+       memset(&info, 0, sizeof(info));
+       info.cmd = ETHTOOL_GET_TS_INFO;
+
+       if (phydev && phydev->drv && phydev->drv->ts_info) {
+
+               err = phydev->drv->ts_info(phydev, &info);
+
+       } else if (dev->ethtool_ops && dev->ethtool_ops->get_ts_info) {
+
+               err = ops->get_ts_info(dev, &info);
+
+       } else {
+               info.so_timestamping =
+                       SOF_TIMESTAMPING_RX_SOFTWARE |
+                       SOF_TIMESTAMPING_SOFTWARE;
+               info.phc_index = -1;
+       }
+
+       if (err)
+               return err;
+
+       if (copy_to_user(useraddr, &info, sizeof(info)))
+               err = -EFAULT;
+
+       return err;
+}
+
+static int ethtool_get_module_info(struct net_device *dev,
+                                  void __user *useraddr)
+{
+       int ret;
+       struct ethtool_modinfo modinfo;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+
+       if (!ops->get_module_info)
+               return -EOPNOTSUPP;
+
+       if (copy_from_user(&modinfo, useraddr, sizeof(modinfo)))
+               return -EFAULT;
+
+       ret = ops->get_module_info(dev, &modinfo);
+       if (ret)
+               return ret;
+
+       if (copy_to_user(useraddr, &modinfo, sizeof(modinfo)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int ethtool_get_module_eeprom(struct net_device *dev,
+                                    void __user *useraddr)
+{
+       int ret;
+       struct ethtool_modinfo modinfo;
+       const struct ethtool_ops *ops = dev->ethtool_ops;
+
+       if (!ops->get_module_info || !ops->get_module_eeprom)
+               return -EOPNOTSUPP;
+
+       ret = ops->get_module_info(dev, &modinfo);
+       if (ret)
+               return ret;
+
+       return ethtool_get_any_eeprom(dev, useraddr, ops->get_module_eeprom,
+                                     modinfo.eeprom_len);
+}
+
 /* The main entry point in this file.  Called from net/core/dev.c */
 
 int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -1295,11 +1393,13 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
                return -EFAULT;
 
        if (!dev->ethtool_ops) {
-               /* ETHTOOL_GDRVINFO does not require any driver support.
-                * It is also unprivileged and does not change anything,
-                * so we can take a shortcut to it. */
+               /* A few commands do not require any driver support,
+                * are unprivileged, and do not change anything, so we
+                * can take a shortcut to them. */
                if (ethcmd == ETHTOOL_GDRVINFO)
                        return ethtool_get_drvinfo(dev, useraddr);
+               else if (ethcmd == ETHTOOL_GET_TS_INFO)
+                       return ethtool_get_ts_info(dev, useraddr);
                else
                        return -EOPNOTSUPP;
        }
@@ -1330,6 +1430,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GRXCLSRULE:
        case ETHTOOL_GRXCLSRLALL:
        case ETHTOOL_GFEATURES:
+       case ETHTOOL_GET_TS_INFO:
                break;
        default:
                if (!capable(CAP_NET_ADMIN))
@@ -1496,6 +1597,15 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
        case ETHTOOL_GET_DUMP_DATA:
                rc = ethtool_get_dump_data(dev, useraddr);
                break;
+       case ETHTOOL_GET_TS_INFO:
+               rc = ethtool_get_ts_info(dev, useraddr);
+               break;
+       case ETHTOOL_GMODULEINFO:
+               rc = ethtool_get_module_info(dev, useraddr);
+               break;
+       case ETHTOOL_GMODULEEEPROM:
+               rc = ethtool_get_module_eeprom(dev, useraddr);
+               break;
        default:
                rc = -EOPNOTSUPP;
        }