Linux Audio

Check our new training course

Loading...
/*
 * Copyright (c) 2015 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr.h>

#include <linker/sections.h>
#include <toolchain.h>

#include <misc/printk.h>

#include <net/net_core.h>
#include <net/net_ip.h>
#include <net/net_pkt.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 K_THREAD_STACK_DEFINE(zperf_tcp_rx_stack, TCP_RX_FIBER_STACK_SIZE);
static struct k_thread zperf_tcp_rx_thread_data;

#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_pkt *pkt,
			 int status,
			 void *user_data)
{
	struct session *session;
	u32_t time;

	if (!pkt) {
		return;
	}

	time = k_cycle_get_32();

	session = get_session(pkt, 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 (pkt) {
			session->length += net_pkt_appdatalen(pkt);
		}

		if (!pkt && status == 0) { /* EOF */
			u32_t rate_in_kbps;
			u32_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 = (u32_t)
					(((u64_t)session->length *
					  (u64_t)8 *
					  (u64_t)USEC_PER_SEC) /
					 ((u64_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_pkt_unref(pkt);
}

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, K_NO_WAIT, 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, K_NO_WAIT, 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_create(&zperf_tcp_rx_thread_data, zperf_tcp_rx_stack,
			K_THREAD_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);
}