aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2008-10-07 09:45:02 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-08 20:19:56 -0400
commitcf85d08fdf4548ee46657ccfb7f9949a85145db5 (patch)
tree583e251b0d772c23ca931a207e9ac0995d679f44 /net/dsa
parent91da11f870f00a3322b81c73042291d7f0be5a17 (diff)
dsa: add support for original DSA tagging format
Most of the DSA switches currently in the field do not support the Ethertype DSA tagging format that one of the previous patches added support for, but only the original DSA tagging format. The original DSA tagging format carries the same information as the Ethertype DSA tagging format, but with the difference that it does not have an ethertype field. In other words, when receiving a packet that is tagged with an original DSA tag, there is no way of telling in eth_type_trans() that this packet is in fact a DSA-tagged packet. This patch adds a hook into eth_type_trans() which is only compiled in if support for a switch chip that doesn't support Ethertype DSA is selected, and which checks whether there is a DSA switch driver instance attached to this network device which uses the old tag format. If so, it sets the protocol field to ETH_P_DSA without looking at the packet, so that the packet ends up in the right place. Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Nicolas Pitre <nico@marvell.com> Tested-by: Peter van Valderen <linux@ddcrew.com> Tested-by: Dirk Teurlings <dirk@upexia.nl> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa')
-rw-r--r--net/dsa/Kconfig4
-rw-r--r--net/dsa/Makefile1
-rw-r--r--net/dsa/dsa.c16
-rw-r--r--net/dsa/dsa_priv.h3
-rw-r--r--net/dsa/mv88e6123_61_65.c18
-rw-r--r--net/dsa/slave.c5
-rw-r--r--net/dsa/tag_dsa.c194
7 files changed, 234 insertions, 7 deletions
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 7cf55e5eb39f..6b68016827da 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -10,6 +10,10 @@ menuconfig NET_DSA
10if NET_DSA 10if NET_DSA
11 11
12# tagging formats 12# tagging formats
13config NET_DSA_TAG_DSA
14 bool
15 default n
16
13config NET_DSA_TAG_EDSA 17config NET_DSA_TAG_EDSA
14 bool 18 bool
15 default n 19 default n
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index b59a6f6bcf56..8b92123315b8 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -1,4 +1,5 @@
1# tagging formats 1# tagging formats
2obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
2obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o 3obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
3 4
4# switch drivers 5# switch drivers
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 6cc5be2ec7f1..f8c549281c30 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -202,6 +202,22 @@ static void dsa_switch_destroy(struct dsa_switch *ds)
202} 202}
203 203
204 204
205/* hooks for ethertype-less tagging formats *********************************/
206/*
207 * The original DSA tag format and some other tag formats have no
208 * ethertype, which means that we need to add a little hack to the
209 * networking receive path to make sure that received frames get
210 * the right ->protocol assigned to them when one of those tag
211 * formats is in use.
212 */
213bool dsa_uses_dsa_tags(void *dsa_ptr)
214{
215 struct dsa_switch *ds = dsa_ptr;
216
217 return !!(ds->tag_protocol == htons(ETH_P_DSA));
218}
219
220
205/* link polling *************************************************************/ 221/* link polling *************************************************************/
206static void dsa_link_poll_work(struct work_struct *ugly) 222static void dsa_link_poll_work(struct work_struct *ugly)
207{ 223{
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 21ee9052079a..2f1d68c495e8 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -103,6 +103,9 @@ struct net_device *dsa_slave_create(struct dsa_switch *ds,
103 struct device *parent, 103 struct device *parent,
104 int port, char *name); 104 int port, char *name);
105 105
106/* tag_dsa.c */
107int dsa_xmit(struct sk_buff *skb, struct net_device *dev);
108
106/* tag_edsa.c */ 109/* tag_edsa.c */
107int edsa_xmit(struct sk_buff *skb, struct net_device *dev); 110int edsa_xmit(struct sk_buff *skb, struct net_device *dev);
108 111
diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c
index 147818cc706e..555b164082fc 100644
--- a/net/dsa/mv88e6123_61_65.c
+++ b/net/dsa/mv88e6123_61_65.c
@@ -192,15 +192,19 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
192 192
193 /* 193 /*
194 * Port Control: disable Drop-on-Unlock, disable Drop-on-Lock, 194 * Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
195 * configure the EDSA tagging mode if this is the CPU port, 195 * configure the requested (DSA/EDSA) tagging mode if this is
196 * disable Header mode, enable IGMP/MLD snooping, disable VLAN 196 * the CPU port, disable Header mode, enable IGMP/MLD snooping,
197 * tunneling, determine priority by looking at 802.1p and IP 197 * disable VLAN tunneling, determine priority by looking at
198 * priority fields (IP prio has precedence), and set STP state 198 * 802.1p and IP priority fields (IP prio has precedence), and
199 * to Forwarding. Finally, if this is the CPU port, additionally 199 * set STP state to Forwarding. Finally, if this is the CPU
200 * enable forwarding of unknown unicast and multicast addresses. 200 * port, additionally enable forwarding of unknown unicast and
201 * multicast addresses.
201 */ 202 */
202 REG_WRITE(addr, 0x04, 203 REG_WRITE(addr, 0x04,
203 (p == ds->cpu_port) ? 0x373f : 0x0433); 204 (p == ds->cpu_port) ?
205 (ds->tag_protocol == htons(ETH_P_DSA)) ?
206 0x053f : 0x373f :
207 0x0433);
204 208
205 /* 209 /*
206 * Port Control 1: disable trunking. Also, if this is the 210 * Port Control 1: disable trunking. Also, if this is the
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 3cb331e98b89..8f8868dd4302 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -239,6 +239,11 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
239 memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN); 239 memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
240 slave_dev->tx_queue_len = 0; 240 slave_dev->tx_queue_len = 0;
241 switch (ds->tag_protocol) { 241 switch (ds->tag_protocol) {
242#ifdef CONFIG_NET_DSA_TAG_DSA
243 case htons(ETH_P_DSA):
244 slave_dev->hard_start_xmit = dsa_xmit;
245 break;
246#endif
242#ifdef CONFIG_NET_DSA_TAG_EDSA 247#ifdef CONFIG_NET_DSA_TAG_EDSA
243 case htons(ETH_P_EDSA): 248 case htons(ETH_P_EDSA):
244 slave_dev->hard_start_xmit = edsa_xmit; 249 slave_dev->hard_start_xmit = edsa_xmit;
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
new file mode 100644
index 000000000000..bdc0510b53b7
--- /dev/null
+++ b/net/dsa/tag_dsa.c
@@ -0,0 +1,194 @@
1/*
2 * net/dsa/tag_dsa.c - (Non-ethertype) DSA tagging
3 * Copyright (c) 2008 Marvell Semiconductor
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11#include <linux/etherdevice.h>
12#include <linux/list.h>
13#include <linux/netdevice.h>
14#include "dsa_priv.h"
15
16#define DSA_HLEN 4
17
18int dsa_xmit(struct sk_buff *skb, struct net_device *dev)
19{
20 struct dsa_slave_priv *p = netdev_priv(dev);
21 u8 *dsa_header;
22
23 dev->stats.tx_packets++;
24 dev->stats.tx_bytes += skb->len;
25
26 /*
27 * Convert the outermost 802.1q tag to a DSA tag for tagged
28 * packets, or insert a DSA tag between the addresses and
29 * the ethertype field for untagged packets.
30 */
31 if (skb->protocol == htons(ETH_P_8021Q)) {
32 if (skb_cow_head(skb, 0) < 0)
33 goto out_free;
34
35 /*
36 * Construct tagged FROM_CPU DSA tag from 802.1q tag.
37 */
38 dsa_header = skb->data + 2 * ETH_ALEN;
39 dsa_header[0] = 0x60;
40 dsa_header[1] = p->port << 3;
41
42 /*
43 * Move CFI field from byte 2 to byte 1.
44 */
45 if (dsa_header[2] & 0x10) {
46 dsa_header[1] |= 0x01;
47 dsa_header[2] &= ~0x10;
48 }
49 } else {
50 if (skb_cow_head(skb, DSA_HLEN) < 0)
51 goto out_free;
52 skb_push(skb, DSA_HLEN);
53
54 memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN);
55
56 /*
57 * Construct untagged FROM_CPU DSA tag.
58 */
59 dsa_header = skb->data + 2 * ETH_ALEN;
60 dsa_header[0] = 0x40;
61 dsa_header[1] = p->port << 3;
62 dsa_header[2] = 0x00;
63 dsa_header[3] = 0x00;
64 }
65
66 skb->protocol = htons(ETH_P_DSA);
67
68 skb->dev = p->parent->master_netdev;
69 dev_queue_xmit(skb);
70
71 return NETDEV_TX_OK;
72
73out_free:
74 kfree_skb(skb);
75 return NETDEV_TX_OK;
76}
77
78static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
79 struct packet_type *pt, struct net_device *orig_dev)
80{
81 struct dsa_switch *ds = dev->dsa_ptr;
82 u8 *dsa_header;
83 int source_port;
84
85 if (unlikely(ds == NULL))
86 goto out_drop;
87
88 skb = skb_unshare(skb, GFP_ATOMIC);
89 if (skb == NULL)
90 goto out;
91
92 if (unlikely(!pskb_may_pull(skb, DSA_HLEN)))
93 goto out_drop;
94
95 /*
96 * The ethertype field is part of the DSA header.
97 */
98 dsa_header = skb->data - 2;
99
100 /*
101 * Check that frame type is either TO_CPU or FORWARD, and
102 * that the source device is zero.
103 */
104 if ((dsa_header[0] & 0xdf) != 0x00 && (dsa_header[0] & 0xdf) != 0xc0)
105 goto out_drop;
106
107 /*
108 * Check that the source port is a registered DSA port.
109 */
110 source_port = (dsa_header[1] >> 3) & 0x1f;
111 if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
112 goto out_drop;
113
114 /*
115 * Convert the DSA header to an 802.1q header if the 'tagged'
116 * bit in the DSA header is set. If the 'tagged' bit is clear,
117 * delete the DSA header entirely.
118 */
119 if (dsa_header[0] & 0x20) {
120 u8 new_header[4];
121
122 /*
123 * Insert 802.1q ethertype and copy the VLAN-related
124 * fields, but clear the bit that will hold CFI (since
125 * DSA uses that bit location for another purpose).
126 */
127 new_header[0] = (ETH_P_8021Q >> 8) & 0xff;
128 new_header[1] = ETH_P_8021Q & 0xff;
129 new_header[2] = dsa_header[2] & ~0x10;
130 new_header[3] = dsa_header[3];
131
132 /*
133 * Move CFI bit from its place in the DSA header to
134 * its 802.1q-designated place.
135 */
136 if (dsa_header[1] & 0x01)
137 new_header[2] |= 0x10;
138
139 /*
140 * Update packet checksum if skb is CHECKSUM_COMPLETE.
141 */
142 if (skb->ip_summed == CHECKSUM_COMPLETE) {
143 __wsum c = skb->csum;
144 c = csum_add(c, csum_partial(new_header + 2, 2, 0));
145 c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0));
146 skb->csum = c;
147 }
148
149 memcpy(dsa_header, new_header, DSA_HLEN);
150 } else {
151 /*
152 * Remove DSA tag and update checksum.
153 */
154 skb_pull_rcsum(skb, DSA_HLEN);
155 memmove(skb->data - ETH_HLEN,
156 skb->data - ETH_HLEN - DSA_HLEN,
157 2 * ETH_ALEN);
158 }
159
160 skb->dev = ds->ports[source_port];
161 skb_push(skb, ETH_HLEN);
162 skb->protocol = eth_type_trans(skb, skb->dev);
163
164 skb->dev->last_rx = jiffies;
165 skb->dev->stats.rx_packets++;
166 skb->dev->stats.rx_bytes += skb->len;
167
168 netif_receive_skb(skb);
169
170 return 0;
171
172out_drop:
173 kfree_skb(skb);
174out:
175 return 0;
176}
177
178static struct packet_type dsa_packet_type = {
179 .type = __constant_htons(ETH_P_DSA),
180 .func = dsa_rcv,
181};
182
183static int __init dsa_init_module(void)
184{
185 dev_add_pack(&dsa_packet_type);
186 return 0;
187}
188module_init(dsa_init_module);
189
190static void __exit dsa_cleanup_module(void)
191{
192 dev_remove_pack(&dsa_packet_type);
193}
194module_exit(dsa_cleanup_module);