zephyr/subsys/net/l2/ethernet/ethernet_mgmt.c
Lukasz Majewski 25addd0984 net: ethernet: Add support for setting T1S PLCA parameters
The Zephyr's core ethernet code had to be adjusted to support setting T1S
PLCA parameters from user Zephyr programs.

Such approach allows more flexibility, as T1S network configuration;
especially PLCA node numbers, can be assigned not only via device tree
at compile time. For example user can read them from EEPROM and then
configure the network accordingly.

For now - the union in struct ethernet_t1s_param only consists of plca
structure. This can change in the future, when other T1S OA parameters -
like Receive/Transmit Cut-Through Enable (bits RXCTE/TXCTE in OA_CONFIG0
register) are made adjustable from user program.

Signed-off-by: Lukasz Majewski <lukma@denx.de>
2023-11-29 10:06:30 +01:00

488 lines
13 KiB
C

/*
* 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_NATIVE_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, &params->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, &params->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, &params->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, &params->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, &params->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 if (mgmt_request == NET_REQUEST_ETHERNET_SET_T1S_PARAM) {
if (net_if_is_up(iface)) {
return -EACCES;
}
memcpy(&config.t1s_param, &params->t1s_param,
sizeof(struct ethernet_t1s_param));
type = ETHERNET_CONFIG_TYPE_T1S_PARAM;
} 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);
NET_MGMT_REGISTER_REQUEST_HANDLER(NET_REQUEST_ETHERNET_SET_T1S_PARAM,
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(&params->qbv_param.cycle_time,
&config.qbv_param.cycle_time,
sizeof(params->qbv_param.cycle_time));
memcpy(&params->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(&params->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(&params->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
}