net: dsa: add support for the SMSC-LAN9303 tagging format
authorJuergen Beisert <jbe@pengutronix.de>
Tue, 18 Apr 2017 08:48:24 +0000 (10:48 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 20 Apr 2017 17:48:54 +0000 (13:48 -0400)
To define the outgoing port and to discover the incoming port a regular
VLAN tag is used by the LAN9303. But its VID meaning is 'special'.

This tag handler/filter depends on some hardware features which must be
enabled in the device to provide and make use of this special VLAN tag
to control the destination and the source of an ethernet packet.

Signed-off-by: Juergen Borleis <jbe@pengutronix.de>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/dsa.h
net/dsa/Kconfig
net/dsa/Makefile
net/dsa/dsa.c
net/dsa/dsa_priv.h
net/dsa/tag_lan9303.c [new file with mode: 0644]

index 04c3fe93f80345ee929240865360b3ffee5ce06e..8e24677b1c62a5842f81496d7d18c071d2cf0af6 100644 (file)
@@ -33,6 +33,7 @@ enum dsa_tag_protocol {
        DSA_TAG_PROTO_BRCM,
        DSA_TAG_PROTO_QCA,
        DSA_TAG_PROTO_MTK,
+       DSA_TAG_PROTO_LAN9303,
        DSA_TAG_LAST,           /* MUST BE LAST */
 };
 
index aa21f49f12156a403086e1625a95829fdf9513f5..81a0868edb1d619a6ade13b7817db90e7a2d69a2 100644 (file)
@@ -33,4 +33,8 @@ config NET_DSA_TAG_QCA
 
 config NET_DSA_TAG_MTK
        bool
+
+config NET_DSA_TAG_LAN9303
+       bool
+
 endif
index 11a082d7e103f120e016a8033d411a87bd65ff66..0b747d75e65a265dc5059586e250a2b97ae3f067 100644 (file)
@@ -9,3 +9,4 @@ dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
 dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
 dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
 dsa_core-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
+dsa_core-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
index e117047174fc5b368d7341f7e56b515ace3a39b9..26130ae438da53f3f99a4e9fa712a572f40ca779 100644 (file)
@@ -57,6 +57,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
 #endif
 #ifdef CONFIG_NET_DSA_TAG_MTK
        [DSA_TAG_PROTO_MTK] = &mtk_netdev_ops,
+#endif
+#ifdef CONFIG_NET_DSA_TAG_LAN9303
+       [DSA_TAG_PROTO_LAN9303] = &lan9303_netdev_ops,
 #endif
        [DSA_TAG_PROTO_NONE] = &none_ops,
 };
index ab397c07880f7759372551f8a68e3109bb646054..f4a88e4852138864081c1871fc882803e53247db 100644 (file)
@@ -93,4 +93,7 @@ extern const struct dsa_device_ops qca_netdev_ops;
 /* tag_mtk.c */
 extern const struct dsa_device_ops mtk_netdev_ops;
 
+/* tag_lan9303.c */
+extern const struct dsa_device_ops lan9303_netdev_ops;
+
 #endif
diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c
new file mode 100644 (file)
index 0000000..563b6c8
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 Pengutronix, Juergen Borleis <jbe@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <net/dsa.h>
+#include "dsa_priv.h"
+
+/* To define the outgoing port and to discover the incoming port a regular
+ * VLAN tag is used by the LAN9303. But its VID meaning is 'special':
+ *
+ *       Dest MAC       Src MAC        TAG    Type
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 | 1 2 |...
+ *                                |<------->|
+ * TAG:
+ *    |<------------->|
+ *    |  1  2 | 3  4  |
+ *      TPID    VID
+ *     0x8100
+ *
+ * VID bit 3 indicates a request for an ALR lookup.
+ *
+ * If VID bit 3 is zero, then bits 0 and 1 specify the destination port
+ * (0, 1, 2) or broadcast (3) or the source port (1, 2).
+ *
+ * VID bit 4 is used to specify if the STP port state should be overridden.
+ * Required when no forwarding between the external ports should happen.
+ */
+
+#define LAN9303_TAG_LEN 4
+#define LAN9303_MAX_PORTS 3
+
+static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct dsa_slave_priv *p = netdev_priv(dev);
+       u16 *lan9303_tag;
+
+       /* insert a special VLAN tag between the MAC addresses
+        * and the current ethertype field.
+        */
+       if (skb_cow_head(skb, LAN9303_TAG_LEN) < 0) {
+               dev_dbg(&dev->dev,
+                       "Cannot make room for the special tag. Dropping packet\n");
+               goto out_free;
+       }
+
+       /* provide 'LAN9303_TAG_LEN' bytes additional space */
+       skb_push(skb, LAN9303_TAG_LEN);
+
+       /* make room between MACs and Ether-Type */
+       memmove(skb->data, skb->data + LAN9303_TAG_LEN, 2 * ETH_ALEN);
+
+       lan9303_tag = (u16 *)(skb->data + 2 * ETH_ALEN);
+       lan9303_tag[0] = htons(ETH_P_8021Q);
+       lan9303_tag[1] = htons(p->dp->index | BIT(4));
+
+       return skb;
+out_free:
+       kfree_skb(skb);
+       return NULL;
+}
+
+static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev,
+                       struct packet_type *pt, struct net_device *orig_dev)
+{
+       u16 *lan9303_tag;
+       struct dsa_switch_tree *dst = dev->dsa_ptr;
+       struct dsa_switch *ds;
+       unsigned int source_port;
+
+       if (unlikely(!dst)) {
+               dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing switch tree device\n");
+               return NULL;
+       }
+
+       ds = dst->ds[0];
+
+       if (unlikely(!ds)) {
+               dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing DSA switch device\n");
+               return NULL;
+       }
+
+       if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) {
+               dev_warn_ratelimited(&dev->dev,
+                                    "Dropping packet, cannot pull\n");
+               return NULL;
+       }
+
+       /* '->data' points into the middle of our special VLAN tag information:
+        *
+        * ~ MAC src   | 0x81 | 0x00 | 0xyy | 0xzz | ether type
+        *                           ^
+        *                        ->data
+        */
+       lan9303_tag = (u16 *)(skb->data - 2);
+
+       if (lan9303_tag[0] != htons(ETH_P_8021Q)) {
+               dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid VLAN marker\n");
+               return NULL;
+       }
+
+       source_port = ntohs(lan9303_tag[1]) & 0x3;
+
+       if (source_port >= LAN9303_MAX_PORTS) {
+               dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n");
+               return NULL;
+       }
+
+       if (!ds->ports[source_port].netdev) {
+               dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid netdev or device\n");
+               return NULL;
+       }
+
+       /* remove the special VLAN tag between the MAC addresses
+        * and the current ethertype field.
+        */
+       skb_pull_rcsum(skb, 2 + 2);
+       memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN),
+               2 * ETH_ALEN);
+
+       /* forward the packet to the dedicated interface */
+       skb->dev = ds->ports[source_port].netdev;
+
+       return skb;
+}
+
+const struct dsa_device_ops lan9303_netdev_ops = {
+       .xmit = lan9303_xmit,
+       .rcv = lan9303_rcv,
+};