]>
Commit | Line | Data |
---|---|---|
e8fe177a JB |
1 | /* |
2 | * Copyright (C) 2017 Pengutronix, Juergen Borleis <[email protected]> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * 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 <linux/list.h> | |
16 | #include <linux/slab.h> | |
ea5dd34b | 17 | |
e8fe177a JB |
18 | #include "dsa_priv.h" |
19 | ||
20 | /* To define the outgoing port and to discover the incoming port a regular | |
21 | * VLAN tag is used by the LAN9303. But its VID meaning is 'special': | |
22 | * | |
23 | * Dest MAC Src MAC TAG Type | |
24 | * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 | 1 2 |... | |
25 | * |<------->| | |
26 | * TAG: | |
27 | * |<------------->| | |
28 | * | 1 2 | 3 4 | | |
29 | * TPID VID | |
30 | * 0x8100 | |
31 | * | |
32 | * VID bit 3 indicates a request for an ALR lookup. | |
33 | * | |
34 | * If VID bit 3 is zero, then bits 0 and 1 specify the destination port | |
35 | * (0, 1, 2) or broadcast (3) or the source port (1, 2). | |
36 | * | |
37 | * VID bit 4 is used to specify if the STP port state should be overridden. | |
38 | * Required when no forwarding between the external ports should happen. | |
39 | */ | |
40 | ||
41 | #define LAN9303_TAG_LEN 4 | |
42 | #define LAN9303_MAX_PORTS 3 | |
43 | ||
44 | static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev) | |
45 | { | |
46 | struct dsa_slave_priv *p = netdev_priv(dev); | |
47 | u16 *lan9303_tag; | |
48 | ||
49 | /* insert a special VLAN tag between the MAC addresses | |
50 | * and the current ethertype field. | |
51 | */ | |
52 | if (skb_cow_head(skb, LAN9303_TAG_LEN) < 0) { | |
53 | dev_dbg(&dev->dev, | |
54 | "Cannot make room for the special tag. Dropping packet\n"); | |
fe47d563 | 55 | return NULL; |
e8fe177a JB |
56 | } |
57 | ||
58 | /* provide 'LAN9303_TAG_LEN' bytes additional space */ | |
59 | skb_push(skb, LAN9303_TAG_LEN); | |
60 | ||
61 | /* make room between MACs and Ether-Type */ | |
62 | memmove(skb->data, skb->data + LAN9303_TAG_LEN, 2 * ETH_ALEN); | |
63 | ||
64 | lan9303_tag = (u16 *)(skb->data + 2 * ETH_ALEN); | |
65 | lan9303_tag[0] = htons(ETH_P_8021Q); | |
66 | lan9303_tag[1] = htons(p->dp->index | BIT(4)); | |
67 | ||
68 | return skb; | |
e8fe177a JB |
69 | } |
70 | ||
71 | static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, | |
72 | struct packet_type *pt, struct net_device *orig_dev) | |
73 | { | |
74 | u16 *lan9303_tag; | |
75 | struct dsa_switch_tree *dst = dev->dsa_ptr; | |
76 | struct dsa_switch *ds; | |
77 | unsigned int source_port; | |
78 | ||
e8fe177a JB |
79 | ds = dst->ds[0]; |
80 | ||
81 | if (unlikely(!ds)) { | |
82 | dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing DSA switch device\n"); | |
83 | return NULL; | |
84 | } | |
85 | ||
86 | if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) { | |
87 | dev_warn_ratelimited(&dev->dev, | |
88 | "Dropping packet, cannot pull\n"); | |
89 | return NULL; | |
90 | } | |
91 | ||
92 | /* '->data' points into the middle of our special VLAN tag information: | |
93 | * | |
94 | * ~ MAC src | 0x81 | 0x00 | 0xyy | 0xzz | ether type | |
95 | * ^ | |
96 | * ->data | |
97 | */ | |
98 | lan9303_tag = (u16 *)(skb->data - 2); | |
99 | ||
100 | if (lan9303_tag[0] != htons(ETH_P_8021Q)) { | |
101 | dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid VLAN marker\n"); | |
102 | return NULL; | |
103 | } | |
104 | ||
105 | source_port = ntohs(lan9303_tag[1]) & 0x3; | |
106 | ||
107 | if (source_port >= LAN9303_MAX_PORTS) { | |
108 | dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); | |
109 | return NULL; | |
110 | } | |
111 | ||
112 | if (!ds->ports[source_port].netdev) { | |
113 | dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid netdev or device\n"); | |
114 | return NULL; | |
115 | } | |
116 | ||
117 | /* remove the special VLAN tag between the MAC addresses | |
118 | * and the current ethertype field. | |
119 | */ | |
120 | skb_pull_rcsum(skb, 2 + 2); | |
121 | memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN), | |
122 | 2 * ETH_ALEN); | |
123 | ||
124 | /* forward the packet to the dedicated interface */ | |
125 | skb->dev = ds->ports[source_port].netdev; | |
126 | ||
127 | return skb; | |
128 | } | |
129 | ||
130 | const struct dsa_device_ops lan9303_netdev_ops = { | |
131 | .xmit = lan9303_xmit, | |
132 | .rcv = lan9303_rcv, | |
133 | }; |