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 <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 <net/net_core.h>
18 #include <net/ethernet.h>
19 #include <net/net_mgmt.h>
20 #include <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 = iface->if_dev->dev;
209 	const struct dsa_api *api =
210 		(const struct dsa_api *)dev->api;
211 
212 	return api->switch_read(dev, reg_addr, value);
213 }
214 
dsa_switch_write(struct net_if * iface,uint16_t reg_addr,uint8_t value)215 int dsa_switch_write(struct net_if *iface, uint16_t reg_addr, uint8_t value)
216 {
217 	const struct device *dev = iface->if_dev->dev;
218 	const struct dsa_api *api =
219 		(const struct dsa_api *)dev->api;
220 
221 	return api->switch_write(dev, reg_addr, value);
222 }
223 
224 /**
225  * @brief      Write static MAC table entry
226  *
227  * @param      iface          Master DSA interface
228  * @param[in]  mac            MAC address
229  * @param[in]  fw_port        The firmware port
230  * @param[in]  tbl_entry_idx  Table entry index
231  * @param[in]  flags          Flags
232  *
233  * @return     0 if successful, negative if error
234  */
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)235 int dsa_switch_set_mac_table_entry(struct net_if *iface,
236 					const uint8_t *mac,
237 					uint8_t fw_port,
238 					uint16_t tbl_entry_idx,
239 					uint16_t flags)
240 {
241 	const struct device *dev = iface->if_dev->dev;
242 	const struct dsa_api *api =
243 		(const struct dsa_api *)dev->api;
244 
245 	return api->switch_set_mac_table_entry(dev, mac, fw_port,
246 							tbl_entry_idx, flags);
247 }
248 
249 /**
250  * @brief      Read static MAC table entry
251  *
252  * @param      iface          Master DSA interface
253  * @param      buf            Buffer to receive MAC address
254  * @param[in]  tbl_entry_idx  Table entry index
255  *
256  * @return     0 if successful, negative if error
257  */
dsa_switch_get_mac_table_entry(struct net_if * iface,uint8_t * buf,uint16_t tbl_entry_idx)258 int dsa_switch_get_mac_table_entry(struct net_if *iface,
259 					uint8_t *buf,
260 					uint16_t tbl_entry_idx)
261 {
262 	const struct device *dev = iface->if_dev->dev;
263 	const struct dsa_api *api =
264 		(const struct dsa_api *)dev->api;
265 
266 	return api->switch_get_mac_table_entry(dev, buf, tbl_entry_idx);
267 }
268