xref: /openbmc/linux/net/dsa/tag_rtl8_4.c (revision aa74c44b)
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 header has the following format:
11  *
12  *  -------------------------------------------
13  *  | MAC DA | MAC SA | 8 byte tag | Type | ...
14  *  -------------------------------------------
15  *     _______________/            \______________________________________
16  *    /                                                                   \
17  *  0                                  7|8                                 15
18  *  |-----------------------------------+-----------------------------------|---
19  *  |                               (16-bit)                                | ^
20  *  |                       Realtek EtherType [0x8899]                      | |
21  *  |-----------------------------------+-----------------------------------| 8
22  *  |              (8-bit)              |              (8-bit)              |
23  *  |          Protocol [0x04]          |              REASON               | b
24  *  |-----------------------------------+-----------------------------------| y
25  *  |   (1)  | (1) | (2) |   (1)  | (3) | (1)  | (1) |    (1)    |   (5)    | t
26  *  | FID_EN |  X  | FID | PRI_EN | PRI | KEEP |  X  | LEARN_DIS |    X     | e
27  *  |-----------------------------------+-----------------------------------| s
28  *  |   (1)  |                       (15-bit)                               | |
29  *  |  ALLOW |                        TX/RX                                 | v
30  *  |-----------------------------------+-----------------------------------|---
31  *
32  * With the following field descriptions:
33  *
34  *    field      | description
35  *   ------------+-------------
36  *    Realtek    | 0x8899: indicates that this is a proprietary Realtek tag;
37  *     EtherType |         note that Realtek uses the same EtherType for
38  *               |         other incompatible tag formats (e.g. tag_rtl4_a.c)
39  *    Protocol   | 0x04: indicates that this tag conforms to this format
40  *    X          | reserved
41  *   ------------+-------------
42  *    REASON     | reason for forwarding packet to CPU
43  *               | 0: packet was forwarded or flooded to CPU
44  *               | 80: packet was trapped to CPU
45  *    FID_EN     | 1: packet has an FID
46  *               | 0: no FID
47  *    FID        | FID of packet (if FID_EN=1)
48  *    PRI_EN     | 1: force priority of packet
49  *               | 0: don't force priority
50  *    PRI        | priority of packet (if PRI_EN=1)
51  *    KEEP       | preserve packet VLAN tag format
52  *    LEARN_DIS  | don't learn the source MAC address of the packet
53  *    ALLOW      | 1: treat TX/RX field as an allowance port mask, meaning the
54  *               |    packet may only be forwarded to ports specified in the
55  *               |    mask
56  *               | 0: no allowance port mask, TX/RX field is the forwarding
57  *               |    port mask
58  *    TX/RX      | TX (switch->CPU): port number the packet was received on
59  *               | RX (CPU->switch): forwarding port mask (if ALLOW=0)
60  *               |                   allowance port mask (if ALLOW=1)
61  */
62 
63 #include <linux/bitfield.h>
64 #include <linux/bits.h>
65 #include <linux/etherdevice.h>
66 
67 #include "dsa_priv.h"
68 
69 /* Protocols supported:
70  *
71  * 0x04 = RTL8365MB DSA protocol
72  */
73 
74 #define RTL8_4_TAG_LEN			8
75 
76 #define RTL8_4_PROTOCOL			GENMASK(15, 8)
77 #define   RTL8_4_PROTOCOL_RTL8365MB	0x04
78 #define RTL8_4_REASON			GENMASK(7, 0)
79 #define   RTL8_4_REASON_FORWARD		0
80 #define   RTL8_4_REASON_TRAP		80
81 
82 #define RTL8_4_LEARN_DIS		BIT(5)
83 
84 #define RTL8_4_TX			GENMASK(3, 0)
85 #define RTL8_4_RX			GENMASK(10, 0)
86 
87 static struct sk_buff *rtl8_4_tag_xmit(struct sk_buff *skb,
88 				       struct net_device *dev)
89 {
90 	struct dsa_port *dp = dsa_slave_to_port(dev);
91 	__be16 *tag;
92 
93 	skb_push(skb, RTL8_4_TAG_LEN);
94 
95 	dsa_alloc_etype_header(skb, RTL8_4_TAG_LEN);
96 	tag = dsa_etype_header_pos_tx(skb);
97 
98 	/* Set Realtek EtherType */
99 	tag[0] = htons(ETH_P_REALTEK);
100 
101 	/* Set Protocol; zero REASON */
102 	tag[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB));
103 
104 	/* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */
105 	tag[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1));
106 
107 	/* Zero ALLOW; set RX (CPU->switch) forwarding port mask */
108 	tag[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(dp->index)));
109 
110 	return skb;
111 }
112 
113 static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb,
114 				      struct net_device *dev)
115 {
116 	__be16 *tag;
117 	u16 etype;
118 	u8 reason;
119 	u8 proto;
120 	u8 port;
121 
122 	if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN)))
123 		return NULL;
124 
125 	tag = dsa_etype_header_pos_rx(skb);
126 
127 	/* Parse Realtek EtherType */
128 	etype = ntohs(tag[0]);
129 	if (unlikely(etype != ETH_P_REALTEK)) {
130 		dev_warn_ratelimited(&dev->dev,
131 				     "non-realtek ethertype 0x%04x\n", etype);
132 		return NULL;
133 	}
134 
135 	/* Parse Protocol */
136 	proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag[1]));
137 	if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) {
138 		dev_warn_ratelimited(&dev->dev,
139 				     "unknown realtek protocol 0x%02x\n",
140 				     proto);
141 		return NULL;
142 	}
143 
144 	/* Parse REASON */
145 	reason = FIELD_GET(RTL8_4_REASON, ntohs(tag[1]));
146 
147 	/* Parse TX (switch->CPU) */
148 	port = FIELD_GET(RTL8_4_TX, ntohs(tag[3]));
149 	skb->dev = dsa_master_find_slave(dev, 0, port);
150 	if (!skb->dev) {
151 		dev_warn_ratelimited(&dev->dev,
152 				     "could not find slave for port %d\n",
153 				     port);
154 		return NULL;
155 	}
156 
157 	/* Remove tag and recalculate checksum */
158 	skb_pull_rcsum(skb, RTL8_4_TAG_LEN);
159 
160 	dsa_strip_etype_header(skb, RTL8_4_TAG_LEN);
161 
162 	if (reason != RTL8_4_REASON_TRAP)
163 		dsa_default_offload_fwd_mark(skb);
164 
165 	return skb;
166 }
167 
168 static const struct dsa_device_ops rtl8_4_netdev_ops = {
169 	.name = "rtl8_4",
170 	.proto = DSA_TAG_PROTO_RTL8_4,
171 	.xmit = rtl8_4_tag_xmit,
172 	.rcv = rtl8_4_tag_rcv,
173 	.needed_headroom = RTL8_4_TAG_LEN,
174 };
175 module_dsa_tag_driver(rtl8_4_netdev_ops);
176 
177 MODULE_LICENSE("GPL");
178 MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4);
179