]>
Commit | Line | Data |
---|---|---|
e8fe177a JB |
1 | /* |
2 | * Copyright (C) 2017 Pengutronix, Juergen Borleis <jbe@pengutronix.de> | |
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 | |
e8fe177a JB |
42 | |
43 | static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev) | |
44 | { | |
45 | struct dsa_slave_priv *p = netdev_priv(dev); | |
46 | u16 *lan9303_tag; | |
47 | ||
48 | /* insert a special VLAN tag between the MAC addresses | |
49 | * and the current ethertype field. | |
50 | */ | |
51 | if (skb_cow_head(skb, LAN9303_TAG_LEN) < 0) { | |
52 | dev_dbg(&dev->dev, | |
53 | "Cannot make room for the special tag. Dropping packet\n"); | |
fe47d563 | 54 | return NULL; |
e8fe177a JB |
55 | } |
56 | ||
57 | /* provide 'LAN9303_TAG_LEN' bytes additional space */ | |
58 | skb_push(skb, LAN9303_TAG_LEN); | |
59 | ||
60 | /* make room between MACs and Ether-Type */ | |
61 | memmove(skb->data, skb->data + LAN9303_TAG_LEN, 2 * ETH_ALEN); | |
62 | ||
63 | lan9303_tag = (u16 *)(skb->data + 2 * ETH_ALEN); | |
64 | lan9303_tag[0] = htons(ETH_P_8021Q); | |
65 | lan9303_tag[1] = htons(p->dp->index | BIT(4)); | |
66 | ||
67 | return skb; | |
e8fe177a JB |
68 | } |
69 | ||
70 | static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, | |
89e49506 | 71 | struct packet_type *pt) |
e8fe177a JB |
72 | { |
73 | u16 *lan9303_tag; | |
74 | struct dsa_switch_tree *dst = dev->dsa_ptr; | |
75 | struct dsa_switch *ds; | |
76 | unsigned int source_port; | |
77 | ||
e8fe177a JB |
78 | ds = dst->ds[0]; |
79 | ||
80 | if (unlikely(!ds)) { | |
81 | dev_warn_ratelimited(&dev->dev, "Dropping packet, due to missing DSA switch device\n"); | |
82 | return NULL; | |
83 | } | |
84 | ||
85 | if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) { | |
86 | dev_warn_ratelimited(&dev->dev, | |
87 | "Dropping packet, cannot pull\n"); | |
88 | return NULL; | |
89 | } | |
90 | ||
91 | /* '->data' points into the middle of our special VLAN tag information: | |
92 | * | |
93 | * ~ MAC src | 0x81 | 0x00 | 0xyy | 0xzz | ether type | |
94 | * ^ | |
95 | * ->data | |
96 | */ | |
97 | lan9303_tag = (u16 *)(skb->data - 2); | |
98 | ||
99 | if (lan9303_tag[0] != htons(ETH_P_8021Q)) { | |
100 | dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid VLAN marker\n"); | |
101 | return NULL; | |
102 | } | |
103 | ||
104 | source_port = ntohs(lan9303_tag[1]) & 0x3; | |
105 | ||
274cdb46 | 106 | if (source_port >= ds->num_ports) { |
e8fe177a JB |
107 | dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); |
108 | return NULL; | |
109 | } | |
110 | ||
111 | if (!ds->ports[source_port].netdev) { | |
112 | dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid netdev or device\n"); | |
113 | return NULL; | |
114 | } | |
115 | ||
116 | /* remove the special VLAN tag between the MAC addresses | |
117 | * and the current ethertype field. | |
118 | */ | |
119 | skb_pull_rcsum(skb, 2 + 2); | |
120 | memmove(skb->data - ETH_HLEN, skb->data - (ETH_HLEN + LAN9303_TAG_LEN), | |
121 | 2 * ETH_ALEN); | |
122 | ||
123 | /* forward the packet to the dedicated interface */ | |
124 | skb->dev = ds->ports[source_port].netdev; | |
125 | ||
126 | return skb; | |
127 | } | |
128 | ||
129 | const struct dsa_device_ops lan9303_netdev_ops = { | |
130 | .xmit = lan9303_xmit, | |
131 | .rcv = lan9303_rcv, | |
132 | }; |