net:apps: zperf application
zperf is a network traffic generator for Zephyr. Same application is able to work dynamically in client or server mode. It allows to assess network bandwidth. zperf is compliant with iperf_2.0.5. zperf can be run in micro or nano kernel. Change-Id: Icbd69e1ad56ad29c678b098e7a2e07c44f90e48e Signed-off-by: jgarcia <jeremie.garcia@intel.com>
This commit is contained in:
parent
c36daba63d
commit
7b8d89d921
6
samples/net/zperf/Makefile
Normal file
6
samples/net/zperf/Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
MDEF_FILE = prj.mdef
|
||||
KERNEL_TYPE = micro
|
||||
BOARD ?= qemu_x86
|
||||
CONF_FILE = prj.conf
|
||||
|
||||
include ${ZEPHYR_BASE}/Makefile.inc
|
7
samples/net/zperf/README
Normal file
7
samples/net/zperf/README
Normal file
|
@ -0,0 +1,7 @@
|
|||
Desc:
|
||||
=====
|
||||
zperf is a network traffic generator for Zephyr.
|
||||
Same application is able to work dynamically in client or server mode.
|
||||
It allows to assess network bandwidth.
|
||||
zperf is compliant with iperf_2.0.5.
|
||||
zperf can be run in micro or nano kernel.
|
1
samples/net/zperf/prj.conf
Symbolic link
1
samples/net/zperf/prj.conf
Symbolic link
|
@ -0,0 +1 @@
|
|||
prj_galileo_ethernet.conf
|
5
samples/net/zperf/prj.mdef
Normal file
5
samples/net/zperf/prj.mdef
Normal file
|
@ -0,0 +1,5 @@
|
|||
% Application : Zperf
|
||||
|
||||
% TASK NAME PRIO ENTRY STACK GROUPS
|
||||
% ==================================
|
||||
TASK MAIN 7 mainloop 2048 [EXE]
|
30
samples/net/zperf/prj_galileo_ethernet.conf
Normal file
30
samples/net/zperf/prj_galileo_ethernet.conf
Normal file
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
#console
|
||||
#
|
||||
CONFIG_STDOUT_CONSOLE=y
|
||||
CONFIG_CONSOLE_HANDLER=y
|
||||
CONFIG_CONSOLE_HANDLER_SHELL=y
|
||||
CONFIG_PRINTK=y
|
||||
CONFIG_MINIMAL_LIBC_EXTENDED=y
|
||||
#
|
||||
# networking
|
||||
#
|
||||
CONFIG_NETWORKING=y
|
||||
CONFIG_IP_BUF_RX_SIZE=5
|
||||
CONFIG_IP_BUF_TX_SIZE=5
|
||||
CONFIG_NETWORKING_WITH_IPV4=y
|
||||
#CONFIG_NETWORKING_WITH_LOGGING=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_NET_BUF=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_DS=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_ICMPV6=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_ND=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_NBR_CACHE=y
|
||||
#CONFIG_NETWORK_IP_STACK_DEBUG_IPV6_ROUTE=y
|
||||
#
|
||||
# Ethernet
|
||||
#
|
||||
CONFIG_ETHERNET=y
|
||||
#CONFIG_ETHERNET_DEBUG=y
|
||||
CONFIG_ETH_DW=y
|
||||
CONFIG_PCI_ENUMERATION=y
|
9
samples/net/zperf/src/Makefile
Normal file
9
samples/net/zperf/src/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
ccflags-y += -I${srctree}/net/ip/contiki
|
||||
ccflags-y += -I${srctree}/net/ip/contiki/os/lib
|
||||
ccflags-y += -I${srctree}/net/ip/contiki/os
|
||||
ccflags-y += -I${srctree}/net/ip
|
||||
|
||||
obj-y += zperf_shell.o
|
||||
obj-y += shell_utils.o
|
||||
obj-y += zperf_uploader.o
|
||||
obj-y += zperf_receiver.o
|
161
samples/net/zperf/src/shell_utils.c
Normal file
161
samples/net/zperf/src/shell_utils.c
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <misc/printk.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include "shell_utils.h"
|
||||
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
#define BASE 16
|
||||
#define SEPARATOR ':'
|
||||
#else
|
||||
#define BASE 10
|
||||
#define SEPARATOR '.'
|
||||
#endif
|
||||
|
||||
int parseIpString(char *str, int res[])
|
||||
{
|
||||
uint32_t index = 0;
|
||||
|
||||
if (str == NULL) {
|
||||
printk("ERROR! null str detected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV4
|
||||
if (strlen(str) > IPV4_STR_LEN_MAX
|
||||
|| strlen(str) < IPV4_STR_LEN_MIN) {
|
||||
printk("ERROR! invalid IP address\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < IP_INDEX_MAX; i++)
|
||||
res[i] = 0;
|
||||
|
||||
while (*str) {
|
||||
if (isdigit((unsigned char)*str)) {
|
||||
res[index] *= BASE;
|
||||
res[index] += *str - '0';
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV4
|
||||
if (res[index] > 255) {
|
||||
printk("ERROR! invalid IP address\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
else if (isalpha((unsigned char)*str)) {
|
||||
int digit = *str - (isupper(*str) ? 'A' - 10 : 'a' - 10);
|
||||
|
||||
if (digit > BASE) {
|
||||
printk("ERROR! invalid IP address\n");
|
||||
return -1;
|
||||
}
|
||||
res[index] *= BASE;
|
||||
res[index] += digit;
|
||||
}
|
||||
#endif
|
||||
else if ((unsigned char) *str == SEPARATOR) {
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
if (str[1] == SEPARATOR) {
|
||||
printk("ERROR! I am sorry but I am not yet able to understand "
|
||||
"the :: syntax!!\n");
|
||||
printk("ERROR! Please expand your @IP or enhance me!!\n");
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
index++;
|
||||
if (index >= IP_INDEX_MAX) {
|
||||
printk("ERROR! invalid IP address\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
printk("ERROR! invalid IP address format\n");
|
||||
return -1;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_address(int *value)
|
||||
{
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
printk("%x:%x:%x:%x:%x:%x:%x:%x",
|
||||
value[0], value[1], value[2], value[3],
|
||||
value[4], value[5], value[6], value[7]);
|
||||
#else
|
||||
printk("%d.%d.%d.%d", value[0], value[1], value[2], value[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
const int TIME_US[] = { 60 * 1000 * 1000, 1000 * 1000, 1000, 0 };
|
||||
const char *TIME_US_UNIT[] = { "m", "s", "ms", "us" };
|
||||
const int KBPS[] = { 1024, 0 };
|
||||
const char *KBPS_UNIT[] = { "Mbps", "Kbps" };
|
||||
const int K[] = { 1024 * 1024, 1024, 0 };
|
||||
const char *K_UNIT[] = { "M", "K", "" };
|
||||
|
||||
void print_number(uint32_t value, const uint32_t *divisor,
|
||||
const char **units)
|
||||
{
|
||||
const char **unit;
|
||||
const uint32_t *div;
|
||||
uint32_t dec, radix;
|
||||
|
||||
unit = units;
|
||||
div = divisor;
|
||||
|
||||
while (value < *div) {
|
||||
div++;
|
||||
unit++;
|
||||
}
|
||||
|
||||
if (*div != 0) {
|
||||
radix = value / *div;
|
||||
dec = (value % *div) * 100 / *div;
|
||||
printk("%u.%s%u %s", radix, (dec < 10) ? "0" : "", dec, *unit);
|
||||
} else {
|
||||
printk("%u %s", value, *unit);
|
||||
}
|
||||
}
|
||||
|
||||
long parse_number(const char *string, const uint32_t *divisor,
|
||||
const char **units)
|
||||
{
|
||||
const char **unit;
|
||||
const uint32_t *div;
|
||||
char *suffix;
|
||||
long dec;
|
||||
int cmp;
|
||||
|
||||
dec = strtoul(string, &suffix, 10);
|
||||
unit = units;
|
||||
div = divisor;
|
||||
|
||||
do {
|
||||
cmp = strncasecmp(suffix, *unit++, 1);
|
||||
} while (cmp != 0 && *++div != 0);
|
||||
|
||||
return (*div == 0) ? dec : dec * *div;
|
||||
}
|
41
samples/net/zperf/src/shell_utils.h
Normal file
41
samples/net/zperf/src/shell_utils.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef __SHELL_UTILS_H
|
||||
#define __SHELL_UTILS_H
|
||||
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
#define IP_INDEX_MAX 8
|
||||
#else
|
||||
#define IP_INDEX_MAX 4
|
||||
#endif
|
||||
|
||||
#define IPV4_STR_LEN_MAX 15
|
||||
#define IPV4_STR_LEN_MIN 7
|
||||
|
||||
extern const int TIME_US[];
|
||||
extern const char *TIME_US_UNIT[];
|
||||
extern const int KBPS[];
|
||||
extern const char *KBPS_UNIT[];
|
||||
extern const int K[];
|
||||
extern const char *K_UNIT[];
|
||||
|
||||
extern int parseIpString(char *str, int res[]);
|
||||
extern void print_address(int *value);
|
||||
extern void print_number(uint32_t value, const uint32_t *divisor,
|
||||
const char **units);
|
||||
extern long parse_number(const char *string, const uint32_t *divisor,
|
||||
const char **units);
|
||||
#endif /* __SHELL_UTILS_H */
|
37
samples/net/zperf/src/zperf.h
Normal file
37
samples/net/zperf/src/zperf.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef __ZPERF_H
|
||||
#define __ZPERF_H
|
||||
|
||||
#define VERSION "1.0"
|
||||
|
||||
typedef struct zperf_results {
|
||||
uint32_t nb_packets_sent;
|
||||
uint32_t nb_packets_rcvd;
|
||||
uint32_t nb_packets_lost;
|
||||
uint32_t nb_packets_outorder;
|
||||
uint32_t nb_bytes_sent;
|
||||
uint32_t time_in_us;
|
||||
uint32_t jitter_in_us;
|
||||
uint32_t client_time_in_us;
|
||||
} zperf_results;
|
||||
|
||||
typedef void (*zperf_callback)(int status, zperf_results*);
|
||||
|
||||
#define IPV4_STR_LEN_MAX 15
|
||||
#define IPV4_STR_LEN_MIN 7
|
||||
|
||||
#endif /* __ZPERF_H */
|
94
samples/net/zperf/src/zperf_internal.h
Normal file
94
samples/net/zperf/src/zperf_internal.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef __ZPERF_INTERNAL_H
|
||||
#define __ZPERF_INTERNAL_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <misc/byteorder.h>
|
||||
|
||||
/* Constants */
|
||||
#define PACKET_SIZE_MAX 1024
|
||||
|
||||
/* Macro */
|
||||
#define HW_CYCLES_TO_USEC(__hw_cycle__) \
|
||||
( \
|
||||
((uint64_t)(__hw_cycle__) * (uint64_t)sys_clock_us_per_tick) / \
|
||||
((uint64_t)sys_clock_hw_cycles_per_tick) \
|
||||
)
|
||||
|
||||
#define HW_CYCLES_TO_SEC(__hw_cycle__) \
|
||||
( \
|
||||
((uint64_t)(HW_CYCLES_TO_USEC(__hw_cycle__))) / \
|
||||
((uint64_t)USEC_PER_SEC) \
|
||||
)
|
||||
|
||||
#define USEC_TO_HW_CYCLES(__usec__) \
|
||||
( \
|
||||
((uint64_t)(__usec__) * (uint64_t)sys_clock_hw_cycles_per_tick) / \
|
||||
((uint64_t)sys_clock_us_per_tick) \
|
||||
)
|
||||
|
||||
#define SEC_TO_HW_CYCLES(__sec__) \
|
||||
USEC_TO_HW_CYCLES((uint64_t)(__sec__) * \
|
||||
(uint64_t)USEC_PER_SEC)
|
||||
|
||||
#define MSEC_TO_HW_CYCLES(__msec__) \
|
||||
USEC_TO_HW_CYCLES((uint64_t)(__msec__) * \
|
||||
(uint64_t)MSEC_PER_SEC)
|
||||
|
||||
/* Types */
|
||||
typedef struct zperf_udp_datagram {
|
||||
int32_t id;
|
||||
uint32_t tv_sec;
|
||||
uint32_t tv_usec;
|
||||
} zperf_udp_datagram;
|
||||
|
||||
typedef struct zperf_server_hdr {
|
||||
int32_t flags;
|
||||
int32_t total_len1;
|
||||
int32_t total_len2;
|
||||
int32_t stop_sec;
|
||||
int32_t stop_usec;
|
||||
int32_t error_cnt;
|
||||
int32_t outorder_cnt;
|
||||
int32_t datagrams;
|
||||
int32_t jitter1;
|
||||
int32_t jitter2;
|
||||
} zperf_server_hdr;
|
||||
|
||||
/* Inline functions */
|
||||
static inline uint32_t time_delta(uint32_t ts, uint32_t t)
|
||||
{
|
||||
return (t >= ts) ? (t - ts) : (ULONG_MAX - ts + t);
|
||||
}
|
||||
|
||||
/* byte order */
|
||||
#define z_htonl(val) sys_cpu_to_be32(val)
|
||||
#define z_ntohl(val) sys_be32_to_cpu(val)
|
||||
|
||||
/* internal functions */
|
||||
extern void zperf_upload(struct net_context *net_context,
|
||||
unsigned int duration_in_ms, unsigned int packet_size,
|
||||
unsigned int rate_in_kbps, zperf_results *results);
|
||||
extern void zperf_upload_fin(struct net_context *net_context,
|
||||
uint32_t nb_packets, uint32_t end_time, uint32_t packet_size,
|
||||
zperf_results *results);
|
||||
extern void zperf_upload_decode_stat(struct net_buf *net_stat,
|
||||
zperf_results *results);
|
||||
|
||||
extern void zperf_receiver_init(int port);
|
||||
extern void connect_ap(char *ssid);
|
||||
#endif /* __ZPERF_INTERNAL_H */
|
305
samples/net/zperf/src/zperf_receiver.c
Normal file
305
samples/net/zperf/src/zperf_receiver.c
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <misc/printk.h>
|
||||
#include <net/net_core.h>
|
||||
#include <net/net_socket.h>
|
||||
#include <net/ip_buf.h>
|
||||
#include <net/net_ip.h>
|
||||
#include <sections.h>
|
||||
#include <toolchain.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include "zperf.h"
|
||||
#include "zperf_internal.h"
|
||||
#include "shell_utils.h"
|
||||
|
||||
#define TAG "[udp.download] "
|
||||
|
||||
#define RX_FIBER_STACK_SIZE 1024
|
||||
#define SESSION_MAX 4
|
||||
|
||||
/* Type definition */
|
||||
enum state {
|
||||
STATE_NULL, /* Session has not yet started */
|
||||
STATE_ONGOING, /* 1st packet has been received, last packet not yet */
|
||||
STATE_LAST_PACKET_RECEIVED, /* Last packet has been received */
|
||||
STATE_COMPLETED /* Session completed, stats pkt can be sent if needed */
|
||||
};
|
||||
|
||||
struct session {
|
||||
/* Tuple */
|
||||
uint16_t port;
|
||||
uip_ipaddr_t ip;
|
||||
|
||||
enum state state;
|
||||
|
||||
/* Stat data */
|
||||
uint32_t counter;
|
||||
uint32_t next_id;
|
||||
uint32_t outorder;
|
||||
uint32_t error;
|
||||
uint64_t length;
|
||||
uint32_t start_time;
|
||||
uint32_t last_time;
|
||||
int32_t jitter;
|
||||
int32_t last_transit_time;
|
||||
|
||||
/* Stats packet*/
|
||||
struct zperf_server_hdr stat;
|
||||
};
|
||||
|
||||
/* Static data */
|
||||
static char __noinit __stack zperf_rx_fiber_stack[RX_FIBER_STACK_SIZE];
|
||||
|
||||
static struct net_addr in_addr_any = {
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
.family = AF_INET6,
|
||||
.in6_addr = IN6ADDR_ANY_INIT
|
||||
#else
|
||||
.family = AF_INET, .in_addr = { { { 0 } } }
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct net_addr in_addr_my = {
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
.family = AF_INET6,
|
||||
.in6_addr = IN6ADDR_ANY_INIT
|
||||
#else
|
||||
.family = AF_INET, .in_addr = { { { 0 } } }
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct session sessions[SESSION_MAX];
|
||||
|
||||
/* Get session from a given packet */
|
||||
static struct session *get_session(struct net_buf *buf)
|
||||
{
|
||||
uint16_t port;
|
||||
uip_ipaddr_t ip;
|
||||
int i = 0;
|
||||
struct session *active = NULL;
|
||||
struct session *free = NULL;
|
||||
|
||||
/* Get tuple of the remote connection */
|
||||
port = NET_BUF_UDP(buf)->srcport;
|
||||
uip_ipaddr_copy(&ip, &NET_BUF_IP(buf)->srcipaddr);
|
||||
|
||||
/* Check whether we already have an active session */
|
||||
while (active == NULL && i < SESSION_MAX) {
|
||||
struct session *ptr = &sessions[i];
|
||||
|
||||
if (ptr->port == port && uip_ipaddr_cmp(&ptr->ip, &ip)) {
|
||||
/* We found an active session */
|
||||
active = ptr;
|
||||
} else if (free == NULL
|
||||
&& (ptr->state == STATE_NULL
|
||||
|| ptr->state == STATE_COMPLETED)) {
|
||||
/* We found a free slot - just in case */
|
||||
free = ptr;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
/* If no active session then create a new one */
|
||||
if (active == NULL && free != NULL) {
|
||||
active = free;
|
||||
active->port = port;
|
||||
uip_ipaddr_copy(&active->ip, &ip);
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
/* Send statistic to the remote client */
|
||||
static int zperf_receiver_send_stat(struct net_context *net_context,
|
||||
struct net_buf *buf, zperf_server_hdr *stat)
|
||||
{
|
||||
/* Fill the packet header */
|
||||
memcpy(ip_buf_appdata(buf) + sizeof(zperf_udp_datagram), stat,
|
||||
sizeof(zperf_server_hdr));
|
||||
|
||||
/* Send the packet */
|
||||
return net_reply(net_context, buf);
|
||||
}
|
||||
|
||||
/* RX fiber entry point */
|
||||
static void zperf_rx_fiber(int port)
|
||||
{
|
||||
struct net_context *net_context = net_context_get(IPPROTO_UDP, &in_addr_any,
|
||||
0, &in_addr_my, port);
|
||||
|
||||
if (!net_context) {
|
||||
printk(TAG "ERROR! Cannot get network context.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
struct net_buf *buf = net_receive(net_context, TICKS_UNLIMITED);
|
||||
|
||||
if (buf == NULL) {
|
||||
continue;
|
||||
} else if (ip_buf_appdatalen(buf) < sizeof(zperf_udp_datagram)) {
|
||||
/* Release the buffer */
|
||||
ip_buf_unref(buf);
|
||||
} else {
|
||||
uint32_t time = sys_cycle_get_32();
|
||||
int32_t id;
|
||||
int32_t transit_time;
|
||||
|
||||
struct session *session = get_session(buf);
|
||||
|
||||
if (session == NULL) {
|
||||
printk(TAG "ERROR! cannot create a session!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
zperf_udp_datagram *hdr = (zperf_udp_datagram *) ip_buf_appdata(buf);
|
||||
|
||||
id = z_ntohl(hdr->id);
|
||||
|
||||
/* Check last packet flags */
|
||||
if (id < 0) {
|
||||
printk(TAG "End of session!\n");
|
||||
if (session->state == STATE_COMPLETED) {
|
||||
/* Session is already completed: Resend the stat buffer
|
||||
* and continue
|
||||
*/
|
||||
if (zperf_receiver_send_stat(net_context, buf,
|
||||
&session->stat) < 0) {
|
||||
printk(TAG "ERROR! Failed to send the buffer\n");
|
||||
|
||||
/* Free the buffer */
|
||||
ip_buf_unref(buf);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
session->state = STATE_LAST_PACKET_RECEIVED;
|
||||
id = -id;
|
||||
}
|
||||
} else if (session->state != STATE_ONGOING) {
|
||||
/* Start a new session! */
|
||||
printk(TAG "New session started.\n");
|
||||
|
||||
/* Reset statistic */
|
||||
session->state = STATE_ONGOING;
|
||||
session->counter = 0;
|
||||
session->start_time = time;
|
||||
session->next_id = 0;
|
||||
session->length = 0;
|
||||
session->outorder = 0;
|
||||
session->error = 0;
|
||||
session->jitter = 0;
|
||||
session->last_transit_time = 0;
|
||||
}
|
||||
|
||||
/* Check header id */
|
||||
if (id != session->next_id) {
|
||||
if (id < session->next_id) {
|
||||
session->outorder++;
|
||||
} else {
|
||||
session->error += id - session->next_id;
|
||||
session->next_id = id + 1;
|
||||
}
|
||||
} else {
|
||||
session->next_id++;
|
||||
}
|
||||
|
||||
/* Update counter */
|
||||
session->counter++;
|
||||
session->length += ip_buf_appdatalen(buf);
|
||||
|
||||
/* Compute jitter */
|
||||
transit_time = time_delta(HW_CYCLES_TO_USEC(time),
|
||||
z_ntohl(hdr->tv_sec) * USEC_PER_SEC + z_ntohl(hdr->tv_usec));
|
||||
if (session->last_transit_time != 0) {
|
||||
int32_t delta_transit = transit_time
|
||||
- session->last_transit_time;
|
||||
delta_transit =
|
||||
(delta_transit < 0) ? -delta_transit : delta_transit;
|
||||
session->jitter += (delta_transit - session->jitter) / 16;
|
||||
}
|
||||
session->last_transit_time = transit_time;
|
||||
|
||||
/* If necessary send statistic */
|
||||
if (session->state == STATE_LAST_PACKET_RECEIVED) {
|
||||
uint32_t rate_in_kbps;
|
||||
uint32_t duration = HW_CYCLES_TO_USEC(
|
||||
time_delta(session->start_time, time));
|
||||
|
||||
/* Update state machine */
|
||||
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;
|
||||
|
||||
/* Fill static */
|
||||
session->stat.flags = z_htonl(0x80000000);
|
||||
session->stat.total_len1 = z_htonl(session->length >> 32);
|
||||
session->stat.total_len2 = z_htonl(
|
||||
session->length % 0xFFFFFFFF);
|
||||
session->stat.stop_sec = z_htonl(duration / USEC_PER_SEC);
|
||||
session->stat.stop_usec = z_htonl(duration % USEC_PER_SEC);
|
||||
session->stat.error_cnt = z_htonl(session->error);
|
||||
session->stat.outorder_cnt = z_htonl(session->outorder);
|
||||
session->stat.datagrams = z_htonl(session->counter);
|
||||
session->stat.jitter1 = 0;
|
||||
session->stat.jitter2 = z_htonl(session->jitter);
|
||||
|
||||
if (zperf_receiver_send_stat(net_context, buf, &session->stat)
|
||||
< 0) {
|
||||
printk(TAG "ERROR! Failed to send the buffer\n");
|
||||
|
||||
/* Free the buffer */
|
||||
ip_buf_unref(buf);
|
||||
}
|
||||
|
||||
printk(TAG " duration:\t\t");
|
||||
print_number(duration, TIME_US, TIME_US_UNIT);
|
||||
printk("\n");
|
||||
|
||||
printk(TAG " received packets:\t%u\n", session->counter);
|
||||
printk(TAG " nb packets lost:\t%u\n", session->outorder);
|
||||
printk(TAG " nb packets outorder:\t%u\n", session->error);
|
||||
|
||||
printk(TAG " jitter:\t\t\t");
|
||||
print_number(session->jitter, TIME_US, TIME_US_UNIT);
|
||||
printk("\n");
|
||||
|
||||
printk(TAG " rate:\t\t\t");
|
||||
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
|
||||
printk("\n");
|
||||
} else {
|
||||
/* Free the buffer */
|
||||
ip_buf_unref(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void zperf_receiver_init(int port)
|
||||
{
|
||||
for (int i = 0; i < SESSION_MAX; i++)
|
||||
sessions[i].state = STATE_NULL;
|
||||
|
||||
fiber_start(zperf_rx_fiber_stack, sizeof(zperf_rx_fiber_stack),
|
||||
(nano_fiber_entry_t) zperf_rx_fiber, port, 0, 7, 0);
|
||||
}
|
295
samples/net/zperf/src/zperf_shell.c
Normal file
295
samples/net/zperf/src/zperf_shell.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <misc/printk.h>
|
||||
#include <misc/shell.h>
|
||||
#include <net/ip_buf.h>
|
||||
#include <net/net_core.h>
|
||||
#include <net/net_socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include "zperf.h"
|
||||
#include "zperf_internal.h"
|
||||
#include "shell_utils.h"
|
||||
|
||||
#define DEVICE_NAME "zperf shell"
|
||||
|
||||
static const char *CONFIG = ""
|
||||
#ifdef CONFIG_MICROKERNEL
|
||||
"microkernel "
|
||||
#else
|
||||
"nanokernel "
|
||||
#endif
|
||||
#ifdef CONFIG_ETH_DW
|
||||
"ethernet "
|
||||
#endif
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV4
|
||||
"ipv4 "
|
||||
#endif
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
"ipv6 "
|
||||
#endif
|
||||
"";
|
||||
|
||||
static struct net_addr in_addr_my = {
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
.family = AF_INET6,
|
||||
.in6_addr = IN6ADDR_ANY_INIT
|
||||
#else
|
||||
.family = AF_INET,
|
||||
.in_addr = { { { 0 } } }
|
||||
#endif
|
||||
};
|
||||
|
||||
#define MY_SRC_PORT 50000
|
||||
|
||||
static void shell_cmd_setip(int argc, char *argv[])
|
||||
{
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV4
|
||||
uint32_t value[4] = {0};
|
||||
|
||||
if (argc != 2) {
|
||||
/* Print usage */
|
||||
printk("\nsetip:\n");
|
||||
printk("Usage:\tsetip <my ip>\n");
|
||||
printk("\nExample setip 10.237.164.178\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (parseIpString(argv[1], value) == 0) {
|
||||
printk("[setip] Setting IP address %d.%d.%d.%d\n",
|
||||
value[0], value[1], value[2], value[3]);
|
||||
uip_ipaddr_t uip_ipaddr_local;
|
||||
|
||||
uip_ipaddr(&uip_ipaddr_local,
|
||||
value[0],
|
||||
value[1],
|
||||
value[2],
|
||||
value[3]);
|
||||
uip_sethostaddr(&uip_ipaddr_local);
|
||||
} else {
|
||||
printk("[setip] ERROR! Unable to set IP\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void shell_cmd_udp_download(int argc, char *argv[])
|
||||
{
|
||||
static bool stopped = true;
|
||||
int port;
|
||||
|
||||
if (argc == 1) {
|
||||
/* Print usage */
|
||||
printk("\nudp.download:\n");
|
||||
printk("Usage:\tudp.download <port>\n");
|
||||
printk("\nExample udp.download 5001\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
port = strtoul(argv[1], NULL, 10);
|
||||
} else {
|
||||
port = 5001;
|
||||
}
|
||||
|
||||
if (stopped == false) {
|
||||
printk("[udp.download] ERROR! UDP server already started!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
zperf_receiver_init(port);
|
||||
stopped = false;
|
||||
printk("[udp.download] UDP server started on port %u\n", port);
|
||||
}
|
||||
|
||||
static void shell_cmd_udp_upload(int argc, char *argv[])
|
||||
{
|
||||
int value[IP_INDEX_MAX] = { 0 };
|
||||
static struct net_addr net_addr_remote = { .family = AF_INET, .in_addr = { {
|
||||
{ 192, 168, 43, 181 } } } };
|
||||
unsigned int duration_in_ms, packet_size, rate_in_kbps, client_rate_in_kbps;
|
||||
uint16_t port;
|
||||
zperf_results results = { 0 };
|
||||
struct net_context *net_context;
|
||||
|
||||
if (argc == 1) {
|
||||
/* Print usage */
|
||||
printk("\nudp.upload:\n");
|
||||
printk(
|
||||
"Usage:\tudp.upload <dest ip> <dest port> <duration> <packet "
|
||||
"size>[K] <baud rate>[K|M]\n");
|
||||
printk("\t<dest ip>:\tIP destination\n");
|
||||
printk("\t<dest port>:\tport destination\n");
|
||||
printk("\t<duration>:\t of the test in seconds\n");
|
||||
printk(
|
||||
"\t<packet size>:\tSize of the paket in byte or kilobyte "
|
||||
"(with suffix K)\n");
|
||||
printk("\t<baud rate>:\tBaudrate in kilobyte or megabyte\n");
|
||||
printk("\nExample udp.upload 10.237.164.178 1111 1 1K 1M\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc > 1 && parseIpString(argv[1], value) == 0) {
|
||||
printk("[udp.upload] Remote IP address is ");
|
||||
print_address(value);
|
||||
printk("\n");
|
||||
#ifdef CONFIG_NETWORKING_WITH_IPV6
|
||||
for (int i = 0; i < IP_INDEX_MAX; i++) {
|
||||
net_addr_remote.in6_addr.s6_addr[i] = (value[i] & 0xFF00) >> 8;
|
||||
net_addr_remote.in6_addr.s6_addr[i] = value[i] & 0xFF;
|
||||
}
|
||||
#else
|
||||
net_addr_remote.in_addr.s4_addr[0] = value[0];
|
||||
net_addr_remote.in_addr.s4_addr[1] = value[1];
|
||||
net_addr_remote.in_addr.s4_addr[2] = value[2];
|
||||
net_addr_remote.in_addr.s4_addr[3] = value[3];
|
||||
#endif
|
||||
} else {
|
||||
printk(
|
||||
"[udp.upload] ERROR! Please specify the IP address of the"
|
||||
" remote server\n");
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
port = strtoul(argv[2], NULL, 10);
|
||||
printk("[udp.upload] Remote port is %u\n", port);
|
||||
} else {
|
||||
port = 5001;
|
||||
}
|
||||
|
||||
net_context = net_context_get(IPPROTO_UDP, &net_addr_remote, port,
|
||||
&in_addr_my, MY_SRC_PORT);
|
||||
|
||||
if (!net_context) {
|
||||
printk("[udp.upload] ERROR! Fail to retrieve a net context\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc > 3)
|
||||
duration_in_ms = strtoul(argv[3], NULL, 10) * MSEC_PER_SEC;
|
||||
else
|
||||
duration_in_ms = 1000;
|
||||
|
||||
if (argc > 4)
|
||||
packet_size = parse_number(argv[4], K, K_UNIT);
|
||||
else
|
||||
packet_size = 256;
|
||||
|
||||
if (argc > 5)
|
||||
rate_in_kbps = (parse_number(argv[5], K, K_UNIT) + 1023) / 1024;
|
||||
else
|
||||
rate_in_kbps = 10;
|
||||
|
||||
/* Print settings */
|
||||
printk("[udp.upload] duration:\t\t");
|
||||
print_number(duration_in_ms * USEC_PER_MSEC, TIME_US, TIME_US_UNIT);
|
||||
printk("\n");
|
||||
|
||||
printk("[udp.upload] packet size:\t%u bytes\n", packet_size);
|
||||
|
||||
printk("[udp.upload] rate:\t\t");
|
||||
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
|
||||
printk("\n");
|
||||
|
||||
printk("[udp.upload] start...\n");
|
||||
|
||||
/* Perform upload test */
|
||||
zperf_upload(net_context, duration_in_ms, packet_size, rate_in_kbps,
|
||||
&results);
|
||||
|
||||
printk("[udp.upload] completed!\n");
|
||||
|
||||
/* Print results */
|
||||
if (results.time_in_us != 0)
|
||||
rate_in_kbps = (uint32_t) (((uint64_t) results.nb_bytes_sent
|
||||
* (uint64_t) 8 * (uint64_t) USEC_PER_SEC)
|
||||
/ ((uint64_t) results.time_in_us * 1024));
|
||||
else
|
||||
rate_in_kbps = 0;
|
||||
|
||||
if (results.client_time_in_us != 0)
|
||||
client_rate_in_kbps = (uint32_t) (((uint64_t) results.nb_packets_sent
|
||||
* (uint64_t) packet_size * (uint64_t) 8
|
||||
* (uint64_t) USEC_PER_SEC)
|
||||
/ ((uint64_t) results.client_time_in_us * 1024));
|
||||
else
|
||||
client_rate_in_kbps = 0;
|
||||
|
||||
if (!rate_in_kbps)
|
||||
printk("[udp.upload] LAST PACKET NOT RECEIVED!!!\n");
|
||||
|
||||
printk("[udp.upload] statistic:\t\t\tserver\t(client)\n");
|
||||
printk("[udp.upload] duration:\t\t\t");
|
||||
print_number(results.time_in_us, TIME_US, TIME_US_UNIT);
|
||||
printk("\t(");
|
||||
print_number(results.client_time_in_us, TIME_US, TIME_US_UNIT);
|
||||
printk(")\n");
|
||||
|
||||
printk("[udp.upload] nb packets:\t\t%u\t(%u)\n", results.nb_packets_rcvd,
|
||||
results.nb_packets_sent);
|
||||
|
||||
printk("[udp.upload] nb packets outorder:\t%u\n",
|
||||
results.nb_packets_outorder);
|
||||
printk("[udp.upload] nb packets lost:\t\t%u\n", results.nb_packets_lost);
|
||||
|
||||
printk("[udp.upload] jitter:\t\t\t");
|
||||
print_number(results.jitter_in_us, TIME_US, TIME_US_UNIT);
|
||||
printk("\n");
|
||||
|
||||
printk("[udp.upload] rate:\t\t\t");
|
||||
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
|
||||
printk("\t(");
|
||||
print_number(client_rate_in_kbps, KBPS, KBPS_UNIT);
|
||||
printk(")\n");
|
||||
|
||||
/* release net context */
|
||||
net_context_put(net_context);
|
||||
}
|
||||
|
||||
static void shell_cmd_connectap(int argc, char *argv[])
|
||||
{
|
||||
printk("[connectap] Zephyr has not been built with Wi-Fi support.\n");
|
||||
}
|
||||
|
||||
static void shell_cmd_version(int argc, char *argv[])
|
||||
{
|
||||
printk("\nZPerf version: %s config: %s\n", VERSION, CONFIG);
|
||||
}
|
||||
|
||||
struct shell_cmd commands[] = {
|
||||
{ "setip", shell_cmd_setip },
|
||||
{ "connectap", shell_cmd_connectap },
|
||||
{ "version", shell_cmd_version },
|
||||
{ "udp.upload", shell_cmd_udp_upload },
|
||||
{ "udp.download", shell_cmd_udp_download },
|
||||
{ NULL, NULL } };
|
||||
|
||||
#ifdef CONFIG_MICROKERNEL
|
||||
void mainloop(void)
|
||||
{
|
||||
#else
|
||||
void main(void)
|
||||
{
|
||||
#endif
|
||||
printk("\nZPerf version: %s config: %s\n", VERSION, CONFIG);
|
||||
|
||||
shell_init("zperf> ", commands);
|
||||
|
||||
net_init();
|
||||
}
|
222
samples/net/zperf/src/zperf_uploader.c
Normal file
222
samples/net/zperf/src/zperf_uploader.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Intel Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <misc/printk.h>
|
||||
#include <net/ip_buf.h>
|
||||
#include <net/net_ip.h>
|
||||
#include <net/net_core.h>
|
||||
#include <net/net_socket.h>
|
||||
#include <zephyr.h>
|
||||
|
||||
#include "zperf.h"
|
||||
#include "zperf_internal.h"
|
||||
|
||||
#define TAG "[udp.upload] "
|
||||
|
||||
void zperf_upload(struct net_context *net_context,
|
||||
unsigned int duration_in_ms,
|
||||
unsigned int packet_size, unsigned int rate_in_kbps,
|
||||
zperf_results *results)
|
||||
{
|
||||
uint32_t print_interval = SEC_TO_HW_CYCLES(1);
|
||||
uint32_t duration = MSEC_TO_HW_CYCLES(duration_in_ms);
|
||||
uint32_t packet_duration = (uint32_t) (((uint64_t) packet_size
|
||||
* SEC_TO_HW_CYCLES(1) * 8) / (uint64_t) (rate_in_kbps * 1024));
|
||||
uint32_t nb_packets = 0;
|
||||
uint32_t start_time, last_print_time, last_loop_time, end_time;
|
||||
uint32_t delay = packet_duration;
|
||||
|
||||
if (packet_size > PACKET_SIZE_MAX) {
|
||||
printk(TAG "WARNING! packet size too large! max size: %u\n",
|
||||
PACKET_SIZE_MAX);
|
||||
packet_size = PACKET_SIZE_MAX;
|
||||
} else if (packet_size < sizeof(zperf_udp_datagram)) {
|
||||
printk(TAG "WARNING! packet size set to the min size: %u\n",
|
||||
sizeof(zperf_udp_datagram));
|
||||
packet_size = sizeof(zperf_udp_datagram);
|
||||
}
|
||||
|
||||
/* Start the loop */
|
||||
start_time = sys_cycle_get_32();
|
||||
last_print_time = start_time;
|
||||
last_loop_time = start_time;
|
||||
|
||||
do {
|
||||
int32_t adjust;
|
||||
uint32_t loop_time;
|
||||
zperf_udp_datagram *datagram;
|
||||
|
||||
/* Timestamp */
|
||||
loop_time = sys_cycle_get_32();
|
||||
|
||||
/* Algorithm to maintain a given baud rate */
|
||||
if (last_loop_time != loop_time) {
|
||||
adjust = packet_duration - time_delta(last_loop_time, loop_time);
|
||||
} else {
|
||||
adjust = 0; /* It's the first iteration so no need for adjustment */
|
||||
}
|
||||
if (adjust >= 0 || -adjust < delay) {
|
||||
delay += adjust;
|
||||
} else {
|
||||
delay = 0; /* delay should never be a negative value */
|
||||
}
|
||||
|
||||
last_loop_time = loop_time;
|
||||
|
||||
/* Get a new TX buffer */
|
||||
struct net_buf *buf = ip_buf_get_tx(net_context);
|
||||
|
||||
if (!buf) {
|
||||
printk(TAG "ERROR! Failed to retrieve a buffer\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill the packet header */
|
||||
datagram = (zperf_udp_datagram *) net_buf_add(buf,
|
||||
sizeof(zperf_udp_datagram));
|
||||
|
||||
datagram->id = z_htonl(nb_packets);
|
||||
datagram->tv_sec = z_htonl(HW_CYCLES_TO_SEC(loop_time));
|
||||
datagram->tv_usec = z_htonl(
|
||||
HW_CYCLES_TO_USEC(loop_time) % USEC_PER_SEC);
|
||||
|
||||
/* Fill the remain part of the datagram */
|
||||
if (packet_size > sizeof(zperf_udp_datagram)) {
|
||||
int size = packet_size - sizeof(zperf_udp_datagram);
|
||||
uint8_t *ptr = net_buf_add(buf, size);
|
||||
|
||||
memset(ptr, 'z', size);
|
||||
}
|
||||
|
||||
/* Send the packet */
|
||||
if (net_send(buf) < 0) {
|
||||
printk(TAG "ERROR! Failed to send the buffer\n");
|
||||
ip_buf_unref(buf);
|
||||
continue;
|
||||
} else {
|
||||
nb_packets++;
|
||||
}
|
||||
|
||||
/* Print log every seconds */
|
||||
if (time_delta(last_print_time, loop_time) > print_interval) {
|
||||
printk(TAG "nb_packets=%u\tdelay=%u\tadjust=%d\n", nb_packets,
|
||||
delay, adjust);
|
||||
last_print_time = loop_time;
|
||||
}
|
||||
|
||||
/* Wait */
|
||||
while (time_delta(loop_time, sys_cycle_get_32()) < delay) {
|
||||
fiber_yield();
|
||||
}
|
||||
} while (time_delta(start_time, last_loop_time) < duration);
|
||||
|
||||
end_time = sys_cycle_get_32();
|
||||
zperf_upload_fin(net_context, nb_packets, end_time, packet_size, results);
|
||||
|
||||
/* Add result coming from the client */
|
||||
results->nb_packets_sent = nb_packets;
|
||||
results->client_time_in_us = HW_CYCLES_TO_USEC(
|
||||
time_delta(start_time, end_time));
|
||||
}
|
||||
|
||||
void zperf_upload_fin(struct net_context *net_context, uint32_t nb_packets,
|
||||
uint32_t end_time, uint32_t packet_size, zperf_results *results)
|
||||
{
|
||||
struct net_buf *net_stat = NULL;
|
||||
int loop = 2;
|
||||
|
||||
while (net_stat == NULL && loop-- > 0) {
|
||||
zperf_udp_datagram *datagram;
|
||||
|
||||
/* Get a new TX buffer */
|
||||
struct net_buf *buf = ip_buf_get_tx(net_context);
|
||||
|
||||
if (!buf) {
|
||||
printk(TAG "ERROR! Failed to retrieve a buffer\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill the packet header */
|
||||
datagram = (zperf_udp_datagram *) net_buf_add(buf,
|
||||
sizeof(zperf_udp_datagram));
|
||||
|
||||
datagram->id = z_htonl(-nb_packets);
|
||||
datagram->tv_sec = z_htonl(HW_CYCLES_TO_SEC(end_time));
|
||||
datagram->tv_usec = z_htonl(HW_CYCLES_TO_USEC(end_time) % USEC_PER_SEC);
|
||||
|
||||
/* Fill the remain part of the datagram */
|
||||
if (packet_size > sizeof(zperf_udp_datagram)) {
|
||||
int size = packet_size - sizeof(zperf_udp_datagram);
|
||||
uint8_t *ptr = net_buf_add(buf, size);
|
||||
|
||||
memset(ptr, 'z', size);
|
||||
}
|
||||
|
||||
/* Send the packet */
|
||||
if (net_send(buf) < 0) {
|
||||
printk(TAG "ERROR! Failed to send the buffer\n");
|
||||
ip_buf_unref(buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Receive statistic */
|
||||
net_stat = net_receive(net_context, 2 * sys_clock_ticks_per_sec);
|
||||
}
|
||||
|
||||
/* Decode statistic */
|
||||
if (net_stat != NULL) {
|
||||
zperf_upload_decode_stat(net_stat, results);
|
||||
|
||||
/* Free the buffer */
|
||||
ip_buf_unref(net_stat);
|
||||
}
|
||||
|
||||
/* Drain RX fifo */
|
||||
while (net_stat != NULL) {
|
||||
net_stat = net_receive(net_context, 2 * sys_clock_ticks_per_sec);
|
||||
ip_buf_unref(net_stat);
|
||||
if (net_stat != NULL)
|
||||
printk(TAG "Drain one spurious stat packet!\n");
|
||||
}
|
||||
}
|
||||
|
||||
void zperf_upload_decode_stat(struct net_buf *net_stat,
|
||||
zperf_results *results)
|
||||
{
|
||||
/* Decode stat */
|
||||
if (net_stat == NULL) {
|
||||
printk(TAG "ERROR! Failed to receive statistic\n");
|
||||
} else if (ip_buf_appdatalen(net_stat)
|
||||
< sizeof(zperf_server_hdr) + sizeof(zperf_udp_datagram)) {
|
||||
printk(TAG "ERROR! Statistic too small\n");
|
||||
} else {
|
||||
zperf_server_hdr *hdr;
|
||||
|
||||
hdr = (zperf_server_hdr *) (ip_buf_appdata(net_stat)
|
||||
+ sizeof(zperf_udp_datagram));
|
||||
|
||||
/* Fill the results struct */
|
||||
results->nb_packets_rcvd = z_ntohl(hdr->datagrams);
|
||||
results->nb_packets_lost = z_ntohl(hdr->error_cnt);
|
||||
results->nb_packets_outorder = z_ntohl(hdr->outorder_cnt);
|
||||
results->nb_bytes_sent = z_ntohl(hdr->total_len2);
|
||||
results->time_in_us = z_ntohl(
|
||||
hdr->stop_usec) + z_ntohl(hdr->stop_sec) * USEC_PER_SEC;
|
||||
results->jitter_in_us = z_ntohl(
|
||||
hdr->jitter2) + z_ntohl(hdr->jitter1) * USEC_PER_SEC;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue