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
rtl8_4_tag_xmit(struct sk_buff * skb,struct net_device * dev)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
rtl8_4_tag_rcv(struct sk_buff * skb,struct net_device * dev)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