]>
Commit | Line | Data |
---|---|---|
5037d532 FF |
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> | |
5037d532 | 14 | #include <linux/slab.h> |
ea5dd34b | 15 | |
5037d532 FF |
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 | ||
f7c39e3d FF |
62 | static struct sk_buff *brcm_tag_xmit_ll(struct sk_buff *skb, |
63 | struct net_device *dev, | |
64 | unsigned int offset) | |
5037d532 | 65 | { |
d945097b | 66 | struct dsa_port *dp = dsa_slave_to_port(dev); |
0f15b098 | 67 | u16 queue = skb_get_queue_mapping(skb); |
5037d532 FF |
68 | u8 *brcm_tag; |
69 | ||
5037d532 | 70 | if (skb_cow_head(skb, BRCM_TAG_LEN) < 0) |
fe47d563 | 71 | return NULL; |
5037d532 | 72 | |
bf08c340 FF |
73 | /* The Ethernet switch we are interfaced with needs packets to be at |
74 | * least 64 bytes (including FCS) otherwise they will be discarded when | |
75 | * they enter the switch port logic. When Broadcom tags are enabled, we | |
76 | * need to make sure that packets are at least 68 bytes | |
77 | * (including FCS and tag) because the length verification is done after | |
78 | * the Broadcom tag is stripped off the ingress packet. | |
79 | * | |
80 | * Let dsa_slave_xmit() free the SKB | |
81 | */ | |
82 | if (__skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN, false)) | |
83 | return NULL; | |
84 | ||
5037d532 FF |
85 | skb_push(skb, BRCM_TAG_LEN); |
86 | ||
f7c39e3d FF |
87 | if (offset) |
88 | memmove(skb->data, skb->data + BRCM_TAG_LEN, offset); | |
5037d532 | 89 | |
f7c39e3d | 90 | brcm_tag = skb->data + offset; |
5037d532 FF |
91 | |
92 | /* Set the ingress opcode, traffic class, tag enforcment is | |
93 | * deprecated | |
94 | */ | |
95 | brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) | | |
0f15b098 | 96 | ((queue & BRCM_IG_TC_MASK) << BRCM_IG_TC_SHIFT); |
5037d532 FF |
97 | brcm_tag[1] = 0; |
98 | brcm_tag[2] = 0; | |
d945097b | 99 | if (dp->index == 8) |
5037d532 | 100 | brcm_tag[2] = BRCM_IG_DSTMAP2_MASK; |
d945097b | 101 | brcm_tag[3] = (1 << dp->index) & BRCM_IG_DSTMAP1_MASK; |
5037d532 | 102 | |
0a5f14ce FF |
103 | /* Now tell the master network device about the desired output queue |
104 | * as well | |
105 | */ | |
d945097b | 106 | skb_set_queue_mapping(skb, BRCM_TAG_SET_PORT_QUEUE(dp->index, queue)); |
0a5f14ce | 107 | |
4ed70ce9 | 108 | return skb; |
5037d532 FF |
109 | } |
110 | ||
f7c39e3d FF |
111 | static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb, |
112 | struct net_device *dev, | |
113 | struct packet_type *pt, | |
114 | unsigned int offset) | |
5037d532 | 115 | { |
5037d532 FF |
116 | int source_port; |
117 | u8 *brcm_tag; | |
118 | ||
5037d532 | 119 | if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) |
54709795 | 120 | return NULL; |
5037d532 | 121 | |
f7c39e3d | 122 | brcm_tag = skb->data - offset; |
5037d532 FF |
123 | |
124 | /* The opcode should never be different than 0b000 */ | |
125 | if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) | |
54709795 | 126 | return NULL; |
5037d532 FF |
127 | |
128 | /* We should never see a reserved reason code without knowing how to | |
129 | * handle it | |
130 | */ | |
82272db8 | 131 | if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD)) |
54709795 | 132 | return NULL; |
5037d532 FF |
133 | |
134 | /* Locate which port this is coming from */ | |
135 | source_port = brcm_tag[3] & BRCM_EG_PID_MASK; | |
136 | ||
2231c43b | 137 | skb->dev = dsa_master_find_slave(dev, 0, source_port); |
3775b1b7 | 138 | if (!skb->dev) |
54709795 | 139 | return NULL; |
5037d532 FF |
140 | |
141 | /* Remove Broadcom tag and update checksum */ | |
142 | skb_pull_rcsum(skb, BRCM_TAG_LEN); | |
143 | ||
f7c39e3d FF |
144 | return skb; |
145 | } | |
146 | ||
b74b70c4 FF |
147 | #ifdef CONFIG_NET_DSA_TAG_BRCM |
148 | static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, | |
149 | struct net_device *dev) | |
150 | { | |
151 | /* Build the tag after the MAC Source Address */ | |
152 | return brcm_tag_xmit_ll(skb, dev, 2 * ETH_ALEN); | |
153 | } | |
154 | ||
155 | ||
f7c39e3d FF |
156 | static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, |
157 | struct packet_type *pt) | |
158 | { | |
159 | struct sk_buff *nskb; | |
160 | ||
161 | /* skb->data points to the EtherType, the tag is right before it */ | |
162 | nskb = brcm_tag_rcv_ll(skb, dev, pt, 2); | |
163 | if (!nskb) | |
164 | return nskb; | |
165 | ||
5037d532 | 166 | /* Move the Ethernet DA and SA */ |
f7c39e3d FF |
167 | memmove(nskb->data - ETH_HLEN, |
168 | nskb->data - ETH_HLEN - BRCM_TAG_LEN, | |
5037d532 FF |
169 | 2 * ETH_ALEN); |
170 | ||
f7c39e3d | 171 | return nskb; |
5037d532 FF |
172 | } |
173 | ||
174 | const struct dsa_device_ops brcm_netdev_ops = { | |
175 | .xmit = brcm_tag_xmit, | |
176 | .rcv = brcm_tag_rcv, | |
177 | }; | |
b74b70c4 FF |
178 | #endif |
179 | ||
180 | #ifdef CONFIG_NET_DSA_TAG_BRCM_PREPEND | |
181 | static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb, | |
182 | struct net_device *dev) | |
183 | { | |
184 | /* tag is prepended to the packet */ | |
185 | return brcm_tag_xmit_ll(skb, dev, 0); | |
186 | } | |
187 | ||
188 | static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb, | |
189 | struct net_device *dev, | |
190 | struct packet_type *pt) | |
191 | { | |
192 | /* tag is prepended to the packet */ | |
193 | return brcm_tag_rcv_ll(skb, dev, pt, ETH_HLEN); | |
194 | } | |
195 | ||
196 | const struct dsa_device_ops brcm_prepend_netdev_ops = { | |
197 | .xmit = brcm_tag_xmit_prepend, | |
198 | .rcv = brcm_tag_rcv_prepend, | |
199 | }; | |
200 | #endif |