#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include "tag.h"
#define MTK_NAME "mtk"
#define MTK_HDR_LEN 4
#define MTK_HDR_XMIT_UNTAGGED 0
#define MTK_HDR_XMIT_TAGGED_TPID_8100 1
#define MTK_HDR_XMIT_TAGGED_TPID_88A8 2
#define MTK_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0)
#define MTK_HDR_XMIT_DP_BIT_MASK GENMASK(5, 0)
#define MTK_HDR_XMIT_SA_DIS BIT(6)
static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct dsa_port *dp = dsa_slave_to_port(dev);
u8 xmit_tpid;
u8 *mtk_tag;
skb_set_queue_mapping(skb, dp->index);
switch (skb->protocol) {
case htons(ETH_P_8021Q):
xmit_tpid = MTK_HDR_XMIT_TAGGED_TPID_8100;
break;
case htons(ETH_P_8021AD):
xmit_tpid = MTK_HDR_XMIT_TAGGED_TPID_88A8;
break;
default:
xmit_tpid = MTK_HDR_XMIT_UNTAGGED;
skb_push(skb, MTK_HDR_LEN);
dsa_alloc_etype_header(skb, MTK_HDR_LEN);
}
mtk_tag = dsa_etype_header_pos_tx(skb);
mtk_tag[0] = xmit_tpid;
mtk_tag[1] = (1 << dp->index) & MTK_HDR_XMIT_DP_BIT_MASK;
if (xmit_tpid == MTK_HDR_XMIT_UNTAGGED) {
mtk_tag[2] = 0;
mtk_tag[3] = 0;
}
return skb;
}
static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev)
{
u16 hdr;
int port;
__be16 *phdr;
if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN)))
return NULL;
phdr = dsa_etype_header_pos_rx(skb);
hdr = ntohs(*phdr);
skb_pull_rcsum(skb, MTK_HDR_LEN);
dsa_strip_etype_header(skb, MTK_HDR_LEN);
port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK);
skb->dev = dsa_master_find_slave(dev, 0, port);
if (!skb->dev)
return NULL;
dsa_default_offload_fwd_mark(skb);
return skb;
}
static const struct dsa_device_ops mtk_netdev_ops = {
.name = MTK_NAME,
.proto = DSA_TAG_PROTO_MTK,
.xmit = mtk_tag_xmit,
.rcv = mtk_tag_rcv,
.needed_headroom = MTK_HDR_LEN,
};
MODULE_LICENSE("GPL");
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MTK, MTK_NAME);
module_dsa_tag_driver