mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feat/move_iperf_component' into 'master'
feat: move iperf to component registry Closes RDT-618 See merge request espressif/esp-idf!29537
This commit is contained in:
commit
38733a752d
@ -3,5 +3,5 @@ dependencies:
|
||||
path: ${IDF_PATH}/examples/bluetooth/esp_ble_mesh/common_components/fast_prov
|
||||
example_init:
|
||||
path: ${IDF_PATH}/examples/bluetooth/esp_ble_mesh/common_components/example_init
|
||||
iperf:
|
||||
path: ${IDF_PATH}/examples/common_components/iperf
|
||||
espressif/iperf:
|
||||
version: "~0.1.1"
|
||||
|
@ -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()
|
@ -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
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
@ -38,7 +38,6 @@ examples/ethernet/iperf:
|
||||
- esp_driver_gpio
|
||||
- esp_driver_spi
|
||||
depends_filepatterns:
|
||||
- examples/common_components/iperf/**/*
|
||||
- examples/common_components/protocol_examples_common/**/*
|
||||
- examples/system/console/advanced/components/cmd_system/**/*
|
||||
- examples/ethernet/basic/components/ethernet_init/**/*
|
||||
|
@ -3,5 +3,5 @@ dependencies:
|
||||
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system
|
||||
ethernet_init:
|
||||
path: ${IDF_PATH}/examples/ethernet/basic/components/ethernet_init
|
||||
iperf:
|
||||
path: ${IDF_PATH}/examples/common_components/iperf
|
||||
espressif/iperf:
|
||||
version: "~0.1.1"
|
||||
|
@ -11,8 +11,6 @@
|
||||
- lwip
|
||||
- openthread
|
||||
depends_filepatterns:
|
||||
- examples/common_components/iperf/*
|
||||
- examples/common_components/iperf/**/*
|
||||
- examples/openthread/*
|
||||
- examples/openthread/**/*
|
||||
|
||||
|
@ -8,7 +8,7 @@ dependencies:
|
||||
version: ">=5.0"
|
||||
protocol_examples_common:
|
||||
path: ${IDF_PATH}/examples/common_components/protocol_examples_common
|
||||
iperf:
|
||||
path: ${IDF_PATH}/examples/common_components/iperf
|
||||
ot_led:
|
||||
path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_led
|
||||
espressif/iperf:
|
||||
version: "~0.1.1"
|
||||
|
@ -4,7 +4,7 @@ dependencies:
|
||||
version: "~1.0.0"
|
||||
idf:
|
||||
version: ">=4.1.0"
|
||||
iperf:
|
||||
path: ${IDF_PATH}/examples/common_components/iperf
|
||||
ot_led:
|
||||
path: ${IDF_PATH}/examples/openthread/ot_common_components/ot_led
|
||||
espressif/iperf:
|
||||
version: "~0.1.1"
|
||||
|
@ -1,3 +1,2 @@
|
||||
idf_component_register(SRCS "cmd_wifi.c"
|
||||
"iperf_example_main.c"
|
||||
idf_component_register(SRCS "iperf_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -1,20 +0,0 @@
|
||||
/* Iperf example — declarations of command registration functions.
|
||||
|
||||
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.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cmd_system.h"
|
||||
#include "cmd_wifi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,616 +0,0 @@
|
||||
/* Iperf Example - wifi commands
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_console.h"
|
||||
#include "argtable3/argtable3.h"
|
||||
#include "cmd_decl.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_mac.h"
|
||||
#include "iperf.h"
|
||||
#include "esp_coexist.h"
|
||||
#include "wifi_cmd.h"
|
||||
|
||||
typedef struct {
|
||||
struct arg_str *ip;
|
||||
struct arg_lit *server;
|
||||
struct arg_lit *udp;
|
||||
struct arg_lit *version;
|
||||
struct arg_int *port;
|
||||
struct arg_int *length;
|
||||
struct arg_int *interval;
|
||||
struct arg_int *time;
|
||||
struct arg_int *bw_limit;
|
||||
struct arg_lit *abort;
|
||||
struct arg_end *end;
|
||||
} wifi_iperf_t;
|
||||
|
||||
typedef struct {
|
||||
struct arg_str *ssid;
|
||||
struct arg_str *password;
|
||||
struct arg_end *end;
|
||||
} wifi_args_t;
|
||||
|
||||
typedef struct {
|
||||
struct arg_str *ssid;
|
||||
struct arg_end *end;
|
||||
} wifi_scan_arg_t;
|
||||
|
||||
static wifi_iperf_t iperf_args;
|
||||
static wifi_args_t sta_args;
|
||||
static wifi_scan_arg_t scan_args;
|
||||
static wifi_args_t ap_args;
|
||||
static bool reconnect = true;
|
||||
static const char *TAG = "cmd_wifi";
|
||||
esp_netif_t *netif_ap = NULL;
|
||||
esp_netif_t *netif_sta = NULL;
|
||||
|
||||
EventGroupHandle_t wifi_event_group;
|
||||
const int CONNECTED_BIT = BIT0;
|
||||
const int DISCONNECTED_BIT = BIT1;
|
||||
|
||||
static void scan_done_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
uint16_t sta_number = 0;
|
||||
uint8_t i;
|
||||
wifi_ap_record_t *ap_list_buffer;
|
||||
|
||||
esp_wifi_scan_get_ap_num(&sta_number);
|
||||
if (!sta_number) {
|
||||
ESP_LOGE(TAG, "No AP found");
|
||||
return;
|
||||
}
|
||||
|
||||
ap_list_buffer = malloc(sta_number * sizeof(wifi_ap_record_t));
|
||||
if (ap_list_buffer == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to malloc buffer to print scan results");
|
||||
esp_wifi_clear_ap_list();
|
||||
return;
|
||||
}
|
||||
|
||||
if (esp_wifi_scan_get_ap_records(&sta_number, (wifi_ap_record_t *)ap_list_buffer) == ESP_OK) {
|
||||
for (i = 0; i < sta_number; i++) {
|
||||
#if CONFIG_SOC_WIFI_HE_SUPPORT
|
||||
char ssid_rssi[46] = { 0, };
|
||||
sprintf(ssid_rssi, "[%s][rssi=%d]", ap_list_buffer[i].ssid, ap_list_buffer[i].rssi);
|
||||
if (ap_list_buffer[i].phy_11ax) {
|
||||
ESP_LOGW(TAG,
|
||||
"[%2d]%45s authmode:0x%x, channel:%2d[%d], phymode:%4s, "MACSTR", bssid-index:%d, bss_color:%d, disabled:%d",
|
||||
i, ssid_rssi, ap_list_buffer[i].authmode,
|
||||
ap_list_buffer[i].primary, ap_list_buffer[i].second,
|
||||
ap_list_buffer[i].phy_11ax ? "11ax" : (ap_list_buffer[i].phy_11n ? "11n" :
|
||||
(ap_list_buffer[i].phy_11g ? "11g" : (ap_list_buffer[i].phy_11b ? "11b" : ""))),
|
||||
MAC2STR(ap_list_buffer[i].bssid), ap_list_buffer[i].he_ap.bssid_index,
|
||||
ap_list_buffer[i].he_ap.bss_color, ap_list_buffer[i].he_ap.bss_color_disabled);
|
||||
} else {
|
||||
ESP_LOGI(TAG,
|
||||
"[%2d]%45s authmode:0x%x, channel:%2d[%d], phymode:%4s, "MACSTR"",
|
||||
i, ssid_rssi, ap_list_buffer[i].authmode,
|
||||
ap_list_buffer[i].primary, ap_list_buffer[i].second,
|
||||
ap_list_buffer[i].phy_11ax ? "11ax" : (ap_list_buffer[i].phy_11n ? "11n" :
|
||||
(ap_list_buffer[i].phy_11g ? "11g" : (ap_list_buffer[i].phy_11b ? "11b" : ""))),
|
||||
MAC2STR(ap_list_buffer[i].bssid));
|
||||
}
|
||||
#else
|
||||
ESP_LOGI(TAG, "[%s][rssi=%d]", ap_list_buffer[i].ssid, ap_list_buffer[i].rssi);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
free(ap_list_buffer);
|
||||
ESP_LOGI(TAG, "sta scan done");
|
||||
}
|
||||
|
||||
static void got_ip_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
xEventGroupClearBits(wifi_event_group, DISCONNECTED_BIT);
|
||||
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
|
||||
}
|
||||
|
||||
static void disconnect_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data)
|
||||
{
|
||||
if (reconnect) {
|
||||
ESP_LOGI(TAG, "sta disconnect, reconnect...");
|
||||
esp_wifi_connect();
|
||||
} else {
|
||||
ESP_LOGI(TAG, "sta disconnect");
|
||||
}
|
||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||
xEventGroupSetBits(wifi_event_group, DISCONNECTED_BIT);
|
||||
}
|
||||
|
||||
void initialise_wifi(void)
|
||||
{
|
||||
esp_log_level_set("wifi", ESP_LOG_WARN);
|
||||
static bool initialized = false;
|
||||
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
wifi_event_group = xEventGroupCreate();
|
||||
ESP_ERROR_CHECK( esp_event_loop_create_default() );
|
||||
netif_ap = esp_netif_create_default_wifi_ap();
|
||||
assert(netif_ap);
|
||||
netif_sta = esp_netif_create_default_wifi_sta();
|
||||
assert(netif_sta);
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
WIFI_EVENT_SCAN_DONE,
|
||||
&scan_done_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
|
||||
WIFI_EVENT_STA_DISCONNECTED,
|
||||
&disconnect_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||
IP_EVENT_STA_GOT_IP,
|
||||
&got_ip_handler,
|
||||
NULL,
|
||||
NULL));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM) );
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL) );
|
||||
ESP_ERROR_CHECK(esp_wifi_start() );
|
||||
|
||||
#if CONFIG_EXTERNAL_COEX_ENABLE
|
||||
esp_external_coex_gpio_set_t gpio_pin;
|
||||
gpio_pin.request = 1;
|
||||
gpio_pin.priority = 2;
|
||||
gpio_pin.grant = 3;
|
||||
#if SOC_EXTERNAL_COEX_LEADER_TX_LINE
|
||||
gpio_pin.tx_line = 4;
|
||||
#endif
|
||||
|
||||
esp_external_coex_set_work_mode(EXTERNAL_COEX_LEADER_ROLE);
|
||||
#if SOC_EXTERNAL_COEX_LEADER_TX_LINE
|
||||
ESP_ERROR_CHECK(esp_enable_extern_coex_gpio_pin(EXTERN_COEX_WIRE_4, gpio_pin));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_enable_extern_coex_gpio_pin(EXTERN_COEX_WIRE_3, gpio_pin));
|
||||
#endif /* SOC_EXTERNAL_COEX_LEADER_TX_LINE */
|
||||
#endif /* CONFIG_EXTERNAL_COEX_ENABLE */
|
||||
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
|
||||
esp_wifi_enable_rx_statistics(true, true);
|
||||
#else
|
||||
esp_wifi_enable_rx_statistics(true, false);
|
||||
#endif
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
|
||||
esp_wifi_enable_tx_statistics(ESP_WIFI_ACI_BE, true);
|
||||
#endif
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
static bool wifi_cmd_sta_join(const char *ssid, const char *pass, bool enable_he_mcs9)
|
||||
{
|
||||
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
|
||||
|
||||
wifi_config_t wifi_config = { 0 };
|
||||
|
||||
strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
|
||||
if (pass) {
|
||||
strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
|
||||
}
|
||||
|
||||
if (enable_he_mcs9 == true) {
|
||||
wifi_config.sta.he_mcs9_enabled = 1;
|
||||
}
|
||||
|
||||
if (bits & CONNECTED_BIT) {
|
||||
reconnect = false;
|
||||
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
|
||||
ESP_ERROR_CHECK( esp_wifi_disconnect() );
|
||||
xEventGroupWaitBits(wifi_event_group, DISCONNECTED_BIT, 0, 1, portTICK_PERIOD_MS);
|
||||
}
|
||||
|
||||
reconnect = true;
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
|
||||
esp_wifi_connect();
|
||||
|
||||
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 5000 / portTICK_PERIOD_MS);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int wifi_cmd_sta(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **) &sta_args);
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, sta_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "sta connecting to '%s'", sta_args.ssid->sval[0]);
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0], false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifi_cmd_sta40(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **) &sta_args);
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, sta_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_protocol(0, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidth(0, WIFI_BW_HT40));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
|
||||
ESP_LOGI(TAG, "sta connecting to '%s'", sta_args.ssid->sval[0]);
|
||||
wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0], false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifi_cmd_sta_mcs89(int argc, char **argv)
|
||||
{
|
||||
#if CONFIG_SOC_WIFI_HE_SUPPORT
|
||||
int nerrors = arg_parse(argc, argv, (void **) &sta_args);
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, sta_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_protocol(0, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_11AX));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidth(0, WIFI_BW_HT20));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
|
||||
ESP_LOGI(TAG, "sta connecting to '%s'", sta_args.ssid->sval[0]);
|
||||
wifi_cmd_sta_join(sta_args.ssid->sval[0], sta_args.password->sval[0], true);
|
||||
#else
|
||||
ESP_LOGW(TAG, "HE-MCS[0, 9] is not supported");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool wifi_cmd_sta_scan(const char *ssid)
|
||||
{
|
||||
wifi_scan_config_t scan_config = { 0 };
|
||||
scan_config.ssid = (uint8_t *) ssid;
|
||||
|
||||
ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
esp_wifi_scan_start(&scan_config, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int wifi_cmd_scan(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **) &scan_args);
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, scan_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "sta start to scan");
|
||||
if ( scan_args.ssid->count == 1 ) {
|
||||
wifi_cmd_sta_scan(scan_args.ssid->sval[0]);
|
||||
} else {
|
||||
wifi_cmd_sta_scan(NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static bool wifi_cmd_ap_set(const char *ssid, const char *pass)
|
||||
{
|
||||
wifi_config_t wifi_config = {
|
||||
.ap = {
|
||||
.ssid = "",
|
||||
.ssid_len = 0,
|
||||
.max_connection = 4,
|
||||
.password = "",
|
||||
.authmode = WIFI_AUTH_WPA_WPA2_PSK
|
||||
},
|
||||
};
|
||||
|
||||
reconnect = false;
|
||||
strlcpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
|
||||
if (pass) {
|
||||
if (strlen(pass) != 0 && strlen(pass) < 8) {
|
||||
reconnect = true;
|
||||
ESP_LOGE(TAG, "password less than 8");
|
||||
return false;
|
||||
}
|
||||
strlcpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
|
||||
}
|
||||
|
||||
if (strlen(pass) == 0) {
|
||||
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
|
||||
return true;
|
||||
}
|
||||
|
||||
static int wifi_cmd_ap(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **) &ap_args);
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, ap_args.end, argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wifi_cmd_ap_set(ap_args.ssid->sval[0], ap_args.password->sval[0]);
|
||||
ESP_LOGI(TAG, "AP mode, %s %s", ap_args.ssid->sval[0], ap_args.password->sval[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifi_cmd_query(int argc, char **argv)
|
||||
{
|
||||
wifi_config_t cfg;
|
||||
wifi_mode_t mode;
|
||||
|
||||
esp_wifi_get_mode(&mode);
|
||||
if (WIFI_MODE_AP == mode) {
|
||||
esp_wifi_get_config(WIFI_IF_AP, &cfg);
|
||||
ESP_LOGI(TAG, "AP mode, %s %s", cfg.ap.ssid, cfg.ap.password);
|
||||
} else if (WIFI_MODE_STA == mode) {
|
||||
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
|
||||
if (bits & CONNECTED_BIT) {
|
||||
esp_wifi_get_config(WIFI_IF_STA, &cfg);
|
||||
ESP_LOGI(TAG, "sta mode, connected %s", cfg.ap.ssid);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "sta mode, disconnected");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGI(TAG, "NULL mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t wifi_get_local_ip(void)
|
||||
{
|
||||
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
|
||||
esp_netif_t *netif = netif_ap;
|
||||
esp_netif_ip_info_t ip_info;
|
||||
wifi_mode_t mode;
|
||||
|
||||
esp_wifi_get_mode(&mode);
|
||||
if (WIFI_MODE_STA == mode) {
|
||||
bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, 0, 1, 0);
|
||||
if (bits & CONNECTED_BIT) {
|
||||
netif = netif_sta;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "sta has no IP");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
esp_netif_get_ip_info(netif, &ip_info);
|
||||
return ip_info.ip.addr;
|
||||
}
|
||||
|
||||
static int wifi_cmd_iperf(int argc, char **argv)
|
||||
{
|
||||
int nerrors = arg_parse(argc, argv, (void **) &iperf_args);
|
||||
iperf_cfg_t cfg;
|
||||
|
||||
if (nerrors != 0) {
|
||||
arg_print_errors(stderr, iperf_args.end, argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
|
||||
// now wifi iperf only support IPV4 address
|
||||
cfg.type = IPERF_IP_TYPE_IPV4;
|
||||
|
||||
if ( iperf_args.abort->count != 0) {
|
||||
iperf_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( ((iperf_args.ip->count == 0) && (iperf_args.server->count == 0)) ||
|
||||
((iperf_args.ip->count != 0) && (iperf_args.server->count != 0)) ) {
|
||||
ESP_LOGE(TAG, "should specific client/server mode");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iperf_args.ip->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_SERVER;
|
||||
} else {
|
||||
cfg.destination_ip4 = esp_ip4addr_aton(iperf_args.ip->sval[0]);
|
||||
cfg.flag |= IPERF_FLAG_CLIENT;
|
||||
}
|
||||
|
||||
cfg.source_ip4 = wifi_get_local_ip();
|
||||
if (cfg.source_ip4 == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iperf_args.udp->count == 0) {
|
||||
cfg.flag |= IPERF_FLAG_TCP;
|
||||
} else {
|
||||
cfg.flag |= IPERF_FLAG_UDP;
|
||||
}
|
||||
|
||||
if (iperf_args.length->count == 0) {
|
||||
cfg.len_send_buf = 0;
|
||||
} else {
|
||||
cfg.len_send_buf = iperf_args.length->ival[0];
|
||||
}
|
||||
|
||||
if (iperf_args.port->count == 0) {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
if (cfg.flag & IPERF_FLAG_SERVER) {
|
||||
cfg.sport = iperf_args.port->ival[0];
|
||||
cfg.dport = IPERF_DEFAULT_PORT;
|
||||
} else {
|
||||
cfg.sport = IPERF_DEFAULT_PORT;
|
||||
cfg.dport = iperf_args.port->ival[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (iperf_args.interval->count == 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
} else {
|
||||
cfg.interval = iperf_args.interval->ival[0];
|
||||
if (cfg.interval <= 0) {
|
||||
cfg.interval = IPERF_DEFAULT_INTERVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (iperf_args.time->count == 0) {
|
||||
cfg.time = IPERF_DEFAULT_TIME;
|
||||
} else {
|
||||
cfg.time = iperf_args.time->ival[0];
|
||||
if (cfg.time <= cfg.interval) {
|
||||
cfg.time = cfg.interval;
|
||||
}
|
||||
}
|
||||
|
||||
/* iperf -b */
|
||||
if (iperf_args.bw_limit->count == 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
} else {
|
||||
cfg.bw_lim = iperf_args.bw_limit->ival[0];
|
||||
if (cfg.bw_lim <= 0) {
|
||||
cfg.bw_lim = IPERF_DEFAULT_NO_BW_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ESP_LOGI(TAG, "mode=%s-%s sip=%" PRId32 ".%" PRId32 ".%" PRId32 ".%" PRId32 ":%d,\
|
||||
dip=%" PRId32 ".%" PRId32 ".%" PRId32 ".%" PRId32 ":%d,\
|
||||
interval=%" PRId32 ", time=%" PRId32 "",
|
||||
cfg.flag & IPERF_FLAG_TCP ? "tcp" : "udp",
|
||||
cfg.flag & IPERF_FLAG_SERVER ? "server" : "client",
|
||||
cfg.source_ip4 & 0xFF, (cfg.source_ip4 >> 8) & 0xFF, (cfg.source_ip4 >> 16) & 0xFF,
|
||||
(cfg.source_ip4 >> 24) & 0xFF, cfg.sport,
|
||||
cfg.destination_ip4 & 0xFF, (cfg.destination_ip4 >> 8) & 0xFF,
|
||||
(cfg.destination_ip4 >> 16) & 0xFF, (cfg.destination_ip4 >> 24) & 0xFF, cfg.dport,
|
||||
cfg.interval, cfg.time);
|
||||
|
||||
iperf_start(&cfg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void register_wifi(void)
|
||||
{
|
||||
sta_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
|
||||
sta_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
|
||||
sta_args.end = arg_end(2);
|
||||
|
||||
const esp_console_cmd_t sta_cmd = {
|
||||
.command = "sta",
|
||||
.help = "WiFi is station mode, join specified soft-AP",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_sta,
|
||||
.argtable = &sta_args
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&sta_cmd) );
|
||||
|
||||
const esp_console_cmd_t sta40_cmd = {
|
||||
.command = "sta40",
|
||||
.help = "WiFi is station mode, set protocol to bgn and cbw to 40MHz, join a specified AP",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_sta40,
|
||||
.argtable = &sta_args
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&sta40_cmd) );
|
||||
|
||||
const esp_console_cmd_t stamcs89_cmd = {
|
||||
.command = "stamcs89",
|
||||
.help = "WiFi is station mode, set protocol to ax and mcs set to HE-MCS[0,9], join a specified AP",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_sta_mcs89,
|
||||
.argtable = &sta_args
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&stamcs89_cmd) );
|
||||
|
||||
scan_args.ssid = arg_str0(NULL, NULL, "<ssid>", "SSID of AP want to be scanned");
|
||||
scan_args.end = arg_end(1);
|
||||
|
||||
const esp_console_cmd_t scan_cmd = {
|
||||
.command = "scan",
|
||||
.help = "WiFi is station mode, start scan ap",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_scan,
|
||||
.argtable = &scan_args
|
||||
};
|
||||
|
||||
ap_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
|
||||
ap_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
|
||||
ap_args.end = arg_end(2);
|
||||
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&scan_cmd) );
|
||||
|
||||
const esp_console_cmd_t ap_cmd = {
|
||||
.command = "ap",
|
||||
.help = "AP mode, configure ssid and password",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_ap,
|
||||
.argtable = &ap_args
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&ap_cmd) );
|
||||
|
||||
const esp_console_cmd_t query_cmd = {
|
||||
.command = "query",
|
||||
.help = "query WiFi info",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_query,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&query_cmd) );
|
||||
|
||||
iperf_args.ip = arg_str0("c", "client", "<ip>", "run in client mode, connecting to <host>");
|
||||
iperf_args.server = arg_lit0("s", "server", "run in server mode");
|
||||
iperf_args.udp = arg_lit0("u", "udp", "use UDP rather than TCP");
|
||||
iperf_args.version = arg_lit0("V", "ipv6_domain", "use IPV6 address rather than IPV4");
|
||||
iperf_args.port = arg_int0("p", "port", "<port>", "server port to listen on/connect to");
|
||||
iperf_args.length = arg_int0("l", "len", "<length>", "Set read/write buffer size");
|
||||
iperf_args.interval = arg_int0("i", "interval", "<interval>", "seconds between periodic bandwidth reports");
|
||||
iperf_args.time = arg_int0("t", "time", "<time>", "time in seconds to transmit for (default 10 secs)");
|
||||
iperf_args.bw_limit = arg_int0("b", "bandwidth", "<bandwidth>", "bandwidth to send at in Mbits/sec");
|
||||
iperf_args.abort = arg_lit0("a", "abort", "abort running iperf");
|
||||
iperf_args.end = arg_end(1);
|
||||
const esp_console_cmd_t iperf_cmd = {
|
||||
.command = "iperf",
|
||||
.help = "iperf command",
|
||||
.hint = NULL,
|
||||
.func = &wifi_cmd_iperf,
|
||||
.argtable = &iperf_args
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&iperf_cmd) );
|
||||
|
||||
register_wifi_cmd();
|
||||
register_wifi_stats();
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Register WiFi functions
|
||||
void register_wifi(void);
|
||||
void initialise_wifi(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,5 +1,9 @@
|
||||
dependencies:
|
||||
cmd_system:
|
||||
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system
|
||||
iperf:
|
||||
path: ${IDF_PATH}/examples/common_components/iperf
|
||||
espressif/iperf-cmd:
|
||||
version: "~0.1.1"
|
||||
esp-qa/wifi-cmd:
|
||||
version: "~0.0.2"
|
||||
esp-qa/ping-cmd:
|
||||
version: "~0.0.1"
|
||||
|
@ -14,7 +14,58 @@
|
||||
#include "esp_err.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_console.h"
|
||||
#include "cmd_decl.h"
|
||||
#include "cmd_system.h"
|
||||
|
||||
/* component manager */
|
||||
#include "iperf.h"
|
||||
#include "wifi_cmd.h"
|
||||
#include "iperf_cmd.h"
|
||||
#include "ping_cmd.h"
|
||||
|
||||
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS || CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
|
||||
#include "esp_wifi_he.h"
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
|
||||
extern int wifi_cmd_get_tx_statistics(int argc, char **argv);
|
||||
extern int wifi_cmd_clr_tx_statistics(int argc, char **argv);
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
|
||||
extern int wifi_cmd_get_rx_statistics(int argc, char **argv);
|
||||
extern int wifi_cmd_clr_rx_statistics(int argc, char **argv);
|
||||
#endif
|
||||
|
||||
|
||||
void iperf_hook_show_wifi_stats(iperf_traffic_type_t type, iperf_status_t status)
|
||||
{
|
||||
if (status == IPERF_STARTED) {
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
|
||||
if (type != IPERF_UDP_SERVER) {
|
||||
wifi_cmd_clr_tx_statistics(0, NULL);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
|
||||
if (type != IPERF_UDP_CLIENT) {
|
||||
wifi_cmd_clr_rx_statistics(0, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (status == IPERF_STOPPED) {
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
|
||||
if (type != IPERF_UDP_SERVER) {
|
||||
wifi_cmd_get_tx_statistics(0, NULL);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
|
||||
if (type != IPERF_UDP_SERVER) {
|
||||
wifi_cmd_get_rx_statistics(0, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
@ -25,7 +76,22 @@ void app_main(void)
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
initialise_wifi();
|
||||
/* initialise wifi */
|
||||
app_wifi_initialise_config_t config = APP_WIFI_CONFIG_DEFAULT();
|
||||
config.storage = WIFI_STORAGE_RAM;
|
||||
config.ps_type = WIFI_PS_NONE;
|
||||
app_initialise_wifi(&config);
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_STATS
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_RX_MU_STATS
|
||||
esp_wifi_enable_rx_statistics(true, true);
|
||||
#else
|
||||
esp_wifi_enable_rx_statistics(true, false);
|
||||
#endif
|
||||
#endif
|
||||
#if CONFIG_ESP_WIFI_ENABLE_WIFI_TX_STATS
|
||||
esp_wifi_enable_tx_statistics(ESP_WIFI_ACI_BE, true);
|
||||
#endif
|
||||
|
||||
|
||||
esp_console_repl_t *repl = NULL;
|
||||
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
|
||||
@ -45,7 +111,11 @@ void app_main(void)
|
||||
|
||||
/* Register commands */
|
||||
register_system();
|
||||
register_wifi();
|
||||
app_register_all_wifi_commands();
|
||||
app_register_iperf_commands();
|
||||
app_register_ping_commands();
|
||||
app_register_iperf_hook_func(iperf_hook_show_wifi_stats);
|
||||
|
||||
|
||||
printf("\n ==================================================\n");
|
||||
printf(" | Steps to test WiFi throughput |\n");
|
||||
|
@ -16,19 +16,12 @@ The test env wifi_iperf do need the following config::
|
||||
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import Tuple
|
||||
|
||||
import pexpect
|
||||
import pytest
|
||||
from common_test_methods import get_env_config_variable
|
||||
from common_test_methods import get_host_ip_by_interface
|
||||
from idf_iperf_test_util import IperfUtility
|
||||
from idf_iperf_test_util.IperfUtility import SCAN_RETRY_COUNT
|
||||
from idf_iperf_test_util.IperfUtility import SCAN_TIMEOUT
|
||||
from idf_iperf_test_util.IperfUtility import TEST_TIME
|
||||
from pytest_embedded import Dut
|
||||
|
||||
# configurations
|
||||
@ -39,105 +32,6 @@ NO_BANDWIDTH_LIMIT = -1 # iperf send bandwith is not limited
|
||||
BEST_PERFORMANCE_CONFIG = '99'
|
||||
|
||||
|
||||
class IperfTestUtilitySoftap(IperfUtility.IperfTestUtility):
|
||||
""" iperf test implementation """
|
||||
def __init__(self, dut: Dut, softap_dut: Dut, config_name:str, test_result:Any=None) -> None:
|
||||
IperfUtility.IperfTestUtility.__init__(self, dut, config_name, 'softap', '1234567890', None, None, test_result)
|
||||
self.softap_dut = softap_dut
|
||||
self.softap_ip = '192.168.4.1'
|
||||
|
||||
def setup(self) -> Tuple[str,int]:
|
||||
"""
|
||||
setup iperf test:
|
||||
|
||||
1. kill current iperf process
|
||||
2. reboot DUT (currently iperf is not very robust, need to reboot DUT)
|
||||
3. scan to get AP RSSI
|
||||
4. connect to AP
|
||||
"""
|
||||
self.softap_dut.write('restart')
|
||||
self.softap_dut.expect_exact("Type 'help' to get the list of commands.")
|
||||
self.softap_dut.expect('iperf>', timeout=30)
|
||||
self.softap_dut.write('ap {} {}'.format(self.ap_ssid, self.ap_password))
|
||||
self.dut.write('restart')
|
||||
self.dut.expect_exact("Type 'help' to get the list of commands.")
|
||||
self.dut.expect('iperf>', timeout=30)
|
||||
self.dut.write('scan {}'.format(self.ap_ssid))
|
||||
for _ in range(SCAN_RETRY_COUNT):
|
||||
try:
|
||||
rssi = int(self.dut.expect(r'\[{}]\[rssi=(-\d+)]'.format(self.ap_ssid),
|
||||
timeout=SCAN_TIMEOUT).group(1))
|
||||
break
|
||||
except pexpect.TIMEOUT:
|
||||
continue
|
||||
else:
|
||||
raise AssertionError('Failed to scan AP')
|
||||
self.dut.write('sta {} {}'.format(self.ap_ssid, self.ap_password))
|
||||
dut_ip = self.dut.expect(r'sta ip: ([\d.]+), mask: ([\d.]+), gw: ([\d.]+)').group(1).decode('utf-8')
|
||||
return dut_ip, rssi
|
||||
|
||||
def _test_once(self, proto:str, direction:str, bw_limit:int) -> Tuple[str, int, int]:
|
||||
""" do measure once for one type """
|
||||
# connect and scan to get RSSI
|
||||
dut_ip, rssi = self.setup()
|
||||
|
||||
assert direction in ['rx', 'tx']
|
||||
assert proto in ['tcp', 'udp']
|
||||
|
||||
# run iperf test
|
||||
if direction == 'tx':
|
||||
if proto == 'tcp':
|
||||
self.softap_dut.write('iperf -s -i 1 -t {}'.format(TEST_TIME))
|
||||
# wait until DUT TCP server created
|
||||
try:
|
||||
self.softap_dut.expect('iperf tcp server create successfully', timeout=1)
|
||||
except pexpect.TIMEOUT:
|
||||
# compatible with old iperf example binary
|
||||
pass
|
||||
if bw_limit > 0:
|
||||
self.dut.write('iperf -c {} -i 1 -t {} -b {}'.format(self.softap_ip, TEST_TIME, bw_limit))
|
||||
else:
|
||||
self.dut.write('iperf -c {} -i 1 -t {}'.format(self.softap_ip, TEST_TIME))
|
||||
else:
|
||||
self.softap_dut.write('iperf -s -u -i 1 -t {}'.format(TEST_TIME))
|
||||
if bw_limit > 0:
|
||||
self.dut.write('iperf -c {} -u -i 1 -t {} -b {}'.format(self.softap_ip, TEST_TIME, bw_limit))
|
||||
else:
|
||||
self.dut.write('iperf -c {} -u -i 1 -t {}'.format(self.softap_ip, TEST_TIME))
|
||||
else:
|
||||
if proto == 'tcp':
|
||||
self.dut.write('iperf -s -i 1 -t {}'.format(TEST_TIME))
|
||||
# wait until DUT TCP server created
|
||||
try:
|
||||
self.dut.expect('iperf tcp server create successfully', timeout=1)
|
||||
except pexpect.TIMEOUT:
|
||||
# compatible with old iperf example binary
|
||||
pass
|
||||
if bw_limit > 0:
|
||||
self.softap_dut.write('iperf -c {} -i 1 -t {} -b {}'.format(dut_ip, TEST_TIME, bw_limit))
|
||||
else:
|
||||
self.softap_dut.write('iperf -c {} -i 1 -t {}'.format(dut_ip, TEST_TIME))
|
||||
else:
|
||||
self.dut.write('iperf -s -u -i 1 -t {}'.format(TEST_TIME))
|
||||
if bw_limit > 0:
|
||||
self.softap_dut.write('iperf -c {} -u -i 1 -t {} -b {}'.format(dut_ip, TEST_TIME, bw_limit))
|
||||
else:
|
||||
self.softap_dut.write('iperf -c {} -u -i 1 -t {}'.format(dut_ip, TEST_TIME))
|
||||
time.sleep(TEST_TIME + 5)
|
||||
|
||||
if direction == 'tx':
|
||||
server_raw_data = self.dut.expect(pexpect.TIMEOUT, timeout=0).decode('utf-8')
|
||||
else:
|
||||
server_raw_data = self.dut.expect(pexpect.TIMEOUT, timeout=0).decode('utf-8')
|
||||
self.dut.write('iperf -a')
|
||||
self.softap_dut.write('iperf -a')
|
||||
self.dut.write('heap')
|
||||
heap_size = self.dut.expect(r'min heap size: (\d+)\D').group(1)
|
||||
|
||||
# return server raw data (for parsing test results) and RSSI
|
||||
return server_raw_data, rssi, heap_size
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.temp_skip_ci(targets=['esp32s2', 'esp32c3', 'esp32s3'], reason='lack of runners (run only for ESP32)')
|
||||
@pytest.mark.timeout(1200)
|
||||
|
@ -1,2 +1,4 @@
|
||||
idf_component_register(SRCS "itwt.c"
|
||||
idf_component_register(SRCS "itwt_main.c"
|
||||
"wifi_stats_cmd.c"
|
||||
"wifi_itwt_cmd.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
@ -3,7 +3,3 @@ dependencies:
|
||||
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_system
|
||||
cmd_nvs:
|
||||
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_nvs
|
||||
cmd_wifi:
|
||||
path: ${IDF_PATH}/examples/system/console/advanced/components/cmd_wifi
|
||||
iperf:
|
||||
path: ${IDF_PATH}/examples/common_components/iperf
|
||||
|
@ -26,11 +26,12 @@
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_console.h"
|
||||
#include "cmd_system.h"
|
||||
#include "wifi_cmd.h"
|
||||
#include "esp_wifi_he.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#include "wifi_cmd.h"
|
||||
|
||||
/*******************************************************
|
||||
* Constants
|
||||
*******************************************************/
|
@ -10,12 +10,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
void register_wifi_cmd(void);
|
||||
void register_wifi_itwt(void);
|
||||
void register_wifi_stats(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1034,8 +1034,6 @@ examples/build_system/cmake/multi_config/main/func.h
|
||||
examples/build_system/cmake/multi_config/main/func_dev.c
|
||||
examples/build_system/cmake/multi_config/main/func_prod.c
|
||||
examples/build_system/cmake/multi_config/main/multi_config_example_main.c
|
||||
examples/common_components/iperf/include/iperf.h
|
||||
examples/common_components/iperf/iperf.c
|
||||
examples/common_components/protocol_examples_common/addr_from_stdin.c
|
||||
examples/common_components/protocol_examples_common/include/addr_from_stdin.h
|
||||
examples/common_components/protocol_examples_common/include/protocol_examples_common.h
|
||||
@ -1192,9 +1190,6 @@ examples/wifi/fast_scan/main/fast_scan.c
|
||||
examples/wifi/ftm/main/ftm_main.c
|
||||
examples/wifi/getting_started/softAP/main/softap_example_main.c
|
||||
examples/wifi/getting_started/station/main/station_example_main.c
|
||||
examples/wifi/iperf/main/cmd_decl.h
|
||||
examples/wifi/iperf/main/cmd_wifi.c
|
||||
examples/wifi/iperf/main/cmd_wifi.h
|
||||
examples/wifi/iperf/main/iperf_example_main.c
|
||||
examples/wifi/power_save/main/power_save.c
|
||||
examples/wifi/roaming/main/roaming_example.c
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import logging
|
||||
import os
|
||||
@ -33,7 +33,7 @@ class TestResult(object):
|
||||
""" record, analysis test result and convert data to output format """
|
||||
|
||||
PC_BANDWIDTH_LOG_PATTERN = re.compile(r'(\d+\.\d+)\s*-\s*(\d+.\d+)\s+sec\s+[\d.]+\s+MBytes\s+([\d.]+)\s+Mbits\/sec')
|
||||
DUT_BANDWIDTH_LOG_PATTERN = re.compile(r'(\d+)-\s+(\d+)\s+sec\s+([\d.]+)\s+Mbits/sec')
|
||||
DUT_BANDWIDTH_LOG_PATTERN = re.compile(r'([\d.]+)\s*-\s*([\d.]+)\s+sec\s+([\d.]+)\s+Mbits/sec')
|
||||
|
||||
ZERO_POINT_THRESHOLD = -88 # RSSI, dbm
|
||||
ZERO_THROUGHPUT_THRESHOLD = -92 # RSSI, dbm
|
||||
@ -278,7 +278,7 @@ class IperfTestUtility(object):
|
||||
self.dut.write('restart')
|
||||
self.dut.expect_exact("Type 'help' to get the list of commands.")
|
||||
self.dut.expect('iperf>')
|
||||
self.dut.write('scan {}'.format(self.ap_ssid))
|
||||
self.dut.write('sta_scan {}'.format(self.ap_ssid))
|
||||
for _ in range(SCAN_RETRY_COUNT):
|
||||
try:
|
||||
rssi = int(self.dut.expect(r'\[{}]\[rssi=(-\d+)]'.format(self.ap_ssid),
|
||||
@ -288,7 +288,7 @@ class IperfTestUtility(object):
|
||||
continue
|
||||
else:
|
||||
raise AssertionError('Failed to scan AP')
|
||||
self.dut.write('sta {} {}'.format(self.ap_ssid, self.ap_password))
|
||||
self.dut.write('sta_connect {} {}'.format(self.ap_ssid, self.ap_password))
|
||||
dut_ip = self.dut.expect(r'sta ip: ([\d.]+), mask: ([\d.]+), gw: ([\d.]+)').group(1)
|
||||
return dut_ip, rssi
|
||||
|
||||
@ -338,7 +338,7 @@ class IperfTestUtility(object):
|
||||
self.dut.write('iperf -s -i 1 -t {}'.format(TEST_TIME))
|
||||
# wait until DUT TCP server created
|
||||
try:
|
||||
self.dut.expect('iperf: Socket created', timeout=5)
|
||||
self.dut.expect('Socket created', timeout=5)
|
||||
except pexpect.TIMEOUT:
|
||||
# compatible with old iperf example binary
|
||||
logging.info('create iperf tcp server fail')
|
||||
@ -358,7 +358,7 @@ class IperfTestUtility(object):
|
||||
self.dut.write('iperf -s -u -i 1 -t {}'.format(TEST_TIME))
|
||||
# wait until DUT TCP server created
|
||||
try:
|
||||
self.dut.expect('iperf: Socket bound', timeout=5)
|
||||
self.dut.expect('Socket bound', timeout=5)
|
||||
except pexpect.TIMEOUT:
|
||||
# compatible with old iperf example binary
|
||||
logging.info('create iperf udp server fail')
|
||||
|
Loading…
Reference in New Issue
Block a user