]>
Commit | Line | Data |
---|---|---|
5cd8985a SW |
1 | /* |
2 | * Mediatek DSA Tag support | |
3 | * Copyright (C) 2017 Landen Chao <[email protected]> | |
4 | * Sean Wang <[email protected]> | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 and | |
7 | * only version 2 as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
15 | #include <linux/etherdevice.h> | |
ea5dd34b | 16 | |
5cd8985a SW |
17 | #include "dsa_priv.h" |
18 | ||
19 | #define MTK_HDR_LEN 4 | |
20 | #define MTK_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) | |
21 | #define MTK_HDR_XMIT_DP_BIT_MASK GENMASK(5, 0) | |
22 | ||
23 | static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb, | |
24 | struct net_device *dev) | |
25 | { | |
d945097b | 26 | struct dsa_port *dp = dsa_slave_to_port(dev); |
5cd8985a SW |
27 | u8 *mtk_tag; |
28 | ||
29 | if (skb_cow_head(skb, MTK_HDR_LEN) < 0) | |
fe47d563 | 30 | return NULL; |
5cd8985a SW |
31 | |
32 | skb_push(skb, MTK_HDR_LEN); | |
33 | ||
34 | memmove(skb->data, skb->data + MTK_HDR_LEN, 2 * ETH_ALEN); | |
35 | ||
36 | /* Build the tag after the MAC Source Address */ | |
37 | mtk_tag = skb->data + 2 * ETH_ALEN; | |
38 | mtk_tag[0] = 0; | |
d945097b | 39 | mtk_tag[1] = (1 << dp->index) & MTK_HDR_XMIT_DP_BIT_MASK; |
5cd8985a SW |
40 | mtk_tag[2] = 0; |
41 | mtk_tag[3] = 0; | |
42 | ||
43 | return skb; | |
5cd8985a SW |
44 | } |
45 | ||
a86d8bec | 46 | static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, |
89e49506 | 47 | struct packet_type *pt) |
5cd8985a | 48 | { |
5cd8985a SW |
49 | int port; |
50 | __be16 *phdr, hdr; | |
51 | ||
5cd8985a | 52 | if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) |
54709795 | 53 | return NULL; |
5cd8985a SW |
54 | |
55 | /* The MTK header is added by the switch between src addr | |
56 | * and ethertype at this point, skb->data points to 2 bytes | |
57 | * after src addr so header should be 2 bytes right before. | |
58 | */ | |
59 | phdr = (__be16 *)(skb->data - 2); | |
60 | hdr = ntohs(*phdr); | |
61 | ||
62 | /* Remove MTK tag and recalculate checksum. */ | |
63 | skb_pull_rcsum(skb, MTK_HDR_LEN); | |
64 | ||
65 | memmove(skb->data - ETH_HLEN, | |
66 | skb->data - ETH_HLEN - MTK_HDR_LEN, | |
67 | 2 * ETH_ALEN); | |
68 | ||
5cd8985a SW |
69 | /* Get source port information */ |
70 | port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK); | |
5cd8985a | 71 | |
2231c43b | 72 | skb->dev = dsa_master_find_slave(dev, 0, port); |
3775b1b7 VD |
73 | if (!skb->dev) |
74 | return NULL; | |
5cd8985a | 75 | |
a86d8bec | 76 | return skb; |
5cd8985a SW |
77 | } |
78 | ||
2dd592b2 JC |
79 | static int mtk_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto, |
80 | int *offset) | |
81 | { | |
82 | *offset = 4; | |
83 | *proto = ((__be16 *)skb->data)[1]; | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
5cd8985a | 88 | const struct dsa_device_ops mtk_netdev_ops = { |
2dd592b2 JC |
89 | .xmit = mtk_tag_xmit, |
90 | .rcv = mtk_tag_rcv, | |
91 | .flow_dissect = mtk_tag_flow_dissect, | |
5cd8985a | 92 | }; |