diff --git a/drivers/wifi/nrf_wifi/inc/fmac_main.h b/drivers/wifi/nrf_wifi/inc/fmac_main.h index 7a863d8b614a6..48d3791961e39 100644 --- a/drivers/wifi/nrf_wifi/inc/fmac_main.h +++ b/drivers/wifi/nrf_wifi/inc/fmac_main.h @@ -36,6 +36,11 @@ #define NRF70_DRIVER_VERSION "1."KERNEL_VERSION_STRING +/* Calculate compile-time maximum for vendor stats */ +#ifdef CONFIG_NET_STATISTICS_ETHERNET_VENDOR +#define MAX_VENDOR_STATS ((sizeof(struct rpu_sys_fw_stats) / sizeof(uint32_t)) + 1) +#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ + #ifndef CONFIG_NRF70_OFFLOADED_RAW_TX #ifndef CONFIG_NRF70_RADIO_TEST struct nrf_wifi_vif_ctx_zep { @@ -61,6 +66,10 @@ struct nrf_wifi_vif_ctx_zep { bool set_if_event_received; int set_if_status; #ifdef CONFIG_NET_STATISTICS_ETHERNET +#ifdef CONFIG_NET_STATISTICS_ETHERNET_VENDOR + struct net_stats_eth_vendor eth_stats_vendor_data[MAX_VENDOR_STATS]; + char vendor_key_strings[MAX_VENDOR_STATS][16]; +#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ struct net_stats_eth eth_stats; #endif /* CONFIG_NET_STATISTICS_ETHERNET */ #if defined(CONFIG_NRF70_STA_MODE) || defined(CONFIG_NRF70_RAW_DATA_RX) diff --git a/drivers/wifi/nrf_wifi/src/net_if.c b/drivers/wifi/nrf_wifi/src/net_if.c index d1fc9195dbb2f..0662c6c80d092 100644 --- a/drivers/wifi/nrf_wifi/src/net_if.c +++ b/drivers/wifi/nrf_wifi/src/net_if.c @@ -1213,6 +1213,16 @@ int nrf_wifi_if_set_config_zep(const struct device *dev, struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) { struct nrf_wifi_vif_ctx_zep *vif_ctx_zep = NULL; +#ifdef CONFIG_NET_STATISTICS_ETHERNET_VENDOR + struct nrf_wifi_ctx_zep *rpu_ctx_zep = NULL; + struct rpu_sys_op_stats stats; + enum nrf_wifi_status status; + size_t fw_stats_size; + size_t num_uint32; + const uint8_t *fw_stats_bytes; + size_t i; + int vendor_idx = 0; +#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ if (!dev) { LOG_ERR("%s Device not found", __func__); @@ -1225,6 +1235,69 @@ struct net_stats_eth *nrf_wifi_eth_stats_get(const struct device *dev) goto out; } +#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; + } + + memset(&stats, 0, sizeof(stats)); + status = nrf_wifi_sys_fmac_stats_get(rpu_ctx_zep->rpu_ctx, + RPU_STATS_TYPE_ALL, + &stats); + if (status != NRF_WIFI_STATUS_SUCCESS) { + LOG_ERR("%s: Failed to get RPU stats", __func__); + goto ret; + } + + /* Treat stats.fw as a blob and divide into uint32_t chunks */ + fw_stats_size = sizeof(stats.fw); + num_uint32 = fw_stats_size / sizeof(uint32_t); + 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)); + + /* Create key name */ + 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 = 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 = 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; + } + + /* Point to the static vendor data */ + vif_ctx_zep->eth_stats.vendor = vif_ctx_zep->eth_stats_vendor_data; + +ret: +#endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ return &vif_ctx_zep->eth_stats; out: return NULL; diff --git a/subsys/net/lib/shell/stats.c b/subsys/net/lib/shell/stats.c index 1c31f56ec8f11..db3845d1ff4d2 100644 --- a/subsys/net/lib/shell/stats.c +++ b/subsys/net/lib/shell/stats.c @@ -14,8 +14,16 @@ LOG_MODULE_DECLARE(net_shell); #include "../ip/net_stats.h" +enum net_shell_stats_format { + NET_SHELL_STATS_FORMAT_DEFAULT, + NET_SHELL_STATS_FORMAT_KEY_VALUE, + NET_SHELL_STATS_FORMAT_HEX_BLOB, + NET_SHELL_STATS_FORMAT_BOTH +}; + #if defined(CONFIG_NET_STATISTICS) + #if NET_TC_COUNT > 1 static const char *priority2str(enum net_priority priority) { @@ -45,7 +53,7 @@ static const char *priority2str(enum net_priority priority) #if defined(CONFIG_NET_STATISTICS_ETHERNET) && \ defined(CONFIG_NET_STATISTICS_USER_API) static void print_eth_stats(struct net_if *iface, struct net_stats_eth *data, - const struct shell *sh) + 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)); @@ -110,16 +118,42 @@ static void print_eth_stats(struct net_if *iface, struct net_stats_eth *data, #if defined(CONFIG_NET_STATISTICS_ETHERNET_VENDOR) if (data->vendor) { - PR("Vendor specific statistics for Ethernet " - "interface %p [%d]:\n", - iface, net_if_get_by_iface(iface)); size_t i = 0; + enum net_shell_stats_format format = NET_SHELL_STATS_FORMAT_DEFAULT; + + if (user_data != NULL) { + format = *(enum net_shell_stats_format *)user_data->user_data; + } - do { - PR("%s : %u\n", data->vendor[i].key, - data->vendor[i].value); - i++; - } while (data->vendor[i].key); + PR("Vendor specific statistics for Ethernet interface %p [%d]:\n", + iface, net_if_get_by_iface(iface)); + + /* Print key-value pairs if requested */ + if (format == NET_SHELL_STATS_FORMAT_DEFAULT || + format == NET_SHELL_STATS_FORMAT_KEY_VALUE || + format == NET_SHELL_STATS_FORMAT_BOTH) { + do { + PR("%s : %u\n", data->vendor[i].key, data->vendor[i].value); + i++; + } while (data->vendor[i].key != NULL); + } + + /* Print hex blob if requested */ + if (format == NET_SHELL_STATS_FORMAT_HEX_BLOB || + format == NET_SHELL_STATS_FORMAT_BOTH) { + /* Suitable for parsing */ + PR("Vendor stats hex blob: "); + for (i = 0; data->vendor[i].key != NULL; i++) { + uint32_t v = data->vendor[i].value; + + PR("%02x%02x%02x%02x", + (uint8_t)(v & 0xFF), + (uint8_t)((v >> 8) & 0xFF), + (uint8_t)((v >> 16) & 0xFF), + (uint8_t)((v >> 24) & 0xFF)); + } + PR("\n"); + } } #endif /* CONFIG_NET_STATISTICS_ETHERNET_VENDOR */ } @@ -591,7 +625,7 @@ static void net_shell_print_statistics(struct net_if *iface, void *user_data) ret = net_mgmt(NET_REQUEST_STATS_GET_ETHERNET, iface, ð_data, sizeof(eth_data)); if (!ret) { - print_eth_stats(iface, ð_data, sh); + print_eth_stats(iface, ð_data, sh, data); } } #endif /* CONFIG_NET_STATISTICS_ETHERNET && CONFIG_NET_STATISTICS_USER_API */ @@ -628,8 +662,23 @@ int cmd_net_stats_all(const struct shell *sh, size_t argc, char *argv[]) #endif #if defined(CONFIG_NET_STATISTICS) + enum net_shell_stats_format format = NET_SHELL_STATS_FORMAT_DEFAULT; + 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; + } + } + + user_data.user_data = &format; + /* Print global network statistics */ net_shell_print_statistics_all(&user_data); #else @@ -673,8 +722,23 @@ 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; + } + } + + data.user_data = &format; + net_shell_print_statistics(iface, &data); #else PR_INFO("Per network interface statistics not collected.\n"); @@ -702,7 +766,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 { - cmd_net_stats_iface(sh, argc, argv); + /* Shift arguments for iface command */ + cmd_net_stats_iface(sh, argc - 1, &argv[1]); } #else ARG_UNUSED(argc); @@ -723,15 +788,20 @@ 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.", + "Show network statistics for all network interfaces.\n" + "Usage: net stats all [key-value|hex-blob|both]", cmd_net_stats_all), SHELL_CMD(iface, IFACE_DYN_CMD, - "'net stats ' shows network statistics for " - "one specific network interface.", + "'net stats [key-value|hex-blob|both]' 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", cmd_net_stats_iface), SHELL_SUBCMD_SET_END ); SHELL_SUBCMD_ADD((net), stats, &net_cmd_stats, "Show network statistics.", - cmd_net_stats, 1, 1); + cmd_net_stats, 1, 3); diff --git a/west.yml b/west.yml index e18bf7eab171d..8fb6957514511 100644 --- a/west.yml +++ b/west.yml @@ -342,7 +342,7 @@ manifest: revision: 40403f5f2805cca210d2a47c8717d89c4e816cda path: modules/bsim_hw_models/nrf_hw_models - name: nrf_wifi - revision: 53500666a59195b44e2e5a939c111effbf23db7b + revision: b822726084f55df19aa6660dc46cf602757e8645 path: modules/lib/nrf_wifi - name: open-amp revision: c30a6d8b92fcebdb797fc1a7698e8729e250f637