<div dir="ltr"><div><div><div><div>Fully agree on converging.<br></div>I missed Rafal's switchdev effort.<br><br></div>Before starting on this work, I first tried to look into net/dsa in the Linux kernel.<br></div>I did notice your patches there and started on that.<br></div>I got to a decent point, but then I was stuck at some issues.<br>I later found out that I needed to adjust the MTU and manually pad potential runt packets.<br><div><div><br></div><div>I also found out the switchdev API, which seems like it's in an rather infant state.<br></div><div><br></div><div>In any case, I think DSA is the better approach, so my vote is for b53 + DSA (or DSA-like) in the kernel.<br></div><div><br></div><div>For this swconfig effort, the reasoning is that our current firmware is @ kernel 3.10, and most of our current target setup logic relies on swconfig; I backported DSA, but because of time/effort constraints to also port, test & validate any b53 PHY setup logic in DSA, we decided that it's easier to adapt the current b53 driver, and later re-visit DSA.<br></div><div>And because we have time constraints, there's no telling if we'll have the time to port b53 to DSA, so this patchset is at least meant to share our effort/findings regarding the b53 + LAN ports + Broadcom tag.<br></div><div></div><div>If things get settled/more calm, I might try to do an effort to do b53 + DSA (if nobody does that before me).<br></div><div><br></div><div>Regarding this patchset, I have no objections regarding whether it gets accepted/dropped.<br></div><div>Either way, we'll keep it in our tree for a while, and re-evaluate any other DSA (or DSA-like) frameworks later.<br></div><div><div><br></div><div>Thanks<br></div><div>Alex<br></div></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Feb 26, 2015 at 8:24 PM, Florian Fainelli <span dir="ltr"><<a href="mailto:f.fainelli@gmail.com" target="_blank">f.fainelli@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On 25/02/15 07:24, Alexandru Ardelean wrote:<br>
> Feature implemented and tested on BCM53128.<br>
><br>
> Slave devices logic copied from the Linux kernel from Marvell's DSA<br>
> driver ( linux/net/dsa/ ).<br>
> Also the logic for the Broadcom tag processing has been copied from there.<br>
<br>
</span>There are different efforts here going on, and I would like to at least<br>
3 different people (you, Rafal and myself) can converge to an identical<br>
solution that fits everybody here.<br>
<br>
What net-next supports today is:<br>
<br>
- broadcom tags in net/dsa/tag_brcm.c, 4-bytes format, identical to<br>
yours AFAICT<br>
- HW bridging support for bcm_sf2 (roboswitch successor)<br>
<br>
What's missing:<br>
- adding VLAN configuration, which is what Rafal has been doing using<br>
here: <a href="http://thread.gmane.org/gmane.linux.network/351503" target="_blank">http://thread.gmane.org/gmane.linux.network/351503</a><br>
<br>
There are a number of things that I want to rework in DSA such that we<br>
can almost immediately leverage OpenWrt's switch drivers, where the<br>
entry point is a phy_driver, and have them register as switches (DSA or<br>
something wider) eventually [1], such that DSA handles the slave devices<br>
creation, and also handles the transmission/reception of Broadcom tags<br>
for us. This is work in progress, but I expect the patches would be<br>
ready by the end of this week.<br>
<br>
[1]: <a href="http://www.spinics.net/lists/netdev/msg295942.html" target="_blank">http://www.spinics.net/lists/netdev/msg295942.html</a><br>
<div class="HOEnZb"><div class="h5"><br>
><br>
> OpenWRT's eth_mangle_rx/tx() patch/code is being used to tap into<br>
> the packets to/from the ethernet chip since it's convenient.<br>
><br>
> This code will create lanX (X = 1..B53_N_PORTS) devices.<br>
> All traffic from the ethX device will be forwarded the proper lanX device.<br>
> So, sw_port0_traffic == lan1_traffic and so on.<br>
><br>
> The slave devices logic has been put into it's own file.<br>
> Should this logic be desired to be extended to swconfig or other<br>
> switch chips, it should be convenient to just move the slave.c/h files.<br>
><br>
> Note: if enable_vlan == 1, be sure to configure VLAN per lanX device<br>
>       in '/etc/config/network'<br>
><br>
> Signed-off-by: Alexandru Ardelean <<a href="mailto:ardeleanalex@gmail.com">ardeleanalex@gmail.com</a>><br>
> ---<br>
>  .../generic/files/drivers/net/phy/b53/Makefile     |   2 +-<br>
>  .../generic/files/drivers/net/phy/b53/b53_common.c |   3 +<br>
>  .../generic/files/drivers/net/phy/b53/b53_hdr.c    | 114 +++++++++++-<br>
>  .../generic/files/drivers/net/phy/b53/b53_priv.h   |   2 +<br>
>  .../generic/files/drivers/net/phy/b53/slave.c      | 196 +++++++++++++++++++++<br>
>  .../generic/files/drivers/net/phy/b53/slave.h      |  38 ++++<br>
>  6 files changed, 352 insertions(+), 3 deletions(-)<br>
>  create mode 100644 target/linux/generic/files/drivers/net/phy/b53/slave.c<br>
>  create mode 100644 target/linux/generic/files/drivers/net/phy/b53/slave.h<br>
><br>
> diff --git a/target/linux/generic/files/drivers/net/phy/b53/Makefile b/target/linux/generic/files/drivers/net/phy/b53/Makefile<br>
> index 6c809f9..c74f82b 100644<br>
> --- a/target/linux/generic/files/drivers/net/phy/b53/Makefile<br>
> +++ b/target/linux/generic/files/drivers/net/phy/b53/Makefile<br>
> @@ -1,5 +1,5 @@<br>
>  obj-$(CONFIG_B53)            += b53_common.o<br>
> -obj-$(CONFIG_B53_HDR)                += b53_hdr.o<br>
> +obj-$(CONFIG_B53_HDR)                += b53_hdr.o slave.o<br>
><br>
>  obj-$(CONFIG_B53_PHY_FIXUP)  += b53_phy_fixup.o<br>
><br>
> diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c<br>
> index 9459b22..3da9efe 100644<br>
> --- a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c<br>
> +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c<br>
> @@ -1377,6 +1377,9 @@ static int b53_global_reset_switch(struct switch_dev *dev)<br>
>       priv->enable_management = 0;<br>
>  #ifdef CONFIG_B53_HDR<br>
>       priv->enable_brcm_hdr = 0;<br>
> +     /* Call this function before the memset on the priv->ports,<br>
> +      * otherwise we may leak devices */<br>
> +     b53_unregister_netdevs(priv);<br>
>  #endif<br>
><br>
>       memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans);<br>
> diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c b/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c<br>
> index 2a562a9..8fa7929 100644<br>
> --- a/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c<br>
> +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_hdr.c<br>
> @@ -21,14 +21,32 @@<br>
><br>
>  #include "b53_regs.h"<br>
>  #include "b53_priv.h"<br>
> +#include "slave.h"<br>
><br>
>  /* This tag length is 4 bytes, older ones were 6 bytes, we do not<br>
>   * handle them<br>
>   */<br>
>  #define BRCM_HDR_LEN    4<br>
>  #define MIN_FRAME_LEN   64<br>
> +#define BRCM_IG_DSTMAP2_MASK 1<br>
> +#define BRCM_IG_DSTMAP1_MASK 0xff<br>
> +#define BRCM_EG_PID_MASK     0x1f<br>
><br>
> -static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb)<br>
> +/* Ingress and egress opcodes */<br>
> +#define BRCM_OPCODE_SHIFT   5<br>
> +<br>
> +static inline void b53_brcm_tag_set(struct sk_buff *skb, int port_idx)<br>
> +{<br>
> +     u8 *brcm_tag = skb->data + 2 * ETH_ALEN;<br>
> +<br>
> +     brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT);<br>
> +     brcm_tag[1] = 0;<br>
> +     brcm_tag[2] = (port_idx == 8) ? BRCM_IG_DSTMAP2_MASK : 0;<br>
> +     brcm_tag[3] = (1 << port_idx) & BRCM_IG_DSTMAP1_MASK;<br>
> +}<br>
> +<br>
> +static struct sk_buff *b53_mangle_tx_port(struct net_device *dev,<br>
> +                     struct sk_buff *skb, struct slave_priv* p)<br>
>  {<br>
>       if (unlikely(skb_headroom(skb) < BRCM_HDR_LEN)) {<br>
>               if (pskb_expand_head(skb, BRCM_HDR_LEN, 0, GFP_ATOMIC) < 0)<br>
> @@ -40,7 +58,20 @@ static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb<br>
>       memmove(skb->data, skb->data + BRCM_HDR_LEN, 2 * ETH_ALEN);<br>
><br>
>       /* Build the tag after the MAC Source Address */<br>
> -     memset(skb->data + 2 * ETH_ALEN, 0, BRCM_HDR_LEN);<br>
> +     if (!p) {<br>
> +             memset(skb->data + 2 * ETH_ALEN, 0, BRCM_HDR_LEN);<br>
> +     } else {<br>
> +             /* Register some TX stats for this device before<br>
> +              * passing the skb to the parent device */<br>
> +             dev->stats.tx_packets++;<br>
> +             dev->stats.tx_bytes += skb->len;<br>
> +<br>
> +             b53_brcm_tag_set(skb, p->port_idx);<br>
> +<br>
> +             skb->dev = p->parent_dev;<br>
> +             skb->protocol = htons(ETH_P_DSA);<br>
> +     }<br>
> +<br>
>  #ifdef CONFIG_B53_HDR_TX_SW_PADDING<br>
>       /* FIXME: we're doing some padding here for runt ( < 64 bytes) packets;<br>
>        *        some drivers/hw refuse to add hw padding for us after we add<br>
> @@ -64,12 +95,88 @@ out_err:<br>
>       return NULL;<br>
>  }<br>
><br>
> +static struct sk_buff *b53_mangle_tx(struct net_device *dev, struct sk_buff *skb)<br>
> +{<br>
> +     /* The b53_mangle_tx() function can get called twice now that there are<br>
> +      * slave devices: once for the lanX device, and one for the ethY device<br>
> +      * which means that the Broadcom Header would get added twice.<br>
> +      * Which is why the packet type has been marked by the slave lanX device<br>
> +      * to tell us that we've tagged this packet already. */<br>
> +     if (unlikely(skb->protocol == htons(ETH_P_DSA)))<br>
> +             return skb;<br>
> +     return b53_mangle_tx_port(dev, skb, NULL);<br>
> +}<br>
> +<br>
>  static void b53_mangle_rx(struct net_device *dev, struct sk_buff *skb)<br>
>  {<br>
> +     struct b53_device *p = dev->phy_ptr;<br>
> +     u8 *brcm_tag;<br>
> +     u8 source_port;<br>
> +<br>
> +     /* We're only interested in the 4th byte of the Broadcom Header right now */<br>
> +     brcm_tag = skb->data + (2 * ETH_ALEN) + 3;<br>
> +     source_port = *brcm_tag & BRCM_EG_PID_MASK;<br>
> +<br>
>       skb_pull(skb, BRCM_HDR_LEN);<br>
>       memmove(skb->data,<br>
>               skb->data - BRCM_HDR_LEN,<br>
>               2 * ETH_ALEN);<br>
> +<br>
> +     if ((source_port < B53_N_PORTS) && (p->ports[source_port].port_dev)) {<br>
> +             skb->dev = p->ports[source_port].port_dev;<br>
> +             skb->dev->stats.rx_packets++;<br>
> +             skb->dev->stats.rx_bytes += skb->len;<br>
> +     }<br>
> +}<br>
> +<br>
> +static void b53_register_netdevs(struct b53_device *dev)<br>
> +{<br>
> +     int i;<br>
> +     bool unlock = false;<br>
> +<br>
> +     if (!rtnl_is_locked()) {<br>
> +             rtnl_lock();<br>
> +             unlock = true;<br>
> +     }<br>
> +     b53_for_each_port(dev, i) {<br>
> +             if (is_cpu_port(dev, i))<br>
> +                     continue;<br>
> +             if (!(dev->enabled_ports & BIT(i))) {<br>
> +                     if (dev->ports[i].port_dev) {<br>
> +                             unregister_netdevice(dev->ports[i].port_dev);<br>
> +                             dev->ports[i].port_dev = NULL;<br>
> +                     }<br>
> +                     continue;<br>
> +             }<br>
> +             /* Check if devices already exist for these devices */<br>
> +             if (!dev->ports[i].port_dev &&<br>
> +                 !(dev->ports[i].port_dev = slave_create(dev->eth_dev, i, b53_mangle_tx_port))) {<br>
> +                     pr_warn("%s: can't create slave device for port %d\n",<br>
> +                             dev-><a href="http://sw_dev.name" target="_blank">sw_dev.name</a>, i);<br>
> +                     continue;<br>
> +             }<br>
> +     }<br>
> +     if (unlock)<br>
> +             rtnl_unlock();<br>
> +}<br>
> +<br>
> +void b53_unregister_netdevs(struct b53_device *dev)<br>
> +{<br>
> +     int i;<br>
> +     bool unlock = false;<br>
> +<br>
> +     if (!rtnl_is_locked()) {<br>
> +             rtnl_lock();<br>
> +             unlock = true;<br>
> +     }<br>
> +     b53_for_each_port(dev, i) {<br>
> +             if (!dev->ports[i].port_dev)<br>
> +                     continue;<br>
> +             unregister_netdevice(dev->ports[i].port_dev);<br>
> +             dev->ports[i].port_dev = NULL;<br>
> +     }<br>
> +     if (unlock)<br>
> +             rtnl_unlock();<br>
>  }<br>
><br>
>  void b53_enable_brcm_hdr(struct b53_device *dev)<br>
> @@ -86,6 +193,7 @@ void b53_enable_brcm_hdr(struct b53_device *dev)<br>
>       }<br>
>       brcm_hdr_ctrl &= ~B53_BRCM_HDR_EN;<br>
>       if (!dev->eth_dev) {<br>
> +             b53_unregister_netdevs(dev);<br>
>               goto out;<br>
>       }<br>
><br>
> @@ -93,6 +201,8 @@ void b53_enable_brcm_hdr(struct b53_device *dev)<br>
>       if (!dev->enable_management)<br>
>               goto out;<br>
><br>
> +     pr_info("%s: registering lan devices\n", dev-><a href="http://sw_dev.name" target="_blank">sw_dev.name</a>);<br>
> +     b53_register_netdevs(dev);<br>
>       dev->eth_dev->phy_ptr = dev;<br>
>       dev->eth_dev->priv_flags |= IFF_NO_IP_ALIGN;<br>
>       dev->eth_dev->eth_mangle_rx = b53_mangle_rx;<br>
> diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h<br>
> index f487bf2..167e042 100644<br>
> --- a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h<br>
> +++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h<br>
> @@ -359,8 +359,10 @@ int b53_global_get_brcm_hdr(struct switch_dev *dev,<br>
>  int b53_global_set_brcm_hdr(struct switch_dev *dev,<br>
>               const struct switch_attr *attr,<br>
>               struct switch_val *val);<br>
> +void b53_unregister_netdevs(struct b53_device *dev);<br>
>  #else<br>
>  #define b53_enable_brcm_hdr(x)<br>
> +#define b53_unregister_netdevs(x)<br>
>  #endif<br>
><br>
>  #ifdef CONFIG_BCM47XX<br>
> diff --git a/target/linux/generic/files/drivers/net/phy/b53/slave.c b/target/linux/generic/files/drivers/net/phy/b53/slave.c<br>
> new file mode 100644<br>
> index 0000000..b8cd2dc<br>
> --- /dev/null<br>
> +++ b/target/linux/generic/files/drivers/net/phy/b53/slave.c<br>
> @@ -0,0 +1,196 @@<br>
> +/*<br>
> + * Slave devices logic.<br>
> + * Adapted/copied from the Linux kernel DSA driver (net/dsa).<br>
> + *<br>
> + * Permission to use, copy, modify, and/or distribute this software for any<br>
> + * purpose with or without fee is hereby granted, provided that the above<br>
> + * copyright notice and this permission notice appear in all copies.<br>
> + *<br>
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES<br>
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF<br>
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR<br>
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES<br>
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN<br>
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF<br>
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.<br>
> + */<br>
> +<br>
> +#include <linux/rtnetlink.h><br>
> +#include <linux/etherdevice.h><br>
> +<br>
> +#include "slave.h"<br>
> +<br>
> +static int slave_init(struct net_device *dev)<br>
> +{<br>
> +     struct slave_priv *p = netdev_priv(dev);<br>
> +     dev->iflink = p->parent_dev->ifindex;<br>
> +     return 0;<br>
> +}<br>
> +<br>
> +static int slave_open(struct net_device *dev)<br>
> +{<br>
> +     struct slave_priv *p = netdev_priv(dev);<br>
> +     struct net_device *parent = p->parent_dev;<br>
> +     int err;<br>
> +<br>
> +     if (!(parent->flags & IFF_UP))<br>
> +             return -ENETDOWN;<br>
> +<br>
> +     if (!ether_addr_equal(dev->dev_addr, parent->dev_addr)) {<br>
> +             err = dev_uc_add(parent, dev->dev_addr);<br>
> +             if (err < 0)<br>
> +                     goto out;<br>
> +     }<br>
> +<br>
> +     if (dev->flags & IFF_ALLMULTI) {<br>
> +             err = dev_set_allmulti(parent, 1);<br>
> +             if (err < 0)<br>
> +                     goto del_unicast;<br>
> +     }<br>
> +     if (dev->flags & IFF_PROMISC) {<br>
> +             err = dev_set_promiscuity(parent, 1);<br>
> +             if (err < 0)<br>
> +                     goto clear_allmulti;<br>
> +     }<br>
> +<br>
> +     return 0;<br>
> +<br>
> +clear_allmulti:<br>
> +     if (dev->flags & IFF_ALLMULTI)<br>
> +             dev_set_allmulti(parent, -1);<br>
> +del_unicast:<br>
> +     if (!ether_addr_equal(dev->dev_addr, parent->dev_addr))<br>
> +             dev_uc_del(parent, dev->dev_addr);<br>
> +out:<br>
> +     return err;<br>
> +}<br>
> +<br>
> +static int slave_close(struct net_device *dev)<br>
> +{<br>
> +     struct slave_priv *p = netdev_priv(dev);<br>
> +     struct net_device *parent = p->parent_dev;<br>
> +<br>
> +     dev_mc_unsync(parent, dev);<br>
> +     dev_uc_unsync(parent, dev);<br>
> +     if (dev->flags & IFF_ALLMULTI)<br>
> +             dev_set_allmulti(parent, -1);<br>
> +     if (dev->flags & IFF_PROMISC)<br>
> +             dev_set_promiscuity(parent, -1);<br>
> +<br>
> +     if (!ether_addr_equal(dev->dev_addr, parent->dev_addr))<br>
> +             dev_uc_del(parent, dev->dev_addr);<br>
> +<br>
> +     return 0;<br>
> +}<br>
> +<br>
> +static void slave_change_rx_flags(struct net_device *dev, int change)<br>
> +{<br>
> +    struct slave_priv *p = netdev_priv(dev);<br>
> +     struct net_device *parent = p->parent_dev;<br>
> +<br>
> +     if (change & IFF_ALLMULTI)<br>
> +             dev_set_allmulti(parent, dev->flags & IFF_ALLMULTI ? 1 : -1);<br>
> +     if (change & IFF_PROMISC)<br>
> +             dev_set_promiscuity(parent, dev->flags & IFF_PROMISC ? 1 : -1);<br>
> +}<br>
> +<br>
> +static void slave_set_rx_mode(struct net_device *dev)<br>
> +{<br>
> +     struct slave_priv *p = netdev_priv(dev);<br>
> +     struct net_device *parent = p->parent_dev;<br>
> +<br>
> +     dev_mc_sync(parent, dev);<br>
> +     dev_uc_sync(parent, dev);<br>
> +}<br>
> +<br>
> +static int slave_set_mac_address(struct net_device *dev, void *a)<br>
> +{<br>
> +     struct slave_priv *p = netdev_priv(dev);<br>
> +     struct net_device *parent = p->parent_dev;<br>
> +     struct sockaddr *addr = a;<br>
> +     int err;<br>
> +<br>
> +     if (!is_valid_ether_addr(addr->sa_data))<br>
> +             return -EADDRNOTAVAIL;<br>
> +<br>
> +     if (!(dev->flags & IFF_UP))<br>
> +             goto out;<br>
> +<br>
> +     if (!ether_addr_equal(addr->sa_data, parent->dev_addr)) {<br>
> +             err = dev_uc_add(parent, addr->sa_data);<br>
> +             if (err < 0)<br>
> +                     return err;<br>
> +     }<br>
> +<br>
> +     if (!ether_addr_equal(dev->dev_addr, parent->dev_addr))<br>
> +             dev_uc_del(parent, dev->dev_addr);<br>
> +<br>
> +out:<br>
> +     memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);<br>
> +<br>
> +     return 0;<br>
> +}<br>
> +<br>
> +static netdev_tx_t slave_xmit(struct sk_buff *skb, struct net_device *dev)<br>
> +{<br>
> +     struct slave_priv *p = netdev_priv(dev);<br>
> +<br>
> +     skb->dev = p->parent_dev;<br>
> +     skb = p->eth_mangle_tx_port(dev, skb, p);<br>
> +     dev_queue_xmit(skb);<br>
> +<br>
> +     return NETDEV_TX_OK;<br>
> +}<br>
> +<br>
> +static const struct net_device_ops slave_netdev_ops = {<br>
> +     .ndo_init               = slave_init,<br>
> +     .ndo_open               = slave_open,<br>
> +     .ndo_stop               = slave_close,<br>
> +     .ndo_start_xmit         = slave_xmit,<br>
> +     .ndo_change_rx_flags    = slave_change_rx_flags,<br>
> +     .ndo_set_rx_mode        = slave_set_rx_mode,<br>
> +     .ndo_set_mac_address    = slave_set_mac_address,<br>
> +};<br>
> +<br>
> +struct net_device *slave_create(struct net_device *parent, int port,<br>
> +             eth_mangle_tx_port_t eth_mangle_tx_port)<br>
> +{<br>
> +     /* Parent dev should not be null according to the code path below */<br>
> +     struct net_device *slave_dev = NULL;<br>
> +     struct slave_priv *p;<br>
> +     char port_name[] = "lan1000";<br>
> +<br>
> +     snprintf(port_name, sizeof(port_name), "lan%d", (port + 1));<br>
> +     slave_dev = alloc_netdev(sizeof(struct slave_priv),<br>
> +                 port_name, ether_setup);<br>
> +     if (!slave_dev) {<br>
> +             pr_err("Failed to allocate memory for slave device");<br>
> +             goto out;<br>
> +     }<br>
> +<br>
> +     slave_dev->features = parent->vlan_features;<br>
> +     eth_hw_addr_inherit(slave_dev, parent);<br>
> +     slave_dev->tx_queue_len = 0;<br>
> +     slave_dev->netdev_ops = &slave_netdev_ops;<br>
> +<br>
> +     SET_NETDEV_DEV(slave_dev, &parent->dev);<br>
> +     slave_dev->vlan_features = parent->vlan_features;<br>
> +<br>
> +     p = netdev_priv(slave_dev);<br>
> +     p->parent_dev = parent;<br>
> +     p->port_dev   = slave_dev;<br>
> +     p->port_idx   = port;<br>
> +     p->eth_mangle_tx_port = eth_mangle_tx_port;<br>
> +<br>
> +     if (register_netdevice(slave_dev)) {<br>
> +             free_netdev(slave_dev);<br>
> +             slave_dev = NULL;<br>
> +             goto out;<br>
> +     }<br>
> +<br>
> +     netif_carrier_on(slave_dev);<br>
> +<br>
> +out:<br>
> +     return slave_dev;<br>
> +}<br>
> +<br>
> diff --git a/target/linux/generic/files/drivers/net/phy/b53/slave.h b/target/linux/generic/files/drivers/net/phy/b53/slave.h<br>
> new file mode 100644<br>
> index 0000000..3cfe7c7<br>
> --- /dev/null<br>
> +++ b/target/linux/generic/files/drivers/net/phy/b53/slave.h<br>
> @@ -0,0 +1,38 @@<br>
> +/*<br>
> + * Slave devices logic.<br>
> + * Adapted/copied from the Linux kernel DSA driver (net/dsa).<br>
> + *<br>
> + * Permission to use, copy, modify, and/or distribute this software for any<br>
> + * purpose with or without fee is hereby granted, provided that the above<br>
> + * copyright notice and this permission notice appear in all copies.<br>
> + *<br>
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES<br>
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF<br>
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR<br>
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES<br>
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN<br>
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF<br>
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.<br>
> + */<br>
> +<br>
> +#ifndef __SLAVE_H__<br>
> +#define __SLAVE_H__<br>
> +<br>
> +#include <linux/etherdevice.h><br>
> +<br>
> +struct slave_priv;<br>
> +typedef struct sk_buff *(*eth_mangle_tx_port_t)(struct net_device *dev,<br>
> +             struct sk_buff *skb, struct slave_priv* p);<br>
> +<br>
> +struct slave_priv {<br>
> +     struct net_device *parent_dev;<br>
> +     struct net_device *port_dev;<br>
> +     eth_mangle_tx_port_t eth_mangle_tx_port;<br>
> +     int port_idx;<br>
> +};<br>
> +<br>
> +/* For the moment, the rtnl_lock() needs to be called by the caller */<br>
> +struct net_device *slave_create(struct net_device *parent, int port,<br>
> +             eth_mangle_tx_port_t eth_mangle_tx_port);<br>
> +<br>
> +#endif /* __SLAVE_H__ */<br>
><br>
<br>
<br>
--<br>
</div></div><span class="HOEnZb"><font color="#888888">Florian<br>
</font></span></blockquote></div><br></div></div>