1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_ethernet_mgmt, CONFIG_NET_L2_ETHERNET_LOG_LEVEL);
9
10 #include <errno.h>
11
12 #include <zephyr/net/net_core.h>
13 #include <zephyr/net/net_if.h>
14 #include <zephyr/net/ethernet_mgmt.h>
15
is_hw_caps_supported(const struct device * dev,enum ethernet_hw_caps caps)16 static inline bool is_hw_caps_supported(const struct device *dev,
17 enum ethernet_hw_caps caps)
18 {
19 const struct ethernet_api *api = dev->api;
20
21 if (!api || !api->get_capabilities) {
22 return false;
23 }
24
25 return ((api->get_capabilities(dev) & caps) != 0);
26 }
27
ethernet_set_config(uint64_t mgmt_request,struct net_if * iface,void * data,size_t len)28 static int ethernet_set_config(uint64_t mgmt_request,
29 struct net_if *iface,
30 void *data, size_t len)
31 {
32 struct ethernet_req_params *params = (struct ethernet_req_params *)data;
33 const struct device *dev = net_if_get_device(iface);
34 const struct ethernet_api *api = dev->api;
35 struct ethernet_config config = { 0 };
36 enum ethernet_config_type type;
37
38 if (!api) {
39 return -ENOENT;
40 }
41
42 if (!api->set_config) {
43 return -ENOTSUP;
44 }
45
46 if (!data || (len != sizeof(struct ethernet_req_params))) {
47 return -EINVAL;
48 }
49
50 if (mgmt_request == NET_REQUEST_ETHERNET_SET_MAC_ADDRESS) {
51 if (net_if_is_admin_up(iface)) {
52 return -EACCES;
53 }
54
55 /* We need to remove the old IPv6 link layer address, that is
56 * generated from old MAC address, from network interface if
57 * needed.
58 */
59 if (IS_ENABLED(CONFIG_NET_NATIVE_IPV6) &&
60 IS_ENABLED(CONFIG_NET_IPV6_IID_EUI_64)) {
61 struct in6_addr iid;
62
63 net_ipv6_addr_create_iid(&iid,
64 net_if_get_link_addr(iface));
65
66 /* No need to check the return value in this case. It
67 * is not an error if the address is not found atm.
68 */
69 (void)net_if_ipv6_addr_rm(iface, &iid);
70 }
71
72 memcpy(&config.mac_address, ¶ms->mac_address,
73 sizeof(struct net_eth_addr));
74 type = ETHERNET_CONFIG_TYPE_MAC_ADDRESS;
75 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QAV_PARAM) {
76 if (!is_hw_caps_supported(dev, ETHERNET_QAV)) {
77 return -ENOTSUP;
78 }
79
80 /* Validate params which need global validating */
81 switch (params->qav_param.type) {
82 case ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH:
83 if (params->qav_param.delta_bandwidth > 100) {
84 return -EINVAL;
85 }
86 break;
87 case ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE:
88 case ETHERNET_QAV_PARAM_TYPE_TRAFFIC_CLASS:
89 /* Read-only parameters */
90 return -EINVAL;
91 default:
92 /* No validation needed */
93 break;
94 }
95
96 memcpy(&config.qav_param, ¶ms->qav_param,
97 sizeof(struct ethernet_qav_param));
98 type = ETHERNET_CONFIG_TYPE_QAV_PARAM;
99 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QBV_PARAM) {
100 if (!is_hw_caps_supported(dev, ETHERNET_QBV)) {
101 return -ENOTSUP;
102 }
103
104 /* Validate params which need global validating */
105 if (params->qbv_param.state == ETHERNET_QBV_STATE_TYPE_OPER) {
106 /* Read-only parameters */
107 return -EINVAL;
108 }
109
110 if (params->qbv_param.type == ETHERNET_QBV_PARAM_TYPE_TIME &&
111 (params->qbv_param.cycle_time.nanosecond >= 1000000000 ||
112 params->qbv_param.base_time.fract_nsecond >= 1000000000)) {
113 return -EINVAL;
114 }
115
116 memcpy(&config.qbv_param, ¶ms->qbv_param,
117 sizeof(struct ethernet_qbv_param));
118 type = ETHERNET_CONFIG_TYPE_QBV_PARAM;
119 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QBU_PARAM) {
120 if (!is_hw_caps_supported(dev, ETHERNET_QBU)) {
121 return -ENOTSUP;
122 }
123
124 if (params->qbu_param.type ==
125 ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS) {
126 /* Read only parameter */
127 return -EINVAL;
128 }
129
130 /* All other fields are rw */
131
132 memcpy(&config.qbu_param, ¶ms->qbu_param,
133 sizeof(struct ethernet_qbu_param));
134 type = ETHERNET_CONFIG_TYPE_QBU_PARAM;
135 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_TXTIME_PARAM) {
136 if (!is_hw_caps_supported(dev, ETHERNET_TXTIME)) {
137 return -ENOTSUP;
138 }
139
140 if (net_if_is_up(iface)) {
141 return -EACCES;
142 }
143
144 memcpy(&config.txtime_param, ¶ms->txtime_param,
145 sizeof(struct ethernet_txtime_param));
146 type = ETHERNET_CONFIG_TYPE_TXTIME_PARAM;
147 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_PROMISC_MODE) {
148 if (!is_hw_caps_supported(dev, ETHERNET_PROMISC_MODE)) {
149 return -ENOTSUP;
150 }
151
152 config.promisc_mode = params->promisc_mode;
153 type = ETHERNET_CONFIG_TYPE_PROMISC_MODE;
154 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_T1S_PARAM) {
155 if (net_if_is_up(iface)) {
156 return -EACCES;
157 }
158
159 memcpy(&config.t1s_param, ¶ms->t1s_param,
160 sizeof(struct ethernet_t1s_param));
161 type = ETHERNET_CONFIG_TYPE_T1S_PARAM;
162 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_TXINJECTION_MODE) {
163 if (!is_hw_caps_supported(dev, ETHERNET_TXINJECTION_MODE)) {
164 return -ENOTSUP;
165 }
166
167 config.txinjection_mode = params->txinjection_mode;
168 type = ETHERNET_CONFIG_TYPE_TXINJECTION_MODE;
169 } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_MAC_FILTER) {
170 if (!is_hw_caps_supported(dev, ETHERNET_HW_FILTERING)) {
171 return -ENOTSUP;
172 }
173
174 memcpy(&config.filter, ¶ms->filter, sizeof(struct ethernet_filter));
175 type = ETHERNET_CONFIG_TYPE_FILTER;
176 } else {
177 return -EINVAL;
178 }
179
180 return api->set_config(net_if_get_device(iface), type, &config);
181 }
182
183 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_MAC_ADDRESS,
184 ethernet_set_config);
185
186 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QAV_PARAM,
187 ethernet_set_config);
188
189 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QBV_PARAM,
190 ethernet_set_config);
191
192 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QBU_PARAM,
193 ethernet_set_config);
194
195 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_TXTIME_PARAM,
196 ethernet_set_config);
197
198 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_PROMISC_MODE,
199 ethernet_set_config);
200
201 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_T1S_PARAM,
202 ethernet_set_config);
203
204 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_TXINJECTION_MODE,
205 ethernet_set_config);
206
207 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_MAC_FILTER,
208 ethernet_set_config);
209
ethernet_get_config(uint64_t mgmt_request,struct net_if * iface,void * data,size_t len)210 static int ethernet_get_config(uint64_t mgmt_request,
211 struct net_if *iface,
212 void *data, size_t len)
213 {
214 struct ethernet_req_params *params = (struct ethernet_req_params *)data;
215 const struct device *dev = net_if_get_device(iface);
216 const struct ethernet_api *api = dev->api;
217 struct ethernet_config config = { 0 };
218 int ret = 0;
219 enum ethernet_config_type type;
220
221 if (!api) {
222 return -ENOENT;
223 }
224
225 if (!api->get_config) {
226 return -ENOTSUP;
227 }
228
229 if (!data || (len != sizeof(struct ethernet_req_params))) {
230 return -EINVAL;
231 }
232
233 if (mgmt_request == NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM) {
234 if (!is_hw_caps_supported(dev, ETHERNET_PRIORITY_QUEUES)) {
235 return -ENOTSUP;
236 }
237
238 type = ETHERNET_CONFIG_TYPE_PRIORITY_QUEUES_NUM;
239
240 ret = api->get_config(dev, type, &config);
241 if (ret) {
242 return ret;
243 }
244
245 params->priority_queues_num = config.priority_queues_num;
246 } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QAV_PARAM) {
247 if (!is_hw_caps_supported(dev, ETHERNET_QAV)) {
248 return -ENOTSUP;
249 }
250
251 config.qav_param.queue_id = params->qav_param.queue_id;
252 config.qav_param.type = params->qav_param.type;
253
254 type = ETHERNET_CONFIG_TYPE_QAV_PARAM;
255
256 ret = api->get_config(dev, type, &config);
257 if (ret) {
258 return ret;
259 }
260
261 switch (config.qav_param.type) {
262 case ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH:
263 params->qav_param.delta_bandwidth =
264 config.qav_param.delta_bandwidth;
265 break;
266 case ETHERNET_QAV_PARAM_TYPE_IDLE_SLOPE:
267 params->qav_param.idle_slope =
268 config.qav_param.idle_slope;
269 break;
270 case ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE:
271 params->qav_param.oper_idle_slope =
272 config.qav_param.oper_idle_slope;
273 break;
274 case ETHERNET_QAV_PARAM_TYPE_TRAFFIC_CLASS:
275 params->qav_param.traffic_class =
276 config.qav_param.traffic_class;
277 break;
278 case ETHERNET_QAV_PARAM_TYPE_STATUS:
279 params->qav_param.enabled = config.qav_param.enabled;
280 break;
281 }
282
283 } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_PORTS_NUM) {
284 type = ETHERNET_CONFIG_TYPE_PORTS_NUM;
285
286 ret = api->get_config(dev, type, &config);
287 if (ret) {
288 return ret;
289 }
290
291 params->ports_num = config.ports_num;
292
293 } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QBV_PARAM) {
294 if (!is_hw_caps_supported(dev, ETHERNET_QBV)) {
295 return -ENOTSUP;
296 }
297
298 config.qbv_param.port_id = params->qbv_param.port_id;
299 config.qbv_param.type = params->qbv_param.type;
300 config.qbv_param.state = params->qbv_param.state;
301
302 if (config.qbv_param.type ==
303 ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST) {
304 config.qbv_param.gate_control.row =
305 params->qbv_param.gate_control.row;
306 }
307
308 type = ETHERNET_CONFIG_TYPE_QBV_PARAM;
309
310 ret = api->get_config(dev, type, &config);
311 if (ret) {
312 return ret;
313 }
314
315 switch (config.qbv_param.type) {
316 case ETHERNET_QBV_PARAM_TYPE_STATUS:
317 params->qbv_param.enabled = config.qbv_param.enabled;
318 break;
319 case ETHERNET_QBV_PARAM_TYPE_TIME:
320 memcpy(¶ms->qbv_param.cycle_time,
321 &config.qbv_param.cycle_time,
322 sizeof(params->qbv_param.cycle_time));
323 memcpy(¶ms->qbv_param.base_time,
324 &config.qbv_param.base_time,
325 sizeof(params->qbv_param.base_time));
326 params->qbv_param.extension_time =
327 config.qbv_param.extension_time;
328 break;
329 case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN:
330 params->qbv_param.gate_control_list_len =
331 config.qbv_param.gate_control_list_len;
332 break;
333 case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST:
334 memcpy(¶ms->qbv_param.gate_control,
335 &config.qbv_param.gate_control,
336 sizeof(params->qbv_param.gate_control));
337 break;
338 }
339
340 } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QBU_PARAM) {
341 if (!is_hw_caps_supported(dev, ETHERNET_QBU)) {
342 return -ENOTSUP;
343 }
344
345 config.qbu_param.port_id = params->qbu_param.port_id;
346 config.qbu_param.type = params->qbu_param.type;
347
348 type = ETHERNET_CONFIG_TYPE_QBU_PARAM;
349
350 ret = api->get_config(dev, type, &config);
351 if (ret) {
352 return ret;
353 }
354
355 switch (config.qbu_param.type) {
356 case ETHERNET_QBU_PARAM_TYPE_STATUS:
357 params->qbu_param.enabled = config.qbu_param.enabled;
358 break;
359 case ETHERNET_QBU_PARAM_TYPE_RELEASE_ADVANCE:
360 params->qbu_param.release_advance =
361 config.qbu_param.release_advance;
362 break;
363 case ETHERNET_QBU_PARAM_TYPE_HOLD_ADVANCE:
364 params->qbu_param.hold_advance =
365 config.qbu_param.hold_advance;
366 break;
367 case ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS:
368 params->qbu_param.link_partner_status =
369 config.qbu_param.link_partner_status;
370 break;
371 case ETHERNET_QBR_PARAM_TYPE_ADDITIONAL_FRAGMENT_SIZE:
372 params->qbu_param.additional_fragment_size =
373 config.qbu_param.additional_fragment_size;
374 break;
375 case ETHERNET_QBU_PARAM_TYPE_PREEMPTION_STATUS_TABLE:
376 memcpy(¶ms->qbu_param.frame_preempt_statuses,
377 &config.qbu_param.frame_preempt_statuses,
378 sizeof(params->qbu_param.frame_preempt_statuses));
379 break;
380 }
381
382 } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_TXTIME_PARAM) {
383 if (!is_hw_caps_supported(dev, ETHERNET_TXTIME)) {
384 return -ENOTSUP;
385 }
386
387 config.txtime_param.queue_id = params->txtime_param.queue_id;
388 config.txtime_param.type = params->txtime_param.type;
389
390 type = ETHERNET_CONFIG_TYPE_TXTIME_PARAM;
391
392 ret = api->get_config(dev, type, &config);
393 if (ret) {
394 return ret;
395 }
396
397 switch (config.txtime_param.type) {
398 case ETHERNET_TXTIME_PARAM_TYPE_ENABLE_QUEUES:
399 params->txtime_param.enable_txtime =
400 config.txtime_param.enable_txtime;
401 break;
402 }
403 } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_TXINJECTION_MODE) {
404 if (!is_hw_caps_supported(dev, ETHERNET_TXINJECTION_MODE)) {
405 return -ENOTSUP;
406 }
407
408 type = ETHERNET_CONFIG_TYPE_TXINJECTION_MODE;
409
410 ret = api->get_config(dev, type, &config);
411 if (ret) {
412 return ret;
413 }
414
415 params->txinjection_mode = config.txinjection_mode;
416 } else {
417 return -EINVAL;
418 }
419
420 return ret;
421 }
422
423 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM,
424 ethernet_get_config);
425
426 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QAV_PARAM,
427 ethernet_get_config);
428
429 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_PORTS_NUM,
430 ethernet_get_config);
431
432 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QBV_PARAM,
433 ethernet_get_config);
434
435 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QBU_PARAM,
436 ethernet_get_config);
437
438 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_TXTIME_PARAM,
439 ethernet_get_config);
440
441 NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_TXINJECTION_MODE,
442 ethernet_get_config);
443
ethernet_mgmt_raise_carrier_on_event(struct net_if * iface)444 void ethernet_mgmt_raise_carrier_on_event(struct net_if *iface)
445 {
446 net_mgmt_event_notify(NET_EVENT_ETHERNET_CARRIER_ON, iface);
447 }
448
ethernet_mgmt_raise_carrier_off_event(struct net_if * iface)449 void ethernet_mgmt_raise_carrier_off_event(struct net_if *iface)
450 {
451 net_mgmt_event_notify(NET_EVENT_ETHERNET_CARRIER_OFF, iface);
452 }
453
ethernet_mgmt_raise_vlan_enabled_event(struct net_if * iface,uint16_t tag)454 void ethernet_mgmt_raise_vlan_enabled_event(struct net_if *iface, uint16_t tag)
455 {
456 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
457 net_mgmt_event_notify_with_info(NET_EVENT_ETHERNET_VLAN_TAG_ENABLED,
458 iface, &tag, sizeof(tag));
459 #else
460 net_mgmt_event_notify(NET_EVENT_ETHERNET_VLAN_TAG_ENABLED,
461 iface);
462 #endif
463 }
464
ethernet_mgmt_raise_vlan_disabled_event(struct net_if * iface,uint16_t tag)465 void ethernet_mgmt_raise_vlan_disabled_event(struct net_if *iface, uint16_t tag)
466 {
467 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
468 net_mgmt_event_notify_with_info(NET_EVENT_ETHERNET_VLAN_TAG_DISABLED,
469 iface, &tag, sizeof(tag));
470 #else
471 net_mgmt_event_notify(NET_EVENT_ETHERNET_VLAN_TAG_DISABLED, iface);
472 #endif
473 }
474