diff options
author | John Crispin <john@phrozen.org> | 2016-09-15 10:26:40 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-09-16 04:31:51 -0400 |
commit | cafdc45c949b9963cbfb8fe3a68d0ab16b0208ce (patch) | |
tree | ac9827ee9762a5edd72c25dc54c9e39fd1ad94c4 /net/dsa | |
parent | e5dcad290a7c62d1c856269dbd13e470e388b704 (diff) |
net-next: dsa: add Qualcomm tag RX/TX handler
Add support for the 2-bytes Qualcomm tag that gigabit switches such as
the QCA8337/N might insert when receiving packets, or that we need
to insert while targeting specific switch ports. The tag is inserted
directly behind the ethernet header.
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa')
-rw-r--r-- | net/dsa/Kconfig | 3 | ||||
-rw-r--r-- | net/dsa/Makefile | 1 | ||||
-rw-r--r-- | net/dsa/dsa.c | 3 | ||||
-rw-r--r-- | net/dsa/dsa_priv.h | 2 | ||||
-rw-r--r-- | net/dsa/tag_qca.c | 138 |
5 files changed, 147 insertions, 0 deletions
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index ff7736f7ff42..96e47c539bee 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig | |||
@@ -38,4 +38,7 @@ config NET_DSA_TAG_EDSA | |||
38 | config NET_DSA_TAG_TRAILER | 38 | config NET_DSA_TAG_TRAILER |
39 | bool | 39 | bool |
40 | 40 | ||
41 | config NET_DSA_TAG_QCA | ||
42 | bool | ||
43 | |||
41 | endif | 44 | endif |
diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 8af4ded70f1c..a3380ed0e0be 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile | |||
@@ -7,3 +7,4 @@ dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o | |||
7 | dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o | 7 | dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o |
8 | dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o | 8 | dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o |
9 | dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o | 9 | dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o |
10 | dsa_core-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o | ||
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index d8d267e9a872..66e31acfcad8 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c | |||
@@ -54,6 +54,9 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { | |||
54 | #ifdef CONFIG_NET_DSA_TAG_BRCM | 54 | #ifdef CONFIG_NET_DSA_TAG_BRCM |
55 | [DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops, | 55 | [DSA_TAG_PROTO_BRCM] = &brcm_netdev_ops, |
56 | #endif | 56 | #endif |
57 | #ifdef CONFIG_NET_DSA_TAG_QCA | ||
58 | [DSA_TAG_PROTO_QCA] = &qca_netdev_ops, | ||
59 | #endif | ||
57 | [DSA_TAG_PROTO_NONE] = &none_ops, | 60 | [DSA_TAG_PROTO_NONE] = &none_ops, |
58 | }; | 61 | }; |
59 | 62 | ||
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 00077a9c97f4..6cfd7388834e 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h | |||
@@ -81,5 +81,7 @@ extern const struct dsa_device_ops trailer_netdev_ops; | |||
81 | /* tag_brcm.c */ | 81 | /* tag_brcm.c */ |
82 | extern const struct dsa_device_ops brcm_netdev_ops; | 82 | extern const struct dsa_device_ops brcm_netdev_ops; |
83 | 83 | ||
84 | /* tag_qca.c */ | ||
85 | extern const struct dsa_device_ops qca_netdev_ops; | ||
84 | 86 | ||
85 | #endif | 87 | #endif |
diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c new file mode 100644 index 000000000000..0c90cacee7aa --- /dev/null +++ b/net/dsa/tag_qca.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/etherdevice.h> | ||
15 | #include "dsa_priv.h" | ||
16 | |||
17 | #define QCA_HDR_LEN 2 | ||
18 | #define QCA_HDR_VERSION 0x2 | ||
19 | |||
20 | #define QCA_HDR_RECV_VERSION_MASK GENMASK(15, 14) | ||
21 | #define QCA_HDR_RECV_VERSION_S 14 | ||
22 | #define QCA_HDR_RECV_PRIORITY_MASK GENMASK(13, 11) | ||
23 | #define QCA_HDR_RECV_PRIORITY_S 11 | ||
24 | #define QCA_HDR_RECV_TYPE_MASK GENMASK(10, 6) | ||
25 | #define QCA_HDR_RECV_TYPE_S 6 | ||
26 | #define QCA_HDR_RECV_FRAME_IS_TAGGED BIT(3) | ||
27 | #define QCA_HDR_RECV_SOURCE_PORT_MASK GENMASK(2, 0) | ||
28 | |||
29 | #define QCA_HDR_XMIT_VERSION_MASK GENMASK(15, 14) | ||
30 | #define QCA_HDR_XMIT_VERSION_S 14 | ||
31 | #define QCA_HDR_XMIT_PRIORITY_MASK GENMASK(13, 11) | ||
32 | #define QCA_HDR_XMIT_PRIORITY_S 11 | ||
33 | #define QCA_HDR_XMIT_CONTROL_MASK GENMASK(10, 8) | ||
34 | #define QCA_HDR_XMIT_CONTROL_S 8 | ||
35 | #define QCA_HDR_XMIT_FROM_CPU BIT(7) | ||
36 | #define QCA_HDR_XMIT_DP_BIT_MASK GENMASK(6, 0) | ||
37 | |||
38 | static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) | ||
39 | { | ||
40 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
41 | u16 *phdr, hdr; | ||
42 | |||
43 | dev->stats.tx_packets++; | ||
44 | dev->stats.tx_bytes += skb->len; | ||
45 | |||
46 | if (skb_cow_head(skb, 0) < 0) | ||
47 | goto out_free; | ||
48 | |||
49 | skb_push(skb, QCA_HDR_LEN); | ||
50 | |||
51 | memmove(skb->data, skb->data + QCA_HDR_LEN, 2 * ETH_ALEN); | ||
52 | phdr = (u16 *)(skb->data + 2 * ETH_ALEN); | ||
53 | |||
54 | /* Set the version field, and set destination port information */ | ||
55 | hdr = QCA_HDR_VERSION << QCA_HDR_XMIT_VERSION_S | | ||
56 | QCA_HDR_XMIT_FROM_CPU | | ||
57 | BIT(p->port); | ||
58 | |||
59 | *phdr = htons(hdr); | ||
60 | |||
61 | return skb; | ||
62 | |||
63 | out_free: | ||
64 | kfree_skb(skb); | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | static int qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, | ||
69 | struct packet_type *pt, struct net_device *orig_dev) | ||
70 | { | ||
71 | struct dsa_switch_tree *dst = dev->dsa_ptr; | ||
72 | struct dsa_switch *ds; | ||
73 | u8 ver; | ||
74 | int port; | ||
75 | __be16 *phdr, hdr; | ||
76 | |||
77 | if (unlikely(!dst)) | ||
78 | goto out_drop; | ||
79 | |||
80 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
81 | if (!skb) | ||
82 | goto out; | ||
83 | |||
84 | if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) | ||
85 | goto out_drop; | ||
86 | |||
87 | /* The QCA header is added by the switch between src addr and Ethertype | ||
88 | * At this point, skb->data points to ethertype so header should be | ||
89 | * right before | ||
90 | */ | ||
91 | phdr = (__be16 *)(skb->data - 2); | ||
92 | hdr = ntohs(*phdr); | ||
93 | |||
94 | /* Make sure the version is correct */ | ||
95 | ver = (hdr & QCA_HDR_RECV_VERSION_MASK) >> QCA_HDR_RECV_VERSION_S; | ||
96 | if (unlikely(ver != QCA_HDR_VERSION)) | ||
97 | goto out_drop; | ||
98 | |||
99 | /* Remove QCA tag and recalculate checksum */ | ||
100 | skb_pull_rcsum(skb, QCA_HDR_LEN); | ||
101 | memmove(skb->data - ETH_HLEN, skb->data - ETH_HLEN - QCA_HDR_LEN, | ||
102 | ETH_HLEN - QCA_HDR_LEN); | ||
103 | |||
104 | /* This protocol doesn't support cascading multiple switches so it's | ||
105 | * safe to assume the switch is first in the tree | ||
106 | */ | ||
107 | ds = dst->ds[0]; | ||
108 | if (!ds) | ||
109 | goto out_drop; | ||
110 | |||
111 | /* Get source port information */ | ||
112 | port = (hdr & QCA_HDR_RECV_SOURCE_PORT_MASK); | ||
113 | if (!ds->ports[port].netdev) | ||
114 | goto out_drop; | ||
115 | |||
116 | /* Update skb & forward the frame accordingly */ | ||
117 | skb_push(skb, ETH_HLEN); | ||
118 | skb->pkt_type = PACKET_HOST; | ||
119 | skb->dev = ds->ports[port].netdev; | ||
120 | skb->protocol = eth_type_trans(skb, skb->dev); | ||
121 | |||
122 | skb->dev->stats.rx_packets++; | ||
123 | skb->dev->stats.rx_bytes += skb->len; | ||
124 | |||
125 | netif_receive_skb(skb); | ||
126 | |||
127 | return 0; | ||
128 | |||
129 | out_drop: | ||
130 | kfree_skb(skb); | ||
131 | out: | ||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | const struct dsa_device_ops qca_netdev_ops = { | ||
136 | .xmit = qca_tag_xmit, | ||
137 | .rcv = qca_tag_rcv, | ||
138 | }; | ||