xref: /linux/net/dsa/tag_rtl8_4.c (revision b83deaa741558babf4b8d51d34f6637ccfff1b26)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Handler for Realtek 8 byte switch tags
4  *
5  * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk>
6  *
7  * NOTE: Currently only supports protocol "4" found in the RTL8365MB, hence
8  * named tag_rtl8_4.
9  *
10  * This tag has the following format:
11  *
12  *  0                                  7|8                                 15
13  *  |-----------------------------------+-----------------------------------|---
14  *  |                               (16-bit)                                | ^
15  *  |                       Realtek EtherType [0x8899]                      | |
16  *  |-----------------------------------+-----------------------------------| 8
17  *  |              (8-bit)              |              (8-bit)              |
18  *  |          Protocol [0x04]          |              REASON               | b
19  *  |-----------------------------------+-----------------------------------| y
20  *  |   (1)  | (1) | (2) |   (1)  | (3) | (1)  | (1) |    (1)    |   (5)    | t
21  *  | FID_EN |  X  | FID | PRI_EN | PRI | KEEP |  X  | LEARN_DIS |    X     | e
22  *  |-----------------------------------+-----------------------------------| s
23  *  |   (1)  |                       (15-bit)                               | |
24  *  |  ALLOW |                        TX/RX                                 | v
25  *  |-----------------------------------+-----------------------------------|---
26  *
27  * With the following field descriptions:
28  *
29  *    field      | description
30  *   ------------+-------------
31  *    Realtek    | 0x8899: indicates that this is a proprietary Realtek tag;
32  *     EtherType |         note that Realtek uses the same EtherType for
33  *               |         other incompatible tag formats (e.g. tag_rtl4_a.c)
34  *    Protocol   | 0x04: indicates that this tag conforms to this format
35  *    X          | reserved
36  *   ------------+-------------
37  *    REASON     | reason for forwarding packet to CPU
38  *               | 0: packet was forwarded or flooded to CPU
39  *               | 80: packet was trapped to CPU
40  *    FID_EN     | 1: packet has an FID
41  *               | 0: no FID
42  *    FID        | FID of packet (if FID_EN=1)
43  *    PRI_EN     | 1: force priority of packet
44  *               | 0: don't force priority
45  *    PRI        | priority of packet (if PRI_EN=1)
46  *    KEEP       | preserve packet VLAN tag format
47  *    LEARN_DIS  | don't learn the source MAC address of the packet
48  *    ALLOW      | 1: treat TX/RX field as an allowance port mask, meaning the
49  *               |    packet may only be forwarded to ports specified in the
50  *               |    mask
51  *               | 0: no allowance port mask, TX/RX field is the forwarding
52  *               |    port mask
53  *    TX/RX      | TX (switch->CPU): port number the packet was received on
54  *               | RX (CPU->switch): forwarding port mask (if ALLOW=0)
55  *               |                   allowance port mask (if ALLOW=1)
56  *
57  * The tag can be positioned before Ethertype, using tag "rtl8_4":
58  *
59  *  +--------+--------+------------+------+-----
60  *  | MAC DA | MAC SA | 8 byte tag | Type | ...
61  *  +--------+--------+------------+------+-----
62  *
63  * The tag can also appear between the end of the payload and before the CRC,
64  * using tag "rtl8_4t":
65  *
66  * +--------+--------+------+-----+---------+------------+-----+
67  * | MAC DA | MAC SA | TYPE | ... | payload | 8-byte tag | CRC |
68  * +--------+--------+------+-----+---------+------------+-----+
69  *
70  * The added bytes after the payload will break most checksums, either in
71  * software or hardware. To avoid this issue, if the checksum is still pending,
72  * this tagger checksums the packet in software before adding the tag.
73  *
74  */
75 
76 #include <linux/bitfield.h>
77 #include <linux/bits.h>
78 #include <linux/etherdevice.h>
79 
80 #include "dsa_priv.h"
81 
82 /* Protocols supported:
83  *
84  * 0x04 = RTL8365MB DSA protocol
85  */
86 
87 #define RTL8_4_TAG_LEN			8
88 
89 #define RTL8_4_PROTOCOL			GENMASK(15, 8)
90 #define   RTL8_4_PROTOCOL_RTL8365MB	0x04
91 #define RTL8_4_REASON			GENMASK(7, 0)
92 #define   RTL8_4_REASON_FORWARD		0
93 #define   RTL8_4_REASON_TRAP		80
94 
95 #define RTL8_4_LEARN_DIS		BIT(5)
96 
97 #define RTL8_4_TX			GENMASK(3, 0)
98 #define RTL8_4_RX			GENMASK(10, 0)
99 
100 static void rtl8_4_write_tag(struct sk_buff *skb, struct net_device *dev,
101 			     void *tag)
102 {
103 	struct dsa_port *dp = dsa_slave_to_port(dev);
104 	__be16 tag16[RTL8_4_TAG_LEN / 2];
105 
106 	/* Set Realtek EtherType */
107 	tag16[0] = htons(ETH_P_REALTEK);
108 
109 	/* Set Protocol; zero REASON */
110 	tag16[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB));
111 
112 	/* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */
113 	tag16[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1));
114 
115 	/* Zero ALLOW; set RX (CPU->switch) forwarding port mask */
116 	tag16[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(dp->index)));
117 
118 	memcpy(tag, tag16, RTL8_4_TAG_LEN);
119 }
120 
121 static struct sk_buff *rtl8_4_tag_xmit(struct sk_buff *skb,
122 				       struct net_device *dev)
123 {
124 	skb_push(skb, RTL8_4_TAG_LEN);
125 
126 	dsa_alloc_etype_header(skb, RTL8_4_TAG_LEN);
127 
128 	rtl8_4_write_tag(skb, dev, dsa_etype_header_pos_tx(skb));
129 
130 	return skb;
131 }
132 
133 static struct sk_buff *rtl8_4t_tag_xmit(struct sk_buff *skb,
134 					struct net_device *dev)
135 {
136 	/* Calculate the checksum here if not done yet as trailing tags will
137 	 * break either software or hardware based checksum
138 	 */
139 	if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb))
140 		return NULL;
141 
142 	rtl8_4_write_tag(skb, dev, skb_put(skb, RTL8_4_TAG_LEN));
143 
144 	return skb;
145 }
146 
147 static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev,
148 			   void *tag)
149 {
150 	__be16 tag16[RTL8_4_TAG_LEN / 2];
151 	u16 etype;
152 	u8 reason;
153 	u8 proto;
154 	u8 port;
155 
156 	memcpy(tag16, tag, RTL8_4_TAG_LEN);
157 
158 	/* Parse Realtek EtherType */
159 	etype = ntohs(tag16[0]);
160 	if (unlikely(etype != ETH_P_REALTEK)) {
161 		dev_warn_ratelimited(&dev->dev,
162 				     "non-realtek ethertype 0x%04x\n", etype);
163 		return -EPROTO;
164 	}
165 
166 	/* Parse Protocol */
167 	proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag16[1]));
168 	if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) {
169 		dev_warn_ratelimited(&dev->dev,
170 				     "unknown realtek protocol 0x%02x\n",
171 				     proto);
172 		return -EPROTO;
173 	}
174 
175 	/* Parse REASON */
176 	reason = FIELD_GET(RTL8_4_REASON, ntohs(tag16[1]));
177 
178 	/* Parse TX (switch->CPU) */
179 	port = FIELD_GET(RTL8_4_TX, ntohs(tag16[3]));
180 	skb->dev = dsa_master_find_slave(dev, 0, port);
181 	if (!skb->dev) {
182 		dev_warn_ratelimited(&dev->dev,
183 				     "could not find slave for port %d\n",
184 				     port);
185 		return -ENOENT;
186 	}
187 
188 	if (reason != RTL8_4_REASON_TRAP)
189 		dsa_default_offload_fwd_mark(skb);
190 
191 	return 0;
192 }
193 
194 static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb,
195 				      struct net_device *dev)
196 {
197 	if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN)))
198 		return NULL;
199 
200 	if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb))))
201 		return NULL;
202 
203 	/* Remove tag and recalculate checksum */
204 	skb_pull_rcsum(skb, RTL8_4_TAG_LEN);
205 
206 	dsa_strip_etype_header(skb, RTL8_4_TAG_LEN);
207 
208 	return skb;
209 }
210 
211 static struct sk_buff *rtl8_4t_tag_rcv(struct sk_buff *skb,
212 				       struct net_device *dev)
213 {
214 	if (skb_linearize(skb))
215 		return NULL;
216 
217 	if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN)))
218 		return NULL;
219 
220 	if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN))
221 		return NULL;
222 
223 	return skb;
224 }
225 
226 /* Ethertype version */
227 static const struct dsa_device_ops rtl8_4_netdev_ops = {
228 	.name = "rtl8_4",
229 	.proto = DSA_TAG_PROTO_RTL8_4,
230 	.xmit = rtl8_4_tag_xmit,
231 	.rcv = rtl8_4_tag_rcv,
232 	.needed_headroom = RTL8_4_TAG_LEN,
233 };
234 
235 DSA_TAG_DRIVER(rtl8_4_netdev_ops);
236 
237 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4);
238 
239 /* Tail version */
240 static const struct dsa_device_ops rtl8_4t_netdev_ops = {
241 	.name = "rtl8_4t",
242 	.proto = DSA_TAG_PROTO_RTL8_4T,
243 	.xmit = rtl8_4t_tag_xmit,
244 	.rcv = rtl8_4t_tag_rcv,
245 	.needed_tailroom = RTL8_4_TAG_LEN,
246 };
247 
248 DSA_TAG_DRIVER(rtl8_4t_netdev_ops);
249 
250 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T);
251 
252 static struct dsa_tag_driver *dsa_tag_drivers[] = {
253 	&DSA_TAG_DRIVER_NAME(rtl8_4_netdev_ops),
254 	&DSA_TAG_DRIVER_NAME(rtl8_4t_netdev_ops),
255 };
256 module_dsa_tag_drivers(dsa_tag_drivers);
257 
258 MODULE_LICENSE("GPL");
259