+/* Allow setting mac address to any valid ethernet address. */
+static int br_set_mac_address(struct net_device *dev, void *p)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct sockaddr *addr = p;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EINVAL;
+
+ spin_lock_bh(&br->lock);
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+ br_stp_change_bridge_id(br, addr->sa_data);
+ spin_unlock_bh(&br->lock);
+
+ return 0;
+}
+
+static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, "bridge");
+ strcpy(info->version, BR_VERSION);
+ strcpy(info->fw_version, "N/A");
+ strcpy(info->bus_info, "N/A");
+}
+
+static int br_set_sg(struct net_device *dev, u32 data)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ if (data)
+ br->feature_mask |= NETIF_F_SG;
+ else
+ br->feature_mask &= ~NETIF_F_SG;
+
+ br_features_recompute(br);
+ return 0;
+}
+
+static int br_set_tso(struct net_device *dev, u32 data)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ if (data)
+ br->feature_mask |= NETIF_F_TSO;
+ else
+ br->feature_mask &= ~NETIF_F_TSO;
+
+ br_features_recompute(br);
+ return 0;
+}
+
+static int br_set_tx_csum(struct net_device *dev, u32 data)
+{
+ struct net_bridge *br = netdev_priv(dev);
+
+ if (data)
+ br->feature_mask |= NETIF_F_NO_CSUM;
+ else
+ br->feature_mask &= ~NETIF_F_ALL_CSUM;
+
+ br_features_recompute(br);
+ return 0;
+}
+
+static struct ethtool_ops br_ethtool_ops = {
+ .get_drvinfo = br_getinfo,
+ .get_link = ethtool_op_get_link,
+ .get_sg = ethtool_op_get_sg,
+ .set_sg = br_set_sg,
+ .get_tx_csum = ethtool_op_get_tx_csum,
+ .set_tx_csum = br_set_tx_csum,
+ .get_tso = ethtool_op_get_tso,
+ .set_tso = br_set_tso,
+};
+