@@ -40,6 +40,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MTK,
DSA_TAG_PROTO_QCA,
DSA_TAG_PROTO_TRAILER,
+ DSA_TAG_PROTO_DADDR,
DSA_TAG_LAST, /* MUST BE LAST */
};
@@ -32,6 +32,9 @@ config NET_DSA_TAG_BRCM
config NET_DSA_TAG_BRCM_PREPEND
bool
+config NET_DSA_TAG_DADDR
+ bool "Support for DADDR tagging, found on Marvell 88e6065"
+
config NET_DSA_TAG_DSA
bool
@@ -7,6 +7,7 @@ dsa_core-$(CONFIG_NET_DSA_LEGACY) += legacy.o
# tagging formats
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o
dsa_core-$(CONFIG_NET_DSA_TAG_BRCM_PREPEND) += tag_brcm.o
+dsa_core-$(CONFIG_NET_DSA_TAG_DADDR) += tag_daddr.o
dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
dsa_core-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
@@ -46,6 +46,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = {
#ifdef CONFIG_NET_DSA_TAG_BRCM_PREPEND
[DSA_TAG_PROTO_BRCM_PREPEND] = &brcm_prepend_netdev_ops,
#endif
+#ifdef CONFIG_NET_DSA_TAG_DADDR
+ [DSA_TAG_PROTO_DADDR] = &daddr_netdev_ops,
+#endif
#ifdef CONFIG_NET_DSA_TAG_DSA
[DSA_TAG_PROTO_DSA] = &dsa_netdev_ops,
#endif
@@ -199,6 +199,9 @@ void dsa_switch_unregister_notifier(struct dsa_switch *ds);
extern const struct dsa_device_ops brcm_netdev_ops;
extern const struct dsa_device_ops brcm_prepend_netdev_ops;
+/* tag_daddr.c */
+extern const struct dsa_device_ops daddr_netdev_ops;
+
/* tag_dsa.c */
extern const struct dsa_device_ops dsa_netdev_ops;
new file mode 100644
@@ -0,0 +1,100 @@
+/*
+ * net/dsa/tag_daddr.c - Daddr tag format handling
+ * Copyright (c) 2018 Pavel Machek <pavel@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include "dsa_priv.h"
+
+static struct sk_buff *daddr_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct sk_buff *nskb;
+ int padlen;
+ u8 *daddr;
+
+ padlen = 0;
+ if (skb->len < 60)
+ padlen = 60 - skb->len;
+
+ nskb = alloc_skb(NET_IP_ALIGN + skb->len + padlen + 2, GFP_ATOMIC);
+ if (!nskb)
+ return NULL;
+ skb_reserve(nskb, NET_IP_ALIGN);
+
+ skb_reset_mac_header(nskb);
+ skb_set_network_header(nskb, skb_network_header(skb) - skb->head);
+ skb_set_transport_header(nskb, skb_transport_header(skb) - skb->head);
+ skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len));
+ consume_skb(skb);
+
+ if (padlen) {
+ skb_put_zero(nskb, padlen);
+ }
+
+ daddr = skb_push(nskb, 2);
+
+ nskb->data[0] = 0xff; /* dbnum << 4, 0x0e -- priority, 0x01 forcemap */
+ nskb->data[1] = 0xff; /* 0xc0 dbnum, 0x3f vlantable */
+
+ /* Hm. Seems same as PORT_VLAN_MAP value, with funny endian */
+ {
+#define PORT_VLAN_MAP_DBNUM_SHIFT 12
+
+ struct dsa_port *dp = dsa_slave_to_port(dev);
+ struct dsa_switch *ds = dp->ds;
+ int p = dp->index;
+
+ u16 vlan_map = ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) |
+ (dsa_is_cpu_port(ds, p) ? dsa_user_ports(ds) :
+ BIT(p));
+
+ nskb->data[0] = vlan_map >> 8;
+ nskb->data[1] = vlan_map & 0xff;
+ }
+
+ return nskb;
+}
+
+#define DADDR_HDR_LEN 2
+
+static struct sk_buff *daddr_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt)
+{
+ u8 ver;
+ int port;
+ u8 *phdr;
+
+ if (unlikely(!pskb_may_pull(skb, DADDR_HDR_LEN)))
+ return NULL;
+
+ /*
+ * At this point, skb->data points to ethertype.
+ */
+ phdr = (skb->data - 2 - 2 * ETH_ALEN);
+
+ /* Remove DADDR tag and recalculate checksum */
+ skb_pull_rcsum(skb, DADDR_HDR_LEN);
+
+ /* Get source port information */
+ port = phdr[1] & 0x07;
+
+ skb->dev = dsa_master_find_slave(dev, 0, port);
+ if (!skb->dev)
+ return NULL;
+
+ return skb;
+}
+
+const struct dsa_device_ops daddr_netdev_ops = {
+ .xmit = daddr_xmit,
+ .rcv = daddr_rcv,
+};
Tagging scheme used by 88e6065 is "interesting" as it moves around ethernet addreses and forces us to use PROMISC mode on the ethernets. I'm not sure how to call it, so I selected tag_daddr ("tag where destination address should be"). Signed-off-by: Pavel Machek <pavel@ucw.cz>