1 /** @file
2 * @brief DSA related functions
3 */
4
5 /*
6 * Copyright (c) 2018 Intel Corporation
7 *
8 * SPDX-License-Identifier: Apache-2.0
9 */
10
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(net_dsa, CONFIG_NET_DSA_LOG_LEVEL);
13
14 #include <errno.h>
15 #include <stdlib.h>
16
17 #include <zephyr/net/net_core.h>
18 #include <zephyr/net/ethernet.h>
19 #include <zephyr/net/net_mgmt.h>
20 #include <zephyr/net/dsa.h>
21
22 /*
23 * Store, in the ethernet_context for master interface, the original
24 * eth_tx() function, which will send packet with tag appended.
25 */
dsa_register_master_tx(struct net_if * iface,dsa_send_t fn)26 int dsa_register_master_tx(struct net_if *iface, dsa_send_t fn)
27 {
28 struct ethernet_context *ctx = net_if_l2_data(iface);
29
30 ctx->dsa_send = fn;
31 return 0;
32 }
33
34 #ifdef CONFIG_NET_L2_ETHERNET
dsa_is_port_master(struct net_if * iface)35 bool dsa_is_port_master(struct net_if *iface)
36 {
37 /* First check if iface points to ETH interface */
38 if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
39 /* Check its capabilities */
40 if (net_eth_get_hw_capabilities(iface) &
41 ETHERNET_DSA_MASTER_PORT) {
42 return true;
43 }
44 }
45
46 return false;
47 }
48 #else
dsa_is_port_master(struct net_if * iface)49 bool dsa_is_port_master(struct net_if *iface)
50 {
51 return false;
52 }
53 #endif
54
55 /*
56 * RECEIVE HANDLING CODE - ingress (ETH -> DSA slave ports)
57 */
58
dsa_check_iface(struct net_if * iface)59 static int dsa_check_iface(struct net_if *iface)
60 {
61 if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
62 return -ENOENT;
63 }
64
65 if (!((net_eth_get_hw_capabilities(iface) & ETHERNET_DSA_MASTER_PORT) ||
66 (net_eth_get_hw_capabilities(iface) & ETHERNET_DSA_SLAVE_PORT))) {
67 return -ESRCH;
68 }
69
70 return 0;
71 }
72
73
dsa_register_recv_callback(struct net_if * iface,dsa_net_recv_cb_t cb)74 int dsa_register_recv_callback(struct net_if *iface, dsa_net_recv_cb_t cb)
75 {
76 struct ethernet_context *ctx;
77 int ret;
78
79 ret = dsa_check_iface(iface);
80 if (ret < 0) {
81 return ret;
82 }
83
84 if (cb) {
85 ctx = net_if_l2_data(iface);
86 ctx->dsa_recv_cb = cb;
87 }
88
89 return 0;
90 }
91
dsa_net_recv(struct net_if * iface,struct net_pkt ** pkt)92 struct net_if *dsa_net_recv(struct net_if *iface, struct net_pkt **pkt)
93 {
94 const struct ethernet_context *c;
95 const struct dsa_context *ctx;
96 struct net_if *iface_sw;
97 int ret;
98
99 if (*pkt == NULL || iface == NULL) {
100 return NULL;
101 }
102
103 c = net_if_l2_data(iface);
104 ctx = c->dsa_ctx;
105
106 if (ctx == NULL || ctx->dapi == NULL) {
107 return iface;
108 }
109
110 if (ctx->dapi->dsa_get_iface == NULL) {
111 NET_ERR("DSA: No callback to set LAN interfaces!");
112 return iface;
113 }
114
115 iface_sw = ctx->dapi->dsa_get_iface(iface, *pkt);
116
117 ret = dsa_check_iface(iface_sw);
118 if (ret < 0) {
119 return iface_sw;
120 }
121
122 /*
123 * Optional code to change the destination interface with some
124 * custom callback (to e.g. filter/switch packets based on MAC).
125 *
126 * The callback shall be only present (and used) for lan1..3, but
127 * not for the master interface, which shall support all other
128 * protocols - i.e. UDP. ICMP, TCP.
129 */
130 c = net_if_l2_data(iface_sw);
131 if (c->dsa_recv_cb) {
132 if (c->dsa_recv_cb(iface_sw, *pkt)) {
133 return iface_sw;
134 }
135 }
136
137 return iface;
138 }
139
140 /*
141 * TRANSMISSION HANDLING CODE egress (DSA slave ports -> ETH)
142 */
dsa_tx(const struct device * dev,struct net_pkt * pkt)143 int dsa_tx(const struct device *dev, struct net_pkt *pkt)
144 {
145 struct net_if *iface_master, *iface;
146 struct ethernet_context *ctx;
147 struct dsa_context *context;
148
149 iface = net_if_lookup_by_dev(dev);
150 if (dsa_is_port_master(iface)) {
151 /*
152 * The master interface's ethernet_context structure holds
153 * pointer to its original eth_tx().
154 * The wrapper (i.e. dsa_tx()) is needed to modify packet -
155 * it appends tag to it.
156 */
157 ctx = net_if_l2_data(iface);
158 context = ctx->dsa_ctx;
159 return ctx->dsa_send(dev,
160 context->dapi->dsa_xmit_pkt(iface, pkt));
161 }
162
163 context = dev->data;
164 iface_master = context->iface_master;
165
166 if (iface_master == NULL) {
167 NET_ERR("DSA: No master interface!");
168 return -ENODEV;
169 }
170
171 /*
172 * Here packets are send via lan{123} interfaces in user program.
173 * Those structs' ethernet_api only have .send callback set to point
174 * to this wrapper function.
175 *
176 * Hence, it is necessary to get this callback from master's ethernet
177 * context structure..
178 */
179
180 /* Adjust packet for DSA routing and send it via master interface */
181 ctx = net_if_l2_data(iface_master);
182 return ctx->dsa_send(net_if_get_device(iface_master),
183 context->dapi->dsa_xmit_pkt(iface, pkt));
184 }
185
dsa_get_slave_port(struct net_if * iface,int slave_num)186 struct net_if *dsa_get_slave_port(struct net_if *iface, int slave_num)
187 {
188 struct ethernet_context *eth_ctx;
189 struct dsa_context *dsa_ctx;
190
191 eth_ctx = net_if_l2_data(iface);
192 if (eth_ctx == NULL) {
193 LOG_ERR("Iface %p context not available!", iface);
194 return NULL;
195 }
196
197 dsa_ctx = eth_ctx->dsa_ctx;
198
199 if (slave_num < 0 || slave_num >= dsa_ctx->num_slave_ports) {
200 return NULL;
201 }
202
203 return dsa_ctx->iface_slave[slave_num];
204 }
205
dsa_switch_read(struct net_if * iface,uint16_t reg_addr,uint8_t * value)206 int dsa_switch_read(struct net_if *iface, uint16_t reg_addr, uint8_t *value)
207 {
208 const struct device *dev = net_if_get_device(iface);
209 struct dsa_context *context = dev->data;
210 const struct dsa_api *api =
211 (const struct dsa_api *)context->dapi;
212
213 return api->switch_read(dev, reg_addr, value);
214 }
215
dsa_switch_write(struct net_if * iface,uint16_t reg_addr,uint8_t value)216 int dsa_switch_write(struct net_if *iface, uint16_t reg_addr, uint8_t value)
217 {
218 const struct device *dev = net_if_get_device(iface);
219 struct dsa_context *context = dev->data;
220 const struct dsa_api *api =
221 (const struct dsa_api *)context->dapi;
222
223 return api->switch_write(dev, reg_addr, value);
224 }
225
226 /**
227 * @brief Write static MAC table entry
228 *
229 * @param iface DSA interface
230 * @param[in] mac MAC address
231 * @param[in] fw_port The firmware port
232 * @param[in] tbl_entry_idx Table entry index
233 * @param[in] flags Flags
234 *
235 * @return 0 if successful, negative if error
236 */
dsa_switch_set_mac_table_entry(struct net_if * iface,const uint8_t * mac,uint8_t fw_port,uint16_t tbl_entry_idx,uint16_t flags)237 int dsa_switch_set_mac_table_entry(struct net_if *iface,
238 const uint8_t *mac,
239 uint8_t fw_port,
240 uint16_t tbl_entry_idx,
241 uint16_t flags)
242 {
243 const struct device *dev = net_if_get_device(iface);
244 struct dsa_context *context = dev->data;
245 const struct dsa_api *api =
246 (const struct dsa_api *)context->dapi;
247
248 return api->switch_set_mac_table_entry(dev, mac, fw_port,
249 tbl_entry_idx, flags);
250 }
251
252 /**
253 * @brief Read static MAC table entry
254 *
255 * @param iface DSA interface
256 * @param buf Buffer to receive MAC address
257 * @param[in] tbl_entry_idx Table entry index
258 *
259 * @return 0 if successful, negative if error
260 */
dsa_switch_get_mac_table_entry(struct net_if * iface,uint8_t * buf,uint16_t tbl_entry_idx)261 int dsa_switch_get_mac_table_entry(struct net_if *iface,
262 uint8_t *buf,
263 uint16_t tbl_entry_idx)
264 {
265 const struct device *dev = net_if_get_device(iface);
266 struct dsa_context *context = dev->data;
267 const struct dsa_api *api =
268 (const struct dsa_api *)context->dapi;
269
270 return api->switch_get_mac_table_entry(dev, buf, tbl_entry_idx);
271 }
272