blob: a9c42c32d03bd1022f1492c1c300b0e7f7e40a63 [file] [log] [blame]
/*
* Copyright (c) 2015 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <sections.h>
#include <toolchain.h>
#include <misc/printk.h>
#include <net/net_core.h>
#include <net/net_ip.h>
#include <net/nbuf.h>
#include "zperf.h"
#include "zperf_internal.h"
#include "shell_utils.h"
#include "zperf_session.h"
/* To get net_sprint_ipv{4|6}_addr() */
#define NET_LOG_ENABLED 1
#include "net_private.h"
#define TAG CMD_STR_TCP_DOWNLOAD" "
#define TCP_RX_FIBER_STACK_SIZE 1024
static char __noinit __stack zperf_tcp_rx_stack[TCP_RX_FIBER_STACK_SIZE];
#if defined(CONFIG_NET_IPV6)
static struct sockaddr_in6 *in6_addr_my;
#endif
#if defined(CONFIG_NET_IPV4)
static struct sockaddr_in *in4_addr_my;
#endif
static void tcp_received(struct net_context *context,
struct net_buf *buf,
int status,
void *user_data)
{
struct session *session;
uint32_t time;
if (!buf) {
return;
}
time = k_cycle_get_32();
session = get_session(buf, SESSION_TCP);
if (!session) {
printk(TAG "ERROR! cannot get a session!\n");
return;
}
switch (session->state) {
case STATE_NULL:
case STATE_COMPLETED:
printk(TAG "New session started\n");
zperf_reset_session_stats(session);
session->start_time = sys_cycle_get_32();
session->state = STATE_ONGOING;
/* fall through */
case STATE_ONGOING:
session->counter++;
if (buf) {
session->length += net_nbuf_appdatalen(buf);
}
if (!buf && status == 0) { /* EOF */
uint32_t rate_in_kbps;
uint32_t duration = HW_CYCLES_TO_USEC(
time_delta(session->start_time, time));
session->state = STATE_COMPLETED;
/* Compute baud rate */
if (duration != 0) {
rate_in_kbps = (uint32_t)
(((uint64_t)session->length *
(uint64_t)8 *
(uint64_t)USEC_PER_SEC) /
((uint64_t)duration * 1024));
} else {
rate_in_kbps = 0;
}
printk(TAG "TCP session ended\n");
printk(TAG " duration:\t\t");
print_number(duration, TIME_US, TIME_US_UNIT);
printk("\n");
printk(TAG " rate:\t\t\t");
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
printk("\n");
}
break;
case STATE_LAST_PACKET_RECEIVED:
break;
default:
printk(TAG "Error! Unsupported case\n");
}
net_nbuf_unref(buf);
}
static void tcp_accepted(struct net_context *context,
struct sockaddr *addr,
socklen_t addrlen,
int error,
void *user_data)
{
int ret;
ret = net_context_recv(context, tcp_received, K_NO_WAIT, user_data);
if (ret < 0) {
printk(TAG "Cannot receive TCP packet (family %d)",
net_context_get_family(context));
}
}
static void zperf_tcp_rx_thread(int port)
{
#if defined(CONFIG_NET_IPV4)
struct net_context *context4 = NULL;
#endif
#if defined(CONFIG_NET_IPV6)
struct net_context *context6 = NULL;
#endif
int ret, fail = 0;
#if defined(CONFIG_NET_IPV4) && defined(MY_IP4ADDR)
ret = net_context_get(AF_INET, SOCK_STREAM, IPPROTO_TCP, &context4);
if (ret < 0) {
printk(TAG "ERROR! Cannot get IPv4 TCP network context.\n");
return;
}
ret = zperf_get_ipv4_addr(MY_IP4ADDR, &in4_addr_my->sin_addr, TAG);
if (ret < 0) {
printk(TAG "ERROR! Unable to set IPv4\n");
return;
}
printk(TAG "Binding to %s\n",
net_sprint_ipv4_addr(&in4_addr_my->sin_addr));
in4_addr_my->sin_port = htons(port);
#endif
#if defined(CONFIG_NET_IPV6) && defined(MY_IP6ADDR)
ret = net_context_get(AF_INET6, SOCK_STREAM, IPPROTO_TCP, &context6);
if (ret < 0) {
printk(TAG "ERROR! Cannot get IPv6 TCP network context.\n");
return;
}
ret = zperf_get_ipv6_addr(MY_IP6ADDR, MY_PREFIX_LEN_STR,
&in6_addr_my->sin6_addr, TAG);
if (ret < 0) {
printk(TAG "ERROR! Unable to set IPv6\n");
return;
}
printk(TAG "Binding to %s\n",
net_sprint_ipv6_addr(&in6_addr_my->sin6_addr));
in6_addr_my->sin6_port = htons(port);
#endif
#if defined(CONFIG_NET_IPV6)
if (context6) {
ret = net_context_bind(context6,
(struct sockaddr *)in6_addr_my,
sizeof(struct sockaddr_in6));
if (ret < 0) {
printk(TAG "Cannot bind IPv6 TCP port %d (%d)\n",
ntohs(in6_addr_my->sin6_port), ret);
fail++;
}
ret = net_context_listen(context6, 0);
if (ret < 0) {
printk(TAG "Cannot listen IPv6 TCP (%d)", ret);
return;
}
ret = net_context_accept(context6, tcp_accepted, 0, NULL);
if (ret < 0) {
printk(TAG "Cannot receive IPv6 TCP packets (%d)", ret);
return;
}
}
#endif
#if defined(CONFIG_NET_IPV4)
if (context4) {
ret = net_context_bind(context4,
(struct sockaddr *)in4_addr_my,
sizeof(struct sockaddr_in));
if (ret < 0) {
printk(TAG "Cannot bind IPv4 TCP port %d (%d)\n",
ntohs(in4_addr_my->sin_port), ret);
fail++;
}
ret = net_context_listen(context4, 0);
if (ret < 0) {
printk(TAG "Cannot listen IPv4 TCP (%d)", ret);
return;
}
ret = net_context_accept(context4, tcp_accepted, 0, NULL);
if (ret < 0) {
printk(TAG "Cannot receive IPv4 TCP packets (%d)", ret);
return;
}
}
#endif
if (fail > 1) {
return;
}
k_sleep(K_FOREVER);
}
void zperf_tcp_receiver_init(int port)
{
#if defined(CONFIG_NET_IPV6)
in6_addr_my = zperf_get_sin6();
#endif
#if defined(CONFIG_NET_IPV4)
in4_addr_my = zperf_get_sin();
#endif
k_thread_spawn(zperf_tcp_rx_stack, sizeof(zperf_tcp_rx_stack),
(k_thread_entry_t)zperf_tcp_rx_thread,
INT_TO_POINTER(port), 0, 0,
K_PRIO_COOP(7), 0, K_NO_WAIT);
}