net: zperf: Add public API to start TCP/UDP server
Add public API for zperf download functionality. The TCP/UDP server modules are decoupled from shell, allowing to trigger download directly from the application code. The shell submodule makes use of this new public API. Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
parent
722ed07287
commit
cd4f7cbc61
5 changed files with 324 additions and 225 deletions
|
@ -39,6 +39,10 @@ struct zperf_upload_params {
|
||||||
} options;
|
} options;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct zperf_download_params {
|
||||||
|
uint16_t port;
|
||||||
|
};
|
||||||
|
|
||||||
struct zperf_results {
|
struct zperf_results {
|
||||||
uint32_t nb_packets_sent;
|
uint32_t nb_packets_sent;
|
||||||
uint32_t nb_packets_rcvd;
|
uint32_t nb_packets_rcvd;
|
||||||
|
@ -117,6 +121,34 @@ int zperf_udp_upload_async(const struct zperf_upload_params *param,
|
||||||
int zperf_tcp_upload_async(const struct zperf_upload_params *param,
|
int zperf_tcp_upload_async(const struct zperf_upload_params *param,
|
||||||
zperf_callback callback, void *user_data);
|
zperf_callback callback, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start UDP server.
|
||||||
|
*
|
||||||
|
* @note Only one UDP server instance can run at a time.
|
||||||
|
*
|
||||||
|
* @param param Download parameters.
|
||||||
|
* @param callback Session results callback.
|
||||||
|
* @param user_data A pointer to the user data to be provided with the callback.
|
||||||
|
*
|
||||||
|
* @return 0 if server was started, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int zperf_udp_download(const struct zperf_download_params *param,
|
||||||
|
zperf_callback callback, void *user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start TCP server.
|
||||||
|
*
|
||||||
|
* @note Only one TCP server instance can run at a time.
|
||||||
|
*
|
||||||
|
* @param param Download parameters.
|
||||||
|
* @param callback Session results callback.
|
||||||
|
* @param user_data A pointer to the user data to be provided with the callback.
|
||||||
|
*
|
||||||
|
* @return 0 if server was started, a negative error code otherwise.
|
||||||
|
*/
|
||||||
|
int zperf_tcp_download(const struct zperf_download_params *param,
|
||||||
|
zperf_callback callback, void *user_data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -81,18 +81,12 @@ static inline uint32_t time_delta(uint32_t ts, uint32_t t)
|
||||||
return (t >= ts) ? (t - ts) : (ULONG_MAX - ts + t);
|
return (t >= ts) ? (t - ts) : (ULONG_MAX - ts + t);
|
||||||
}
|
}
|
||||||
|
|
||||||
int zperf_get_ipv6_addr(const struct shell *sh, char *host,
|
int zperf_get_ipv6_addr(char *host, char *prefix_str, struct in6_addr *addr);
|
||||||
char *prefix_str, struct in6_addr *addr);
|
|
||||||
struct sockaddr_in6 *zperf_get_sin6(void);
|
struct sockaddr_in6 *zperf_get_sin6(void);
|
||||||
|
|
||||||
int zperf_get_ipv4_addr(const struct shell *sh, char *host,
|
int zperf_get_ipv4_addr(char *host, struct in_addr *addr);
|
||||||
struct in_addr *addr);
|
|
||||||
struct sockaddr_in *zperf_get_sin(void);
|
struct sockaddr_in *zperf_get_sin(void);
|
||||||
|
|
||||||
extern void zperf_udp_receiver_init(const struct shell *sh, int port);
|
|
||||||
|
|
||||||
extern void zperf_tcp_receiver_init(const struct shell *sh, int port);
|
|
||||||
|
|
||||||
extern void connect_ap(char *ssid);
|
extern void connect_ap(char *ssid);
|
||||||
|
|
||||||
const struct in_addr *zperf_get_default_if_in4_addr(void);
|
const struct in_addr *zperf_get_default_if_in4_addr(void);
|
||||||
|
|
|
@ -127,8 +127,7 @@ static int parse_ipv6_addr(const struct shell *sh, char *host, char *port,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int zperf_get_ipv6_addr(const struct shell *sh, char *host,
|
int zperf_get_ipv6_addr(char *host, char *prefix_str, struct in6_addr *addr)
|
||||||
char *prefix_str, struct in6_addr *addr)
|
|
||||||
{
|
{
|
||||||
struct net_if_ipv6_prefix *prefix;
|
struct net_if_ipv6_prefix *prefix;
|
||||||
struct net_if_addr *ifaddr;
|
struct net_if_addr *ifaddr;
|
||||||
|
@ -149,8 +148,7 @@ int zperf_get_ipv6_addr(const struct shell *sh, char *host,
|
||||||
ifaddr = net_if_ipv6_addr_add(net_if_get_default(),
|
ifaddr = net_if_ipv6_addr_add(net_if_get_default(),
|
||||||
addr, NET_ADDR_MANUAL, 0);
|
addr, NET_ADDR_MANUAL, 0);
|
||||||
if (!ifaddr) {
|
if (!ifaddr) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot set IPv6 address %s", host);
|
||||||
"Cannot set IPv6 address %s\n", host);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,8 +156,7 @@ int zperf_get_ipv6_addr(const struct shell *sh, char *host,
|
||||||
addr, prefix_len,
|
addr, prefix_len,
|
||||||
NET_IPV6_ND_INFINITE_LIFETIME);
|
NET_IPV6_ND_INFINITE_LIFETIME);
|
||||||
if (!prefix) {
|
if (!prefix) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot set IPv6 prefix %s", prefix_str);
|
||||||
"Cannot set IPv6 prefix %s\n", prefix_str);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,8 +189,7 @@ static int parse_ipv4_addr(const struct shell *sh, char *host, char *port,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int zperf_get_ipv4_addr(const struct shell *sh, char *host,
|
int zperf_get_ipv4_addr(char *host, struct in_addr *addr)
|
||||||
struct in_addr *addr)
|
|
||||||
{
|
{
|
||||||
struct net_if_addr *ifaddr;
|
struct net_if_addr *ifaddr;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -210,8 +206,7 @@ int zperf_get_ipv4_addr(const struct shell *sh, char *host,
|
||||||
ifaddr = net_if_ipv4_addr_add(net_if_get_default(),
|
ifaddr = net_if_ipv4_addr_add(net_if_get_default(),
|
||||||
addr, NET_ADDR_MANUAL, 0);
|
addr, NET_ADDR_MANUAL, 0);
|
||||||
if (!ifaddr) {
|
if (!ifaddr) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot set IPv4 address %s", host);
|
||||||
"Cannot set IPv4 address %s\n", host);
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +245,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zperf_get_ipv6_addr(sh, argv[start + 1], argv[start + 2],
|
if (zperf_get_ipv6_addr(argv[start + 1], argv[start + 2],
|
||||||
&ipv6) < 0) {
|
&ipv6) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"Unable to set IP\n");
|
"Unable to set IP\n");
|
||||||
|
@ -268,7 +263,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zperf_get_ipv4_addr(sh, argv[start + 1], &ipv4) < 0) {
|
if (zperf_get_ipv4_addr(argv[start + 1], &ipv4) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"Unable to set IP\n");
|
"Unable to set IP\n");
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
@ -286,7 +281,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zperf_get_ipv4_addr(sh, argv[start + 1],
|
if (zperf_get_ipv4_addr(argv[start + 1],
|
||||||
&ipv4) < 0) {
|
&ipv4) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"Unable to set IP\n");
|
"Unable to set IP\n");
|
||||||
|
@ -302,7 +297,7 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zperf_get_ipv6_addr(sh, argv[start + 1],
|
if (zperf_get_ipv6_addr(argv[start + 1],
|
||||||
argv[start + 2], &ipv6) < 0) {
|
argv[start + 2], &ipv6) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"Unable to set IP\n");
|
"Unable to set IP\n");
|
||||||
|
@ -318,35 +313,90 @@ static int cmd_setip(const struct shell *sh, size_t argc, char *argv[])
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void udp_session_cb(enum zperf_status status,
|
||||||
|
struct zperf_results *result,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
const struct shell *sh = user_data;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case ZPERF_SESSION_STARTED:
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "New session started.\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ZPERF_SESSION_FINISHED: {
|
||||||
|
uint32_t rate_in_kbps;
|
||||||
|
|
||||||
|
/* Compute baud rate */
|
||||||
|
if (result->time_in_us != 0U) {
|
||||||
|
rate_in_kbps = (uint32_t)
|
||||||
|
(((uint64_t)result->total_len * 8ULL *
|
||||||
|
(uint64_t)USEC_PER_SEC) /
|
||||||
|
((uint64_t)result->time_in_us * 1024ULL));
|
||||||
|
} else {
|
||||||
|
rate_in_kbps = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "End of session!\n");
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " duration:\t\t");
|
||||||
|
print_number(sh, result->time_in_us, TIME_US, TIME_US_UNIT);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " received packets:\t%u\n",
|
||||||
|
result->nb_packets_rcvd);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " nb packets lost:\t%u\n",
|
||||||
|
result->nb_packets_lost);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " nb packets outorder:\t%u\n",
|
||||||
|
result->nb_packets_outorder);
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " jitter:\t\t\t");
|
||||||
|
print_number(sh, result->jitter_in_us, TIME_US, TIME_US_UNIT);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
|
||||||
|
print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ZPERF_SESSION_ERROR:
|
||||||
|
shell_fprintf(sh, SHELL_ERROR, "UDP session error.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_udp_download(const struct shell *sh, size_t argc,
|
static int cmd_udp_download(const struct shell *sh, size_t argc,
|
||||||
char *argv[])
|
char *argv[])
|
||||||
{
|
{
|
||||||
if (IS_ENABLED(CONFIG_NET_UDP)) {
|
if (IS_ENABLED(CONFIG_NET_UDP)) {
|
||||||
static bool udp_stopped = true;
|
struct zperf_download_params param = { 0 };
|
||||||
int port, start = 0;
|
int ret;
|
||||||
|
|
||||||
do_init(sh);
|
do_init(sh);
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
port = strtoul(argv[start + 1], NULL, 10);
|
param.port = strtoul(argv[1], NULL, 10);
|
||||||
} else {
|
} else {
|
||||||
port = DEF_PORT;
|
param.port = DEF_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!udp_stopped) {
|
ret = zperf_udp_download(¶m, udp_session_cb, (void *)sh);
|
||||||
|
if (ret == -EALREADY) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"UDP server already started!\n");
|
"UDP server already started!\n");
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
} else if (ret < 0) {
|
||||||
|
shell_fprintf(sh, SHELL_ERROR,
|
||||||
|
"Failed to start UDP server!\n");
|
||||||
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
zperf_udp_receiver_init(sh, port);
|
|
||||||
|
|
||||||
k_yield();
|
k_yield();
|
||||||
|
|
||||||
udp_stopped = false;
|
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
shell_fprintf(sh, SHELL_NORMAL,
|
||||||
"UDP server started on port %u\n", port);
|
"UDP server started on port %u\n", param.port);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -974,30 +1024,77 @@ void zperf_tcp_started(void)
|
||||||
tcp_running = true;
|
tcp_running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tcp_session_cb(enum zperf_status status,
|
||||||
|
struct zperf_results *result,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
const struct shell *sh = user_data;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case ZPERF_SESSION_STARTED:
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "New TCP session started.\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ZPERF_SESSION_FINISHED: {
|
||||||
|
uint32_t rate_in_kbps;
|
||||||
|
|
||||||
|
/* Compute baud rate */
|
||||||
|
if (result->time_in_us != 0U) {
|
||||||
|
rate_in_kbps = (uint32_t)
|
||||||
|
(((uint64_t)result->total_len * 8ULL *
|
||||||
|
(uint64_t)USEC_PER_SEC) /
|
||||||
|
((uint64_t)result->time_in_us * 1024ULL));
|
||||||
|
} else {
|
||||||
|
rate_in_kbps = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "TCP session ended\n");
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " Duration:\t\t");
|
||||||
|
print_number(sh, result->time_in_us, TIME_US, TIME_US_UNIT);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
||||||
|
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
|
||||||
|
print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
|
||||||
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ZPERF_SESSION_ERROR:
|
||||||
|
shell_fprintf(sh, SHELL_ERROR, "TCP session error.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_tcp_download(const struct shell *sh, size_t argc,
|
static int cmd_tcp_download(const struct shell *sh, size_t argc,
|
||||||
char *argv[])
|
char *argv[])
|
||||||
{
|
{
|
||||||
if (IS_ENABLED(CONFIG_NET_TCP)) {
|
if (IS_ENABLED(CONFIG_NET_TCP)) {
|
||||||
int port;
|
struct zperf_download_params param = { 0 };
|
||||||
|
int ret;
|
||||||
|
|
||||||
do_init(sh);
|
do_init(sh);
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
port = strtoul(argv[1], NULL, 10);
|
param.port = strtoul(argv[1], NULL, 10);
|
||||||
} else {
|
} else {
|
||||||
port = DEF_PORT;
|
param.port = DEF_PORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tcp_running) {
|
ret = zperf_tcp_download(¶m, tcp_session_cb, (void *)sh);
|
||||||
|
if (ret == -EALREADY) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"TCP server already started!\n");
|
"TCP server already started!\n");
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
} else if (ret < 0) {
|
||||||
|
shell_fprintf(sh, SHELL_ERROR,
|
||||||
|
"Failed to start TCP server!\n");
|
||||||
|
return -ENOEXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
zperf_tcp_receiver_init(sh, port);
|
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
shell_fprintf(sh, SHELL_NORMAL,
|
||||||
"TCP server started on port %u\n", port);
|
"TCP server started on port %u\n", param.port);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1020,7 +1117,7 @@ static void zperf_init(const struct shell *sh)
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_NET_IPV6) && MY_IP6ADDR) {
|
if (IS_ENABLED(CONFIG_NET_IPV6) && MY_IP6ADDR) {
|
||||||
if (zperf_get_ipv6_addr(sh, MY_IP6ADDR, MY_PREFIX_LEN_STR,
|
if (zperf_get_ipv6_addr(MY_IP6ADDR, MY_PREFIX_LEN_STR,
|
||||||
&ipv6) < 0) {
|
&ipv6) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"Unable to set IP\n");
|
"Unable to set IP\n");
|
||||||
|
@ -1047,7 +1144,7 @@ static void zperf_init(const struct shell *sh)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_NET_IPV4) && MY_IP4ADDR) {
|
if (IS_ENABLED(CONFIG_NET_IPV4) && MY_IP4ADDR) {
|
||||||
if (zperf_get_ipv4_addr(sh, MY_IP4ADDR, &ipv4) < 0) {
|
if (zperf_get_ipv4_addr(MY_IP4ADDR, &ipv4) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
shell_fprintf(sh, SHELL_WARNING,
|
||||||
"Unable to set IP\n");
|
"Unable to set IP\n");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -16,7 +16,6 @@ LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
|
||||||
#include <zephyr/net/zperf.h>
|
#include <zephyr/net/zperf.h>
|
||||||
|
|
||||||
#include "zperf_internal.h"
|
#include "zperf_internal.h"
|
||||||
#include "shell_utils.h"
|
|
||||||
#include "zperf_session.h"
|
#include "zperf_session.h"
|
||||||
|
|
||||||
/* To get net_sprint_ipv{4|6}_addr() */
|
/* To get net_sprint_ipv{4|6}_addr() */
|
||||||
|
@ -44,10 +43,14 @@ static bool init_done;
|
||||||
|
|
||||||
#define TCP_RECEIVER_BUF_SIZE 1500
|
#define TCP_RECEIVER_BUF_SIZE 1500
|
||||||
|
|
||||||
K_THREAD_STACK_DEFINE(tcp_receiver_stack_area, TCP_RECEIVER_STACK_SIZE);
|
static K_THREAD_STACK_DEFINE(tcp_receiver_stack_area, TCP_RECEIVER_STACK_SIZE);
|
||||||
struct k_thread tcp_receiver_thread_data;
|
static struct k_thread tcp_receiver_thread_data;
|
||||||
|
|
||||||
static void tcp_received(const struct shell *sh, int sock, size_t datalen)
|
static zperf_callback tcp_session_cb;
|
||||||
|
static void *tcp_user_data;
|
||||||
|
static bool tcp_server_running;
|
||||||
|
|
||||||
|
static void tcp_received(int sock, size_t datalen)
|
||||||
{
|
{
|
||||||
struct session *session;
|
struct session *session;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
|
@ -56,7 +59,7 @@ static void tcp_received(const struct shell *sh, int sock, size_t datalen)
|
||||||
|
|
||||||
session = get_tcp_session(sock);
|
session = get_tcp_session(sock);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
shell_fprintf(sh, SHELL_WARNING, "Cannot get a session!\n");
|
NET_ERR("Cannot get a session!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,47 +67,34 @@ static void tcp_received(const struct shell *sh, int sock, size_t datalen)
|
||||||
case STATE_COMPLETED:
|
case STATE_COMPLETED:
|
||||||
break;
|
break;
|
||||||
case STATE_NULL:
|
case STATE_NULL:
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
"New TCP session started\n");
|
|
||||||
zperf_reset_session_stats(session);
|
zperf_reset_session_stats(session);
|
||||||
session->start_time = k_uptime_ticks();
|
session->start_time = k_uptime_ticks();
|
||||||
session->state = STATE_ONGOING;
|
session->state = STATE_ONGOING;
|
||||||
|
|
||||||
|
if (tcp_session_cb != NULL) {
|
||||||
|
tcp_session_cb(ZPERF_SESSION_STARTED, NULL,
|
||||||
|
tcp_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
__fallthrough;
|
__fallthrough;
|
||||||
case STATE_ONGOING:
|
case STATE_ONGOING:
|
||||||
session->counter++;
|
session->counter++;
|
||||||
session->length += datalen;
|
session->length += datalen;
|
||||||
|
|
||||||
if (datalen == 0) { /* EOF */
|
if (datalen == 0) { /* EOF */
|
||||||
uint32_t rate_in_kbps;
|
struct zperf_results results = { 0 };
|
||||||
uint32_t duration;
|
|
||||||
|
|
||||||
duration = k_ticks_to_us_ceil32(time -
|
|
||||||
session->start_time);
|
|
||||||
|
|
||||||
session->state = STATE_COMPLETED;
|
session->state = STATE_COMPLETED;
|
||||||
|
|
||||||
/* Compute baud rate */
|
results.total_len = session->length;
|
||||||
if (duration != 0U) {
|
results.time_in_us = k_ticks_to_us_ceil32(
|
||||||
rate_in_kbps = (uint32_t)
|
time - session->start_time);
|
||||||
((session->length * 8ULL *
|
|
||||||
(uint64_t)USEC_PER_SEC) /
|
if (tcp_session_cb != NULL) {
|
||||||
((uint64_t)duration * 1024ULL));
|
tcp_session_cb(ZPERF_SESSION_FINISHED, &results,
|
||||||
} else {
|
tcp_user_data);
|
||||||
rate_in_kbps = 0U;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
"TCP session ended\n");
|
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
" Duration:\t\t");
|
|
||||||
print_number(sh, duration, TIME_US, TIME_US_UNIT);
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, " rate:\t\t\t");
|
|
||||||
print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
|
||||||
|
|
||||||
zperf_tcp_stopped();
|
zperf_tcp_stopped();
|
||||||
|
|
||||||
session->state = STATE_NULL;
|
session->state = STATE_NULL;
|
||||||
|
@ -115,16 +105,16 @@ static void tcp_received(const struct shell *sh, int sock, size_t datalen)
|
||||||
case STATE_LAST_PACKET_RECEIVED:
|
case STATE_LAST_PACKET_RECEIVED:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
shell_fprintf(sh, SHELL_WARNING, "Unsupported case\n");
|
NET_ERR("Unsupported case");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
{
|
{
|
||||||
|
ARG_UNUSED(ptr1);
|
||||||
ARG_UNUSED(ptr3);
|
ARG_UNUSED(ptr3);
|
||||||
|
|
||||||
static uint8_t buf[TCP_RECEIVER_BUF_SIZE];
|
static uint8_t buf[TCP_RECEIVER_BUF_SIZE];
|
||||||
const struct shell *sh = ptr1;
|
|
||||||
int port = POINTER_TO_INT(ptr2);
|
int port = POINTER_TO_INT(ptr2);
|
||||||
struct zsock_pollfd fds[SOCK_ID_MAX] = { 0 };
|
struct zsock_pollfd fds[SOCK_ID_MAX] = { 0 };
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -141,18 +131,16 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
fds[SOCK_ID_IPV4_LISTEN].fd = zsock_socket(AF_INET, SOCK_STREAM,
|
fds[SOCK_ID_IPV4_LISTEN].fd = zsock_socket(AF_INET, SOCK_STREAM,
|
||||||
IPPROTO_TCP);
|
IPPROTO_TCP);
|
||||||
if (fds[SOCK_ID_IPV4_LISTEN].fd < 0) {
|
if (fds[SOCK_ID_IPV4_LISTEN].fd < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot create IPv4 network socket.");
|
||||||
"Cannot create IPv4 network socket.\n");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MY_IP4ADDR && strlen(MY_IP4ADDR)) {
|
if (MY_IP4ADDR && strlen(MY_IP4ADDR)) {
|
||||||
/* Use Setting IP */
|
/* Use Setting IP */
|
||||||
ret = zperf_get_ipv4_addr(sh, MY_IP4ADDR,
|
ret = zperf_get_ipv4_addr(MY_IP4ADDR,
|
||||||
&in4_addr_my->sin_addr);
|
&in4_addr_my->sin_addr);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_WARN("Unable to set IPv4");
|
||||||
"Unable to set IPv4\n");
|
|
||||||
goto use_existing_ipv4;
|
goto use_existing_ipv4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -160,9 +148,8 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
/* Use existing IP */
|
/* Use existing IP */
|
||||||
in4_addr = zperf_get_default_if_in4_addr();
|
in4_addr = zperf_get_default_if_in4_addr();
|
||||||
if (!in4_addr) {
|
if (!in4_addr) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Unable to get IPv4 by default");
|
||||||
"Unable to get IPv4 by default\n");
|
goto cleanup;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
memcpy(&in4_addr_my->sin_addr, in4_addr,
|
memcpy(&in4_addr_my->sin_addr, in4_addr,
|
||||||
sizeof(struct in_addr));
|
sizeof(struct in_addr));
|
||||||
|
@ -170,24 +157,21 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
|
|
||||||
in4_addr_my->sin_port = htons(port);
|
in4_addr_my->sin_port = htons(port);
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "Binding to %s\n",
|
NET_INFO("Binding to %s",
|
||||||
net_sprint_ipv4_addr(&in4_addr_my->sin_addr));
|
net_sprint_ipv4_addr(&in4_addr_my->sin_addr));
|
||||||
|
|
||||||
ret = zsock_bind(fds[SOCK_ID_IPV4_LISTEN].fd,
|
ret = zsock_bind(fds[SOCK_ID_IPV4_LISTEN].fd,
|
||||||
(struct sockaddr *)in4_addr_my,
|
(struct sockaddr *)in4_addr_my,
|
||||||
sizeof(struct sockaddr_in));
|
sizeof(struct sockaddr_in));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot bind IPv4 UDP port %d (%d)",
|
||||||
"Cannot bind IPv4 UDP port %d (%d)\n",
|
ntohs(in4_addr_my->sin_port), errno);
|
||||||
ntohs(in4_addr_my->sin_port),
|
|
||||||
errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = zsock_listen(fds[SOCK_ID_IPV4_LISTEN].fd, 1);
|
ret = zsock_listen(fds[SOCK_ID_IPV4_LISTEN].fd, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot listen IPv4 TCP (%d)", errno);
|
||||||
"Cannot listen IPv4 TCP (%d)", errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,19 +186,17 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
fds[SOCK_ID_IPV6_LISTEN].fd = zsock_socket(AF_INET6, SOCK_STREAM,
|
fds[SOCK_ID_IPV6_LISTEN].fd = zsock_socket(AF_INET6, SOCK_STREAM,
|
||||||
IPPROTO_TCP);
|
IPPROTO_TCP);
|
||||||
if (fds[SOCK_ID_IPV6_LISTEN].fd < 0) {
|
if (fds[SOCK_ID_IPV6_LISTEN].fd < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot create IPv6 network socket.");
|
||||||
"Cannot create IPv6 network socket.\n");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MY_IP6ADDR && strlen(MY_IP6ADDR)) {
|
if (MY_IP6ADDR && strlen(MY_IP6ADDR)) {
|
||||||
/* Use Setting IP */
|
/* Use Setting IP */
|
||||||
ret = zperf_get_ipv6_addr(sh, MY_IP6ADDR,
|
ret = zperf_get_ipv6_addr(MY_IP6ADDR,
|
||||||
MY_PREFIX_LEN_STR,
|
MY_PREFIX_LEN_STR,
|
||||||
&in6_addr_my->sin6_addr);
|
&in6_addr_my->sin6_addr);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_WARN("Unable to set IPv6");
|
||||||
"Unable to set IPv6\n");
|
|
||||||
goto use_existing_ipv6;
|
goto use_existing_ipv6;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -222,9 +204,8 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
/* Use existing IP */
|
/* Use existing IP */
|
||||||
in6_addr = zperf_get_default_if_in6_addr();
|
in6_addr = zperf_get_default_if_in6_addr();
|
||||||
if (!in6_addr) {
|
if (!in6_addr) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Unable to get IPv4 by default");
|
||||||
"Unable to get IPv4 by default\n");
|
goto cleanup;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
memcpy(&in6_addr_my->sin6_addr, in6_addr,
|
memcpy(&in6_addr_my->sin6_addr, in6_addr,
|
||||||
sizeof(struct in6_addr));
|
sizeof(struct in6_addr));
|
||||||
|
@ -232,32 +213,28 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
|
|
||||||
in6_addr_my->sin6_port = htons(port);
|
in6_addr_my->sin6_port = htons(port);
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "Binding to %s\n",
|
NET_INFO("Binding to %s",
|
||||||
net_sprint_ipv6_addr(&in6_addr_my->sin6_addr));
|
net_sprint_ipv6_addr(&in6_addr_my->sin6_addr));
|
||||||
|
|
||||||
ret = zsock_bind(fds[SOCK_ID_IPV6_LISTEN].fd,
|
ret = zsock_bind(fds[SOCK_ID_IPV6_LISTEN].fd,
|
||||||
(struct sockaddr *)in6_addr_my,
|
(struct sockaddr *)in6_addr_my,
|
||||||
sizeof(struct sockaddr_in6));
|
sizeof(struct sockaddr_in6));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot bind IPv6 UDP port %d (%d)",
|
||||||
"Cannot bind IPv6 UDP port %d (%d)\n",
|
ntohs(in6_addr_my->sin6_port), errno);
|
||||||
ntohs(in6_addr_my->sin6_port),
|
|
||||||
errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = zsock_listen(fds[SOCK_ID_IPV6_LISTEN].fd, 1);
|
ret = zsock_listen(fds[SOCK_ID_IPV6_LISTEN].fd, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot listen IPv6 TCP (%d)", errno);
|
||||||
"Cannot listen IPv6 TCP (%d)", errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
fds[SOCK_ID_IPV6_LISTEN].events = ZSOCK_POLLIN;
|
fds[SOCK_ID_IPV6_LISTEN].events = ZSOCK_POLLIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
NET_INFO("Listening on port %d", port);
|
||||||
"Listening on port %d\n", port);
|
|
||||||
|
|
||||||
/* TODO Investigate started/stopped logic */
|
/* TODO Investigate started/stopped logic */
|
||||||
zperf_tcp_started();
|
zperf_tcp_started();
|
||||||
|
@ -266,9 +243,7 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
while (true) {
|
while (true) {
|
||||||
ret = zsock_poll(fds, ARRAY_SIZE(fds), -1);
|
ret = zsock_poll(fds, ARRAY_SIZE(fds), -1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("TCP receiver poll error (%d)", errno);
|
||||||
"TCP receiver poll error (%d)\n",
|
|
||||||
errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,9 +253,7 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
|
|
||||||
if ((fds[i].revents & ZSOCK_POLLERR) ||
|
if ((fds[i].revents & ZSOCK_POLLERR) ||
|
||||||
(fds[i].revents & ZSOCK_POLLNVAL)) {
|
(fds[i].revents & ZSOCK_POLLNVAL)) {
|
||||||
shell_fprintf(
|
NET_ERR("TCP receiver IPv%d socket error",
|
||||||
sh, SHELL_WARNING,
|
|
||||||
"TCP receiver IPv%d socket error\n",
|
|
||||||
(i <= SOCK_ID_IPV4_DATA) ? 4 : 6);
|
(i <= SOCK_ID_IPV4_DATA) ? 4 : 6);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -296,9 +269,7 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
&addrlen);
|
&addrlen);
|
||||||
|
|
||||||
if (sock < 0) {
|
if (sock < 0) {
|
||||||
shell_fprintf(
|
NET_ERR("TCP receiver IPv%d accept error",
|
||||||
sh, SHELL_WARNING,
|
|
||||||
"TCP receiver IPv%d accept error\n",
|
|
||||||
(i <= SOCK_ID_IPV4_DATA) ? 4 : 6);
|
(i <= SOCK_ID_IPV4_DATA) ? 4 : 6);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -324,15 +295,13 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
case SOCK_ID_IPV6_DATA:
|
case SOCK_ID_IPV6_DATA:
|
||||||
ret = zsock_recv(fds[i].fd, buf, sizeof(buf), 0);
|
ret = zsock_recv(fds[i].fd, buf, sizeof(buf), 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(
|
NET_ERR("recv failed on IPv%d socket (%d)",
|
||||||
sh, SHELL_WARNING,
|
|
||||||
"recv failed on IPv%d socket (%d)\n",
|
|
||||||
(i <= SOCK_ID_IPV4_DATA) ? 4 : 6,
|
(i <= SOCK_ID_IPV4_DATA) ? 4 : 6,
|
||||||
errno);
|
errno);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
tcp_received(sh, fds[i].fd, ret);
|
tcp_received(fds[i].fd, ret);
|
||||||
|
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
zsock_close(fds[i].fd);
|
zsock_close(fds[i].fd);
|
||||||
|
@ -345,6 +314,11 @@ void tcp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
if (tcp_session_cb != NULL) {
|
||||||
|
tcp_session_cb(ZPERF_SESSION_ERROR, NULL,
|
||||||
|
tcp_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(fds); i++) {
|
for (int i = 0; i < ARRAY_SIZE(fds); i++) {
|
||||||
if (fds[i].fd >= 0) {
|
if (fds[i].fd >= 0) {
|
||||||
zsock_close(fds[i].fd);
|
zsock_close(fds[i].fd);
|
||||||
|
@ -352,7 +326,7 @@ cleanup:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void zperf_tcp_receiver_init(const struct shell *sh, int port)
|
void zperf_tcp_receiver_init(int port)
|
||||||
{
|
{
|
||||||
if (init_done) {
|
if (init_done) {
|
||||||
zperf_tcp_started();
|
zperf_tcp_started();
|
||||||
|
@ -363,9 +337,29 @@ void zperf_tcp_receiver_init(const struct shell *sh, int port)
|
||||||
tcp_receiver_stack_area,
|
tcp_receiver_stack_area,
|
||||||
K_THREAD_STACK_SIZEOF(tcp_receiver_stack_area),
|
K_THREAD_STACK_SIZEOF(tcp_receiver_stack_area),
|
||||||
tcp_receiver_thread,
|
tcp_receiver_thread,
|
||||||
(void *)sh, INT_TO_POINTER(port), NULL,
|
NULL, INT_TO_POINTER(port), NULL,
|
||||||
TCP_RECEIVER_THREAD_PRIORITY,
|
TCP_RECEIVER_THREAD_PRIORITY,
|
||||||
IS_ENABLED(CONFIG_USERSPACE) ? K_USER |
|
IS_ENABLED(CONFIG_USERSPACE) ? K_USER |
|
||||||
K_INHERIT_PERMS : 0,
|
K_INHERIT_PERMS : 0,
|
||||||
K_NO_WAIT);
|
K_NO_WAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int zperf_tcp_download(const struct zperf_download_params *param,
|
||||||
|
zperf_callback callback, void *user_data)
|
||||||
|
{
|
||||||
|
if (param == NULL || callback == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcp_server_running) {
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp_session_cb = callback;
|
||||||
|
tcp_user_data = user_data;
|
||||||
|
tcp_server_running = true;
|
||||||
|
|
||||||
|
zperf_tcp_receiver_init(param->port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
|
||||||
#include <zephyr/net/zperf.h>
|
#include <zephyr/net/zperf.h>
|
||||||
|
|
||||||
#include "zperf_internal.h"
|
#include "zperf_internal.h"
|
||||||
#include "shell_utils.h"
|
|
||||||
#include "zperf_session.h"
|
#include "zperf_session.h"
|
||||||
|
|
||||||
/* To get net_sprint_ipv{4|6}_addr() */
|
/* To get net_sprint_ipv{4|6}_addr() */
|
||||||
|
@ -40,8 +39,12 @@ static struct sockaddr_in *in4_addr_my;
|
||||||
|
|
||||||
#define UDP_RECEIVER_BUF_SIZE 1500
|
#define UDP_RECEIVER_BUF_SIZE 1500
|
||||||
|
|
||||||
K_THREAD_STACK_DEFINE(udp_receiver_stack_area, UDP_RECEIVER_STACK_SIZE);
|
static K_THREAD_STACK_DEFINE(udp_receiver_stack_area, UDP_RECEIVER_STACK_SIZE);
|
||||||
struct k_thread udp_receiver_thread_data;
|
static struct k_thread udp_receiver_thread_data;
|
||||||
|
|
||||||
|
static zperf_callback udp_session_cb;
|
||||||
|
static void *udp_user_data;
|
||||||
|
static bool udp_server_running;
|
||||||
|
|
||||||
static inline void build_reply(struct zperf_udp_datagram *hdr,
|
static inline void build_reply(struct zperf_udp_datagram *hdr,
|
||||||
struct zperf_server_hdr *stat,
|
struct zperf_server_hdr *stat,
|
||||||
|
@ -71,8 +74,7 @@ static inline void build_reply(struct zperf_udp_datagram *hdr,
|
||||||
#define BUF_SIZE sizeof(struct zperf_udp_datagram) + \
|
#define BUF_SIZE sizeof(struct zperf_udp_datagram) + \
|
||||||
sizeof(struct zperf_server_hdr)
|
sizeof(struct zperf_server_hdr)
|
||||||
|
|
||||||
static int zperf_receiver_send_stat(const struct shell *sh,
|
static int zperf_receiver_send_stat(int sock, const struct sockaddr *addr,
|
||||||
int sock, const struct sockaddr *addr,
|
|
||||||
struct zperf_udp_datagram *hdr,
|
struct zperf_udp_datagram *hdr,
|
||||||
struct zperf_server_hdr *stat)
|
struct zperf_server_hdr *stat)
|
||||||
{
|
{
|
||||||
|
@ -86,15 +88,13 @@ static int zperf_receiver_send_stat(const struct shell *sh,
|
||||||
sizeof(struct sockaddr_in6) :
|
sizeof(struct sockaddr_in6) :
|
||||||
sizeof(struct sockaddr_in));
|
sizeof(struct sockaddr_in));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot send data to peer (%d)", errno);
|
||||||
" Cannot send data to peer (%d)", errno);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void udp_received(const struct shell *sh, int sock,
|
static void udp_received(int sock, const struct sockaddr *addr, uint8_t *data,
|
||||||
const struct sockaddr *addr, uint8_t *data,
|
|
||||||
size_t datalen)
|
size_t datalen)
|
||||||
{
|
{
|
||||||
struct zperf_udp_datagram *hdr;
|
struct zperf_udp_datagram *hdr;
|
||||||
|
@ -104,8 +104,7 @@ static void udp_received(const struct shell *sh, int sock,
|
||||||
int32_t id;
|
int32_t id;
|
||||||
|
|
||||||
if (datalen < sizeof(struct zperf_udp_datagram)) {
|
if (datalen < sizeof(struct zperf_udp_datagram)) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_WARN("Short iperf packet!");
|
||||||
"Short iperf packet!\n");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,8 +113,7 @@ static void udp_received(const struct shell *sh, int sock,
|
||||||
|
|
||||||
session = get_session(addr, SESSION_UDP);
|
session = get_session(addr, SESSION_UDP);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot get a session!");
|
||||||
"Cannot get a session!\n");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,44 +126,33 @@ static void udp_received(const struct shell *sh, int sock,
|
||||||
/* Session is already completed: Resend the stat packet
|
/* Session is already completed: Resend the stat packet
|
||||||
* and continue
|
* and continue
|
||||||
*/
|
*/
|
||||||
if (zperf_receiver_send_stat(sh, sock, addr, hdr,
|
if (zperf_receiver_send_stat(sock, addr, hdr,
|
||||||
&session->stat) < 0) {
|
&session->stat) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Failed to send the packet");
|
||||||
"Failed to send the packet\n");
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Start a new session! */
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
"New session started.\n");
|
|
||||||
|
|
||||||
zperf_reset_session_stats(session);
|
zperf_reset_session_stats(session);
|
||||||
session->state = STATE_ONGOING;
|
session->state = STATE_ONGOING;
|
||||||
session->start_time = time;
|
session->start_time = time;
|
||||||
|
|
||||||
|
/* Start a new session! */
|
||||||
|
if (udp_session_cb != NULL) {
|
||||||
|
udp_session_cb(ZPERF_SESSION_STARTED, NULL,
|
||||||
|
udp_user_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STATE_ONGOING:
|
case STATE_ONGOING:
|
||||||
if (id < 0) { /* Negative id means session end. */
|
if (id < 0) { /* Negative id means session end. */
|
||||||
uint32_t rate_in_kbps;
|
struct zperf_results results = { 0 };
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "End of session!\n");
|
|
||||||
|
|
||||||
duration = k_ticks_to_us_ceil32(time -
|
duration = k_ticks_to_us_ceil32(time -
|
||||||
session->start_time);
|
session->start_time);
|
||||||
|
|
||||||
/* Update state machine */
|
/* Update state machine */
|
||||||
session->state = STATE_COMPLETED;
|
session->state = STATE_COMPLETED;
|
||||||
|
|
||||||
/* Compute baud rate */
|
|
||||||
if (duration != 0U) {
|
|
||||||
rate_in_kbps = (uint32_t)
|
|
||||||
((session->length * 8ULL *
|
|
||||||
(uint64_t)USEC_PER_SEC) /
|
|
||||||
((uint64_t)duration * 1024ULL));
|
|
||||||
} else {
|
|
||||||
rate_in_kbps = 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill statistics */
|
/* Fill statistics */
|
||||||
session->stat.flags = 0x80000000;
|
session->stat.flags = 0x80000000;
|
||||||
session->stat.total_len1 = session->length >> 32;
|
session->stat.total_len1 = session->length >> 32;
|
||||||
|
@ -179,37 +166,23 @@ static void udp_received(const struct shell *sh, int sock,
|
||||||
session->stat.jitter1 = 0;
|
session->stat.jitter1 = 0;
|
||||||
session->stat.jitter2 = session->jitter;
|
session->stat.jitter2 = session->jitter;
|
||||||
|
|
||||||
if (zperf_receiver_send_stat(sh, sock, addr, hdr,
|
if (zperf_receiver_send_stat(sock, addr, hdr,
|
||||||
&session->stat) < 0) {
|
&session->stat) < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Failed to send the packet");
|
||||||
"Failed to send the packet\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
results.nb_packets_rcvd = session->counter;
|
||||||
" duration:\t\t");
|
results.nb_packets_lost = session->error;
|
||||||
print_number(sh, duration, TIME_US, TIME_US_UNIT);
|
results.nb_packets_outorder = session->outorder;
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
results.total_len = session->length;
|
||||||
|
results.time_in_us = duration;
|
||||||
|
results.jitter_in_us = session->jitter;
|
||||||
|
results.packet_size = session->length / session->counter;
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
if (udp_session_cb != NULL) {
|
||||||
" received packets:\t%u\n",
|
udp_session_cb(ZPERF_SESSION_FINISHED, &results,
|
||||||
session->counter);
|
udp_user_data);
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
}
|
||||||
" nb packets lost:\t%u\n",
|
|
||||||
session->error);
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
" nb packets outorder:\t%u\n",
|
|
||||||
session->outorder);
|
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
" jitter:\t\t\t");
|
|
||||||
print_number(sh, session->jitter, TIME_US,
|
|
||||||
TIME_US_UNIT);
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
|
||||||
" rate:\t\t\t");
|
|
||||||
print_number(sh, rate_in_kbps, KBPS, KBPS_UNIT);
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "\n");
|
|
||||||
} else {
|
} else {
|
||||||
/* Update counter */
|
/* Update counter */
|
||||||
session->counter++;
|
session->counter++;
|
||||||
|
@ -254,10 +227,10 @@ static void udp_received(const struct shell *sh, int sock,
|
||||||
|
|
||||||
void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
{
|
{
|
||||||
|
ARG_UNUSED(ptr1);
|
||||||
ARG_UNUSED(ptr3);
|
ARG_UNUSED(ptr3);
|
||||||
|
|
||||||
static uint8_t buf[UDP_RECEIVER_BUF_SIZE];
|
static uint8_t buf[UDP_RECEIVER_BUF_SIZE];
|
||||||
const struct shell *sh = ptr1;
|
|
||||||
int port = POINTER_TO_INT(ptr2);
|
int port = POINTER_TO_INT(ptr2);
|
||||||
struct zsock_pollfd fds[SOCK_ID_MAX] = { 0 };
|
struct zsock_pollfd fds[SOCK_ID_MAX] = { 0 };
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -274,18 +247,16 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
fds[SOCK_ID_IPV4].fd = zsock_socket(AF_INET, SOCK_DGRAM,
|
fds[SOCK_ID_IPV4].fd = zsock_socket(AF_INET, SOCK_DGRAM,
|
||||||
IPPROTO_UDP);
|
IPPROTO_UDP);
|
||||||
if (fds[SOCK_ID_IPV4].fd < 0) {
|
if (fds[SOCK_ID_IPV4].fd < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot create IPv4 network socket.");
|
||||||
"Cannot create IPv4 network socket.\n");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MY_IP4ADDR && strlen(MY_IP4ADDR)) {
|
if (MY_IP4ADDR && strlen(MY_IP4ADDR)) {
|
||||||
/* Use setting IP */
|
/* Use setting IP */
|
||||||
ret = zperf_get_ipv4_addr(sh, MY_IP4ADDR,
|
ret = zperf_get_ipv4_addr(MY_IP4ADDR,
|
||||||
&in4_addr_my->sin_addr);
|
&in4_addr_my->sin_addr);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_WARN("Unable to set IPv4");
|
||||||
"Unable to set IPv4\n");
|
|
||||||
goto use_existing_ipv4;
|
goto use_existing_ipv4;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,16 +264,15 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
/* Use existing IP */
|
/* Use existing IP */
|
||||||
in4_addr = zperf_get_default_if_in4_addr();
|
in4_addr = zperf_get_default_if_in4_addr();
|
||||||
if (!in4_addr) {
|
if (!in4_addr) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Unable to get IPv4 by default");
|
||||||
"Unable to get IPv4 by default\n");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
memcpy(&in4_addr_my->sin_addr, in4_addr,
|
memcpy(&in4_addr_my->sin_addr, in4_addr,
|
||||||
sizeof(struct in_addr));
|
sizeof(struct in_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "Binding to %s\n",
|
NET_INFO("Binding to %s",
|
||||||
net_sprint_ipv4_addr(&in4_addr_my->sin_addr));
|
net_sprint_ipv4_addr(&in4_addr_my->sin_addr));
|
||||||
|
|
||||||
in4_addr_my->sin_port = htons(port);
|
in4_addr_my->sin_port = htons(port);
|
||||||
|
|
||||||
|
@ -310,10 +280,9 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
(struct sockaddr *)in4_addr_my,
|
(struct sockaddr *)in4_addr_my,
|
||||||
sizeof(struct sockaddr_in));
|
sizeof(struct sockaddr_in));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot bind IPv4 UDP port %d (%d)",
|
||||||
"Cannot bind IPv4 UDP port %d (%d)\n",
|
ntohs(in4_addr_my->sin_port),
|
||||||
ntohs(in4_addr_my->sin_port),
|
errno);
|
||||||
errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,19 +297,17 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
fds[SOCK_ID_IPV6].fd = zsock_socket(AF_INET6, SOCK_DGRAM,
|
fds[SOCK_ID_IPV6].fd = zsock_socket(AF_INET6, SOCK_DGRAM,
|
||||||
IPPROTO_UDP);
|
IPPROTO_UDP);
|
||||||
if (fds[SOCK_ID_IPV6].fd < 0) {
|
if (fds[SOCK_ID_IPV6].fd < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot create IPv4 network socket.");
|
||||||
"Cannot create IPv4 network socket.\n");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MY_IP6ADDR && strlen(MY_IP6ADDR)) {
|
if (MY_IP6ADDR && strlen(MY_IP6ADDR)) {
|
||||||
/* Use setting IP */
|
/* Use setting IP */
|
||||||
ret = zperf_get_ipv6_addr(sh, MY_IP6ADDR,
|
ret = zperf_get_ipv6_addr(MY_IP6ADDR,
|
||||||
MY_PREFIX_LEN_STR,
|
MY_PREFIX_LEN_STR,
|
||||||
&in6_addr_my->sin6_addr);
|
&in6_addr_my->sin6_addr);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_WARN("Unable to set IPv6");
|
||||||
"Unable to set IPv6\n");
|
|
||||||
goto use_existing_ipv6;
|
goto use_existing_ipv6;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -348,16 +315,15 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
/* Use existing IP */
|
/* Use existing IP */
|
||||||
in6_addr = zperf_get_default_if_in6_addr();
|
in6_addr = zperf_get_default_if_in6_addr();
|
||||||
if (!in6_addr) {
|
if (!in6_addr) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Unable to get IPv4 by default");
|
||||||
"Unable to get IPv4 by default\n");
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
memcpy(&in6_addr_my->sin6_addr, in6_addr,
|
memcpy(&in6_addr_my->sin6_addr, in6_addr,
|
||||||
sizeof(struct in6_addr));
|
sizeof(struct in6_addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL, "Binding to %s\n",
|
NET_INFO("Binding to %s",
|
||||||
net_sprint_ipv6_addr(&in6_addr_my->sin6_addr));
|
net_sprint_ipv6_addr(&in6_addr_my->sin6_addr));
|
||||||
|
|
||||||
in6_addr_my->sin6_port = htons(port);
|
in6_addr_my->sin6_port = htons(port);
|
||||||
|
|
||||||
|
@ -365,25 +331,21 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
(struct sockaddr *)in6_addr_my,
|
(struct sockaddr *)in6_addr_my,
|
||||||
sizeof(struct sockaddr_in6));
|
sizeof(struct sockaddr_in6));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("Cannot bind IPv6 UDP port %d (%d)",
|
||||||
"Cannot bind IPv6 UDP port %d (%d)\n",
|
ntohs(in6_addr_my->sin6_port),
|
||||||
ntohs(in6_addr_my->sin6_port),
|
ret);
|
||||||
ret);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
fds[SOCK_ID_IPV6].events = ZSOCK_POLLIN;
|
fds[SOCK_ID_IPV6].events = ZSOCK_POLLIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell_fprintf(sh, SHELL_NORMAL,
|
NET_INFO("Listening on port %d", port);
|
||||||
"Listening on port %d\n", port);
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ret = zsock_poll(fds, ARRAY_SIZE(fds), -1);
|
ret = zsock_poll(fds, ARRAY_SIZE(fds), -1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(sh, SHELL_WARNING,
|
NET_ERR("UDP receiver poll error (%d)", errno);
|
||||||
"UDP receiver poll error (%d)\n",
|
|
||||||
errno);
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,9 +355,7 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
|
|
||||||
if ((fds[i].revents & ZSOCK_POLLERR) ||
|
if ((fds[i].revents & ZSOCK_POLLERR) ||
|
||||||
(fds[i].revents & ZSOCK_POLLNVAL)) {
|
(fds[i].revents & ZSOCK_POLLNVAL)) {
|
||||||
shell_fprintf(
|
NET_ERR("UDP receiver IPv%d socket error",
|
||||||
sh, SHELL_WARNING,
|
|
||||||
"UDP receiver IPv%d socket error\n",
|
|
||||||
(i == SOCK_ID_IPV4) ? 4 : 6);
|
(i == SOCK_ID_IPV4) ? 4 : 6);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -407,18 +367,20 @@ void udp_receiver_thread(void *ptr1, void *ptr2, void *ptr3)
|
||||||
ret = zsock_recvfrom(fds[i].fd, buf, sizeof(buf), 0,
|
ret = zsock_recvfrom(fds[i].fd, buf, sizeof(buf), 0,
|
||||||
&addr, &addrlen);
|
&addr, &addrlen);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
shell_fprintf(
|
NET_ERR("recv failed on IPv%d socket (%d)",
|
||||||
sh, SHELL_WARNING,
|
|
||||||
"recv failed on IPv%d socket (%d)\n",
|
|
||||||
(i == SOCK_ID_IPV4) ? 4 : 6, errno);
|
(i == SOCK_ID_IPV4) ? 4 : 6, errno);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
udp_received(sh, fds[i].fd, &addr, buf, ret);
|
udp_received(fds[i].fd, &addr, buf, ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
if (udp_session_cb != NULL) {
|
||||||
|
udp_session_cb(ZPERF_SESSION_ERROR, NULL, udp_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(fds); i++) {
|
for (int i = 0; i < ARRAY_SIZE(fds); i++) {
|
||||||
if (fds[i].fd >= 0) {
|
if (fds[i].fd >= 0) {
|
||||||
zsock_close(fds[i].fd);
|
zsock_close(fds[i].fd);
|
||||||
|
@ -426,15 +388,35 @@ cleanup:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void zperf_udp_receiver_init(const struct shell *sh, int port)
|
static void zperf_udp_receiver_init(int port)
|
||||||
{
|
{
|
||||||
k_thread_create(&udp_receiver_thread_data,
|
k_thread_create(&udp_receiver_thread_data,
|
||||||
udp_receiver_stack_area,
|
udp_receiver_stack_area,
|
||||||
K_THREAD_STACK_SIZEOF(udp_receiver_stack_area),
|
K_THREAD_STACK_SIZEOF(udp_receiver_stack_area),
|
||||||
udp_receiver_thread,
|
udp_receiver_thread,
|
||||||
(void *)sh, INT_TO_POINTER(port), NULL,
|
NULL, INT_TO_POINTER(port), NULL,
|
||||||
UDP_RECEIVER_THREAD_PRIORITY,
|
UDP_RECEIVER_THREAD_PRIORITY,
|
||||||
IS_ENABLED(CONFIG_USERSPACE) ? K_USER |
|
IS_ENABLED(CONFIG_USERSPACE) ? K_USER |
|
||||||
K_INHERIT_PERMS : 0,
|
K_INHERIT_PERMS : 0,
|
||||||
K_NO_WAIT);
|
K_NO_WAIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int zperf_udp_download(const struct zperf_download_params *param,
|
||||||
|
zperf_callback callback, void *user_data)
|
||||||
|
{
|
||||||
|
if (param == NULL || callback == NULL) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (udp_server_running) {
|
||||||
|
return -EALREADY;
|
||||||
|
}
|
||||||
|
|
||||||
|
udp_session_cb = callback;
|
||||||
|
udp_user_data = user_data;
|
||||||
|
udp_server_running = true;
|
||||||
|
|
||||||
|
zperf_udp_receiver_init(param->port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue