| /* |
| * Copyright (c) 2018 Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(net_ethernet_mgmt, CONFIG_NET_L2_ETHERNET_LOG_LEVEL); |
| |
| #include <errno.h> |
| |
| #include <zephyr/net/net_core.h> |
| #include <zephyr/net/net_if.h> |
| #include <zephyr/net/ethernet_mgmt.h> |
| |
| static inline bool is_hw_caps_supported(const struct device *dev, |
| enum ethernet_hw_caps caps) |
| { |
| const struct ethernet_api *api = dev->api; |
| |
| if (!api) { |
| return false; |
| } |
| |
| return !!(api->get_capabilities(dev) & caps); |
| } |
| |
| static int ethernet_set_config(uint32_t mgmt_request, |
| struct net_if *iface, |
| void *data, size_t len) |
| { |
| struct ethernet_req_params *params = (struct ethernet_req_params *)data; |
| const struct device *dev = net_if_get_device(iface); |
| const struct ethernet_api *api = dev->api; |
| struct ethernet_config config = { 0 }; |
| enum ethernet_config_type type; |
| |
| if (!api) { |
| return -ENOENT; |
| } |
| |
| if (!api->set_config) { |
| return -ENOTSUP; |
| } |
| |
| if (!data || (len != sizeof(struct ethernet_req_params))) { |
| return -EINVAL; |
| } |
| |
| if (mgmt_request == NET_REQUEST_ETHERNET_SET_AUTO_NEGOTIATION) { |
| if (!is_hw_caps_supported(dev, |
| ETHERNET_AUTO_NEGOTIATION_SET)) { |
| return -ENOTSUP; |
| } |
| |
| config.auto_negotiation = params->auto_negotiation; |
| type = ETHERNET_CONFIG_TYPE_AUTO_NEG; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_LINK) { |
| if (params->l.link_10bt) { |
| if (!is_hw_caps_supported(dev, |
| ETHERNET_LINK_10BASE_T)) { |
| return -ENOTSUP; |
| } |
| |
| config.l.link_10bt = true; |
| } else if (params->l.link_100bt) { |
| if (!is_hw_caps_supported(dev, |
| ETHERNET_LINK_100BASE_T)) { |
| return -ENOTSUP; |
| } |
| |
| config.l.link_100bt = true; |
| } else if (params->l.link_1000bt) { |
| if (!is_hw_caps_supported(dev, |
| ETHERNET_LINK_1000BASE_T)) { |
| return -ENOTSUP; |
| } |
| |
| config.l.link_1000bt = true; |
| } else { |
| return -EINVAL; |
| } |
| |
| type = ETHERNET_CONFIG_TYPE_LINK; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_DUPLEX) { |
| if (!is_hw_caps_supported(dev, ETHERNET_DUPLEX_SET)) { |
| return -ENOTSUP; |
| } |
| |
| config.full_duplex = params->full_duplex; |
| type = ETHERNET_CONFIG_TYPE_DUPLEX; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_MAC_ADDRESS) { |
| if (net_if_is_up(iface)) { |
| return -EACCES; |
| } |
| |
| /* We need to remove the old IPv6 link layer address, that is |
| * generated from old MAC address, from network interface if |
| * needed. |
| */ |
| if (IS_ENABLED(CONFIG_NET_IPV6)) { |
| struct in6_addr iid; |
| |
| net_ipv6_addr_create_iid(&iid, |
| net_if_get_link_addr(iface)); |
| |
| /* No need to check the return value in this case. It |
| * is not an error if the address is not found atm. |
| */ |
| (void)net_if_ipv6_addr_rm(iface, &iid); |
| } |
| |
| memcpy(&config.mac_address, ¶ms->mac_address, |
| sizeof(struct net_eth_addr)); |
| type = ETHERNET_CONFIG_TYPE_MAC_ADDRESS; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QAV_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_QAV)) { |
| return -ENOTSUP; |
| } |
| |
| /* Validate params which need global validating */ |
| switch (params->qav_param.type) { |
| case ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH: |
| if (params->qav_param.delta_bandwidth > 100) { |
| return -EINVAL; |
| } |
| break; |
| case ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE: |
| case ETHERNET_QAV_PARAM_TYPE_TRAFFIC_CLASS: |
| /* Read-only parameters */ |
| return -EINVAL; |
| default: |
| /* No validation needed */ |
| break; |
| } |
| |
| memcpy(&config.qav_param, ¶ms->qav_param, |
| sizeof(struct ethernet_qav_param)); |
| type = ETHERNET_CONFIG_TYPE_QAV_PARAM; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QBV_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_QBV)) { |
| return -ENOTSUP; |
| } |
| |
| /* Validate params which need global validating */ |
| if (params->qbv_param.state == ETHERNET_QBV_STATE_TYPE_OPER) { |
| /* Read-only parameters */ |
| return -EINVAL; |
| } |
| |
| if (params->qbv_param.type == ETHERNET_QBV_PARAM_TYPE_TIME && |
| (params->qbv_param.cycle_time.nanosecond >= 1000000000 || |
| params->qbv_param.base_time.fract_nsecond >= 1000000000)) { |
| return -EINVAL; |
| } |
| |
| memcpy(&config.qbv_param, ¶ms->qbv_param, |
| sizeof(struct ethernet_qbv_param)); |
| type = ETHERNET_CONFIG_TYPE_QBV_PARAM; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_QBU_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_QBU)) { |
| return -ENOTSUP; |
| } |
| |
| if (params->qbu_param.type == |
| ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS) { |
| /* Read only parameter */ |
| return -EINVAL; |
| } |
| |
| /* All other fields are rw */ |
| |
| memcpy(&config.qbu_param, ¶ms->qbu_param, |
| sizeof(struct ethernet_qbu_param)); |
| type = ETHERNET_CONFIG_TYPE_QBU_PARAM; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_TXTIME_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_TXTIME)) { |
| return -ENOTSUP; |
| } |
| |
| if (net_if_is_up(iface)) { |
| return -EACCES; |
| } |
| |
| memcpy(&config.txtime_param, ¶ms->txtime_param, |
| sizeof(struct ethernet_txtime_param)); |
| type = ETHERNET_CONFIG_TYPE_TXTIME_PARAM; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_SET_PROMISC_MODE) { |
| if (!is_hw_caps_supported(dev, ETHERNET_PROMISC_MODE)) { |
| return -ENOTSUP; |
| } |
| |
| config.promisc_mode = params->promisc_mode; |
| type = ETHERNET_CONFIG_TYPE_PROMISC_MODE; |
| } else { |
| return -EINVAL; |
| } |
| |
| return api->set_config(net_if_get_device(iface), type, &config); |
| } |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_AUTO_NEGOTIATION, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_LINK, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_DUPLEX, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_MAC_ADDRESS, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QAV_PARAM, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QBV_PARAM, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_QBU_PARAM, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_TXTIME_PARAM, |
| ethernet_set_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_PROMISC_MODE, |
| ethernet_set_config); |
| |
| static int ethernet_get_config(uint32_t mgmt_request, |
| struct net_if *iface, |
| void *data, size_t len) |
| { |
| struct ethernet_req_params *params = (struct ethernet_req_params *)data; |
| const struct device *dev = net_if_get_device(iface); |
| const struct ethernet_api *api = dev->api; |
| struct ethernet_config config = { 0 }; |
| int ret = 0; |
| enum ethernet_config_type type; |
| |
| if (!api) { |
| return -ENOENT; |
| } |
| |
| if (!api->get_config) { |
| return -ENOTSUP; |
| } |
| |
| if (!data || (len != sizeof(struct ethernet_req_params))) { |
| return -EINVAL; |
| } |
| |
| if (mgmt_request == NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_PRIORITY_QUEUES)) { |
| return -ENOTSUP; |
| } |
| |
| type = ETHERNET_CONFIG_TYPE_PRIORITY_QUEUES_NUM; |
| |
| ret = api->get_config(dev, type, &config); |
| if (ret) { |
| return ret; |
| } |
| |
| params->priority_queues_num = config.priority_queues_num; |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QAV_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_QAV)) { |
| return -ENOTSUP; |
| } |
| |
| config.qav_param.queue_id = params->qav_param.queue_id; |
| config.qav_param.type = params->qav_param.type; |
| |
| type = ETHERNET_CONFIG_TYPE_QAV_PARAM; |
| |
| ret = api->get_config(dev, type, &config); |
| if (ret) { |
| return ret; |
| } |
| |
| switch (config.qav_param.type) { |
| case ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH: |
| params->qav_param.delta_bandwidth = |
| config.qav_param.delta_bandwidth; |
| break; |
| case ETHERNET_QAV_PARAM_TYPE_IDLE_SLOPE: |
| params->qav_param.idle_slope = |
| config.qav_param.idle_slope; |
| break; |
| case ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE: |
| params->qav_param.oper_idle_slope = |
| config.qav_param.oper_idle_slope; |
| break; |
| case ETHERNET_QAV_PARAM_TYPE_TRAFFIC_CLASS: |
| params->qav_param.traffic_class = |
| config.qav_param.traffic_class; |
| break; |
| case ETHERNET_QAV_PARAM_TYPE_STATUS: |
| params->qav_param.enabled = config.qav_param.enabled; |
| break; |
| } |
| |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_PORTS_NUM) { |
| type = ETHERNET_CONFIG_TYPE_PORTS_NUM; |
| |
| ret = api->get_config(dev, type, &config); |
| if (ret) { |
| return ret; |
| } |
| |
| params->ports_num = config.ports_num; |
| |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QBV_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_QBV)) { |
| return -ENOTSUP; |
| } |
| |
| config.qbv_param.port_id = params->qbv_param.port_id; |
| config.qbv_param.type = params->qbv_param.type; |
| config.qbv_param.state = params->qbv_param.state; |
| |
| if (config.qbv_param.type == |
| ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST) { |
| config.qbv_param.gate_control.row = |
| params->qbv_param.gate_control.row; |
| } |
| |
| type = ETHERNET_CONFIG_TYPE_QBV_PARAM; |
| |
| ret = api->get_config(dev, type, &config); |
| if (ret) { |
| return ret; |
| } |
| |
| switch (config.qbv_param.type) { |
| case ETHERNET_QBV_PARAM_TYPE_STATUS: |
| params->qbv_param.enabled = config.qbv_param.enabled; |
| break; |
| case ETHERNET_QBV_PARAM_TYPE_TIME: |
| memcpy(¶ms->qbv_param.cycle_time, |
| &config.qbv_param.cycle_time, |
| sizeof(params->qbv_param.cycle_time)); |
| memcpy(¶ms->qbv_param.base_time, |
| &config.qbv_param.base_time, |
| sizeof(params->qbv_param.base_time)); |
| params->qbv_param.extension_time = |
| config.qbv_param.extension_time; |
| break; |
| case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN: |
| params->qbv_param.gate_control_list_len = |
| config.qbv_param.gate_control_list_len; |
| break; |
| case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST: |
| memcpy(¶ms->qbv_param.gate_control, |
| &config.qbv_param.gate_control, |
| sizeof(params->qbv_param.gate_control)); |
| break; |
| } |
| |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_QBU_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_QBU)) { |
| return -ENOTSUP; |
| } |
| |
| config.qbu_param.port_id = params->qbu_param.port_id; |
| config.qbu_param.type = params->qbu_param.type; |
| |
| type = ETHERNET_CONFIG_TYPE_QBU_PARAM; |
| |
| ret = api->get_config(dev, type, &config); |
| if (ret) { |
| return ret; |
| } |
| |
| switch (config.qbu_param.type) { |
| case ETHERNET_QBU_PARAM_TYPE_STATUS: |
| params->qbu_param.enabled = config.qbu_param.enabled; |
| break; |
| case ETHERNET_QBU_PARAM_TYPE_RELEASE_ADVANCE: |
| params->qbu_param.release_advance = |
| config.qbu_param.release_advance; |
| break; |
| case ETHERNET_QBU_PARAM_TYPE_HOLD_ADVANCE: |
| params->qbu_param.hold_advance = |
| config.qbu_param.hold_advance; |
| break; |
| case ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS: |
| params->qbu_param.link_partner_status = |
| config.qbu_param.link_partner_status; |
| break; |
| case ETHERNET_QBR_PARAM_TYPE_ADDITIONAL_FRAGMENT_SIZE: |
| params->qbu_param.additional_fragment_size = |
| config.qbu_param.additional_fragment_size; |
| break; |
| case ETHERNET_QBU_PARAM_TYPE_PREEMPTION_STATUS_TABLE: |
| memcpy(¶ms->qbu_param.frame_preempt_statuses, |
| &config.qbu_param.frame_preempt_statuses, |
| sizeof(params->qbu_param.frame_preempt_statuses)); |
| break; |
| } |
| |
| } else if (mgmt_request == NET_REQUEST_ETHERNET_GET_TXTIME_PARAM) { |
| if (!is_hw_caps_supported(dev, ETHERNET_TXTIME)) { |
| return -ENOTSUP; |
| } |
| |
| config.txtime_param.queue_id = params->txtime_param.queue_id; |
| config.txtime_param.type = params->txtime_param.type; |
| |
| type = ETHERNET_CONFIG_TYPE_TXTIME_PARAM; |
| |
| ret = api->get_config(dev, type, &config); |
| if (ret) { |
| return ret; |
| } |
| |
| switch (config.txtime_param.type) { |
| case ETHERNET_TXTIME_PARAM_TYPE_ENABLE_QUEUES: |
| params->txtime_param.enable_txtime = |
| config.txtime_param.enable_txtime; |
| break; |
| } |
| } else { |
| return -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM, |
| ethernet_get_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QAV_PARAM, |
| ethernet_get_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_PORTS_NUM, |
| ethernet_get_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QBV_PARAM, |
| ethernet_get_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_QBU_PARAM, |
| ethernet_get_config); |
| |
| NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_GET_TXTIME_PARAM, |
| ethernet_get_config); |
| |
| void ethernet_mgmt_raise_carrier_on_event(struct net_if *iface) |
| { |
| net_mgmt_event_notify(NET_EVENT_ETHERNET_CARRIER_ON, iface); |
| } |
| |
| void ethernet_mgmt_raise_carrier_off_event(struct net_if *iface) |
| { |
| net_mgmt_event_notify(NET_EVENT_ETHERNET_CARRIER_OFF, iface); |
| } |
| |
| void ethernet_mgmt_raise_vlan_enabled_event(struct net_if *iface, uint16_t tag) |
| { |
| #if defined(CONFIG_NET_MGMT_EVENT_INFO) |
| net_mgmt_event_notify_with_info(NET_EVENT_ETHERNET_VLAN_TAG_ENABLED, |
| iface, &tag, sizeof(tag)); |
| #else |
| net_mgmt_event_notify(NET_EVENT_ETHERNET_VLAN_TAG_ENABLED, |
| iface); |
| #endif |
| } |
| |
| void ethernet_mgmt_raise_vlan_disabled_event(struct net_if *iface, uint16_t tag) |
| { |
| #if defined(CONFIG_NET_MGMT_EVENT_INFO) |
| net_mgmt_event_notify_with_info(NET_EVENT_ETHERNET_VLAN_TAG_DISABLED, |
| iface, &tag, sizeof(tag)); |
| #else |
| net_mgmt_event_notify(NET_EVENT_ETHERNET_VLAN_TAG_DISABLED, iface); |
| #endif |
| } |