diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2014-08-27 20:04:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-08-28 01:59:40 -0400 |
commit | 5037d532b83d7325a2743dffe82882a64697a8e8 (patch) | |
tree | 6e972e759c9facbcbc7952baa619e991b9fa0343 /net/dsa | |
parent | ce31b31c68e7e39f29b1257581fbd08ce3ca5589 (diff) |
net: dsa: add Broadcom tag RX/TX handler
Add support for the 4-bytes Broadcom tag that built-in switches such as
the Starfighter 2 might insert when receiving packets, or that we need
to insert while targetting specific switch ports. We use a fake local
EtherType value for this 4-bytes switch tag: ETH_P_BRCMTAG to make sure
we can assign DSA-specific network operations within the DSA drivers.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
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_priv.h | 3 | ||||
-rw-r--r-- | net/dsa/slave.c | 5 | ||||
-rw-r--r-- | net/dsa/tag_brcm.c | 173 |
5 files changed, 185 insertions, 0 deletions
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig index f5eede1d6cb8..a585fd6352eb 100644 --- a/net/dsa/Kconfig +++ b/net/dsa/Kconfig | |||
@@ -12,6 +12,9 @@ config NET_DSA | |||
12 | if NET_DSA | 12 | if NET_DSA |
13 | 13 | ||
14 | # tagging formats | 14 | # tagging formats |
15 | config NET_DSA_TAG_BRCM | ||
16 | bool | ||
17 | |||
15 | config NET_DSA_TAG_DSA | 18 | config NET_DSA_TAG_DSA |
16 | bool | 19 | bool |
17 | 20 | ||
diff --git a/net/dsa/Makefile b/net/dsa/Makefile index 7b9fcbbeda5d..da06ed1df620 100644 --- a/net/dsa/Makefile +++ b/net/dsa/Makefile | |||
@@ -3,6 +3,7 @@ obj-$(CONFIG_NET_DSA) += dsa_core.o | |||
3 | dsa_core-y += dsa.o slave.o | 3 | dsa_core-y += dsa.o slave.o |
4 | 4 | ||
5 | # tagging formats | 5 | # tagging formats |
6 | dsa_core-$(CONFIG_NET_DSA_TAG_BRCM) += tag_brcm.o | ||
6 | dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o | 7 | dsa_core-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o |
7 | dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o | 8 | dsa_core-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o |
8 | dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o | 9 | dsa_core-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o |
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index d20364ac1574..98afed4d92ba 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h | |||
@@ -57,5 +57,8 @@ extern const struct dsa_device_ops edsa_netdev_ops; | |||
57 | /* tag_trailer.c */ | 57 | /* tag_trailer.c */ |
58 | extern const struct dsa_device_ops trailer_netdev_ops; | 58 | extern const struct dsa_device_ops trailer_netdev_ops; |
59 | 59 | ||
60 | /* tag_brcm.c */ | ||
61 | extern const struct dsa_device_ops brcm_netdev_ops; | ||
62 | |||
60 | 63 | ||
61 | #endif | 64 | #endif |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 18ff53836fe3..7333a4aebb7d 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -451,6 +451,11 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent, | |||
451 | ds->dst->ops = &trailer_netdev_ops; | 451 | ds->dst->ops = &trailer_netdev_ops; |
452 | break; | 452 | break; |
453 | #endif | 453 | #endif |
454 | #ifdef CONFIG_NET_DSA_TAG_BRCM | ||
455 | case htons(ETH_P_BRCMTAG): | ||
456 | ds->dst->ops = &brcm_netdev_ops; | ||
457 | break; | ||
458 | #endif | ||
454 | default: | 459 | default: |
455 | ds->dst->ops = ¬ag_netdev_ops; | 460 | ds->dst->ops = ¬ag_netdev_ops; |
456 | break; | 461 | break; |
diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c new file mode 100644 index 000000000000..e0b759ec209e --- /dev/null +++ b/net/dsa/tag_brcm.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Broadcom tag support | ||
3 | * | ||
4 | * Copyright (C) 2014 Broadcom Corporation | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/etherdevice.h> | ||
13 | #include <linux/list.h> | ||
14 | #include <linux/netdevice.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include "dsa_priv.h" | ||
17 | |||
18 | /* This tag length is 4 bytes, older ones were 6 bytes, we do not | ||
19 | * handle them | ||
20 | */ | ||
21 | #define BRCM_TAG_LEN 4 | ||
22 | |||
23 | /* Tag is constructed and desconstructed using byte by byte access | ||
24 | * because the tag is placed after the MAC Source Address, which does | ||
25 | * not make it 4-bytes aligned, so this might cause unaligned accesses | ||
26 | * on most systems where this is used. | ||
27 | */ | ||
28 | |||
29 | /* Ingress and egress opcodes */ | ||
30 | #define BRCM_OPCODE_SHIFT 5 | ||
31 | #define BRCM_OPCODE_MASK 0x7 | ||
32 | |||
33 | /* Ingress fields */ | ||
34 | /* 1st byte in the tag */ | ||
35 | #define BRCM_IG_TC_SHIFT 2 | ||
36 | #define BRCM_IG_TC_MASK 0x7 | ||
37 | /* 2nd byte in the tag */ | ||
38 | #define BRCM_IG_TE_MASK 0x3 | ||
39 | #define BRCM_IG_TS_SHIFT 7 | ||
40 | /* 3rd byte in the tag */ | ||
41 | #define BRCM_IG_DSTMAP2_MASK 1 | ||
42 | #define BRCM_IG_DSTMAP1_MASK 0xff | ||
43 | |||
44 | /* Egress fields */ | ||
45 | |||
46 | /* 2nd byte in the tag */ | ||
47 | #define BRCM_EG_CID_MASK 0xff | ||
48 | |||
49 | /* 3rd byte in the tag */ | ||
50 | #define BRCM_EG_RC_MASK 0xff | ||
51 | #define BRCM_EG_RC_RSVD (3 << 6) | ||
52 | #define BRCM_EG_RC_EXCEPTION (1 << 5) | ||
53 | #define BRCM_EG_RC_PROT_SNOOP (1 << 4) | ||
54 | #define BRCM_EG_RC_PROT_TERM (1 << 3) | ||
55 | #define BRCM_EG_RC_SWITCH (1 << 2) | ||
56 | #define BRCM_EG_RC_MAC_LEARN (1 << 1) | ||
57 | #define BRCM_EG_RC_MIRROR (1 << 0) | ||
58 | #define BRCM_EG_TC_SHIFT 5 | ||
59 | #define BRCM_EG_TC_MASK 0x7 | ||
60 | #define BRCM_EG_PID_MASK 0x1f | ||
61 | |||
62 | static netdev_tx_t brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) | ||
63 | { | ||
64 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
65 | u8 *brcm_tag; | ||
66 | |||
67 | dev->stats.tx_packets++; | ||
68 | dev->stats.tx_bytes += skb->len; | ||
69 | |||
70 | if (skb_cow_head(skb, BRCM_TAG_LEN) < 0) | ||
71 | goto out_free; | ||
72 | |||
73 | skb_push(skb, BRCM_TAG_LEN); | ||
74 | |||
75 | memmove(skb->data, skb->data + BRCM_TAG_LEN, 2 * ETH_ALEN); | ||
76 | |||
77 | /* Build the tag after the MAC Source Address */ | ||
78 | brcm_tag = skb->data + 2 * ETH_ALEN; | ||
79 | |||
80 | /* Set the ingress opcode, traffic class, tag enforcment is | ||
81 | * deprecated | ||
82 | */ | ||
83 | brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) | | ||
84 | ((skb->priority << BRCM_IG_TC_SHIFT) & BRCM_IG_TC_MASK); | ||
85 | brcm_tag[1] = 0; | ||
86 | brcm_tag[2] = 0; | ||
87 | if (p->port == 8) | ||
88 | brcm_tag[2] = BRCM_IG_DSTMAP2_MASK; | ||
89 | brcm_tag[3] = (1 << p->port) & BRCM_IG_DSTMAP1_MASK; | ||
90 | |||
91 | /* Queue the SKB for transmission on the parent interface, but | ||
92 | * do not modify its EtherType | ||
93 | */ | ||
94 | skb->protocol = htons(ETH_P_BRCMTAG); | ||
95 | skb->dev = p->parent->dst->master_netdev; | ||
96 | dev_queue_xmit(skb); | ||
97 | |||
98 | return NETDEV_TX_OK; | ||
99 | |||
100 | out_free: | ||
101 | kfree_skb(skb); | ||
102 | return NETDEV_TX_OK; | ||
103 | } | ||
104 | |||
105 | static int brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, | ||
106 | struct packet_type *pt, struct net_device *orig_dev) | ||
107 | { | ||
108 | struct dsa_switch_tree *dst = dev->dsa_ptr; | ||
109 | struct dsa_switch *ds; | ||
110 | int source_port; | ||
111 | u8 *brcm_tag; | ||
112 | |||
113 | if (unlikely(dst == NULL)) | ||
114 | goto out_drop; | ||
115 | |||
116 | ds = dst->ds[0]; | ||
117 | |||
118 | skb = skb_unshare(skb, GFP_ATOMIC); | ||
119 | if (skb == NULL) | ||
120 | goto out; | ||
121 | |||
122 | if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) | ||
123 | goto out_drop; | ||
124 | |||
125 | /* skb->data points to the EtherType, the tag is right before it */ | ||
126 | brcm_tag = skb->data - 2; | ||
127 | |||
128 | /* The opcode should never be different than 0b000 */ | ||
129 | if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) | ||
130 | goto out_drop; | ||
131 | |||
132 | /* We should never see a reserved reason code without knowing how to | ||
133 | * handle it | ||
134 | */ | ||
135 | WARN_ON(brcm_tag[2] & BRCM_EG_RC_RSVD); | ||
136 | |||
137 | /* Locate which port this is coming from */ | ||
138 | source_port = brcm_tag[3] & BRCM_EG_PID_MASK; | ||
139 | |||
140 | /* Validate port against switch setup, either the port is totally */ | ||
141 | if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL) | ||
142 | goto out_drop; | ||
143 | |||
144 | /* Remove Broadcom tag and update checksum */ | ||
145 | skb_pull_rcsum(skb, BRCM_TAG_LEN); | ||
146 | |||
147 | /* Move the Ethernet DA and SA */ | ||
148 | memmove(skb->data - ETH_HLEN, | ||
149 | skb->data - ETH_HLEN - BRCM_TAG_LEN, | ||
150 | 2 * ETH_ALEN); | ||
151 | |||
152 | skb_push(skb, ETH_HLEN); | ||
153 | skb->pkt_type = PACKET_HOST; | ||
154 | skb->dev = ds->ports[source_port]; | ||
155 | skb->protocol = eth_type_trans(skb, skb->dev); | ||
156 | |||
157 | skb->dev->stats.rx_packets++; | ||
158 | skb->dev->stats.rx_bytes += skb->len; | ||
159 | |||
160 | netif_receive_skb(skb); | ||
161 | |||
162 | return 0; | ||
163 | |||
164 | out_drop: | ||
165 | kfree_skb(skb); | ||
166 | out: | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | const struct dsa_device_ops brcm_netdev_ops = { | ||
171 | .xmit = brcm_tag_xmit, | ||
172 | .rcv = brcm_tag_rcv, | ||
173 | }; | ||