[OpenWrt-Devel] [PATCH 3/3][RFC] ag71xx_ar7240 add new switch func export_netdevs

Alexander Couzens lynxis at fe80.eu
Tue Dec 9 14:21:21 EST 2014


export_netdevs will export a net device for every port. These netdev represent a port
with out traffic.
When such a device is broght down via ifconfig the port is shutdown and
vice versa. Carrier sense is working too and ethtool can be used to
control advertise, autoneg, ...
---
 .../net/ethernet/atheros/ag71xx/ag71xx_ar7240.c    | 275 +++++++++++++++++++++
 1 file changed, 275 insertions(+)

diff --git a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
index a94837a..9dc913f 100644
--- a/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
+++ b/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_ar7240.c
@@ -197,6 +197,17 @@
 #define AR7240_PHY_ID1		0x004d
 #define AR7240_PHY_ID2		0xd041
 
+#define MII_ATH_REG_FUNCTION_CTRL 0x10
+#define   MII_ATH_FUNCTION_CTRL_MDIX BITS(5, 2)
+#define   MII_ATH_FUNCTION_CTRL_MDIX_OFFSET 5
+#define   MII_ATH_FUNCTION_CTRL_MDIX_MDI 0x0
+#define   MII_ATH_FUNCTION_CTRL_MDIX_MDIX 0x1
+#define   MII_ATH_FUNCTION_CTRL_MDIX_AUTO 0x3
+#define MII_ATH_REG_PHY_SPECIFIC_STATUS  0x11
+#define   MII_ATH_PHY_SPECIFIC_STATUS_MDIX BIT(6)
+#define   MII_ATH_PHY_SPECIFIC_STATUS_MDIX_MDI 0x0
+#define   MII_ATH_PHY_SPECIFIC_STATUS_MDIX_MDIX 0x1
+
 #define AR934X_PHY_ID1		0x004d
 #define AR934X_PHY_ID2		0xd042
 
@@ -304,6 +315,8 @@ struct ar7240sw {
 
 	rwlock_t stats_lock;
 	struct ar7240sw_port_stat port_stats[AR7240_NUM_PORTS];
+	bool export_netdev;
+	struct net_device *port_netdev[AR7240_NUM_PORTS];
 };
 
 struct ar7240sw_hw_stat {
@@ -836,6 +849,260 @@ ar7240_set_ports(struct switch_dev *dev, struct switch_val *val)
 	return 0;
 }
 
+struct priv_netdev_port {
+	uint8_t port;
+	struct net_device *netdev;
+};
+
+int port_init(struct net_device *dev) {
+	return 0;
+}
+
+void port_dummy_cb(struct net_device *dev) {
+}
+
+int port_open(struct net_device *dev) {
+	phy_start(dev->phydev);
+	netif_start_queue(dev);
+	phy_start_aneg(dev->phydev);
+	return 0;
+}
+
+int port_stop(struct net_device *dev) {
+	netif_stop_queue(dev);
+	phy_stop(dev->phydev);
+	return 0;
+}
+
+static int port_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+	dev_kfree_skb(skb);
+	return NETDEV_TX_OK;
+}
+
+static int port_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct mii_bus *bus;
+	struct phy_device *phydev;
+	int reg;
+
+	if (!dev->phydev || !dev->phydev->bus)
+		return -EINVAL;
+
+	phydev = dev->phydev;
+	bus = dev->phydev->bus;
+
+	/* get mdix ctrl */
+	mutex_lock(&bus->mdio_lock);
+	reg = bus->read(bus, phydev->addr, MII_ATH_REG_FUNCTION_CTRL);
+	mutex_unlock(&bus->mdio_lock);
+	reg &= MII_ATH_FUNCTION_CTRL_MDIX;
+	reg = reg >> MII_ATH_FUNCTION_CTRL_MDIX_OFFSET;
+	switch (reg) {
+		case MII_ATH_FUNCTION_CTRL_MDIX_MDI: 
+			cmd->eth_tp_mdix_ctrl = ETH_TP_MDI;
+			break;
+		case MII_ATH_FUNCTION_CTRL_MDIX_MDIX:
+			cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_X;
+			break;
+		case MII_ATH_FUNCTION_CTRL_MDIX_AUTO:
+			cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
+			break;
+		default:
+			printk(KERN_ERR "%s:%d Unknown state %x\n", __FILE__, __LINE__, reg);
+	}
+
+	/* get mdix status */
+	mutex_lock(&bus->mdio_lock);
+	reg = bus->read(bus, phydev->addr, MII_ATH_REG_PHY_SPECIFIC_STATUS);
+	mutex_unlock(&bus->mdio_lock);
+
+	cmd->eth_tp_mdix = reg & MII_ATH_PHY_SPECIFIC_STATUS_MDIX ? ETH_TP_MDI_X : ETH_TP_MDI;
+
+	/* phy_ethtool_gset sets everything correct beside the port */
+	phy_ethtool_gset(dev->phydev, cmd);
+	cmd->port = PORT_TP;
+
+	return 0;
+}
+
+static int port_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	int reg = 0;
+	int ret = 0;
+	struct mii_bus *bus;
+	struct phy_device *phydev;
+
+	if (!dev->phydev || !dev->phydev->bus)
+		return -EINVAL;
+
+	phydev = dev->phydev;
+	bus = dev->phydev->bus;
+
+	/* mdix is vendor specific */
+	if (cmd->eth_tp_mdix_ctrl)  {
+		int mdi;
+
+		switch (cmd->eth_tp_mdix_ctrl) {
+			case ETH_TP_MDI:
+				mdi = MII_ATH_FUNCTION_CTRL_MDIX_MDI;
+				break;
+			case ETH_TP_MDI_X:
+				mdi = MII_ATH_FUNCTION_CTRL_MDIX_MDIX;
+				break;
+			case ETH_TP_MDI_AUTO:
+				mdi = MII_ATH_FUNCTION_CTRL_MDIX_AUTO;
+				break;
+			default:
+				return -EINVAL;
+		}
+
+		mutex_lock(&bus->mdio_lock);
+		reg = bus->read(bus, phydev->addr, MII_ATH_REG_FUNCTION_CTRL);
+		reg &= ~MII_ATH_FUNCTION_CTRL_MDIX;
+		reg |= mdi << MII_ATH_FUNCTION_CTRL_MDIX_OFFSET;
+		bus->write(bus, phydev->addr, MII_ATH_REG_FUNCTION_CTRL, reg);
+		mutex_unlock(&bus->mdio_lock);
+		ret = genphy_soft_reset(phydev);
+		if (ret)
+			return ret;
+	}
+
+	return phy_ethtool_sset(dev->phydev, cmd);
+}
+
+static int port_nway_reset(struct net_device *dev)
+{
+	if (dev->phydev)
+		return phy_start_aneg(dev->phydev);
+
+	return -EINVAL;
+}
+
+int port_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	if (!dev->phydev)
+		return -EINVAL;
+
+	return phy_mii_ioctl(dev->phydev, rq, cmd);
+}
+
+static const struct net_device_ops port_netdev_ops = {
+  .ndo_init   = port_init,
+  .ndo_uninit   = port_dummy_cb,
+  .ndo_open   = port_open,
+  .ndo_stop   = port_stop,
+  .ndo_start_xmit = port_start_xmit,
+  .ndo_tx_timeout = port_dummy_cb,
+  .ndo_set_mac_address  = eth_mac_addr,
+  .ndo_validate_addr  = eth_validate_addr,
+  .ndo_do_ioctl = port_ioctl,
+};
+
+static const struct ethtool_ops port_ethtool_ops = {
+  .get_settings = port_get_settings,
+  .set_settings = port_set_settings,
+  .get_link = ethtool_op_get_link,
+  .nway_reset = port_nway_reset,
+};
+
+void port_dev_setup(struct net_device *dev) {
+}
+
+static int
+ar7240_set_export_netdevs(struct switch_dev *dev, const struct switch_attr *attr,
+		struct switch_val *val)
+{
+	struct ar7240sw *as = sw_to_ar7240(dev);
+	bool create = !!val->value.i;
+	int i;
+	int err = 0;
+
+	if (as->export_netdev == create)
+		return 0;
+
+	if (create == as->export_netdev)
+		return 0;
+
+	if (create) {
+		for (i = 0; i < AR7240_NUM_PORTS; i++) {
+			struct net_device *netdev;
+			struct phy_device *phy;
+			struct priv_netdev_port *priv;
+
+			phy = as->mii_bus->phy_map[i];
+			if (!phy)
+				continue;
+
+			netdev = alloc_netdev_mqs(sizeof(struct priv_netdev_port), "phy%d", ether_setup, 1, 1);
+			if (!netdev) {
+				printk(KERN_ERR "can not allow netdev\n");
+				/* TODO: free and set export = 0 */
+				return -1;
+			}
+
+			SET_NETDEV_DEV(netdev, &dev->netdev->dev);
+			netdev->netdev_ops = &port_netdev_ops;
+			SET_ETHTOOL_OPS(netdev, &port_ethtool_ops);
+			eth_hw_addr_random(netdev);
+
+			priv = netdev_priv(netdev);
+			priv->netdev = netdev;
+			priv->port = i;
+
+			err = phy_connect_direct(netdev, phy, &port_dummy_cb, PHY_INTERFACE_MODE_MII);
+			if (err) {
+				printk(KERN_ERR "phy attach failed %d with err %d\n", i, err);
+				continue;
+			}
+
+			phy->supported = (PHY_BASIC_FEATURES & ~SUPPORTED_MII);
+			phy->advertising = phy->supported;
+
+
+			err = register_netdev(netdev);
+			if (err) {
+				dev_err(&dev->netdev->dev, "register netdevice failed %d\n", err);
+				/* TODO: exit */
+				return -1;
+			}
+
+			as->port_netdev[i] = netdev;
+
+			/* after creating the port phys we put them up */
+			rtnl_lock();
+			dev_open(netdev);
+			rtnl_unlock();
+		}
+
+		as->export_netdev = true;
+	} else {
+		/* disable ports */
+		struct net_device *netdev;
+		for (i = 0; i < AR7240_NUM_PORTS; i++) {
+			netdev = as->port_netdev[i];
+
+			if (!netdev)
+				continue;
+
+			unregister_netdev(netdev);
+			as->port_netdev[i] = NULL;
+		}
+		as->export_netdev = false;
+	}
+
+	return 0;
+}
+
+	static int
+ar7240_get_export_netdevs(struct switch_dev *dev, const struct switch_attr *attr,
+		struct switch_val *val)
+{
+	struct ar7240sw *as = sw_to_ar7240(dev);
+	val->value.i = as->export_netdev;
+
+	return 0;
+}
+
 static int
 ar7240_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
 		struct switch_val *val)
@@ -996,6 +1263,14 @@ static struct switch_attr ar7240_globals[] = {
 		.get = ar7240_get_vlan,
 		.max = 1
 	},
+  {
+		.type = SWITCH_TYPE_INT,
+		.name = "export_netdevs",
+		.description = "Export ports a netdev (no traffic!)",
+		.set = ar7240_set_export_netdevs,
+		.get = ar7240_get_export_netdevs,
+		.max = 1
+  }
 };
 
 static struct switch_attr ar7240_port[] = {
-- 
2.1.3
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list