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
[email protected]
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

Reply via email to