From 8fe52e57a38b062eae25e261476dddcbef180d68 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 10 Dec 2025 01:55:08 +0530 Subject: [PATCH 1/4] net: l2: ethernet: Add new stats API Add a new API that takes stat type, the networking stack only needs NATIVE stats per-packet, it doesn't need to update vendor stats per-packet. This saves unncessary exchanges in case driver needs to query the firmware for the vendor stats. Signed-off-by: Chaitanya Tata --- doc/releases/release-notes-4.4.rst | 7 + include/zephyr/net/ethernet.h | 18 +++ subsys/net/l2/ethernet/eth_stats.h | 178 ++++++------------------ subsys/net/l2/ethernet/ethernet_stats.c | 10 +- 4 files changed, 78 insertions(+), 135 deletions(-) diff --git a/doc/releases/release-notes-4.4.rst b/doc/releases/release-notes-4.4.rst index 8ede8b4a2a2cf..9479cb2287be1 100644 --- a/doc/releases/release-notes-4.4.rst +++ b/doc/releases/release-notes-4.4.rst @@ -130,6 +130,13 @@ New APIs and options * :c:struct:`net_eth_mac_config` * :c:macro:`NET_ETH_MAC_DT_CONFIG_INIT` and :c:macro:`NET_ETH_MAC_DT_INST_CONFIG_INIT` + * Added :c:enum:`ethernet_stats_type` and optional ``get_stats_type`` callback in + :c:struct:`ethernet_api` to allow filtering of ethernet statistics by type + (common, vendor, or all). Drivers that support vendor-specific statistics can + implement ``get_stats_type`` to skip expensive FW queries when only common stats + are requested. The existing ``get_stats`` API remains unchanged for backward + compatibility. + * Flash * :dtcompatible:`jedec,mspi-nor` now allows MSPI configuration of read, write and diff --git a/include/zephyr/net/ethernet.h b/include/zephyr/net/ethernet.h index f9dac8b1c5368..22110d6fc6d22 100644 --- a/include/zephyr/net/ethernet.h +++ b/include/zephyr/net/ethernet.h @@ -529,6 +529,16 @@ struct ethernet_config { /** @endcond */ +/** Ethernet statistics type (bitmap) */ +enum ethernet_stats_type { + /** Common statistics only (excludes vendor statistics) */ + ETHERNET_STATS_TYPE_COMMON = BIT(0), + /** Vendor statistics only */ + ETHERNET_STATS_TYPE_VENDOR = BIT(1), + /** All statistics */ + ETHERNET_STATS_TYPE_ALL = 0xFFFFFFFFU, +}; + /** Ethernet L2 API operations. */ struct ethernet_api { /** @@ -543,6 +553,14 @@ struct ethernet_api { */ #if defined(CONFIG_NET_STATISTICS_ETHERNET) struct net_stats_eth *(*get_stats)(const struct device *dev); + + /** Optional function to collect ethernet specific statistics with + * type filter. If NULL, get_stats() will be called instead, which + * is equivalent to calling this with ETHERNET_STATS_TYPE_ALL. + * @param type Bitmask of ethernet_stats_type values. + */ + struct net_stats_eth *(*get_stats_type)(const struct device *dev, + uint32_t type); #endif /** Start the device */ diff --git a/subsys/net/l2/ethernet/eth_stats.h b/subsys/net/l2/ethernet/eth_stats.h index b9860938f573b..a30b5b8647c42 100644 --- a/subsys/net/l2/ethernet/eth_stats.h +++ b/subsys/net/l2/ethernet/eth_stats.h @@ -14,211 +14,123 @@ #include #include -static inline void eth_stats_update_bytes_rx(struct net_if *iface, - uint32_t bytes) +static inline struct net_stats_eth *eth_stats_get_common(struct net_if *iface) { const struct ethernet_api *api = (const struct ethernet_api *) net_if_get_device(iface)->api; - struct net_stats_eth *stats; - - if (!api->get_stats) { - return; - } - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (api->get_stats_type != NULL) { + return api->get_stats_type(net_if_get_device(iface), + ETHERNET_STATS_TYPE_COMMON); + } else if (api->get_stats != NULL) { + return api->get_stats(net_if_get_device(iface)); } - stats->bytes.received += bytes; + return NULL; } -static inline void eth_stats_update_bytes_tx(struct net_if *iface, - uint32_t bytes) +static inline void eth_stats_update_bytes_rx(struct net_if *iface, uint32_t bytes) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; + struct net_stats_eth *stats = eth_stats_get_common(iface); - if (!api->get_stats) { - return; + if (stats != NULL) { + stats->bytes.received += bytes; } +} - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; - } +static inline void eth_stats_update_bytes_tx(struct net_if *iface, uint32_t bytes) +{ + struct net_stats_eth *stats = eth_stats_get_common(iface); - stats->bytes.sent += bytes; + if (stats != NULL) { + stats->bytes.sent += bytes; + } } static inline void eth_stats_update_pkts_rx(struct net_if *iface) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; + struct net_stats_eth *stats = eth_stats_get_common(iface); - if (!api->get_stats) { - return; - } - - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (stats != NULL) { + stats->pkts.rx++; } - - stats->pkts.rx++; } static inline void eth_stats_update_pkts_tx(struct net_if *iface) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; - - if (!api->get_stats) { - return; - } + struct net_stats_eth *stats = eth_stats_get_common(iface); - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (stats != NULL) { + stats->pkts.tx++; } - - stats->pkts.tx++; } static inline void eth_stats_update_broadcast_rx(struct net_if *iface) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; - - if (!api->get_stats) { - return; - } + struct net_stats_eth *stats = eth_stats_get_common(iface); - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (stats != NULL) { + stats->broadcast.rx++; } - - stats->broadcast.rx++; } static inline void eth_stats_update_broadcast_tx(struct net_if *iface) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; + struct net_stats_eth *stats = eth_stats_get_common(iface); - if (!api->get_stats) { - return; + if (stats != NULL) { + stats->broadcast.tx++; } - - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; - } - - stats->broadcast.tx++; } static inline void eth_stats_update_multicast_rx(struct net_if *iface) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; - - if (!api->get_stats) { - return; - } + struct net_stats_eth *stats = eth_stats_get_common(iface); - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (stats != NULL) { + stats->multicast.rx++; } - - stats->multicast.rx++; } static inline void eth_stats_update_multicast_tx(struct net_if *iface) { - const struct ethernet_api *api = (const struct ethernet_api *) - net_if_get_device(iface)->api; - struct net_stats_eth *stats; + struct net_stats_eth *stats = eth_stats_get_common(iface); - if (!api->get_stats) { - return; - } - - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (stats != NULL) { + stats->multicast.tx++; } - - stats->multicast.tx++; } - static inline void eth_stats_update_errors_rx(struct net_if *iface) { struct net_stats_eth *stats; - const struct ethernet_api *api; if (!iface) { return; } - api = ((const struct ethernet_api *) - net_if_get_device(iface)->api); - - if (!api->get_stats) { - return; - } - - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + stats = eth_stats_get_common(iface); + if (stats != NULL) { + stats->errors.rx++; } - - stats->errors.rx++; } static inline void eth_stats_update_errors_tx(struct net_if *iface) { - struct net_stats_eth *stats; - const struct ethernet_api *api = ((const struct ethernet_api *) - net_if_get_device(iface)->api); - - if (!api->get_stats) { - return; - } + struct net_stats_eth *stats = eth_stats_get_common(iface); - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; + if (stats != NULL) { + stats->errors.tx++; } - - stats->errors.tx++; } static inline void eth_stats_update_unknown_protocol(struct net_if *iface) { - struct net_stats_eth *stats; - const struct ethernet_api *api = ((const struct ethernet_api *) - net_if_get_device(iface)->api); + struct net_stats_eth *stats = eth_stats_get_common(iface); - if (!api->get_stats) { - return; + if (stats != NULL) { + stats->unknown_protocol++; } - - stats = api->get_stats(net_if_get_device(iface)); - if (!stats) { - return; - } - - stats->unknown_protocol++; } #else /* CONFIG_NET_STATISTICS_ETHERNET */ diff --git a/subsys/net/l2/ethernet/ethernet_stats.c b/subsys/net/l2/ethernet/ethernet_stats.c index 57d354623f66f..3dd211e4cf7d4 100644 --- a/subsys/net/l2/ethernet/ethernet_stats.c +++ b/subsys/net/l2/ethernet/ethernet_stats.c @@ -31,12 +31,18 @@ static int eth_stats_get(uint64_t mgmt_request, struct net_if *iface, } eth = net_if_get_device(iface)->api; - if (eth == NULL || eth->get_stats == NULL) { + if (eth == NULL || + (eth->get_stats == NULL && eth->get_stats_type == NULL)) { return -ENOENT; } len_chk = sizeof(struct net_stats_eth); - src = eth->get_stats(net_if_get_device(iface)); + if (eth->get_stats_type != NULL) { + src = eth->get_stats_type(net_if_get_device(iface), + ETHERNET_STATS_TYPE_ALL); + } else { + src = eth->get_stats(net_if_get_device(iface)); + } break; } From 9b40d0956da54e7a8b9e3ae4b172e68aa19b5f06 Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 10 Dec 2025 01:56:31 +0530 Subject: [PATCH 2/4] drivers: nrf_wifi: Switch to new stats API nRF70 queries FW to fet the stats, use the new stats API and filter FW query depending on the type. Signed-off-by: Chaitanya Tata --- drivers/wifi/nrf_wifi/inc/net_if.h | 5 +- drivers/wifi/nrf_wifi/src/fmac_main.c | 2 +- drivers/wifi/nrf_wifi/src/net_if.c | 66 +++++++++++++-------------- 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/drivers/wifi/nrf_wifi/inc/net_if.h b/drivers/wifi/nrf_wifi/inc/net_if.h index 315740ac5d9df..b6c48e00598df 100644 --- a/drivers/wifi/nrf_wifi/inc/net_if.h +++ b/drivers/wifi/nrf_wifi/inc/net_if.h @@ -56,7 +56,10 @@ enum nrf_wifi_status nrf_wifi_if_carr_state_chg(void *os_vif_ctx, int nrf_wifi_stats_get(const struct device *dev, struct net_stats_wifi *stats); -struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev); +#ifdef CONFIG_NET_STATISTICS_ETHERNET +struct net_stats_eth *nrf_wifi_eth_stats_get_type(const struct device *dev, + uint32_t type); +#endif /* CONFIG_NET_STATISTICS_ETHERNET */ void nrf_wifi_set_iface_event_handler(void *os_vif_ctx, struct nrf_wifi_umac_event_set_interface *event, diff --git a/drivers/wifi/nrf_wifi/src/fmac_main.c b/drivers/wifi/nrf_wifi/src/fmac_main.c index 897cb76809cb6..a113563cce16c 100644 --- a/drivers/wifi/nrf_wifi/src/fmac_main.c +++ b/drivers/wifi/nrf_wifi/src/fmac_main.c @@ -978,7 +978,7 @@ static const struct net_wifi_mgmt_offload wifi_offload_ops = { .wifi_iface.get_capabilities = nrf_wifi_if_caps_get, .wifi_iface.send = nrf_wifi_if_send, #ifdef CONFIG_NET_STATISTICS_ETHERNET - .wifi_iface.get_stats = nrf_wifi_eth_stats_get, + .wifi_iface.get_stats_type = nrf_wifi_eth_stats_get_type, #endif /* CONFIG_NET_STATISTICS_ETHERNET */ #ifdef CONFIG_NET_L2_WIFI_MGMT .wifi_mgmt_api = &nrf_wifi_mgmt_ops, diff --git a/drivers/wifi/nrf_wifi/src/net_if.c b/drivers/wifi/nrf_wifi/src/net_if.c index 8f7750031476c..ea1b146e0538f 100644 --- a/drivers/wifi/nrf_wifi/src/net_if.c +++ b/drivers/wifi/nrf_wifi/src/net_if.c @@ -1210,7 +1210,8 @@ int nrf_wifi_if_set_config_zep(const struct device *dev, } #ifdef CONFIG_NET_STATISTICS_ETHERNET -struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) +struct net_stats_eth *nrf_wifi_eth_stats_get_type(const struct device *dev, + uint32_t type) { struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; #ifdef CONFIG_NET_STATISTICS_ETHERNET_VENDOR @@ -1222,24 +1223,34 @@ struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) const uint8_t *fw_stats_bytes; size_t i; int vendor_idx = 0; -#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ + const char **key_ptr; + uint32_t *val_ptr; + uint32_t val; +#endif if (!dev) { LOG_ERR("%s Device not found", __func__); - goto out; + goto err; } vif_ctx_zep = dev->data; if (!vif_ctx_zep) { LOG_ERR("%s: vif_ctx_zep is NULL", __func__); - goto out; + goto err; + } + + if (!(type & ETHERNET_STATS_TYPE_VENDOR)) { +#ifdef CONFIG_NET_STATISTICS_ETHERNET_VENDOR + vif_ctx_zep->eth_stats.vendor = NULL; +#endif + goto done; } #ifdef CONFIG_NET_STATISTICS_ETHERNET_VENDOR rpu_ctx_zep = vif_ctx_zep->rpu_ctx_zep; if (!rpu_ctx_zep || !rpu_ctx_zep->rpu_ctx) { LOG_ERR("%s: rpu_ctx_zep or rpu_ctx is NULL", __func__); - goto out; + goto err; } memset(&stats, 0, sizeof(stats)); @@ -1248,7 +1259,7 @@ struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) &stats); if (status != NRF_WIFI_STATUS_SUCCESS) { LOG_ERR("%s: Failed to get RPU stats", __func__); - goto ret; + goto done; } /* Treat stats.fw as a blob and divide into uint32_t chunks */ @@ -1257,49 +1268,34 @@ struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) fw_stats_bytes = (const uint8_t *)&stats.fw; vendor_idx = 0; - for (i = 0; i < num_uint32 && vendor_idx < MAX_VENDOR_STATS - 1; i++) { - uint32_t val; - const char **key_ptr; - uint32_t *val_ptr; - - /* Extract uint32_t value from blob */ - memcpy(&val, fw_stats_bytes + i * sizeof(uint32_t), sizeof(uint32_t)); + memcpy(&val, fw_stats_bytes + i * sizeof(uint32_t), + sizeof(uint32_t)); - /* Create key name */ - snprintk(vif_ctx_zep->vendor_key_strings[vendor_idx], 16, "fw_%zu", i); + snprintk(vif_ctx_zep->vendor_key_strings[vendor_idx], 16, + "fw_%zu", i); - /* Assign key */ - key_ptr = (const char **) - &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].key; + key_ptr = (const char **) &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].key; *key_ptr = vif_ctx_zep->vendor_key_strings[vendor_idx]; - /* Assign value */ - val_ptr = (uint32_t *) - &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].value; + val_ptr = (uint32_t *) &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].value; *val_ptr = val; vendor_idx++; } - /* Null terminator entry */ - { - const char **key_ptr = (const char **) - &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].key; - uint32_t *val_ptr = (uint32_t *) - &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].value; - - *key_ptr = NULL; - *val_ptr = 0; - } + key_ptr = (const char **) &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].key; + val_ptr = (uint32_t *) &vif_ctx_zep->eth_stats_vendor_data[vendor_idx].value; + *key_ptr = NULL; + *val_ptr = 0; - /* Point to the static vendor data */ vif_ctx_zep->eth_stats.vendor = vif_ctx_zep->eth_stats_vendor_data; +#endif -ret: -#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ +done: return &vif_ctx_zep->eth_stats; -out: + +err: return NULL; } #endif /* CONFIG_NET_STATISTICS_ETHERNET */ From 135ced9e665b55797383831d319fee292fcb994a Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 10 Dec 2025 02:04:06 +0530 Subject: [PATCH 3/4] tests: drivers: nrf_wifi: Add twister combo for vendor stats This ensures vendor stats always builds. Signed-off-by: Chaitanya Tata --- tests/drivers/wifi/nrf_wifi/testcase.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/drivers/wifi/nrf_wifi/testcase.yaml b/tests/drivers/wifi/nrf_wifi/testcase.yaml index 085d02f407d25..6dc68db29a977 100644 --- a/tests/drivers/wifi/nrf_wifi/testcase.yaml +++ b/tests/drivers/wifi/nrf_wifi/testcase.yaml @@ -44,3 +44,7 @@ tests: extra_configs: - CONFIG_NET_PKT_BUF_TX_DATA_ALLOC_ALIGN_LEN=4 - CONFIG_NET_BUF_VARIABLE_DATA_SIZE=y + drivers.wifi.build.ethernet_stats: + extra_configs: + - CONFIG_NET_STATISTICS_ETHERNET=y + - CONFIG_NET_STATISTICS_ETHERNET_VENDOR=y From f70b105a69737795ea27a605f3dbdb060f26e8ca Mon Sep 17 00:00:00 2001 From: Chaitanya Tata Date: Wed, 10 Dec 2025 22:46:10 +0530 Subject: [PATCH 4/4] net: lib: shell: Rejig statistics Use the newly added API to get specific type of stats (if supported) but also keep the backwards compatibility. Signed-off-by: Chaitanya Tata --- subsys/net/lib/shell/stats.c | 259 ++++++++++++++++++++--------------- 1 file changed, 152 insertions(+), 107 deletions(-) diff --git a/subsys/net/lib/shell/stats.c b/subsys/net/lib/shell/stats.c index bac8eb31f2358..76a985f285fd4 100644 --- a/subsys/net/lib/shell/stats.c +++ b/subsys/net/lib/shell/stats.c @@ -9,6 +9,7 @@ LOG_MODULE_DECLARE(net_shell); #include +#include #include "net_shell_private.h" @@ -21,6 +22,12 @@ enum net_shell_stats_format { NET_SHELL_STATS_FORMAT_BOTH }; +/** Shell stats options passed via user_data */ +struct net_shell_stats_options { + enum net_shell_stats_format format; + uint32_t type; /* Bitmask of ethernet_stats_type */ +}; + #if defined(CONFIG_NET_STATISTICS) @@ -55,74 +62,86 @@ static const char *priority2str(enum net_priority priority) static void print_eth_stats(struct net_if *iface, struct net_stats_eth *data, const struct shell *sh, struct net_shell_user_data *user_data) { - PR("Statistics for Ethernet interface %p [%d]\n", iface, - net_if_get_by_iface(iface)); - - PR("Bytes received : %llu\n", data->bytes.received); - PR("Bytes sent : %llu\n", data->bytes.sent); - PR("Packets received : %u\n", data->pkts.rx); - PR("Packets sent : %u\n", data->pkts.tx); - PR("Bcast received : %u\n", data->broadcast.rx); - PR("Bcast sent : %u\n", data->broadcast.tx); - PR("Mcast received : %u\n", data->multicast.rx); - PR("Mcast sent : %u\n", data->multicast.tx); - - PR("Send errors : %u\n", data->errors.tx); - PR("Receive errors : %u\n", data->errors.rx); - PR("Collisions : %u\n", data->collisions); - PR("Send Drops : %u\n", data->tx_dropped); - PR("Send timeouts : %u\n", data->tx_timeout_count); - PR("Send restarts : %u\n", data->tx_restart_queue); - PR("Unknown protocol : %u\n", data->unknown_protocol); - - PR("Checksum offload : RX good %u errors %u\n", - data->csum.rx_csum_offload_good, - data->csum.rx_csum_offload_errors); - PR("Flow control : RX xon %u xoff %u TX xon %u xoff %u\n", - data->flow_control.rx_flow_control_xon, - data->flow_control.rx_flow_control_xoff, - data->flow_control.tx_flow_control_xon, - data->flow_control.tx_flow_control_xoff); - PR("ECC errors : uncorrected %u corrected %u\n", - data->error_details.uncorr_ecc_errors, - data->error_details.corr_ecc_errors); - PR("HW timestamp : RX cleared %u TX timeout %u skipped %u\n", - data->hw_timestamp.rx_hwtstamp_cleared, - data->hw_timestamp.tx_hwtstamp_timeouts, - data->hw_timestamp.tx_hwtstamp_skipped); - - PR("RX errors : %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s\n", - "Len", "Over", "CRC", "Frame", "NoBuf", "Miss", "Long", "Short", - "Align", "DMA", "Alloc"); - PR(" %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u\n", - data->error_details.rx_length_errors, - data->error_details.rx_over_errors, - data->error_details.rx_crc_errors, - data->error_details.rx_frame_errors, - data->error_details.rx_no_buffer_count, - data->error_details.rx_missed_errors, - data->error_details.rx_long_length_errors, - data->error_details.rx_short_length_errors, - data->error_details.rx_align_errors, - data->error_details.rx_dma_failed, - data->error_details.rx_buf_alloc_failed); - PR("TX errors : %5s %8s %5s %10s %7s %5s\n", - "Abort", "Carrier", "Fifo", "Heartbeat", "Window", "DMA"); - PR(" %5u %8u %5u %10u %7u %5u\n", - data->error_details.tx_aborted_errors, - data->error_details.tx_carrier_errors, - data->error_details.tx_fifo_errors, - data->error_details.tx_heartbeat_errors, - data->error_details.tx_window_errors, - data->error_details.tx_dma_failed); + struct net_shell_stats_options *opts = NULL; + uint32_t type = ETHERNET_STATS_TYPE_ALL; + + if (user_data != NULL && user_data->user_data != NULL) { + opts = (struct net_shell_stats_options *)user_data->user_data; + type = opts->type; + } + + /* Print common stats if requested */ + if (type & ETHERNET_STATS_TYPE_COMMON) { + PR("Statistics for Ethernet interface %p [%d]\n", iface, + net_if_get_by_iface(iface)); + + PR("Bytes received : %llu\n", data->bytes.received); + PR("Bytes sent : %llu\n", data->bytes.sent); + PR("Packets received : %u\n", data->pkts.rx); + PR("Packets sent : %u\n", data->pkts.tx); + PR("Bcast received : %u\n", data->broadcast.rx); + PR("Bcast sent : %u\n", data->broadcast.tx); + PR("Mcast received : %u\n", data->multicast.rx); + PR("Mcast sent : %u\n", data->multicast.tx); + + PR("Send errors : %u\n", data->errors.tx); + PR("Receive errors : %u\n", data->errors.rx); + PR("Collisions : %u\n", data->collisions); + PR("Send Drops : %u\n", data->tx_dropped); + PR("Send timeouts : %u\n", data->tx_timeout_count); + PR("Send restarts : %u\n", data->tx_restart_queue); + PR("Unknown protocol : %u\n", data->unknown_protocol); + + PR("Checksum offload : RX good %u errors %u\n", + data->csum.rx_csum_offload_good, + data->csum.rx_csum_offload_errors); + PR("Flow control : RX xon %u xoff %u TX xon %u xoff %u\n", + data->flow_control.rx_flow_control_xon, + data->flow_control.rx_flow_control_xoff, + data->flow_control.tx_flow_control_xon, + data->flow_control.tx_flow_control_xoff); + PR("ECC errors : uncorrected %u corrected %u\n", + data->error_details.uncorr_ecc_errors, + data->error_details.corr_ecc_errors); + PR("HW timestamp : RX cleared %u TX timeout %u skipped %u\n", + data->hw_timestamp.rx_hwtstamp_cleared, + data->hw_timestamp.tx_hwtstamp_timeouts, + data->hw_timestamp.tx_hwtstamp_skipped); + + PR("RX errors : %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s %5s\n", + "Len", "Over", "CRC", "Frame", "NoBuf", "Miss", "Long", "Short", + "Align", "DMA", "Alloc"); + PR(" %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u %5u\n", + data->error_details.rx_length_errors, + data->error_details.rx_over_errors, + data->error_details.rx_crc_errors, + data->error_details.rx_frame_errors, + data->error_details.rx_no_buffer_count, + data->error_details.rx_missed_errors, + data->error_details.rx_long_length_errors, + data->error_details.rx_short_length_errors, + data->error_details.rx_align_errors, + data->error_details.rx_dma_failed, + data->error_details.rx_buf_alloc_failed); + PR("TX errors : %5s %8s %5s %10s %7s %5s\n", + "Abort", "Carrier", "Fifo", "Heartbeat", "Window", "DMA"); + PR(" %5u %8u %5u %10u %7u %5u\n", + data->error_details.tx_aborted_errors, + data->error_details.tx_carrier_errors, + data->error_details.tx_fifo_errors, + data->error_details.tx_heartbeat_errors, + data->error_details.tx_window_errors, + data->error_details.tx_dma_failed); + } #if defined(CONFIG_NET_STATISTICS_ETHERNET_VENDOR) - if (data->vendor) { - size_t i = 0; + /* Print vendor stats if requested - format options only apply here */ + if ((type & ETHERNET_STATS_TYPE_VENDOR) && data->vendor) { enum net_shell_stats_format format = NET_SHELL_STATS_FORMAT_DEFAULT; + size_t i = 0; - if (user_data != NULL) { - format = *(enum net_shell_stats_format *)user_data->user_data; + if (opts != NULL) { + format = opts->format; } PR("Vendor specific statistics for Ethernet interface %p [%d]:\n", @@ -619,13 +638,30 @@ static void net_shell_print_statistics(struct net_if *iface, void *user_data) #if defined(CONFIG_NET_STATISTICS_ETHERNET) && \ defined(CONFIG_NET_STATISTICS_USER_API) if (iface && net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { - struct net_stats_eth eth_data; - int ret; + const struct ethernet_api *eth_api; + struct net_stats_eth *eth_data = NULL; + uint32_t type = ETHERNET_STATS_TYPE_ALL; - ret = net_mgmt(NET_REQUEST_STATS_GET_ETHERNET, iface, - ð_data, sizeof(eth_data)); - if (!ret) { - print_eth_stats(iface, ð_data, sh, data); + if (data != NULL && data->user_data != NULL) { + struct net_shell_stats_options *opts = data->user_data; + + type = opts->type; + } + + eth_api = net_if_get_device(iface)->api; + if (eth_api != NULL) { + /* Use get_stats_type if available for type filtering */ + if (eth_api->get_stats_type != NULL) { + eth_data = eth_api->get_stats_type( + net_if_get_device(iface), type); + } else if (eth_api->get_stats != NULL) { + eth_data = eth_api->get_stats( + net_if_get_device(iface)); + } + } + + if (eth_data != NULL) { + print_eth_stats(iface, eth_data, sh, data); } } #endif /* CONFIG_NET_STATISTICS_ETHERNET && CONFIG_NET_STATISTICS_USER_API */ @@ -653,31 +689,45 @@ static void net_shell_print_statistics_all(struct net_shell_user_data *data) net_if_foreach(net_shell_print_statistics, data); } } + +static void parse_stats_options(size_t argc, char *argv[], int start_idx, + struct net_shell_stats_options *opts) +{ + opts->format = NET_SHELL_STATS_FORMAT_DEFAULT; + opts->type = ETHERNET_STATS_TYPE_ALL; + + for (int i = start_idx; i < argc && argv[i] != NULL; i++) { + /* Format options */ + if (strcmp(argv[i], "key-value") == 0) { + opts->format = NET_SHELL_STATS_FORMAT_KEY_VALUE; + } else if (strcmp(argv[i], "hex-blob") == 0) { + opts->format = NET_SHELL_STATS_FORMAT_HEX_BLOB; + } else if (strcmp(argv[i], "both") == 0) { + opts->format = NET_SHELL_STATS_FORMAT_BOTH; + /* Type filter options */ + } else if (strcmp(argv[i], "common") == 0) { + opts->type = ETHERNET_STATS_TYPE_COMMON; + } else if (strcmp(argv[i], "vendor") == 0) { + opts->type = ETHERNET_STATS_TYPE_VENDOR; + } else if (strcmp(argv[i], "all") == 0) { + opts->type = ETHERNET_STATS_TYPE_ALL; + } + } +} #endif /* CONFIG_NET_STATISTICS */ int cmd_net_stats_all(const struct shell *sh, size_t argc, char *argv[]) { #if defined(CONFIG_NET_STATISTICS) struct net_shell_user_data user_data; -#endif - -#if defined(CONFIG_NET_STATISTICS) - enum net_shell_stats_format format = NET_SHELL_STATS_FORMAT_DEFAULT; + struct net_shell_stats_options opts; user_data.sh = sh; - /* Parse format argument if provided */ - if (argc > 1) { - if (strcmp(argv[1], "key-value") == 0) { - format = NET_SHELL_STATS_FORMAT_KEY_VALUE; - } else if (strcmp(argv[1], "hex-blob") == 0) { - format = NET_SHELL_STATS_FORMAT_HEX_BLOB; - } else if (strcmp(argv[1], "both") == 0) { - format = NET_SHELL_STATS_FORMAT_BOTH; - } - } + /* Parse options starting from argv[1] */ + parse_stats_options(argc, argv, 1, &opts); - user_data.user_data = &format; + user_data.user_data = &opts; /* Print global network statistics */ net_shell_print_statistics_all(&user_data); @@ -697,6 +747,7 @@ int cmd_net_stats_iface(const struct shell *sh, size_t argc, char *argv[]) #if defined(CONFIG_NET_STATISTICS) #if defined(CONFIG_NET_STATISTICS_PER_INTERFACE) struct net_shell_user_data data; + struct net_shell_stats_options opts; struct net_if *iface; char *endptr; int idx; @@ -722,22 +773,12 @@ int cmd_net_stats_iface(const struct shell *sh, size_t argc, char *argv[]) return -ENOEXEC; } - enum net_shell_stats_format format = NET_SHELL_STATS_FORMAT_DEFAULT; - data.sh = sh; - /* Parse format argument if provided */ - if (argc > 2) { - if (strcmp(argv[2], "key-value") == 0) { - format = NET_SHELL_STATS_FORMAT_KEY_VALUE; - } else if (strcmp(argv[2], "hex-blob") == 0) { - format = NET_SHELL_STATS_FORMAT_HEX_BLOB; - } else if (strcmp(argv[2], "both") == 0) { - format = NET_SHELL_STATS_FORMAT_BOTH; - } - } + /* Parse options starting from argv[2] */ + parse_stats_options(argc, argv, 2, &opts); - data.user_data = &format; + data.user_data = &opts; net_shell_print_statistics(iface, &data); #else @@ -766,8 +807,8 @@ static int cmd_net_stats(const struct shell *sh, size_t argc, char *argv[]) if (strcmp(argv[1], "reset") == 0) { net_stats_reset(NULL); } else { - /* Shift arguments for iface command */ - cmd_net_stats_iface(sh, argc - 1, &argv[1]); + /* Pass arguments directly - cmd_net_stats_iface expects index in argv[1] */ + cmd_net_stats_iface(sh, argc, argv); } #else ARG_UNUSED(argc); @@ -789,19 +830,23 @@ static int cmd_net_stats(const struct shell *sh, size_t argc, char *argv[]) SHELL_STATIC_SUBCMD_SET_CREATE(net_cmd_stats, SHELL_CMD(all, NULL, "Show network statistics for all network interfaces.\n" - "Usage: net stats all [key-value|hex-blob|both]", + "Usage: net stats all [common|vendor|all] [key-value|hex-blob|both]", cmd_net_stats_all), SHELL_CMD(iface, IFACE_DYN_CMD, - "'net stats [key-value|hex-blob|both]' shows network statistics for " + "'net stats [options]' shows network statistics for " "one specific network interface.\n" - "Format options:\n" - " key-value: Show vendor stats as key-value pairs (default)\n" - " hex-blob: Show vendor stats as hex blob for parsing\n" - " both: Show both key-value and hex blob formats", + "Type filter:\n" + " common: Only common stats (skips FW query)\n" + " vendor: Only vendor-specific stats\n" + " all: All stats (default)\n" + "Vendor stats format (only applies when vendor stats shown):\n" + " key-value: Key-value pairs (default)\n" + " hex-blob: Hex blob for parsing\n" + " both: Both formats", cmd_net_stats_iface), SHELL_SUBCMD_SET_END ); SHELL_SUBCMD_ADD((net), stats, &net_cmd_stats, "Show network statistics.", - cmd_net_stats, 1, 3); + cmd_net_stats, 1, 4);