OSDN Git Service

Merge tag 'memblock-v5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rppt...
[uclinux-h8/linux.git] / net / dsa / tag_rtl8_4.c
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);