/* * Copyright (c) 2018 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #define NET_LOG_LEVEL CONFIG_NET_L2_ETHERNET_LOG_LEVEL #include LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); #include #include #include #include #include #if NET_LOG_LEVEL >= LOG_LEVEL_DBG #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) #else #define DBG(fmt, ...) #endif static struct net_if *default_iface; static const uint8_t mac_addr_init[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; static const uint8_t mac_addr_change[6] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x07 }; struct eth_fake_context { struct net_if *iface; uint8_t mac_address[6]; bool auto_negotiation; bool full_duplex; bool link_10bt; bool link_100bt; bool promisc_mode; struct { bool qav_enabled; int idle_slope; int delta_bandwidth; } priority_queues[2]; struct { /* Qbv parameters */ struct { bool gate_status[NET_TC_TX_COUNT]; enum ethernet_gate_state_operation operation; uint32_t time_interval; uint16_t row; } gate_control; uint32_t gate_control_list_len; bool qbv_enabled; struct net_ptp_extended_time base_time; struct net_ptp_time cycle_time; uint32_t extension_time; /* Qbu parameters */ uint32_t hold_advance; uint32_t release_advance; enum ethernet_qbu_preempt_status frame_preempt_statuses[NET_TC_TX_COUNT]; bool qbu_enabled; bool link_partner_status; uint8_t additional_fragment_size : 2; } ports[2]; /* TXTIME parameters */ bool txtime_statuses[NET_TC_TX_COUNT]; }; static struct eth_fake_context eth_fake_data; static void eth_fake_iface_init(struct net_if *iface) { const struct device *dev = net_if_get_device(iface); struct eth_fake_context *ctx = dev->data; ctx->iface = iface; net_if_set_link_addr(iface, ctx->mac_address, sizeof(ctx->mac_address), NET_LINK_ETHERNET); ethernet_init(iface); } static int eth_fake_send(const struct device *dev, struct net_pkt *pkt) { ARG_UNUSED(dev); ARG_UNUSED(pkt); return 0; } static enum ethernet_hw_caps eth_fake_get_capabilities(const struct device *dev) { return ETHERNET_AUTO_NEGOTIATION_SET | ETHERNET_LINK_10BASE_T | ETHERNET_LINK_100BASE_T | ETHERNET_DUPLEX_SET | ETHERNET_QAV | ETHERNET_PROMISC_MODE | ETHERNET_PRIORITY_QUEUES | ETHERNET_QBV | ETHERNET_QBU | ETHERNET_TXTIME; } static int eth_fake_get_total_bandwidth(struct eth_fake_context *ctx) { if (ctx->link_100bt) { return 100 * 1000 * 1000 / 8; } if (ctx->link_10bt) { return 10 * 1000 * 1000 / 8; } /* No link */ return 0; } static void eth_fake_recalc_qav_delta_bandwidth(struct eth_fake_context *ctx) { int bw; int i; bw = eth_fake_get_total_bandwidth(ctx); for (i = 0; i < ARRAY_SIZE(ctx->priority_queues); ++i) { if (bw == 0) { ctx->priority_queues[i].delta_bandwidth = 0; } else { ctx->priority_queues[i].delta_bandwidth = (ctx->priority_queues[i].idle_slope * 100); ctx->priority_queues[i].delta_bandwidth /= bw; } } } static void eth_fake_recalc_qav_idle_slopes(struct eth_fake_context *ctx) { int bw; int i; bw = eth_fake_get_total_bandwidth(ctx); for (i = 0; i < ARRAY_SIZE(ctx->priority_queues); ++i) { ctx->priority_queues[i].idle_slope = (ctx->priority_queues[i].delta_bandwidth * bw) / 100; } } static int eth_fake_set_config(const struct device *dev, enum ethernet_config_type type, const struct ethernet_config *config) { struct eth_fake_context *ctx = dev->data; int priority_queues_num = ARRAY_SIZE(ctx->priority_queues); int ports_num = ARRAY_SIZE(ctx->ports); enum ethernet_qav_param_type qav_param_type; enum ethernet_qbv_param_type qbv_param_type; enum ethernet_qbu_param_type qbu_param_type; enum ethernet_txtime_param_type txtime_param_type; int queue_id, port_id; switch (type) { case ETHERNET_CONFIG_TYPE_AUTO_NEG: if (config->auto_negotiation == ctx->auto_negotiation) { return -EALREADY; } ctx->auto_negotiation = config->auto_negotiation; break; case ETHERNET_CONFIG_TYPE_LINK: if ((config->l.link_10bt && ctx->link_10bt) || (config->l.link_100bt && ctx->link_100bt)) { return -EALREADY; } if (config->l.link_10bt) { ctx->link_10bt = true; ctx->link_100bt = false; } else { ctx->link_10bt = false; ctx->link_100bt = true; } eth_fake_recalc_qav_idle_slopes(ctx); break; case ETHERNET_CONFIG_TYPE_DUPLEX: if (config->full_duplex == ctx->full_duplex) { return -EALREADY; } ctx->full_duplex = config->full_duplex; break; case ETHERNET_CONFIG_TYPE_MAC_ADDRESS: memcpy(ctx->mac_address, config->mac_address.addr, 6); net_if_set_link_addr(ctx->iface, ctx->mac_address, sizeof(ctx->mac_address), NET_LINK_ETHERNET); break; case ETHERNET_CONFIG_TYPE_QAV_PARAM: queue_id = config->qav_param.queue_id; qav_param_type = config->qav_param.type; if (queue_id < 0 || queue_id >= priority_queues_num) { return -EINVAL; } switch (qav_param_type) { case ETHERNET_QAV_PARAM_TYPE_STATUS: ctx->priority_queues[queue_id].qav_enabled = config->qav_param.enabled; break; case ETHERNET_QAV_PARAM_TYPE_IDLE_SLOPE: ctx->priority_queues[queue_id].idle_slope = config->qav_param.idle_slope; eth_fake_recalc_qav_delta_bandwidth(ctx); break; case ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH: ctx->priority_queues[queue_id].delta_bandwidth = config->qav_param.delta_bandwidth; eth_fake_recalc_qav_idle_slopes(ctx); break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_QBV_PARAM: port_id = config->qbv_param.port_id; qbv_param_type = config->qbv_param.type; if (port_id < 0 || port_id >= ports_num) { return -EINVAL; } switch (qbv_param_type) { case ETHERNET_QBV_PARAM_TYPE_STATUS: ctx->ports[port_id].qbv_enabled = config->qbv_param.enabled; break; case ETHERNET_QBV_PARAM_TYPE_TIME: memcpy(&ctx->ports[port_id].cycle_time, &config->qbv_param.cycle_time, sizeof(ctx->ports[port_id].cycle_time)); ctx->ports[port_id].extension_time = config->qbv_param.extension_time; memcpy(&ctx->ports[port_id].base_time, &config->qbv_param.base_time, sizeof(ctx->ports[port_id].base_time)); break; case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST: ctx->ports[port_id].gate_control.gate_status[0] = config->qbv_param.gate_control.gate_status[0]; ctx->ports[port_id].gate_control.gate_status[1] = config->qbv_param.gate_control.gate_status[1]; break; case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN: ctx->ports[port_id].gate_control_list_len = config->qbv_param.gate_control_list_len; break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_QBU_PARAM: port_id = config->qbu_param.port_id; qbu_param_type = config->qbu_param.type; if (port_id < 0 || port_id >= ports_num) { return -EINVAL; } switch (qbu_param_type) { case ETHERNET_QBU_PARAM_TYPE_STATUS: ctx->ports[port_id].qbu_enabled = config->qbu_param.enabled; break; case ETHERNET_QBU_PARAM_TYPE_RELEASE_ADVANCE: ctx->ports[port_id].release_advance = config->qbu_param.release_advance; break; case ETHERNET_QBU_PARAM_TYPE_HOLD_ADVANCE: ctx->ports[port_id].hold_advance = config->qbu_param.hold_advance; break; case ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS: ctx->ports[port_id].link_partner_status = config->qbu_param.link_partner_status; break; case ETHERNET_QBR_PARAM_TYPE_ADDITIONAL_FRAGMENT_SIZE: ctx->ports[port_id].additional_fragment_size = config->qbu_param.additional_fragment_size; break; case ETHERNET_QBU_PARAM_TYPE_PREEMPTION_STATUS_TABLE: memcpy(&ctx->ports[port_id].frame_preempt_statuses, &config->qbu_param.frame_preempt_statuses, sizeof(ctx->ports[port_id].frame_preempt_statuses)); break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_TXTIME_PARAM: queue_id = config->txtime_param.queue_id; txtime_param_type = config->txtime_param.type; if (queue_id < 0 || queue_id >= priority_queues_num) { return -EINVAL; } switch (txtime_param_type) { case ETHERNET_TXTIME_PARAM_TYPE_ENABLE_QUEUES: ctx->txtime_statuses[queue_id] = config->txtime_param.enable_txtime; break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_PROMISC_MODE: if (config->promisc_mode == ctx->promisc_mode) { return -EALREADY; } ctx->promisc_mode = config->promisc_mode; break; default: return -ENOTSUP; } return 0; } static int eth_fake_get_config(const struct device *dev, enum ethernet_config_type type, struct ethernet_config *config) { struct eth_fake_context *ctx = dev->data; int priority_queues_num = ARRAY_SIZE(ctx->priority_queues); int ports_num = ARRAY_SIZE(ctx->ports); enum ethernet_qav_param_type qav_param_type; enum ethernet_qbv_param_type qbv_param_type; enum ethernet_qbu_param_type qbu_param_type; enum ethernet_txtime_param_type txtime_param_type; int queue_id, port_id; switch (type) { case ETHERNET_CONFIG_TYPE_PRIORITY_QUEUES_NUM: config->priority_queues_num = ARRAY_SIZE(ctx->priority_queues); break; case ETHERNET_CONFIG_TYPE_PORTS_NUM: config->ports_num = ARRAY_SIZE(ctx->ports); break; case ETHERNET_CONFIG_TYPE_QAV_PARAM: queue_id = config->qav_param.queue_id; qav_param_type = config->qav_param.type; if (queue_id < 0 || queue_id >= priority_queues_num) { return -EINVAL; } switch (qav_param_type) { case ETHERNET_QAV_PARAM_TYPE_STATUS: config->qav_param.enabled = ctx->priority_queues[queue_id].qav_enabled; break; case ETHERNET_QAV_PARAM_TYPE_IDLE_SLOPE: case ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE: /* No distinction between idle slopes for fake eth */ config->qav_param.idle_slope = ctx->priority_queues[queue_id].idle_slope; break; case ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH: config->qav_param.delta_bandwidth = ctx->priority_queues[queue_id].delta_bandwidth; break; case ETHERNET_QAV_PARAM_TYPE_TRAFFIC_CLASS: /* Default TC for BE - it doesn't really matter here */ config->qav_param.traffic_class = net_tx_priority2tc(NET_PRIORITY_BE); break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_QBV_PARAM: port_id = config->qbv_param.port_id; qbv_param_type = config->qbv_param.type; if (port_id < 0 || port_id >= ports_num) { return -EINVAL; } switch (qbv_param_type) { case ETHERNET_QBV_PARAM_TYPE_STATUS: config->qbv_param.enabled = ctx->ports[port_id].qbv_enabled; break; case ETHERNET_QBV_PARAM_TYPE_TIME: memcpy(&config->qbv_param.base_time, &ctx->ports[port_id].base_time, sizeof(config->qbv_param.base_time)); memcpy(&config->qbv_param.cycle_time, &ctx->ports[port_id].cycle_time, sizeof(config->qbv_param.cycle_time)); config->qbv_param.extension_time = ctx->ports[port_id].extension_time; break; case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN: config->qbv_param.gate_control_list_len = ctx->ports[port_id].gate_control_list_len; break; case ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST: memcpy(&config->qbv_param.gate_control, &ctx->ports[port_id].gate_control, sizeof(config->qbv_param.gate_control)); break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_QBU_PARAM: port_id = config->qbu_param.port_id; qbu_param_type = config->qbu_param.type; if (port_id < 0 || port_id >= ports_num) { return -EINVAL; } switch (qbu_param_type) { case ETHERNET_QBU_PARAM_TYPE_STATUS: config->qbu_param.enabled = ctx->ports[port_id].qbu_enabled; break; case ETHERNET_QBU_PARAM_TYPE_RELEASE_ADVANCE: config->qbu_param.release_advance = ctx->ports[port_id].release_advance; break; case ETHERNET_QBU_PARAM_TYPE_HOLD_ADVANCE: config->qbu_param.hold_advance = ctx->ports[port_id].hold_advance; break; case ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS: config->qbu_param.link_partner_status = ctx->ports[port_id].link_partner_status; break; case ETHERNET_QBR_PARAM_TYPE_ADDITIONAL_FRAGMENT_SIZE: config->qbu_param.additional_fragment_size = ctx->ports[port_id].additional_fragment_size; break; case ETHERNET_QBU_PARAM_TYPE_PREEMPTION_STATUS_TABLE: memcpy(&config->qbu_param.frame_preempt_statuses, &ctx->ports[port_id].frame_preempt_statuses, sizeof(config->qbu_param.frame_preempt_statuses)); break; default: return -ENOTSUP; } break; case ETHERNET_CONFIG_TYPE_TXTIME_PARAM: queue_id = config->txtime_param.queue_id; txtime_param_type = config->txtime_param.type; if (queue_id < 0 || queue_id >= priority_queues_num) { return -EINVAL; } switch (txtime_param_type) { case ETHERNET_TXTIME_PARAM_TYPE_ENABLE_QUEUES: config->txtime_param.enable_txtime = ctx->txtime_statuses[queue_id]; break; default: return -ENOTSUP; } break; default: return -ENOTSUP; } return 0; } static struct ethernet_api eth_fake_api_funcs = { .iface_api.init = eth_fake_iface_init, .get_capabilities = eth_fake_get_capabilities, .set_config = eth_fake_set_config, .get_config = eth_fake_get_config, .send = eth_fake_send, }; static int eth_fake_init(const struct device *dev) { struct eth_fake_context *ctx = dev->data; int i; ctx->auto_negotiation = true; ctx->full_duplex = true; ctx->link_10bt = true; ctx->link_100bt = false; memcpy(ctx->mac_address, mac_addr_init, 6); /* Initialize priority queues */ for (i = 0; i < ARRAY_SIZE(ctx->priority_queues); ++i) { ctx->priority_queues[i].qav_enabled = true; if (i + 1 == ARRAY_SIZE(ctx->priority_queues)) { /* 75% for the last priority queue */ ctx->priority_queues[i].delta_bandwidth = 75; } else { /* 0% for the rest */ ctx->priority_queues[i].delta_bandwidth = 0; } } eth_fake_recalc_qav_idle_slopes(ctx); return 0; } ETH_NET_DEVICE_INIT(eth_fake, "eth_fake", eth_fake_init, NULL, ð_fake_data, NULL, CONFIG_ETH_INIT_PRIORITY, ð_fake_api_funcs, NET_ETH_MTU); #if NET_LOG_LEVEL >= LOG_LEVEL_DBG static const char *iface2str(struct net_if *iface) { #ifdef CONFIG_NET_L2_ETHERNET if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { return "Ethernet"; } #endif #ifdef CONFIG_NET_L2_DUMMY if (net_if_l2(iface) == &NET_L2_GET_NAME(DUMMY)) { return "Dummy"; } #endif return ""; } #endif static void iface_cb(struct net_if *iface, void *user_data) { struct net_if **my_iface = user_data; DBG("Interface %p (%s) [%d]\n", iface, iface2str(iface), net_if_get_by_iface(iface)); if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { if (PART_OF_ARRAY(NET_IF_GET_NAME(eth_fake, 0), iface)) { *my_iface = iface; } } } static void *ethernet_mgmt_setup(void) { net_if_foreach(iface_cb, &default_iface); zassert_not_null(default_iface, "Cannot find test interface"); return NULL; } static void change_mac_when_up(void) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; memcpy(params.mac_address.addr, mac_addr_change, 6); net_if_up(iface); ret = net_mgmt(NET_REQUEST_ETHERNET_SET_MAC_ADDRESS, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "mac address change should not be possible"); } static void change_mac_when_down(void) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; memcpy(params.mac_address.addr, mac_addr_change, 6); net_if_down(iface); ret = net_mgmt(NET_REQUEST_ETHERNET_SET_MAC_ADDRESS, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "unable to change mac address"); ret = memcmp(net_if_get_link_addr(iface)->addr, mac_addr_change, sizeof(mac_addr_change)); zassert_equal(ret, 0, "invalid mac address change"); net_if_up(iface); } ZTEST(net_ethernet_mgmt, test_change_mac) { change_mac_when_up(); change_mac_when_down(); } static void change_auto_neg(bool is_auto_neg) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; params.auto_negotiation = is_auto_neg; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_AUTO_NEGOTIATION, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "invalid auto negotiation change"); } static void change_to_same_auto_neg(bool is_auto_neg) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; params.auto_negotiation = is_auto_neg; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_AUTO_NEGOTIATION, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "invalid change to already auto negotiation"); } ZTEST(net_ethernet_mgmt, test_change_auto_neg) { change_auto_neg(false); change_to_same_auto_neg(false); change_auto_neg(true); } static void change_link_10bt(void) { struct net_if *iface = default_iface; struct ethernet_req_params params = { 0 }; int ret; params.l.link_10bt = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_LINK, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "invalid link change"); } static void change_link_100bt(void) { struct net_if *iface = default_iface; struct ethernet_req_params params = { 0 }; int ret; params.l.link_100bt = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_LINK, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "invalid link change"); } static void change_same_link_100bt(void) { struct net_if *iface = default_iface; struct ethernet_req_params params = { 0 }; int ret; params.l.link_100bt = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_LINK, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "invalid same link change"); } static void change_unsupported_link_1000bt(void) { struct net_if *iface = default_iface; struct ethernet_req_params params = { 0 }; int ret; params.l.link_1000bt = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_LINK, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "invalid change to unsupported link"); } ZTEST(net_ethernet_mgmt, test_change_link) { change_link_100bt(); change_same_link_100bt(); change_unsupported_link_1000bt(); change_link_10bt(); } static void change_duplex(bool is_full_duplex) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; params.full_duplex = is_full_duplex; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_DUPLEX, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "invalid duplex change"); } static void change_same_duplex(bool is_full_duplex) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; params.full_duplex = is_full_duplex; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_DUPLEX, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "invalid change to already set duplex"); } ZTEST(net_ethernet_mgmt, test_change_duplex) { change_duplex(false); change_same_duplex(false); change_duplex(true); } ZTEST(net_ethernet_mgmt, test_change_qav_params) { struct net_if *iface = default_iface; const struct device *dev = net_if_get_device(iface); struct eth_fake_context *ctx = dev->data; struct ethernet_req_params params; int available_priority_queues; int i; int ret; /* Try to get the number of the priority queues */ ret = net_mgmt(NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not get the number of priority queues"); available_priority_queues = params.priority_queues_num; zassert_not_equal(available_priority_queues, 0, "returned no priority queues"); zassert_equal(available_priority_queues, ARRAY_SIZE(ctx->priority_queues), "an invalid number of priority queues returned"); for (i = 0; i < available_priority_queues; ++i) { /* Try to set correct params to a correct queue id */ params.qav_param.queue_id = i; /* Disable Qav for queue */ params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_STATUS; params.qav_param.enabled = false; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not disable qav"); /* Invert it to make sure the read-back value is proper */ params.qav_param.enabled = true; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read qav status"); zassert_equal(false, params.qav_param.enabled, "qav should be disabled"); /* Re-enable Qav for queue */ params.qav_param.enabled = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not enable qav"); /* Invert it to make sure the read-back value is proper */ params.qav_param.enabled = false; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read qav status"); zassert_equal(true, params.qav_param.enabled, "qav should be enabled"); /* Starting with delta bandwidth */ params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH; params.qav_param.delta_bandwidth = 10U; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set delta bandwidth"); /* Reset local value - read-back and verify it */ params.qav_param.delta_bandwidth = 0U; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read delta bandwidth"); zassert_equal(params.qav_param.delta_bandwidth, 10, "delta bandwidth did not change"); /* And them the idle slope */ params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_IDLE_SLOPE; params.qav_param.idle_slope = 10U; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set idle slope"); /* Reset local value - read-back and verify it */ params.qav_param.idle_slope = 0U; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read idle slope"); zassert_equal(params.qav_param.idle_slope, 10, "idle slope did not change"); /* Oper idle slope should also be the same */ params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read oper idle slope"); zassert_equal(params.qav_param.oper_idle_slope, 10, "oper idle slope should equal admin idle slope"); /* Now try to set incorrect params to a correct queue */ params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH; params.qav_param.delta_bandwidth = -10; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "allowed to set invalid delta bandwidth"); params.qav_param.delta_bandwidth = 101U; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "allowed to set invalid delta bandwidth"); } /* Try to set read-only parameters */ params.qav_param.queue_id = 0; params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_OPER_IDLE_SLOPE; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "should not be able to set oper idle slope"); params.qav_param.queue_id = 0; params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_TRAFFIC_CLASS; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "should not be able to set traffic class"); /* Now try to set valid parameters to an invalid queue id */ params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_DELTA_BANDWIDTH; params.qav_param.queue_id = available_priority_queues; params.qav_param.delta_bandwidth = 10U; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "should not be able to set delta bandwidth"); params.qav_param.type = ETHERNET_QAV_PARAM_TYPE_IDLE_SLOPE; params.qav_param.idle_slope = 10U; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QAV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "should not be able to set idle slope"); } ZTEST(net_ethernet_mgmt, test_change_qbv_params) { struct net_if *iface = default_iface; const struct device *dev = net_if_get_device(iface); struct eth_fake_context *ctx = dev->data; struct ethernet_req_params params; struct net_ptp_time cycle_time; int available_ports; int i; int ret; /* Try to get the number of the ports */ ret = net_mgmt(NET_REQUEST_ETHERNET_GET_PORTS_NUM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not get the number of ports (%d)", ret); available_ports = params.ports_num; zassert_not_equal(available_ports, 0, "returned no priority queues"); zassert_equal(available_ports, ARRAY_SIZE(ctx->ports), "an invalid number of ports returned"); for (i = 0; i < available_ports; ++i) { /* Try to set correct params to a correct queue id */ params.qbv_param.port_id = i; /* Disable Qbv for port */ params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_STATUS; params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_ADMIN; params.qbv_param.enabled = false; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not disable qbv for port %d", i); /* Invert it to make sure the read-back value is proper */ params.qbv_param.enabled = true; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read qbv status (%d)", ret); zassert_equal(false, params.qbv_param.enabled, "qbv should be disabled"); /* Re-enable Qbv for queue */ params.qbv_param.enabled = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not enable qbv (%d)", ret); /* Invert it to make sure the read-back value is proper */ params.qbv_param.enabled = false; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read qbv status (%d)", ret); zassert_equal(true, params.qbv_param.enabled, "qbv should be enabled"); /* Then the Qbv parameter checks */ params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_TIME; params.qbv_param.base_time.second = 10ULL; params.qbv_param.base_time.fract_nsecond = 20ULL; params.qbv_param.cycle_time.second = 30ULL; params.qbv_param.cycle_time.nanosecond = 20; params.qbv_param.extension_time = 40; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set base time (%d)", ret); /* Reset local value - read-back and verify it */ params.qbv_param.base_time.second = 0ULL; params.qbv_param.base_time.fract_nsecond = 0ULL; params.qbv_param.cycle_time.second = 0ULL; params.qbv_param.cycle_time.nanosecond = 0; params.qbv_param.extension_time = 0; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read times (%d)", ret); zassert_equal(params.qbv_param.base_time.second, 10ULL, "base_time.second did not change"); zassert_equal(params.qbv_param.base_time.fract_nsecond, 20ULL, "base_time.fract_nsecond did not change"); cycle_time.second = 30ULL; cycle_time.nanosecond = 20; zassert_true(params.qbv_param.cycle_time.second == cycle_time.second && params.qbv_param.cycle_time.nanosecond == cycle_time.nanosecond, "cycle time did not change"); zassert_equal(params.qbv_param.extension_time, 40, "extension time did not change"); params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST; params.qbv_param.gate_control.gate_status[0] = true; params.qbv_param.gate_control.gate_status[1] = false; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set gate control list (%d)", ret); /* Reset local value - read-back and verify it */ ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read gate control (%d)", ret); params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_GATE_CONTROL_LIST_LEN; params.qbv_param.gate_control_list_len = 1; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set gate control list len (%d)", ret); /* Reset local value - read-back and verify it */ params.qbv_param.gate_control_list_len = 0; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read gate control list len (%d)", ret); zassert_equal(params.qbv_param.gate_control_list_len, 1, "gate control list len did not change"); } /* Try to set read-only parameters */ params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_OPER; params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_TIME; params.qbv_param.extension_time = 50; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "allowed to set oper status parameter (%d)", ret); params.qbv_param.state = ETHERNET_QBV_STATE_TYPE_ADMIN; params.qbv_param.type = ETHERNET_QBV_PARAM_TYPE_TIME; params.qbv_param.base_time.fract_nsecond = 1000000000; params.qbv_param.cycle_time.nanosecond = 1000000000; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBV_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_not_equal(ret, 0, "allowed to set base_time parameter (%d)", ret); } ZTEST(net_ethernet_mgmt, test_change_qbu_params) { struct net_if *iface = default_iface; const struct device *dev = net_if_get_device(iface); struct eth_fake_context *ctx = dev->data; struct ethernet_req_params params; int available_ports; int i, j; int ret; /* Try to get the number of the ports */ ret = net_mgmt(NET_REQUEST_ETHERNET_GET_PORTS_NUM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not get the number of ports (%d)", ret); available_ports = params.ports_num; zassert_not_equal(available_ports, 0, "returned no priority queues"); zassert_equal(available_ports, ARRAY_SIZE(ctx->ports), "an invalid number of ports returned"); for (i = 0; i < available_ports; ++i) { /* Try to set correct params to a correct queue id */ params.qbu_param.port_id = i; /* Disable Qbu for port */ params.qbu_param.type = ETHERNET_QBU_PARAM_TYPE_STATUS; params.qbu_param.enabled = false; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not disable qbu for port %d (%d)", i, ret); /* Invert it to make sure the read-back value is proper */ params.qbu_param.enabled = true; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read qbu status (%d)", ret); zassert_equal(false, params.qbu_param.enabled, "qbu should be disabled"); /* Re-enable Qbu for queue */ params.qbu_param.enabled = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not enable qbu (%d)", ret); /* Invert it to make sure the read-back value is proper */ params.qbu_param.enabled = false; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read qbu status (%d)", ret); zassert_equal(true, params.qbu_param.enabled, "qbu should be enabled"); /* Then the Qbu parameter checks */ params.qbu_param.type = ETHERNET_QBU_PARAM_TYPE_RELEASE_ADVANCE; params.qbu_param.release_advance = 10; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set release advance (%d)", ret); /* Reset local value - read-back and verify it */ params.qbu_param.release_advance = 0; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read release advance (%d)", ret); zassert_equal(params.qbu_param.release_advance, 10, "release_advance did not change"); /* And them the hold advance */ params.qbu_param.type = ETHERNET_QBU_PARAM_TYPE_HOLD_ADVANCE; params.qbu_param.hold_advance = 20; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set hold advance (%d)", ret); /* Reset local value - read-back and verify it */ params.qbu_param.hold_advance = 0; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read hold advance (%d)", ret); zassert_equal(params.qbu_param.hold_advance, 20, "hold advance did not change"); params.qbu_param.type = ETHERNET_QBR_PARAM_TYPE_LINK_PARTNER_STATUS; params.qbu_param.link_partner_status = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, -EINVAL, "could set link partner status (%d)", ret); /* Reset local value - read-back and verify it */ params.qbu_param.link_partner_status = false; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read link partner status (%d)", ret); zassert_equal(params.qbu_param.link_partner_status, false, "link partner status changed"); params.qbu_param.type = ETHERNET_QBR_PARAM_TYPE_ADDITIONAL_FRAGMENT_SIZE; params.qbu_param.additional_fragment_size = 2; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set additional frag size (%d)", ret); /* Reset local value - read-back and verify it */ params.qbu_param.additional_fragment_size = 1; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read additional frag size (%d)", ret); zassert_equal(params.qbu_param.additional_fragment_size, 2, "additional fragment size did not change"); params.qbu_param.type = ETHERNET_QBU_PARAM_TYPE_PREEMPTION_STATUS_TABLE; for (j = 0; j < ARRAY_SIZE(params.qbu_param.frame_preempt_statuses); j++) { /* Set the preempt status for different priorities. */ params.qbu_param.frame_preempt_statuses[j] = j % 2 ? ETHERNET_QBU_STATUS_EXPRESS : ETHERNET_QBU_STATUS_PREEMPTABLE; } ret = net_mgmt(NET_REQUEST_ETHERNET_SET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not set frame preempt status (%d)", ret); /* Reset local value - read-back and verify it */ for (j = 0; j < ARRAY_SIZE(params.qbu_param.frame_preempt_statuses); j++) { params.qbu_param.frame_preempt_statuses[j] = j % 2 ? ETHERNET_QBU_STATUS_PREEMPTABLE : ETHERNET_QBU_STATUS_EXPRESS; } ret = net_mgmt(NET_REQUEST_ETHERNET_GET_QBU_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read frame preempt status (%d)", ret); for (j = 0; j < ARRAY_SIZE(params.qbu_param.frame_preempt_statuses); j++) { zassert_equal( params.qbu_param.frame_preempt_statuses[j], j % 2 ? ETHERNET_QBU_STATUS_EXPRESS : ETHERNET_QBU_STATUS_PREEMPTABLE, "frame preempt status did not change"); } } } ZTEST(net_ethernet_mgmt, test_change_txtime_params) { struct net_if *iface = default_iface; const struct device *dev = net_if_get_device(iface); struct eth_fake_context *ctx = dev->data; struct ethernet_req_params params; int available_priority_queues; int ret; int i; /* Try to get the number of the priority queues */ ret = net_mgmt(NET_REQUEST_ETHERNET_GET_PRIORITY_QUEUES_NUM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not get the number of priority queues"); available_priority_queues = params.priority_queues_num; zassert_not_equal(available_priority_queues, 0, "returned no priority queues"); zassert_equal(available_priority_queues, ARRAY_SIZE(ctx->priority_queues), "an invalid number of priority queues returned"); net_if_up(iface); /* Make sure we cannot enable txtime if the interface is up */ params.txtime_param.queue_id = 0; params.txtime_param.type = ETHERNET_TXTIME_PARAM_TYPE_ENABLE_QUEUES; params.txtime_param.enable_txtime = false; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_TXTIME_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, -EACCES, "could disable TXTIME for queue 0 (%d)", ret); net_if_down(iface); for (i = 0; i < available_priority_queues; ++i) { /* Try to set correct params to a correct queue id */ params.txtime_param.queue_id = i; /* Disable TXTIME for queue */ params.txtime_param.type = ETHERNET_TXTIME_PARAM_TYPE_ENABLE_QUEUES; params.txtime_param.enable_txtime = false; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_TXTIME_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not disable TXTIME for queue %d (%d)", i, ret); /* Invert it to make sure the read-back value is proper */ params.txtime_param.enable_txtime = true; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_TXTIME_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read txtime status (%d)", ret); zassert_equal(false, params.txtime_param.enable_txtime, "txtime should be disabled"); /* Re-enable TXTIME for queue */ params.txtime_param.enable_txtime = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_TXTIME_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not enable txtime (%d)", ret); /* Invert it to make sure the read-back value is proper */ params.txtime_param.enable_txtime = false; ret = net_mgmt(NET_REQUEST_ETHERNET_GET_TXTIME_PARAM, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "could not read txtime status (%d)", ret); zassert_equal(true, params.txtime_param.enable_txtime, "txtime should be enabled"); } } static void change_promisc_mode(bool mode) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; params.promisc_mode = mode; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_PROMISC_MODE, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, 0, "invalid promisc mode change"); } static void change_promisc_mode_on(void) { change_promisc_mode(true); } static void change_promisc_mode_off(void) { change_promisc_mode(false); } static void change_to_same_promisc_mode(void) { struct net_if *iface = default_iface; struct ethernet_req_params params; int ret; params.promisc_mode = true; ret = net_mgmt(NET_REQUEST_ETHERNET_SET_PROMISC_MODE, iface, ¶ms, sizeof(struct ethernet_req_params)); zassert_equal(ret, -EALREADY, "invalid change to already set promisc mode"); } ZTEST(net_ethernet_mgmt, test_change_to_promisc_mode) { change_promisc_mode_on(); change_to_same_promisc_mode(); change_promisc_mode_off(); } ZTEST_SUITE(net_ethernet_mgmt, NULL, ethernet_mgmt_setup, NULL, NULL, NULL);