net: Implement SFEATURES compatibility for not updated drivers
[pandora-kernel.git] / net / core / ethtool.c
index 66cdc76..c1a71bb 100644 (file)
@@ -168,6 +168,76 @@ EXPORT_SYMBOL(ethtool_ntuple_flush);
 
 #define ETHTOOL_DEV_FEATURE_WORDS      1
 
+static void ethtool_get_features_compat(struct net_device *dev,
+       struct ethtool_get_features_block *features)
+{
+       if (!dev->ethtool_ops)
+               return;
+
+       /* getting RX checksum */
+       if (dev->ethtool_ops->get_rx_csum)
+               if (dev->ethtool_ops->get_rx_csum(dev))
+                       features[0].active |= NETIF_F_RXCSUM;
+
+       /* mark legacy-changeable features */
+       if (dev->ethtool_ops->set_sg)
+               features[0].available |= NETIF_F_SG;
+       if (dev->ethtool_ops->set_tx_csum)
+               features[0].available |= NETIF_F_ALL_CSUM;
+       if (dev->ethtool_ops->set_tso)
+               features[0].available |= NETIF_F_ALL_TSO;
+       if (dev->ethtool_ops->set_rx_csum)
+               features[0].available |= NETIF_F_RXCSUM;
+       if (dev->ethtool_ops->set_flags)
+               features[0].available |= flags_dup_features;
+}
+
+static int ethtool_set_feature_compat(struct net_device *dev,
+       int (*legacy_set)(struct net_device *, u32),
+       struct ethtool_set_features_block *features, u32 mask)
+{
+       u32 do_set;
+
+       if (!legacy_set)
+               return 0;
+
+       if (!(features[0].valid & mask))
+               return 0;
+
+       features[0].valid &= ~mask;
+
+       do_set = !!(features[0].requested & mask);
+
+       if (legacy_set(dev, do_set) < 0)
+               netdev_info(dev,
+                       "Legacy feature change (%s) failed for 0x%08x\n",
+                       do_set ? "set" : "clear", mask);
+
+       return 1;
+}
+
+static int ethtool_set_features_compat(struct net_device *dev,
+       struct ethtool_set_features_block *features)
+{
+       int compat;
+
+       if (!dev->ethtool_ops)
+               return 0;
+
+       compat  = ethtool_set_feature_compat(dev, dev->ethtool_ops->set_sg,
+               features, NETIF_F_SG);
+       compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tx_csum,
+               features, NETIF_F_ALL_CSUM);
+       compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_tso,
+               features, NETIF_F_ALL_TSO);
+       compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_rx_csum,
+               features, NETIF_F_RXCSUM);
+       compat |= ethtool_set_feature_compat(dev, dev->ethtool_ops->set_flags,
+               features, flags_dup_features);
+
+       return compat;
+}
+
 static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
 {
        struct ethtool_gfeatures cmd = {
@@ -185,6 +255,8 @@ static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
        u32 __user *sizeaddr;
        u32 copy_size;
 
+       ethtool_get_features_compat(dev, features);
+
        sizeaddr = useraddr + offsetof(struct ethtool_gfeatures, size);
        if (get_user(copy_size, sizeaddr))
                return -EFAULT;
@@ -220,6 +292,9 @@ static int ethtool_set_features(struct net_device *dev, void __user *useraddr)
        if (features[0].valid & ~NETIF_F_ETHTOOL_BITS)
                return -EINVAL;
 
+       if (ethtool_set_features_compat(dev, features))
+               ret |= ETHTOOL_F_COMPAT;
+
        if (features[0].valid & ~dev->hw_features) {
                features[0].valid &= dev->hw_features;
                ret |= ETHTOOL_F_UNSUPPORTED;