feat: move iperf to component registry

This commit is contained in:
Chen Yudong
2024-03-11 14:28:11 +08:00
parent 5e47ed70c2
commit c48eb1055b
28 changed files with 99 additions and 2668 deletions

View File

@@ -1,11 +0,0 @@
idf_component_register(SRCS "iperf.c"
"wifi_stats.c"
"wifi_twt.c"
"wifi_cmd.c"
INCLUDE_DIRS "include"
REQUIRES lwip
PRIV_REQUIRES esp_timer)
if(CONFIG_SOC_WIFI_HE_SUPPORT)
idf_component_optional_requires(PRIVATE esp_wifi console)
endif()

View File

@@ -1,31 +0,0 @@
menu "Iperf Configuration"
config IPERF_SOCKET_RX_TIMEOUT
int "iperf socket TCP/UDP rx timeout in seconds"
default 10
help
The value is used for iperf socket TCP/UDP rx timeout, iperf will be aborted
and socket will be closed and shutdown.
config IPERF_SOCKET_TCP_TX_TIMEOUT
int "iperf socket TCP tx timeout in seconds"
default 10
help
The value is used for iperf socket TCP tx timeout, iperf will be aborted
and socket will be closed and shutdown.
config IPERF_TRAFFIC_TASK_PRIORITY
int "iperf traffic task priority"
default 4
range 1 24
help
The value is used for iperf traffic task priority.
config IPERF_REPORT_TASK_PRIORITY
int "iperf result report task priority"
default 6
range 1 24
help
The value is used for iperf result report task priority.
endmenu

View File

@@ -1,14 +0,0 @@
# iperf Component
This directory contains an implementation for iperf network performance tester
Note that it's incompatible with `iperf3`
This component is used as part of the following ESP-IDF examples:
- [esp_wifi](../../wifi/iperf).
- [wifi_coexist](../../bluetooth/esp_ble_mesh/wifi_coexist).
- [ethernet](../../ethernet/iperf)
To learn more about how to use this component, please check API Documentation from header file [iperf.h](./include/iperf.h).
Please note that this component is not considered to be a part of ESP-IDF stable API, and only for example use for now.

View File

@@ -1,89 +0,0 @@
/* Iperf Example - iperf declaration
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef __IPERF_H_
#define __IPERF_H_
#include "esp_err.h"
#include "esp_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define IPERF_IP_TYPE_IPV4 0
#define IPERF_IP_TYPE_IPV6 1
#define IPERF_TRANS_TYPE_TCP 0
#define IPERF_TRANS_TYPE_UDP 1
#define IPERF_FLAG_SET(cfg, flag) ((cfg) |= (flag))
#define IPERF_FLAG_CLR(cfg, flag) ((cfg) &= (~(flag)))
#define IPERF_FLAG_CLIENT (1)
#define IPERF_FLAG_SERVER (1 << 1)
#define IPERF_FLAG_TCP (1 << 2)
#define IPERF_FLAG_UDP (1 << 3)
#define IPERF_DEFAULT_PORT 5001
#define IPERF_DEFAULT_INTERVAL 3
#define IPERF_DEFAULT_TIME 30
#define IPERF_DEFAULT_NO_BW_LIMIT -1
#define IPERF_TRAFFIC_TASK_NAME "iperf_traffic"
#define IPERF_TRAFFIC_TASK_PRIORITY CONFIG_IPERF_TRAFFIC_TASK_PRIORITY
#define IPERF_TRAFFIC_TASK_STACK 4096
#define IPERF_REPORT_TASK_NAME "iperf_report"
#define IPERF_REPORT_TASK_PRIORITY CONFIG_IPERF_REPORT_TASK_PRIORITY
#define IPERF_REPORT_TASK_STACK 4096
#define IPERF_DEFAULT_IPV4_UDP_TX_LEN (1470)
#define IPERF_DEFAULT_IPV6_UDP_TX_LEN (1450)
#define IPERF_DEFAULT_UDP_RX_LEN (16 << 10)
#define IPERF_DEFAULT_TCP_TX_LEN (16 << 10)
#define IPERF_DEFAULT_TCP_RX_LEN (16 << 10)
#define IPERF_MAX_DELAY 64
#define IPERF_SOCKET_RX_TIMEOUT CONFIG_IPERF_SOCKET_RX_TIMEOUT
#define IPERF_SOCKET_TCP_TX_TIMEOUT CONFIG_IPERF_SOCKET_TCP_TX_TIMEOUT
#define IPERF_SOCKET_ACCEPT_TIMEOUT 5
typedef enum {
MBITS_PER_SEC, KBITS_PER_SEC, BITS_PER_SEC
} iperf_output_format;
typedef struct {
uint32_t flag;
union {
uint32_t destination_ip4;
char *destination_ip6;
};
union {
uint32_t source_ip4;
char *source_ip6;
};
uint8_t type;
uint16_t dport;
uint16_t sport;
uint32_t interval;
uint32_t time;
uint16_t len_send_buf;
int32_t bw_lim;
iperf_output_format format;
} iperf_cfg_t;
esp_err_t iperf_start(iperf_cfg_t *cfg);
esp_err_t iperf_stop(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -1,21 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void register_wifi_cmd(void);
void register_wifi_itwt(void);
void register_wifi_stats(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,26 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS || CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
int wifi_cmd_get_tx_statistics(int argc, char **argv);
int wifi_cmd_clr_tx_statistics(int argc, char **argv);
int wifi_cmd_get_rx_statistics(int argc, char **argv);
int wifi_cmd_clr_rx_statistics(int argc, char **argv);
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -1,609 +0,0 @@
/* Iperf Example - iperf implementation
This example code is in the Public Domain (or CC0 licensed, at your option.)
/
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "sdkconfig.h"
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "esp_timer.h"
#include "iperf.h"
#include "wifi_stats.h"
typedef struct {
iperf_cfg_t cfg;
bool finish;
uint32_t actual_len;
uint32_t buffer_len;
uint8_t *buffer;
uint32_t sockfd;
} iperf_ctrl_t;
static bool s_iperf_is_running = false;
static iperf_ctrl_t s_iperf_ctrl;
static const char *TAG = "iperf";
inline static bool iperf_is_udp_client(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
}
inline static bool iperf_is_udp_server(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_UDP));
}
inline static bool iperf_is_tcp_client(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_CLIENT) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
}
inline static bool iperf_is_tcp_server(void)
{
return ((s_iperf_ctrl.cfg.flag & IPERF_FLAG_SERVER) && (s_iperf_ctrl.cfg.flag & IPERF_FLAG_TCP));
}
static int iperf_get_socket_error_code(int sockfd)
{
return errno;
}
static int iperf_show_socket_error_reason(const char *str, int sockfd)
{
int err = errno;
if (err != 0) {
ESP_LOGW(TAG, "%s error, error code: %d, reason: %s", str, err, strerror(err));
}
return err;
}
static void iperf_report_task(void *arg)
{
uint32_t interval = s_iperf_ctrl.cfg.interval;
uint32_t time = s_iperf_ctrl.cfg.time;
TickType_t delay_interval = (interval * 1000) / portTICK_PERIOD_MS;
uint32_t cur = 0;
double average = 0;
double actual_bandwidth = 0;
int k = 1;
const double coefficient[3] = {1048576.0, 1024.0, 1.0};
const char unit[3] = {'M', 'K', '\0'};
iperf_output_format format = s_iperf_ctrl.cfg.format;
printf("\n%16s %s\n", "Interval", "Bandwidth");
while (!s_iperf_ctrl.finish) {
vTaskDelay(delay_interval);
actual_bandwidth = (s_iperf_ctrl.actual_len / coefficient[format] * 8) / interval;
printf("%4" PRIi32 "-%4" PRIi32 " sec %.2f %cbits/sec\n", cur, cur + interval,
actual_bandwidth, unit[format]);
cur += interval;
average = ((average * (k - 1) / k) + (actual_bandwidth / k));
k++;
s_iperf_ctrl.actual_len = 0;
if (cur >= time) {
printf("%4d-%4" PRIu32 " sec %.2f %cbits/sec\n", 0, time,
average, unit[format]);
break;
}
}
s_iperf_ctrl.finish = true;
vTaskDelete(NULL);
}
static esp_err_t iperf_start_report(void)
{
int ret;
ret = xTaskCreatePinnedToCore(iperf_report_task, IPERF_REPORT_TASK_NAME, IPERF_REPORT_TASK_STACK, NULL, IPERF_REPORT_TASK_PRIORITY, NULL, CONFIG_FREERTOS_NUMBER_OF_CORES - 1);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_REPORT_TASK_NAME);
return ESP_FAIL;
}
return ESP_OK;
}
static void IRAM_ATTR socket_recv(int recv_socket, struct sockaddr_storage listen_addr, uint8_t type)
{
bool iperf_recv_start = true;
uint8_t *buffer;
int want_recv = 0;
int actual_recv = 0;
#ifdef CONFIG_LWIP_IPV6
socklen_t socklen = (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
#else
socklen_t socklen = sizeof(struct sockaddr_in);
#endif
const char *error_log = (type == IPERF_TRANS_TYPE_TCP) ? "tcp server recv" : "udp server recv";
buffer = s_iperf_ctrl.buffer;
want_recv = s_iperf_ctrl.buffer_len;
while (!s_iperf_ctrl.finish) {
actual_recv = recvfrom(recv_socket, buffer, want_recv, 0, (struct sockaddr *)&listen_addr, &socklen);
if (actual_recv < 0) {
iperf_show_socket_error_reason(error_log, recv_socket);
s_iperf_ctrl.finish = true;
break;
} else {
if (iperf_recv_start) {
iperf_start_report();
iperf_recv_start = false;
}
s_iperf_ctrl.actual_len += actual_recv;
}
}
}
static void IRAM_ATTR socket_send(int send_socket, struct sockaddr_storage dest_addr, uint8_t type, int bw_lim)
{
uint8_t *buffer;
uint32_t *pkt_id_p;
uint32_t pkt_cnt = 0;
int actual_send = 0;
int want_send = 0;
int period_us = -1;
int delay_us = 0;
int64_t prev_time = 0;
int64_t send_time = 0;
int err = 0;
#ifdef CONFIG_LWIP_IPV6
const socklen_t socklen = (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
#else
const socklen_t socklen = sizeof(struct sockaddr_in);
#endif
const char *error_log = (type == IPERF_TRANS_TYPE_TCP) ? "tcp client send" : "udp client send";
buffer = s_iperf_ctrl.buffer;
pkt_id_p = (uint32_t *)s_iperf_ctrl.buffer;
want_send = s_iperf_ctrl.buffer_len;
iperf_start_report();
if (bw_lim > 0) {
period_us = want_send * 8 / bw_lim;
}
while (!s_iperf_ctrl.finish) {
if (period_us > 0) {
send_time = esp_timer_get_time();
if (actual_send > 0){
// Last packet "send" was successful, check how much off the previous loop duration was to the ideal send period. Result will adjust the
// next send delay.
delay_us += period_us + (int32_t)(prev_time - send_time);
} else {
// Last packet "send" was not successful. Ideally we should try to catch up the whole previous loop duration (e.g. prev_time - send_time).
// However, that's not possible since the most probable reason why the send was unsuccessful is the HW was not able to process the packet.
// Hence, we cannot queue more packets with shorter (or no) delay to catch up since we are already at the performance edge. The best we
// can do is to reset the send delay (which is probably big negative number) and start all over again.
delay_us = 0;
}
prev_time = send_time;
}
*pkt_id_p = htonl(pkt_cnt++); // datagrams need to be sequentially numbered
actual_send = sendto(send_socket, buffer, want_send, 0, (struct sockaddr *)&dest_addr, socklen);
if (actual_send != want_send) {
if (type == IPERF_TRANS_TYPE_UDP) {
err = iperf_get_socket_error_code(send_socket);
// ENOMEM is expected under heavy load => do not print it
if (err != ENOMEM) {
iperf_show_socket_error_reason(error_log, send_socket);
}
} else if (type == IPERF_TRANS_TYPE_TCP) {
iperf_show_socket_error_reason(error_log, send_socket);
break;
}
} else {
s_iperf_ctrl.actual_len += actual_send;
}
// The send delay may be negative, it indicates we are trying to catch up and hence to not delay the loop at all.
if (delay_us > 0) {
esp_rom_delay_us(delay_us);
}
}
}
static esp_err_t iperf_run_tcp_server(void)
{
int listen_socket = -1;
int client_socket = -1;
int opt = 1;
int err = 0;
esp_err_t ret = ESP_OK;
struct sockaddr_in remote_addr;
struct timeval timeout = { 0 };
socklen_t addr_len = sizeof(struct sockaddr);
struct sockaddr_storage listen_addr = { 0 };
struct sockaddr_in listen_addr4 = { 0 };
#ifdef CONFIG_LWIP_IPV6
struct sockaddr_in6 listen_addr6 = { 0 };
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
#else
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Invalid AF types");
#endif
#ifdef CONFIG_LWIP_IPV6
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
// The TCP server listen at the address "::", which means all addresses can be listened to.
inet6_aton("::", &listen_addr6.sin6_addr);
listen_addr6.sin6_family = AF_INET6;
listen_addr6.sin6_port = htons(s_iperf_ctrl.cfg.sport);
listen_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6);
ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(listen_socket, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
err = bind(listen_socket, (struct sockaddr *)&listen_addr6, sizeof(listen_addr6));
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d, IPPROTO: %d", errno, AF_INET6);
err = listen(listen_socket, 1);
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Error occurred during listen: errno %d", errno);
memcpy(&listen_addr, &listen_addr6, sizeof(listen_addr6));
} else
#endif
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
listen_addr4.sin_family = AF_INET;
listen_addr4.sin_port = htons(s_iperf_ctrl.cfg.sport);
listen_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.source_ip4;
listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
err = bind(listen_socket, (struct sockaddr *)&listen_addr4, sizeof(listen_addr4));
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d, IPPROTO: %d", errno, AF_INET);
err = listen(listen_socket, 5);
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Error occurred during listen: errno %d", errno);
memcpy(&listen_addr, &listen_addr4, sizeof(listen_addr4));
}
timeout.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(listen_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
client_socket = accept(listen_socket, (struct sockaddr *)&remote_addr, &addr_len);
ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to accept connection: errno %d", errno);
ESP_LOGI(TAG, "accept: %s,%d", inet_ntoa(remote_addr.sin_addr), htons(remote_addr.sin_port));
timeout.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(client_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
wifi_cmd_clr_tx_statistics(0, NULL);
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
wifi_cmd_clr_rx_statistics(0, NULL);
#endif
socket_recv(client_socket, listen_addr, IPERF_TRANS_TYPE_TCP);
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
wifi_cmd_get_rx_statistics(0, NULL);
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
wifi_cmd_get_tx_statistics(0, NULL);
#endif
exit:
if (client_socket != -1) {
close(client_socket);
}
if (listen_socket != -1) {
shutdown(listen_socket, 0);
close(listen_socket);
ESP_LOGI(TAG, "TCP Socket server is closed.");
}
s_iperf_ctrl.finish = true;
return ret;
}
static esp_err_t iperf_run_tcp_client(void)
{
int client_socket = -1;
int err = 0;
esp_err_t ret = ESP_OK;
struct sockaddr_storage dest_addr = { 0 };
struct sockaddr_in dest_addr4 = { 0 };
struct timeval timeout = { 0 };
#ifdef CONFIG_LWIP_IPV6
struct sockaddr_in6 dest_addr6 = { 0 };
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
#else
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Invalid AF types");
#endif
#ifdef CONFIG_LWIP_IPV6
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
client_socket = socket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6);
ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
inet6_aton(s_iperf_ctrl.cfg.destination_ip6, &dest_addr6.sin6_addr);
dest_addr6.sin6_family = AF_INET6;
dest_addr6.sin6_port = htons(s_iperf_ctrl.cfg.dport);
err = connect(client_socket, (struct sockaddr *)&dest_addr6, sizeof(struct sockaddr_in6));
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to connect: errno %d", errno);
ESP_LOGI(TAG, "Successfully connected");
memcpy(&dest_addr, &dest_addr6, sizeof(dest_addr6));
} else
#endif
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
dest_addr4.sin_family = AF_INET;
dest_addr4.sin_port = htons(s_iperf_ctrl.cfg.dport);
dest_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.destination_ip4;
err = connect(client_socket, (struct sockaddr *)&dest_addr4, sizeof(struct sockaddr_in));
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to connect: errno %d", errno);
ESP_LOGI(TAG, "Successfully connected");
memcpy(&dest_addr, &dest_addr4, sizeof(dest_addr4));
}
timeout.tv_sec = IPERF_SOCKET_TCP_TX_TIMEOUT;
setsockopt(client_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
wifi_cmd_clr_rx_statistics(0, NULL);
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
wifi_cmd_clr_tx_statistics(0, NULL);
#endif
socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_TCP, s_iperf_ctrl.cfg.bw_lim);
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
wifi_cmd_get_rx_statistics(0, NULL);
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
wifi_cmd_get_tx_statistics(0, NULL);
#endif
exit:
if (client_socket != -1) {
shutdown(client_socket, 0);
close(client_socket);
ESP_LOGI(TAG, "TCP Socket client is closed.");
}
s_iperf_ctrl.finish = true;
return ret;
}
static esp_err_t iperf_run_udp_server(void)
{
int listen_socket = -1;
int opt = 1;
int err = 0;
esp_err_t ret = ESP_OK;
struct timeval timeout = { 0 };
struct sockaddr_storage listen_addr = { 0 };
struct sockaddr_in listen_addr4 = { 0 };
#ifdef CONFIG_LWIP_IPV6
struct sockaddr_in6 listen_addr6 = { 0 };
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
#else
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
#endif
#ifdef CONFIG_LWIP_IPV6
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
// The UDP server listen at the address "::", which means all addresses can be listened to.
inet6_aton("::", &listen_addr6.sin6_addr);
listen_addr6.sin6_family = AF_INET6;
listen_addr6.sin6_port = htons(s_iperf_ctrl.cfg.sport);
listen_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
ESP_LOGI(TAG, "Socket created");
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
err = bind(listen_socket, (struct sockaddr *)&listen_addr6, sizeof(struct sockaddr_in6));
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d", errno);
ESP_LOGI(TAG, "Socket bound, port %" PRIu16, listen_addr6.sin6_port);
memcpy(&listen_addr, &listen_addr6, sizeof(listen_addr6));
} else
#endif
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
listen_addr4.sin_family = AF_INET;
listen_addr4.sin_port = htons(s_iperf_ctrl.cfg.sport);
listen_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.source_ip4;
listen_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
ESP_GOTO_ON_FALSE((listen_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
ESP_LOGI(TAG, "Socket created");
setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
err = bind(listen_socket, (struct sockaddr *)&listen_addr4, sizeof(struct sockaddr_in));
ESP_GOTO_ON_FALSE((err == 0), ESP_FAIL, exit, TAG, "Socket unable to bind: errno %d", errno);
ESP_LOGI(TAG, "Socket bound, port %d", listen_addr4.sin_port);
memcpy(&listen_addr, &listen_addr4, sizeof(listen_addr4));
}
timeout.tv_sec = IPERF_SOCKET_RX_TIMEOUT;
setsockopt(listen_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
wifi_cmd_clr_rx_statistics(0, NULL);
#endif
socket_recv(listen_socket, listen_addr, IPERF_TRANS_TYPE_UDP);
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
wifi_cmd_get_rx_statistics(0, NULL);
#endif
exit:
if (listen_socket != -1) {
shutdown(listen_socket, 0);
close(listen_socket);
}
ESP_LOGI(TAG, "Udp socket server is closed.");
s_iperf_ctrl.finish = true;
return ret;
}
static esp_err_t iperf_run_udp_client(void)
{
int client_socket = -1;
int opt = 1;
esp_err_t ret = ESP_OK;
struct sockaddr_storage dest_addr = { 0 };
struct sockaddr_in dest_addr4 = { 0 };
#ifdef CONFIG_LWIP_IPV6
struct sockaddr_in6 dest_addr6 = { 0 };
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6 || s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
#else
ESP_GOTO_ON_FALSE((s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4), ESP_FAIL, exit, TAG, "Ivalid AF types");
#endif
#ifdef CONFIG_LWIP_IPV6
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
inet6_aton(s_iperf_ctrl.cfg.destination_ip6, &dest_addr6.sin6_addr);
dest_addr6.sin6_family = AF_INET6;
dest_addr6.sin6_port = htons(s_iperf_ctrl.cfg.dport);
client_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IPV6);
ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
ESP_LOGI(TAG, "Socket created, sending to %s:%" PRIu16, s_iperf_ctrl.cfg.destination_ip6, s_iperf_ctrl.cfg.dport);
setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memcpy(&dest_addr, &dest_addr6, sizeof(dest_addr6));
} else
#endif
if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV4) {
dest_addr4.sin_family = AF_INET;
dest_addr4.sin_port = htons(s_iperf_ctrl.cfg.dport);
dest_addr4.sin_addr.s_addr = s_iperf_ctrl.cfg.destination_ip4;
client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
ESP_GOTO_ON_FALSE((client_socket >= 0), ESP_FAIL, exit, TAG, "Unable to create socket: errno %d", errno);
ESP_LOGI(TAG, "Socket created, sending to %d.%d.%d.%d:%" PRIu16,
(uint16_t) s_iperf_ctrl.cfg.destination_ip4 & 0xFF,
(uint16_t) (s_iperf_ctrl.cfg.destination_ip4 >> 8) & 0xFF,
(uint16_t) (s_iperf_ctrl.cfg.destination_ip4 >> 16) & 0xFF,
(uint16_t) (s_iperf_ctrl.cfg.destination_ip4 >> 24) & 0xFF,
s_iperf_ctrl.cfg.dport);
setsockopt(client_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
memcpy(&dest_addr, &dest_addr4, sizeof(dest_addr4));
}
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
wifi_cmd_clr_tx_statistics(0, NULL);
#endif
socket_send(client_socket, dest_addr, IPERF_TRANS_TYPE_UDP, s_iperf_ctrl.cfg.bw_lim);
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
wifi_cmd_get_tx_statistics(0, NULL);
#endif
exit:
if (client_socket != -1) {
shutdown(client_socket, 0);
close(client_socket);
}
s_iperf_ctrl.finish = true;
ESP_LOGI(TAG, "UDP Socket client is closed");
return ret;
}
static void iperf_task_traffic(void *arg)
{
if (iperf_is_udp_client()) {
iperf_run_udp_client();
} else if (iperf_is_udp_server()) {
iperf_run_udp_server();
} else if (iperf_is_tcp_client()) {
iperf_run_tcp_client();
} else {
iperf_run_tcp_server();
}
if (s_iperf_ctrl.buffer) {
free(s_iperf_ctrl.buffer);
s_iperf_ctrl.buffer = NULL;
}
ESP_LOGI(TAG, "iperf exit");
s_iperf_is_running = false;
vTaskDelete(NULL);
}
static uint32_t iperf_get_buffer_len(void)
{
if (iperf_is_udp_client()) {
#ifdef CONFIG_LWIP_IPV6
if (s_iperf_ctrl.cfg.len_send_buf) {
return s_iperf_ctrl.cfg.len_send_buf;
} else if (s_iperf_ctrl.cfg.type == IPERF_IP_TYPE_IPV6) {
return IPERF_DEFAULT_IPV6_UDP_TX_LEN;
} else {
return IPERF_DEFAULT_IPV4_UDP_TX_LEN;
}
#else
return (s_iperf_ctrl.cfg.len_send_buf == 0 ? IPERF_DEFAULT_IPV4_UDP_TX_LEN : s_iperf_ctrl.cfg.len_send_buf);
#endif
} else if (iperf_is_udp_server()) {
return IPERF_DEFAULT_UDP_RX_LEN;
} else if (iperf_is_tcp_client()) {
return (s_iperf_ctrl.cfg.len_send_buf == 0 ? IPERF_DEFAULT_TCP_TX_LEN : s_iperf_ctrl.cfg.len_send_buf);
} else {
return IPERF_DEFAULT_TCP_RX_LEN;
}
return 0;
}
esp_err_t iperf_start(iperf_cfg_t *cfg)
{
BaseType_t ret;
if (!cfg) {
return ESP_FAIL;
}
if (s_iperf_is_running) {
ESP_LOGW(TAG, "iperf is running");
return ESP_FAIL;
}
memset(&s_iperf_ctrl, 0, sizeof(s_iperf_ctrl));
memcpy(&s_iperf_ctrl.cfg, cfg, sizeof(*cfg));
s_iperf_is_running = true;
s_iperf_ctrl.finish = false;
s_iperf_ctrl.buffer_len = iperf_get_buffer_len();
s_iperf_ctrl.buffer = (uint8_t *)malloc(s_iperf_ctrl.buffer_len);
if (!s_iperf_ctrl.buffer) {
ESP_LOGE(TAG, "create buffer: not enough memory");
return ESP_FAIL;
}
memset(s_iperf_ctrl.buffer, 0, s_iperf_ctrl.buffer_len);
ret = xTaskCreatePinnedToCore(iperf_task_traffic, IPERF_TRAFFIC_TASK_NAME, IPERF_TRAFFIC_TASK_STACK, NULL, IPERF_TRAFFIC_TASK_PRIORITY, NULL, CONFIG_FREERTOS_NUMBER_OF_CORES - 1);
if (ret != pdPASS) {
ESP_LOGE(TAG, "create task %s failed", IPERF_TRAFFIC_TASK_NAME);
free(s_iperf_ctrl.buffer);
s_iperf_ctrl.buffer = NULL;
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t iperf_stop(void)
{
if (s_iperf_is_running) {
s_iperf_ctrl.finish = true;
}
while (s_iperf_is_running) {
ESP_LOGI(TAG, "wait current iperf to stop ...");
vTaskDelay(300 / portTICK_PERIOD_MS);
}
return ESP_OK;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,585 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS || CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "wifi_stats.h"
#include "esp_private/esp_wifi_he_private.h"
/*******************************************************
* Macros
*******************************************************/
/*
* enable/disable rx/tx statistics after Wi-Fi started:
* (1) esp_wifi_enable_rx_statistics(true, true); //rx_stats=true, rx_mu_stats=true
* (2) esp_wifi_enable_tx_statistics(ESP_WIFI_ACI_BE, true); //aci=ESP_WIFI_ACI_BE, tx_stats=true
*/
/*******************************************************
* Constants
*******************************************************/
static const char *TAG = "stats";
/*******************************************************
* Structures
*******************************************************/
/*******************************************************
* Variable Definitions
*******************************************************/
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
esp_test_rx_mu_statistics_t rx_mu_stats = { 0, }; //10932 bytes
#endif
/*******************************************************
* Function Declarations
*******************************************************/
/*******************************************************
* Function Definitions
*******************************************************/
const char *tx_fail_error2str(esp_test_tx_fail_error_t error)
{
switch (error) {
case TEST_TX_FAIL_ERROR_H00:
return "0x00";
case TEST_TX_FAIL_ERROR_H53:
return "0x53";
case TEST_TX_FAIL_ERROR_H63:
return "0x63";
case TEST_TX_FAIL_ERROR_H75:
return "0x75";
case TEST_TX_FAIL_ERROR_H41:
return "0x41";
case TEST_TX_FAIL_ERROR_H42:
return "0x42";
case TEST_TX_FAIL_ERROR_H47:
return "0x47";
case TEST_TX_FAIL_ERROR_H80:
return "0x80";
case TEST_TX_FAIL_ERROR_H5A:
return "0x5A";
case TEST_TX_FAIL_ERROR_HXX:
return "Others";
case TEST_TX_FAIL_ERROR_MAX:
return "Undefined";
}
return "Undefined";
}
const char *tx_fail_match2str(esp_test_tx_fail_match_t match)
{
switch (match) {
case TEST_TX_WAIT_MATCH:
return "MATCH";
case TEST_TX_WAIT_NOT2SELF:
return "NOT2SELF";
case TEST_TX_MISMATCH:
return "MISMATCH";
case TEST_TX_WAIT_TIMEOUT:
return "TIMEOUT";
case TEST_TX_WAIT_MAX:
return "Undefined";
}
return "Undefined";
}
const char *tx_fail_state2str(esp_test_tx_fail_state_t state)
{
switch (state) {
case TEST_TX_SUCCESS:
return "TX Success";
case TEST_TX_FAIL_RTS:
return "TX RTS";
case TEST_TX_WAIT_CTS: //RX
return "Wait CTS";
case TEST_TX_FAIL_CTS:
return "TX RTS";
case TEST_TX_FAIL_DATA:
return "TX DATA";
case TEST_TX_WAIT_ACK: //RX
return "Wait ACK/BA";
case TEST_TX_FAIL_MAX:
return "Undefined";
}
return "Undefined";
}
int wifi_cmd_clr_tx_statistics(int argc, char **argv)
{
ESP_LOGW(TAG, "Clear tx statistics");
int i;
for (i = 0; i < 3; i++) {
esp_wifi_clr_tx_statistics(i); //BE
esp_wifi_clr_tx_tb_statistics(i);
}
esp_test_clr_hw_statistics();
return 0;
}
void print_hw_tb_statistics(void)
{
esp_test_hw_tb_statistics_t hw_tb_stats = { 0, };
esp_test_get_hw_tb_statistics(&hw_tb_stats);
printf("(test)rx_trig:%d, tx_bfrpt:%d, tb_times:%d, tb_qos_null:%d, tb_qos_data:%d, tb_cca_cancel:%d, tb_sifs_abort:%d, tb_pwr_outof_range:%d\n",
hw_tb_stats.rx_trig,
hw_tb_stats.tx_bfrpt, //including TB and Non-TB
hw_tb_stats.tb_times,
hw_tb_stats.tb_qos_null,
hw_tb_stats.tb_times - hw_tb_stats.tb_qos_null,
hw_tb_stats.tb_cca_cancel,
hw_tb_stats.tb_sifs_abort,
hw_tb_stats.tb_pwr_outof_range);
}
int wifi_cmd_get_tx_statistics(int argc, char **argv)
{
uint8_t i, h, j, k;
ESP_LOGW(TAG, "Get tx statistics");
esp_test_tx_tb_statistics_t tb_stats = { 0, }; //32 bytes
esp_test_tx_statistics_t tx_stats = { 0, }; //136 bytes
esp_test_tx_fail_statistics_t tx_fail[TEST_TX_FAIL_MAX] = { 0, }; //TEST_TX_FAIL_MAX * 164 bytes
print_hw_tb_statistics();
//only check BE
for (i = 2; i < 3; i++) {
esp_wifi_get_tx_tb_statistics(i, &tb_stats);
/* TB */
printf("(test)aci:%" PRIu8 ", tb(suc:%" PRIu32 ", ack:%" PRIu32 ", err:%" PRIu32 "), "
"count(suc:%" PRIu32 ", ack:%" PRIu32 ", err:%" PRIu32 ", tot:%" PRIu32 ", max_sent:%" PRIu32 ")\n",
i,
tb_stats.complete_suc_tb,
tb_stats.complete_ack_tb,
tb_stats.complete_err_tb,
tb_stats.complete_tb_suc_count,
tb_stats.complete_tb_ack_count,
tb_stats.complete_tb_err_count,
tb_stats.complete_tb_tot_count,
tb_stats.complete_tb_pack_sent);
esp_wifi_get_tx_statistics(i, &tx_stats, (esp_test_tx_fail_statistics_t *) &tx_fail);
int tot_tx_times = tx_stats.tb_times + (tx_stats.tx_enable - tx_stats.tb_last); //TB + EDCA
int tot_fail = tx_fail[1].count + tx_fail[2].count + tx_fail[3].count + tx_fail[4].count + tx_fail[5].count;
printf("(test)aci:%" PRIu8 ", enable:%" PRIu32 ", complete:%" PRIu32 ", tb_times:%" PRIu32 ", tb_last:%" PRIu32 ", edca:%" PRIu32 ", "
"succ:%" PRIu32 ", fail(%" PRIu32 ",%" PRIu32 ",%" PRIu32 ", cts:%" PRIu32 "/%2.2f%%, ack:%" PRIu32 "/%2.2f%%, tot:%d, %.2f%%), "
"edca(ack:%" PRIu32 ", ba:%" PRIu32 "), tb(hw-ba:%" PRIu32 ", sw-ba:%" PRIu32 ")\n",
i, tx_stats.tx_enable,
tx_stats.tx_complete,
tx_stats.tb_times,
tx_stats.tb_last,
tx_stats.tx_enable - tx_stats.tb_last,
tx_fail[0].count,
tx_fail[1].count,
tx_fail[3].count,
tx_fail[4].count,
tx_fail[2].count,
(float) ((float) tx_fail[2].count / (float) tot_tx_times) * 100, //rx cts
tx_fail[5].count, (float) ((float) tx_fail[5].count / (float) tot_tx_times) * 100, //rx ack
tot_fail,
(float) ((float) tot_fail / (float) tot_tx_times) * 100,
tx_stats.rx_ack,
tx_stats.rx_ba,
tx_stats.tb_rx_ba, //including ACKs
tx_stats.rx_dump_ba);
printf("(test)aci:%" PRIu8 ", txFrames:%" PRIu32 ", s-mpdu:%" PRIu32 "(%.2f%%), "
"bitmap(max:%d, min:%d, tot:%" PRIu32 ", avg:%.2f), "
"retry(edca:%" PRIu32 ", tb:%" PRIu32 ", %.2f%%), collision:%" PRIu32 ", timeout:%" PRIu32 "\n",
i,
tx_stats.tx_succ,
tx_stats.rx_ack,
((float) (tx_stats.rx_ack) / (float) tot_tx_times) * 100,
tx_stats.rx_max_bitmap,
tx_stats.rx_min_bitmap,
tx_stats.rx_tot_bitmap,
(float) tx_stats.rx_tot_bitmap / (float) (tx_stats.tb_rx_ba + tx_stats.rx_ba),
tx_stats.retry_edca, tx_stats.retry_tb, (float) (tx_stats.retry_edca + tx_stats.retry_tb) / (float) tx_stats.tx_succ * 100,
tx_stats.collision, tx_stats.timeout);
float tot_rtt_ms = (float) tx_stats.tx_tot_rtt / (float) 1000;
printf("(test)aci:%" PRIu8 ", seqno_rtt[%" PRIu32 ",%" PRIu32 "], hw_rtt[%" PRIu32 ", %" PRIu32 "], muedca[enable:%" PRIu32 ", times:%" PRIu32 ", %.2f, %.2f, tot:%.2f], avg:%.3f ms, tot:%.3f secs\n",
i,
tx_stats.tx_seq_min_rtt,
tx_stats.tx_seq_max_rtt,
tx_stats.tx_min_rtt,
tx_stats.tx_max_rtt,
tx_stats.tx_muedca_enable,
tx_stats.muedca_times,
(float) tx_stats.tx_min_muedca_time / (float) 1000,
(float) tx_stats.tx_max_muedca_time / (float) 1000,
(float) tx_stats.tx_tot_muedca_time / (float) 1000, //ms
(float) tot_rtt_ms / (float) tot_tx_times, //ms
(float) tot_rtt_ms / (float) 1000); //seconds
/* fail state */
for (h = 1; h < TEST_TX_FAIL_MAX; h++) { //state
for (j = 0; j < TEST_TX_WAIT_MAX; j++) { //match
for (k = 0; k < TEST_TX_FAIL_ERROR_MAX; k++) { //error
if (tx_fail[h].match[j][k]) {
printf("(test)[%d][%d][%d](%16s + %16s + %16s)%3" PRIu32 "/%3" PRIu32 "(%.2f%%)\n", h, j, k, tx_fail_state2str(h),
tx_fail_match2str(j), tx_fail_error2str(k),
tx_fail[h].match[j][k], tx_fail[h].count,
((float) tx_fail[h].match[j][k] / (float) tx_fail[h].count) * 100);
}
}
}
}
printf("\n");
}
wifi_cmd_clr_tx_statistics(0, 0);
return 0;
}
void print_rx_statistics_nonmimo(const esp_test_rx_mu_statistics_t *mu_stats)
{
if (!mu_stats->nonmimo_rx) {
return;
}
int i, j;
int tot_rx_nonmimo = 0;
ESP_LOGW(TAG, "(nonmimo)dut rx:%" PRIu32, mu_stats->nonmimo_rx);
ESP_LOGW(TAG, "(nonmimo)ru_alloc_96_num_2046:%" PRIu32 ", ru_alloc_112_num_2046:%" PRIu32, mu_stats->ru_alloc_96_num_2046, mu_stats->ru_alloc_112_num_2046);
ESP_LOGW(TAG, "(nonmimo)sigb, mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), mcs5:%" PRIu32 "(%2.2f%%)",
mu_stats->nonmimo_sigb_mcs[0], ((float) mu_stats->nonmimo_sigb_mcs[0] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[1], ((float) mu_stats->nonmimo_sigb_mcs[1] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[2], ((float) mu_stats->nonmimo_sigb_mcs[2] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[3], ((float) mu_stats->nonmimo_sigb_mcs[3] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[4], ((float) mu_stats->nonmimo_sigb_mcs[4] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_sigb_mcs[5], ((float) mu_stats->nonmimo_sigb_mcs[5] / (float) mu_stats->nonmimo_rx) * 100);
ESP_LOGW(TAG, "(nonmimo)users, num1:%" PRIu32 "(%2.2f%%), num2:%" PRIu32 "(%2.2f%%), num3:%" PRIu32 "(%2.2f%%), num4:%" PRIu32 "(%2.2f%%), num5:%" PRIu32 "(%2.2f%%), num6:%" PRIu32 "(%2.2f%%), num7:%" PRIu32 "(%2.2f%%), num8:%" PRIu32 "(%2.2f%%), num9:%" PRIu32 "(%2.2f%%)",
mu_stats->nonmimo_user_num_occu[0], ((float) mu_stats->nonmimo_user_num_occu[0] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[1], ((float) mu_stats->nonmimo_user_num_occu[1] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[2], ((float) mu_stats->nonmimo_user_num_occu[2] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[3], ((float) mu_stats->nonmimo_user_num_occu[3] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[4], ((float) mu_stats->nonmimo_user_num_occu[4] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[5], ((float) mu_stats->nonmimo_user_num_occu[5] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[6], ((float) mu_stats->nonmimo_user_num_occu[6] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[7], ((float) mu_stats->nonmimo_user_num_occu[7] / (float) mu_stats->nonmimo_rx) * 100,
mu_stats->nonmimo_user_num_occu[8], ((float) mu_stats->nonmimo_user_num_occu[8] / (float) mu_stats->nonmimo_rx) * 100);
for (i = 0; i < 256; i++) {
for (j = 0; j < 9; j++) {
if (!mu_stats->nonmimo_ru_alloc[i][j]) {
continue;
}
ESP_LOGI(TAG, "(nonmimo)ru_allocation:0x%2x(%3" PRIu8 "), position:%" PRIu8 ", %5" PRIu32 "(%2.2f%%)", i, i, j + 1, mu_stats->nonmimo_ru_alloc[i][j],
((float) mu_stats->nonmimo_ru_alloc[i][j] / (float) mu_stats->nonmimo_rx) * 100);
}
}
for (i = 0; i < ESP_TEST_RX_MU_USER_NUM; i++) {
if (!mu_stats->nonmimo[i].aid) {
continue;
}
if (mu_stats->aid != mu_stats->nonmimo[i].aid) {
continue;
}
tot_rx_nonmimo = mu_stats->nonmimo[i].occu_nsts[0] + mu_stats->nonmimo[i].occu_nsts[1] + mu_stats->nonmimo[i].occu_nsts[2] + mu_stats->nonmimo[i].occu_nsts[3];
printf("[%" PRIu8 "]%said:0x%x, txbf:%" PRIu32 ", dcm:%" PRIu32 "\n", i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
mu_stats->nonmimo[i].txbf, mu_stats->nonmimo[i].dcm);
printf("[%d]%said:0x%x, "
"mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), "
"mcs5:%" PRIu32 "(%2.2f%%), mcs6:%" PRIu32 "(%2.2f%%), mcs7:%" PRIu32 "(%2.2f%%), mcs8:%" PRIu32 "(%2.2f%%), mcs9:%" PRIu32 "(%2.2f%%), "
"mcs10:%" PRIu32 "(%2.2f%%), mcs11:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
mu_stats->nonmimo[i].occu_mcs[0], ((float) mu_stats->nonmimo[i].occu_mcs[0] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[1], ((float) mu_stats->nonmimo[i].occu_mcs[1] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[2], ((float) mu_stats->nonmimo[i].occu_mcs[2] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[3], ((float) mu_stats->nonmimo[i].occu_mcs[3] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[4], ((float) mu_stats->nonmimo[i].occu_mcs[4] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[5], ((float) mu_stats->nonmimo[i].occu_mcs[5] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[6], ((float) mu_stats->nonmimo[i].occu_mcs[6] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[7], ((float) mu_stats->nonmimo[i].occu_mcs[7] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[8], ((float) mu_stats->nonmimo[i].occu_mcs[8] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[9], ((float) mu_stats->nonmimo[i].occu_mcs[9] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[10], ((float) mu_stats->nonmimo[i].occu_mcs[10] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_mcs[11], ((float) mu_stats->nonmimo[i].occu_mcs[11] / (float) tot_rx_nonmimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"nsts0:%" PRIu32 "(%2.2f%%), nsts1:%" PRIu32 "(%2.2f%%), nsts2:%" PRIu32 "(%2.2f%%), nsts3:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
mu_stats->nonmimo[i].occu_nsts[0], ((float) mu_stats->nonmimo[i].occu_nsts[0] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_nsts[1], ((float) mu_stats->nonmimo[i].occu_nsts[1] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_nsts[2], ((float) mu_stats->nonmimo[i].occu_nsts[2] / (float) tot_rx_nonmimo) * 100,
mu_stats->nonmimo[i].occu_nsts[3], ((float) mu_stats->nonmimo[i].occu_nsts[3] / (float) tot_rx_nonmimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"tot_rx_nonmimo:%8d, sta/dut:%2.2f%%\n",
i, (mu_stats->aid == mu_stats->nonmimo[i].aid) ? "#" : " ", mu_stats->nonmimo[i].aid,
tot_rx_nonmimo, ((float) tot_rx_nonmimo / (float) mu_stats->nonmimo_rx) * 100);
}
}
void print_rx_statistics_mimo(const esp_test_rx_mu_statistics_t *mu_stats)
{
if (!mu_stats->mimo_rx) {
return;
}
int i;
int tot_rx_mimo = 0;
ESP_LOGW(TAG, "(mimo)dut rx:%" PRIu32 "", mu_stats->mimo_rx);
ESP_LOGW(TAG, "(mimo)sigb, mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), mcs5:%" PRIu32 "(%2.2f%%)",
mu_stats->mimo_sigb_mcs[0], ((float) mu_stats->mimo_sigb_mcs[0] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[1], ((float) mu_stats->mimo_sigb_mcs[1] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[2], ((float) mu_stats->mimo_sigb_mcs[2] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[3], ((float) mu_stats->mimo_sigb_mcs[3] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[4], ((float) mu_stats->mimo_sigb_mcs[4] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_sigb_mcs[5], ((float) mu_stats->mimo_sigb_mcs[5] / (float) mu_stats->mimo_rx) * 100);
ESP_LOGW(TAG, "(mimo)users num2:%" PRIu32 "(%2.2f%%), num3:%" PRIu32 "(%2.2f%%), num4:%" PRIu32 "(%2.2f%%), num5:%" PRIu32 "(%2.2f%%), num6:%" PRIu32 "(%2.2f%%), num7:%" PRIu32 "(%2.2f%%), num8:%" PRIu32 "(%2.2f%%)",
mu_stats->mimo_user_num_occu[0], ((float) mu_stats->mimo_user_num_occu[0] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[1], ((float) mu_stats->mimo_user_num_occu[1] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[2], ((float) mu_stats->mimo_user_num_occu[2] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[3], ((float) mu_stats->mimo_user_num_occu[3] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[4], ((float) mu_stats->mimo_user_num_occu[4] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[5], ((float) mu_stats->mimo_user_num_occu[5] / (float) mu_stats->mimo_rx) * 100,
mu_stats->mimo_user_num_occu[6], ((float) mu_stats->mimo_user_num_occu[6] / (float) mu_stats->mimo_rx) * 100);
for (i = 0; i < ESP_TEST_RX_MU_USER_NUM; i++) {
if (!mu_stats->mimo[i].aid) {
continue;
}
tot_rx_mimo = mu_stats->mimo[i].occu_ss[0] + mu_stats->mimo[i].occu_ss[1] + mu_stats->mimo[i].occu_ss[2] + mu_stats->mimo[i].occu_ss[3];
printf("[%" PRIu8 "]%said:0x%x, "
"mcs0:%" PRIu32 "(%2.2f%%), mcs1:%" PRIu32 "(%2.2f%%), mcs2:%" PRIu32 "(%2.2f%%), mcs3:%" PRIu32 "(%2.2f%%), mcs4:%" PRIu32 "(%2.2f%%), "
"mcs5:%" PRIu32 "(%2.2f%%), mcs6:%" PRIu32 "(%2.2f%%), mcs7:%" PRIu32 "(%2.2f%%), mcs8:%" PRIu32 "(%2.2f%%), mcs9:%" PRIu32 "(%2.2f%%), "
"mcs10:%" PRIu32 "(%2.2f%%), mcs11:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->mimo[i].aid) ? "#" : " ", mu_stats->mimo[i].aid,
mu_stats->mimo[i].occu_mcs[0], ((float) mu_stats->mimo[i].occu_mcs[0] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[1], ((float) mu_stats->mimo[i].occu_mcs[1] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[2], ((float) mu_stats->mimo[i].occu_mcs[2] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[3], ((float) mu_stats->mimo[i].occu_mcs[3] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[4], ((float) mu_stats->mimo[i].occu_mcs[4] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[5], ((float) mu_stats->mimo[i].occu_mcs[5] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[6], ((float) mu_stats->mimo[i].occu_mcs[6] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[7], ((float) mu_stats->mimo[i].occu_mcs[7] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[8], ((float) mu_stats->mimo[i].occu_mcs[8] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[9], ((float) mu_stats->mimo[i].occu_mcs[9] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[10], ((float) mu_stats->mimo[i].occu_mcs[10] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_mcs[11], ((float) mu_stats->mimo[i].occu_mcs[11] / (float) tot_rx_mimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"ss0:%" PRIu32 "(%2.2f%%), ss1:%" PRIu32 "(%2.2f%%), ss2:%" PRIu32 "(%2.2f%%), ss3:%" PRIu32 "(%2.2f%%)\n",
i, (mu_stats->aid == mu_stats->mimo[i].aid) ? "#" : " ", mu_stats->mimo[i].aid,
mu_stats->mimo[i].occu_ss[0], ((float) mu_stats->mimo[i].occu_ss[0] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_ss[1], ((float) mu_stats->mimo[i].occu_ss[1] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_ss[2], ((float) mu_stats->mimo[i].occu_ss[2] / (float) tot_rx_mimo) * 100,
mu_stats->mimo[i].occu_ss[3], ((float) mu_stats->mimo[i].occu_ss[3] / (float) tot_rx_mimo) * 100);
printf("[%" PRIu8 "]%said:0x%x, "
"tot_rx_mimo:%8d, sta/dut:%2.2f%%\n",
i, (mu_stats->aid == mu_stats->mimo[i].aid) ? "#" : " ", mu_stats->mimo[i].aid,
tot_rx_mimo, ((float) tot_rx_mimo / (float) mu_stats->mimo_rx) * 100);
}
}
void print_hw_rx_statistics(void)
{
esp_test_hw_rx_statistics_t hw_rx_stats = { 0, };
esp_test_get_hw_rx_statistics(&hw_rx_stats);
printf(
"WDEVRX_FCS_ERR :%d\n"
"WDEVRX_ABORT :%d\n"
"WDEVRX_ABORT_FCS_PASS :%d\n"
"NRX_ERR_PWRDROP :%d\n"
"NRX_HESIGB_ERR :%d\n"
"WDEVRX_SAMEBM_ERRCNT :%d\n"
"WDEVRX_MPDU :%d\n"
"WDEVRX_END_CNT :%d\n"
"WDEVRX_DATASUC :%d\n"
"WDEVRX_LASTUNMATCH_ERR :%d\n"
"RXHUNG_STATIS :%d\n"
"TXHUNG_STATIS :%d\n"
"RXTXHUNG :%" PRIu32 "\n"
"WDEVRX_CFO :%d\n"
"WDEVRX_SF :%d\n"
"WDEVRX_OTHER_UCAST :%d\n"
"WDEVRX_BUF_FULLCNT :%d\n"
"WDEVRX_FIFO_OVFCNT :%d\n"
"WDEVRX_TKIP_ERRCNT :%d\n"
"WDEVRX_BTBLOCK_ERR :%d\n"
"WDEVRX_FREQHOP_ERR :%d\n"
"WDEVRX_ACK_INT_CNT :%d\n"
"WDEVRX_RTS_INT_CNT :%d\n"
"BRX_ERR_AGC :%d\n"
"BRX_ERR :%d\n"
"NRX_ERR :%d\n"
"NRX_ERR_ABORT :%d\n"
"NRX_ERR_AGCEXIT :%d\n"
"NRX_ERR_BBOFF :%d\n"
"NRX_ERR_FDM_WDG :%d\n"
"NRX_ERR_RESTART :%d\n"
"NRX_ERR_SERV :%d\n"
"NRX_ERR_TXOVER :%d\n"
"NRX_HE_UNSUPPORT :%d\n"
"NRX_HTSIG_ERR :%d\n"
"NRX_HEUNSUPPORT :%d\n"
"NRX_HESIGA_CRC :%d\n",
hw_rx_stats.rx_fcs_err,
hw_rx_stats.rx_abort,
hw_rx_stats.rx_abort_fcs_pass,
hw_rx_stats.nrx_err_pwrdrop,
hw_rx_stats.nrx_hesigb_err,
hw_rx_stats.rx_samebm_errcnt,
hw_rx_stats.rx_mpdu,
hw_rx_stats.rx_end_cnt,
hw_rx_stats.rx_datasuc,
hw_rx_stats.rx_lastunmatch_err,
hw_rx_stats.rxhung_statis,
hw_rx_stats.txhung_statis,
hw_rx_stats.rxtxhung,
hw_rx_stats.rx_cfo_hz,
hw_rx_stats.rx_sf,
hw_rx_stats.rx_other_ucast,
hw_rx_stats.rx_buf_fullcnt,
hw_rx_stats.rx_fifo_ovfcnt,
hw_rx_stats.rx_tkip_errcnt,
hw_rx_stats.rx_btblock_err,
hw_rx_stats.rx_freqhop_err,
hw_rx_stats.rx_ack_int_cnt,
hw_rx_stats.rx_rts_int_cnt,
hw_rx_stats.brx_err_agc,
hw_rx_stats.brx_err,
hw_rx_stats.nrx_err,
hw_rx_stats.nrx_err_abort,
hw_rx_stats.nrx_err_agcexit,
hw_rx_stats.nrx_err_bboff,
hw_rx_stats.nrx_err_fdm_wdg,
hw_rx_stats.nrx_err_restart,
hw_rx_stats.nrx_err_serv,
hw_rx_stats.nrx_err_txover,
hw_rx_stats.nrx_err_unsupport,
hw_rx_stats.nrx_htsig_err,
hw_rx_stats.nrx_heunsupport,
hw_rx_stats.nrx_hesiga_crc
);
}
int wifi_cmd_clr_rx_statistics(int argc, char **argv)
{
ESP_LOGW(TAG, "Clear rx statistics");
esp_wifi_clr_rx_statistics(0);
esp_wifi_clr_rx_statistics(7);
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
esp_test_clr_rx_error_occurs();
esp_wifi_clr_rx_mu_statistics();
#endif
esp_test_clr_hw_statistics();
return 0;
}
void print_rx_mu_statistics(void)
{
/* mu */
esp_wifi_get_rx_mu_statistics(&rx_mu_stats);
/* MIMO */
print_rx_statistics_mimo(&rx_mu_stats);
/* non-MIMO */
print_rx_statistics_nonmimo(&rx_mu_stats);
}
int wifi_cmd_get_rx_statistics(int argc, char **argv)
{
ESP_LOGW(TAG, "Get rx statistics");
esp_test_rx_statistics_t rx_stats = { 0, };
esp_test_rx_error_occurs_t rx_error_occurs = { 0, };
esp_wifi_get_rx_statistics(0, &rx_stats); //tid=0
print_hw_tb_statistics();
ESP_LOGW(TAG, "(0)legacy:%" PRIu32 ", ht(ht:%" PRIu32 ", ht_retry:%" PRIu32 "/%2.2f%%, ht_noeb:%" PRIu32 "/%2.2f%%)",
rx_stats.legacy,
rx_stats.ht, rx_stats.ht_retry,
rx_stats.ht_retry ? ((float) ((float) rx_stats.ht_retry / (float) rx_stats.ht) * 100) : 0,
rx_stats.ht_noeb, rx_stats.ht_noeb ? ((float) ((float) rx_stats.ht_noeb / (float) rx_stats.ht) * 100) : 0);
ESP_LOGW(TAG, "(0)su(su:%" PRIu32 ", su_txbf:%" PRIu32 ", su_stbc:%" PRIu32 ", su_retry:%" PRIu32 "/%2.2f%%, ersu:%" PRIu32 ", ersu_dcm:%" PRIu32 ", su_noeb:%" PRIu32 "/%2.2f%%)",
rx_stats.su,
rx_stats.su_txbf, rx_stats.su_stbc,
rx_stats.su_retry,
rx_stats.su_retry ? ((float) ((float) rx_stats.su_retry / (float) rx_stats.su) * 100) : 0,
rx_stats.ersu,
rx_stats.ersu_dcm,
rx_stats.su_noeb, rx_stats.su_noeb ? ((float) ((float) rx_stats.su_noeb / (float) rx_stats.su) * 100) : 0);
ESP_LOGW(TAG, "(0)mu(mu:%" PRIu32 ", mimo:%" PRIu32 ", non-mimo:%" PRIu32 ", txbf:%" PRIu32 ", stbc:%" PRIu32 ", mu_retry:%" PRIu32 "/%2.2f%%, mu_noeb:%" PRIu32 "/%2.2f%%)",
rx_stats.mu,
rx_stats.mu_mimo,
rx_stats.mu_ofdma, rx_stats.mu_txbf, rx_stats.mu_stbc,
rx_stats.mu_retry,
rx_stats.mu_retry ? ((float) ((float) rx_stats.mu_retry / (float) rx_stats.mu) * 100) : 0,
rx_stats.mu_noeb, rx_stats.mu_noeb ? ((float) ((float) rx_stats.mu_noeb / (float) rx_stats.mu) * 100) : 0);
memset(&rx_stats, 0, sizeof(rx_stats));
esp_wifi_get_rx_statistics(7, &rx_stats); //tid=7
ESP_LOGW(TAG, "(7)legacy:%" PRIu32 ", ht:%" PRIu32 ", su:%" PRIu32 ", su_txbf:%" PRIu32 ", ersu:%" PRIu32 ", mu:%" PRIu32, rx_stats.legacy,
rx_stats.ht, rx_stats.su, rx_stats.su_txbf, rx_stats.ersu, rx_stats.mu);
ESP_LOGW(TAG, "(hw)isr:%" PRIu32 ", nblks:%" PRIu32, rx_stats.rx_isr, rx_stats.rx_nblks);
/* hw rx statistics */
print_hw_rx_statistics();
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
print_rx_mu_statistics();
#endif
esp_test_get_rx_error_occurs(&rx_error_occurs);
ESP_LOGW(TAG, "(rx)tot_errors:%" PRIu32, rx_error_occurs.tot);
int known_errors = 0; //rx error: 0x40-0xff
int i;
for (i = 0; i < 2; i++) {
if (rx_error_occurs.occurs[i]) {
known_errors += rx_error_occurs.occurs[i];
printf("[%3d] 0x%x, %8" PRIu32 ", %2.2f%%\n", i, (i ? 0xf5 : 0xc6), rx_error_occurs.occurs[i], ((float) rx_error_occurs.occurs[i] / (float) rx_error_occurs.tot) * 100);
}
}
if (rx_error_occurs.tot - known_errors) {
printf("[%3d]others, %8" PRIu32 ", %2.2f%%\n\n", i, rx_error_occurs.tot - known_errors, ((float) known_errors / (float) rx_error_occurs.tot) * 100);
}
wifi_cmd_clr_rx_statistics(0, 0);
return 0;
}
#endif /* CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS || CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS */
void register_wifi_stats(void)
{
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
/* get tx statistics */
const esp_console_cmd_t tx_stats_cmd = {
.command = "tx",
.help = "get tx statistics",
.hint = NULL,
.func = &wifi_cmd_get_tx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tx_stats_cmd));
/* clear tx statistics */
const esp_console_cmd_t clr_cmd = {
.command = "clrtx",
.help = "clear tx statistics",
.hint = NULL,
.func = &wifi_cmd_clr_tx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&clr_cmd));
#endif
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
/* get rx statistics */
const esp_console_cmd_t rx_stats_cmd = {
.command = "rx",
.help = "get rx statistics",
.hint = NULL,
.func = &wifi_cmd_get_rx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&rx_stats_cmd));
/* clear rx statistics */
const esp_console_cmd_t clr_rx_cmd = {
.command = "clrrx",
.help = "clear rx statistics",
.hint = NULL,
.func = &wifi_cmd_clr_rx_statistics,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&clr_rx_cmd));
#endif
}

View File

@@ -1,227 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#if CONFIG_SOC_WIFI_HE_SUPPORT
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_netif.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_wifi_he.h"
/*******************************************************
* Constants
*******************************************************/
static const char *TAG = "twt";
/*******************************************************
* Structures
*******************************************************/
typedef struct {
struct arg_int *setup;
struct arg_int *teardown;
struct arg_int *suspend;
struct arg_int *trigger; //1-trigger-enabled, 0-non-trigger-enabled, setup
struct arg_int *flowtype; //1-unannounced, 0-announced, setup
struct arg_int *negtype;
struct arg_int *wakeinvlexp; //setup
struct arg_int *wakeduraunit; //1-TU, 0-256us
struct arg_int *wakeinvlman; //setup
struct arg_int *minwakedur; //setup
struct arg_int *flowid;
struct arg_int *twtid;
struct arg_int *setup_timeout_time_ms;
struct arg_int *suspend_time_ms;
struct arg_int *all_twt;
struct arg_end *end;
} wifi_itwt_args_t;
typedef struct {
struct arg_int *timeout;
struct arg_end *end;
} wifi_itwt_send_probereq_t;
/*******************************************************
* Variable Definitions
*******************************************************/
static wifi_itwt_args_t itwt_args;
static wifi_itwt_send_probereq_t itwt_probe_args;
/*******************************************************
* Function Declarations
*******************************************************/
/*******************************************************
* Function Definitions
*******************************************************/
static int wifi_cmd_itwt(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &itwt_args);
if (nerrors != 0) {
arg_print_errors(stderr, itwt_args.end, argv[0]);
return 1;
}
esp_err_t err = ESP_OK;
if (itwt_args.setup->count) {
if (itwt_args.wakeinvlman->count) {
if (itwt_args.wakeinvlman->ival[0] < 0 || itwt_args.wakeinvlman->ival[0] > 65535) {
ESP_LOGE(TAG, "(itwt)expect [0, 65535], wake_invl_mant: %d", itwt_args.wakeinvlman->ival[0]);
return 1;
}
}
if (itwt_args.wakeinvlexp->count) {
if (itwt_args.wakeinvlexp->ival[0] < 0 || itwt_args.wakeinvlexp->ival[0] > 31) {
ESP_LOGE(TAG, "(itwt)expect [0, 31], wake_invl_expn: %d", itwt_args.wakeinvlexp->ival[0]);
return 1;
}
}
if (itwt_args.minwakedur->count) {
if (itwt_args.minwakedur->ival[0] < 0 || itwt_args.minwakedur->ival[0] > 255) {
ESP_LOGE(TAG, "(itwt)expect [0, 255], min_wake_dura: %d", itwt_args.minwakedur->ival[0]);
return 1;
}
}
if (itwt_args.wakeduraunit->count) {
if (itwt_args.wakeduraunit->ival[0] < 0 || itwt_args.wakeduraunit->ival[0] > 1) {
ESP_LOGE(TAG, "(itwt)expect [0, 1], wake duration unit: %d", itwt_args.wakeduraunit->ival[0]);
return 1;
}
}
if (itwt_args.twtid->count) {
if (itwt_args.twtid->ival[0] < 0 || itwt_args.twtid->ival[0] > 32767) {
ESP_LOGE(TAG, "(itwt)expect [0, 32767], twt id: %d", itwt_args.twtid->ival[0]);
return 1;
}
}
if (itwt_args.setup_timeout_time_ms->count) {
if (itwt_args.setup_timeout_time_ms->ival[0] < 0 || itwt_args.setup_timeout_time_ms->ival[0] > 65535) {
ESP_LOGE(TAG, "(itwt)expect [0, 65535], setup timeout time: %d", itwt_args.setup_timeout_time_ms->ival[0]);
return 1;
}
}
wifi_twt_setup_config_t setup_config = {
.setup_cmd = (itwt_args.setup->ival[0] <= TWT_DEMAND) ? itwt_args.setup->ival[0] : TWT_REQUEST,
.flow_id = 0,
.twt_id = itwt_args.twtid->count ? itwt_args.twtid->ival[0] : 0,
.flow_type = itwt_args.flowtype->count ? ((itwt_args.flowtype->ival[0] == 0) ? 0 : 1) : 0,
.min_wake_dura = itwt_args.minwakedur->count ? itwt_args.minwakedur->ival[0] : 255,
.wake_duration_unit = itwt_args.wakeduraunit->count ? itwt_args.wakeduraunit->ival[0] : 0,
.wake_invl_expn = itwt_args.wakeinvlexp->count ? itwt_args.wakeinvlexp->ival[0] : 10,
.wake_invl_mant = itwt_args.wakeinvlman->count ? itwt_args.wakeinvlman->ival[0] : 512,
.trigger = itwt_args.trigger->count ? (itwt_args.trigger->ival[0] ? 1 : 0) : 1,
.timeout_time_ms = itwt_args.setup_timeout_time_ms->count ? itwt_args.setup_timeout_time_ms->ival[0] : 5000,
};
err = esp_wifi_sta_itwt_setup(&setup_config);
ESP_LOGI(TAG, "(itwt)setup, trigger:%d, %s, flow_id:%d, err:0x%x",
setup_config.trigger, setup_config.flow_type ? "unannounce" : "announced", setup_config.flow_id, err);
}
if (itwt_args.teardown->count) {
// teardown a given flow id, all_twt has a high priority
int flow_id = itwt_args.flowid->count ? itwt_args.flowid->ival[0] : (-1);
bool all_twt = itwt_args.all_twt->count ? ((itwt_args.all_twt->ival[0] == 1) ? true : false) : false;
flow_id = (all_twt == true) ? FLOW_ID_ALL : flow_id;
if (flow_id >= 0) {
err = esp_wifi_sta_itwt_teardown(flow_id);
ESP_LOGI(TAG, "(itwt)teardown, flow_id:%d, all_twt:%d, err:0x%x", flow_id, all_twt, err);
} else {
ESP_LOGE(TAG, "(itwt)teardown, should specify an existing flow id");
}
}
if (itwt_args.suspend->count) {
// suspend a given flow id
int flow_id = itwt_args.flowid->count ? itwt_args.flowid->ival[0] : (-1);
bool all_twt = itwt_args.all_twt->count ? (itwt_args.all_twt->ival[0] ? true : false) : false;
flow_id = (all_twt == true) ? FLOW_ID_ALL : flow_id;
int suspend_time_ms = itwt_args.suspend_time_ms->count ? itwt_args.suspend_time_ms->ival[0] : 0;
if (flow_id > 0) {
err = esp_wifi_sta_itwt_suspend(flow_id, suspend_time_ms);
ESP_LOGI(TAG, "(itwt)suspend, flow_id:%d, all_twt:%d, suspend:%d ms, err:0x%x", flow_id, all_twt, suspend_time_ms, err);
} else {
ESP_LOGE(TAG, "(itwt)suspend, should specify an existing flow id");
}
}
return 0;
}
static int wifi_cmd_itwt_probe(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &itwt_probe_args);
if (nerrors != 0) {
arg_print_errors(stderr, itwt_probe_args.end, argv[0]);
return 1;
}
esp_err_t err = ESP_OK;
if (itwt_probe_args.timeout->count) {
if (itwt_probe_args.timeout->ival[0] > 0) {
ESP_LOGI(TAG, "(itwt)send probe req, timeout:%d ms", itwt_probe_args.timeout->ival[0]);
err = esp_wifi_sta_itwt_send_probe_req(itwt_probe_args.timeout->ival[0]);
} else {
ESP_LOGE(TAG, "(itwt)invalid input, timeout:%d ms", itwt_probe_args.timeout->ival[0]);
}
}
ESP_LOGI(TAG, "err:0x%x", err);
return err;
}
void register_wifi_itwt(void)
{
/* itwt setup/teardown */
itwt_args.setup = arg_int0(NULL, "setup", "<setup>", "twt setup/teardown an individual flow id");
itwt_args.teardown = arg_int0(NULL, "teardown", "<setup>", "twt setup/teardown an individual flow id");
itwt_args.suspend = arg_int0(NULL, "suspend", "<setup>", "twt setup/teardown an individual flow id");
itwt_args.trigger = arg_int0("t", NULL, "<trigger>", "trigger");
itwt_args.flowtype = arg_int0("f", NULL, "<flow_type>", "flow type: 0-announced, 1-unannounced");
itwt_args.negtype = arg_int0("n", NULL, "<neg_type>", "negotiate type");
itwt_args.minwakedur = arg_int0("d", NULL, "<minwakedur>", "Norminal Min. Wake Duration");
itwt_args.wakeduraunit = arg_int0("u", NULL, "<wakeduraunit>", "wake duration unit 0-256us, 1-TU (TU = 1024us)");
itwt_args.wakeinvlexp = arg_int0("e", NULL, "<wakeinvlexp>", "Wake Interval Exponent");
itwt_args.wakeinvlman = arg_int0("m", NULL, "<wakeinvlman>", "Wake Interval Mantissa");
itwt_args.flowid = arg_int0("i", NULL, "<flow_id>", "Flow ID");
itwt_args.suspend_time_ms = arg_int0("s", NULL, "<suspend_time_ms>", "time of suspending iTWT agreements, unit ms");
itwt_args.twtid = arg_int0("w", NULL, "<twt_id>", "TWT ID");
itwt_args.setup_timeout_time_ms = arg_int0("u", NULL, "<setup_timeout_time_ms>", "iTWT setup timeout time, unit ms");
itwt_args.all_twt = arg_int0("a", NULL, "<all_twt>", "All TWT");
itwt_args.end = arg_end(1);
const esp_console_cmd_t itwt_cmd = {
.command = "itwt",
.help = "itwt setup, teardown or suspend",
.hint = NULL,
.func = &wifi_cmd_itwt,
.argtable = &itwt_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&itwt_cmd));
/* itwt probe */
itwt_probe_args.timeout = arg_int0("t", NULL, "[timeout]", "time of sending a probe request frame and receiving a probe response frame from ap, unit ms");
itwt_probe_args.end = arg_end(1);
const esp_console_cmd_t itwt_probe_cmd = {
.command = "probe",
.help = "send probe request for TSF update when at lease one itwt agreement setup",
.hint = NULL,
.func = &wifi_cmd_itwt_probe,
.argtable = &itwt_probe_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&itwt_probe_cmd));
}
#else
void register_wifi_itwt(void)
{
;
}
#endif /* CONFIG_SOC_WIFI_HE_SUPPORT */