Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Broadcom tag support
  4 *
  5 * Copyright (C) 2014 Broadcom Corporation
  6 */
  7
  8#include <linux/etherdevice.h>
  9#include <linux/list.h>
 10#include <linux/slab.h>
 11
 12#include "dsa_priv.h"
 13
 14/* This tag length is 4 bytes, older ones were 6 bytes, we do not
 15 * handle them
 16 */
 17#define BRCM_TAG_LEN	4
 18
 19/* Tag is constructed and desconstructed using byte by byte access
 20 * because the tag is placed after the MAC Source Address, which does
 21 * not make it 4-bytes aligned, so this might cause unaligned accesses
 22 * on most systems where this is used.
 23 */
 24
 25/* Ingress and egress opcodes */
 26#define BRCM_OPCODE_SHIFT	5
 27#define BRCM_OPCODE_MASK	0x7
 28
 29/* Ingress fields */
 30/* 1st byte in the tag */
 31#define BRCM_IG_TC_SHIFT	2
 32#define BRCM_IG_TC_MASK		0x7
 33/* 2nd byte in the tag */
 34#define BRCM_IG_TE_MASK		0x3
 35#define BRCM_IG_TS_SHIFT	7
 36/* 3rd byte in the tag */
 37#define BRCM_IG_DSTMAP2_MASK	1
 38#define BRCM_IG_DSTMAP1_MASK	0xff
 39
 40/* Egress fields */
 41
 42/* 2nd byte in the tag */
 43#define BRCM_EG_CID_MASK	0xff
 44
 45/* 3rd byte in the tag */
 46#define BRCM_EG_RC_MASK		0xff
 47#define  BRCM_EG_RC_RSVD	(3 << 6)
 48#define  BRCM_EG_RC_EXCEPTION	(1 << 5)
 49#define  BRCM_EG_RC_PROT_SNOOP	(1 << 4)
 50#define  BRCM_EG_RC_PROT_TERM	(1 << 3)
 51#define  BRCM_EG_RC_SWITCH	(1 << 2)
 52#define  BRCM_EG_RC_MAC_LEARN	(1 << 1)
 53#define  BRCM_EG_RC_MIRROR	(1 << 0)
 54#define BRCM_EG_TC_SHIFT	5
 55#define BRCM_EG_TC_MASK		0x7
 56#define BRCM_EG_PID_MASK	0x1f
 57
 58#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM) || \
 59	IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
 60
 61static struct sk_buff *brcm_tag_xmit_ll(struct sk_buff *skb,
 62					struct net_device *dev,
 63					unsigned int offset)
 64{
 65	struct dsa_port *dp = dsa_slave_to_port(dev);
 66	u16 queue = skb_get_queue_mapping(skb);
 67	u8 *brcm_tag;
 68
 69	if (skb_cow_head(skb, BRCM_TAG_LEN) < 0)
 70		return NULL;
 71
 72	/* The Ethernet switch we are interfaced with needs packets to be at
 73	 * least 64 bytes (including FCS) otherwise they will be discarded when
 74	 * they enter the switch port logic. When Broadcom tags are enabled, we
 75	 * need to make sure that packets are at least 68 bytes
 76	 * (including FCS and tag) because the length verification is done after
 77	 * the Broadcom tag is stripped off the ingress packet.
 78	 *
 79	 * Let dsa_slave_xmit() free the SKB
 80	 */
 81	if (__skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN, false))
 82		return NULL;
 83
 84	skb_push(skb, BRCM_TAG_LEN);
 85
 86	if (offset)
 87		memmove(skb->data, skb->data + BRCM_TAG_LEN, offset);
 88
 89	brcm_tag = skb->data + offset;
 90
 91	/* Set the ingress opcode, traffic class, tag enforcment is
 92	 * deprecated
 93	 */
 94	brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) |
 95		       ((queue & BRCM_IG_TC_MASK) << BRCM_IG_TC_SHIFT);
 96	brcm_tag[1] = 0;
 97	brcm_tag[2] = 0;
 98	if (dp->index == 8)
 99		brcm_tag[2] = BRCM_IG_DSTMAP2_MASK;
100	brcm_tag[3] = (1 << dp->index) & BRCM_IG_DSTMAP1_MASK;
101
102	/* Now tell the master network device about the desired output queue
103	 * as well
104	 */
105	skb_set_queue_mapping(skb, BRCM_TAG_SET_PORT_QUEUE(dp->index, queue));
106
107	return skb;
108}
109
110static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb,
111				       struct net_device *dev,
112				       struct packet_type *pt,
113				       unsigned int offset)
114{
115	int source_port;
116	u8 *brcm_tag;
117
118	if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN)))
119		return NULL;
120
121	brcm_tag = skb->data - offset;
122
123	/* The opcode should never be different than 0b000 */
124	if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK))
125		return NULL;
126
127	/* We should never see a reserved reason code without knowing how to
128	 * handle it
129	 */
130	if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD))
131		return NULL;
132
133	/* Locate which port this is coming from */
134	source_port = brcm_tag[3] & BRCM_EG_PID_MASK;
135
136	skb->dev = dsa_master_find_slave(dev, 0, source_port);
137	if (!skb->dev)
138		return NULL;
139
140	/* Remove Broadcom tag and update checksum */
141	skb_pull_rcsum(skb, BRCM_TAG_LEN);
142
143	skb->offload_fwd_mark = 1;
144
145	return skb;
146}
147
148static int brcm_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto,
149				 int *offset)
150{
151	/* We have been called on the DSA master network device after
152	 * eth_type_trans() which pulled the Ethernet header already.
153	 * Frames have one of these two layouts:
154	 * -----------------------------------
155	 * | MAC DA | MAC SA | 4b tag | Type | DSA_TAG_PROTO_BRCM
156	 * -----------------------------------
157	 * -----------------------------------
158	 * | 4b tag | MAC DA | MAC SA | Type | DSA_TAG_PROTO_BRCM_PREPEND
159	 * -----------------------------------
160	 * skb->data points 2 bytes before the actual Ethernet type field and
161	 * we have an offset of 4bytes between where skb->data and where the
162	 * payload starts.
163	 */
164	*offset = BRCM_TAG_LEN;
165	*proto = ((__be16 *)skb->data)[1];
166	return 0;
167}
168#endif
169
170#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
171static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb,
172				     struct net_device *dev)
173{
174	/* Build the tag after the MAC Source Address */
175	return brcm_tag_xmit_ll(skb, dev, 2 * ETH_ALEN);
176}
177
178
179static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev,
180				    struct packet_type *pt)
181{
182	struct sk_buff *nskb;
183
184	/* skb->data points to the EtherType, the tag is right before it */
185	nskb = brcm_tag_rcv_ll(skb, dev, pt, 2);
186	if (!nskb)
187		return nskb;
188
189	/* Move the Ethernet DA and SA */
190	memmove(nskb->data - ETH_HLEN,
191		nskb->data - ETH_HLEN - BRCM_TAG_LEN,
192		2 * ETH_ALEN);
193
194	return nskb;
195}
196
197static const struct dsa_device_ops brcm_netdev_ops = {
198	.name	= "brcm",
199	.proto	= DSA_TAG_PROTO_BRCM,
200	.xmit	= brcm_tag_xmit,
201	.rcv	= brcm_tag_rcv,
202	.overhead = BRCM_TAG_LEN,
203	.flow_dissect = brcm_tag_flow_dissect,
204};
205
206DSA_TAG_DRIVER(brcm_netdev_ops);
207MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM);
208#endif
209
210#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
211static struct sk_buff *brcm_tag_xmit_prepend(struct sk_buff *skb,
212					     struct net_device *dev)
213{
214	/* tag is prepended to the packet */
215	return brcm_tag_xmit_ll(skb, dev, 0);
216}
217
218static struct sk_buff *brcm_tag_rcv_prepend(struct sk_buff *skb,
219					    struct net_device *dev,
220					    struct packet_type *pt)
221{
222	/* tag is prepended to the packet */
223	return brcm_tag_rcv_ll(skb, dev, pt, ETH_HLEN);
224}
225
226static const struct dsa_device_ops brcm_prepend_netdev_ops = {
227	.name	= "brcm-prepend",
228	.proto	= DSA_TAG_PROTO_BRCM_PREPEND,
229	.xmit	= brcm_tag_xmit_prepend,
230	.rcv	= brcm_tag_rcv_prepend,
231	.overhead = BRCM_TAG_LEN,
232	.flow_dissect = brcm_tag_flow_dissect,
233};
234
235DSA_TAG_DRIVER(brcm_prepend_netdev_ops);
236MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_BRCM_PREPEND);
237#endif
238
239static struct dsa_tag_driver *dsa_tag_driver_array[] =	{
240#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM)
241	&DSA_TAG_DRIVER_NAME(brcm_netdev_ops),
242#endif
243#if IS_ENABLED(CONFIG_NET_DSA_TAG_BRCM_PREPEND)
244	&DSA_TAG_DRIVER_NAME(brcm_prepend_netdev_ops),
245#endif
246};
247
248module_dsa_tag_drivers(dsa_tag_driver_array);
249
250MODULE_LICENSE("GPL");