blob: 6018d6f43e55c2471630931493f2d744542a0b0c [file] [log] [blame]
/*
* 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"
#include "zperf_session.h"
#if defined(CONFIG_NETWORKING_WITH_IPV6)
#include <contiki/ipv6/uip-ds6.h>
#endif
#define DEVICE_NAME "zperf shell"
static const char *CONFIG =
#ifdef CONFIG_WLAN
"wlan "
#endif
#ifdef CONFIG_ETH_DW
"ethernet "
#endif
#ifdef CONFIG_NETWORKING_WITH_IPV4
"ipv4 "
#endif
#ifdef CONFIG_NETWORKING_WITH_IPV6
"ipv6 "
#endif
"";
#ifdef CONFIG_NETWORKING_WITH_IPV6
#define MY_IPADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2 } } }
#define DST_IPADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1 } } }
#define MY_PREFIX_LEN 64
#endif
#ifdef PROFILER
#include "profiler.h"
#endif
static struct net_addr in_addr_my = {
#ifdef CONFIG_NETWORKING_WITH_IPV6
.family = AF_INET6,
.in6_addr = MY_IPADDR /* IN6ADDR_ANY_INIT */
#else
.family = AF_INET,
.in_addr = { { { 0 } } }
#endif
};
static struct net_addr in_addr_dst = {
#ifdef CONFIG_NETWORKING_WITH_IPV6
.family = AF_INET6,
.in6_addr = DST_IPADDR /* IN6ADDR_ANY_INIT */
#else
.family = AF_INET,
.in_addr = { { { 192, 168, 43, 181 } } }
#endif
};
#define MY_SRC_PORT 50000
#define DEF_PORT 5001
static int shell_cmd_setip(int argc, char *argv[])
{
uint32_t value[IP_INDEX_MAX] = { 0 };
#if defined(CONFIG_NETWORKING_WITH_IPV6)
int prefix;
if (argc != 3) {
/* Print usage */
printk("\n%s:\n", CMD_STR_SETIP);
printk("Usage:\t%s <my ip> <prefix len>\n", CMD_STR_SETIP);
printk("\nExample %s 2001:db8::2 64\n", CMD_STR_SETIP);
return -1;
}
if (parseIpString(argv[1], value) != 0) {
printk("[%s] ERROR! Unable to set IP\n", CMD_STR_SETIP);
return 0;
}
prefix = strtoul(argv[2], NULL, 10);
for (int i = 0; i < IP_INDEX_MAX; i++) {
in_addr_my.in6_addr.s6_addr[2*i] = (value[i] & 0xFF00) >> 8;
in_addr_my.in6_addr.s6_addr[2*i+1] = value[i] & 0xFF;
}
uip_ds6_addr_add((uip_ipaddr_t *)&in_addr_my.in6_addr, 0, ADDR_MANUAL);
uip_ds6_prefix_add((uip_ipaddr_t *)&in_addr_my.in6_addr, prefix, 0);
#else
if (argc != 2) {
/* Print usage */
printk("\n%s:\n", CMD_STR_SETIP);
printk("Usage:\t%s <my ip>\n", CMD_STR_SETIP);
printk("\nExample %s 10.237.164.178\n", CMD_STR_SETIP);
return -1;
}
if (parseIpString(argv[1], value) != 0) {
printk("[%s] ERROR! Unable to set IP\n", CMD_STR_SETIP);
return -1;
}
uip_ipaddr_t uip_ipaddr_local;
uip_ipaddr(&uip_ipaddr_local,
value[0],
value[1],
value[2],
value[3]);
uip_sethostaddr(&uip_ipaddr_local);
#endif
printk("[%s] Setting IP address ", CMD_STR_SETIP);
print_address(value);
printk("\n");
return 0;
}
static int shell_cmd_udp_download(int argc, char *argv[])
{
static bool udp_stopped = true;
int port;
if (argc == 1) {
/* Print usage */
printk("\n%s:\n", CMD_STR_UDP_DOWNLOAD);
printk("Usage:\t%s <port>\n", CMD_STR_UDP_DOWNLOAD);
printk("\nExample %s 5001\n", CMD_STR_UDP_DOWNLOAD);
return -1;
}
if (argc > 1) {
port = strtoul(argv[1], NULL, 10);
} else {
port = DEF_PORT;
}
if (udp_stopped == false) {
printk("[%s] ERROR! UDP server already started!\n",
CMD_STR_UDP_DOWNLOAD);
return -1;
}
zperf_receiver_init(port);
udp_stopped = false;
printk("[%s] UDP server started on port %u\n", CMD_STR_UDP_DOWNLOAD, port);
return 0;
}
static void shell_udp_upload_usage(void)
{
/* Print usage */
printk("\n%s:\n", CMD_STR_UDP_UPLOAD);
printk(
"Usage:\t%s <dest ip> <dest port> <duration> <packet "
"size>[K] <baud rate>[K|M]\n", CMD_STR_UDP_UPLOAD);
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 packet in byte or kilobyte "
"(with suffix K)\n");
printk("\t<baud rate>:\tBaudrate in kilobyte or megabyte\n");
printk("\nExample %s 10.237.164.178 1111 1 1K 1M\n", CMD_STR_UDP_UPLOAD);
}
static void shell_tcp_upload_usage(void)
{
/* Print usage */
printk("\n%s:\n", CMD_STR_TCP_UPLOAD);
printk(
"Usage:\t%s <dest ip> <dest port> <duration> <packet "
"size>[K]\n", CMD_STR_TCP_UPLOAD);
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 packet in byte or kilobyte "
"(with suffix K)\n");
printk("\nExample %s 10.237.164.178 1111 1 1K 1M\n", CMD_STR_TCP_UPLOAD);
}
static void shell_udp_upload_print_stats(zperf_results *results)
{
unsigned int rate_in_kbps, client_rate_in_kbps;
printk("[%s] Upload completed!\n", CMD_STR_UDP_UPLOAD);
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) results->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("[%s] LAST PACKET NOT RECEIVED!!!\n", CMD_STR_UDP_UPLOAD);
printk("[%s] statistic:\t\t\tserver\t(client)\n", CMD_STR_UDP_UPLOAD);
printk("[%s] duration:\t\t\t", CMD_STR_UDP_UPLOAD);
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("[%s] nb packets:\t\t%u\t(%u)\n", CMD_STR_UDP_UPLOAD,
results->nb_packets_rcvd,
results->nb_packets_sent);
printk("[%s] nb packets outorder:\t%u\n", CMD_STR_UDP_UPLOAD,
results->nb_packets_outorder);
printk("[%s] nb packets lost:\t\t%u\n", CMD_STR_UDP_UPLOAD,
results->nb_packets_lost);
printk("[%s] jitter:\t\t\t", CMD_STR_UDP_UPLOAD);
print_number(results->jitter_in_us, TIME_US, TIME_US_UNIT);
printk("\n");
printk("[%s] rate:\t\t\t", CMD_STR_UDP_UPLOAD);
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
printk("\t(");
print_number(client_rate_in_kbps, KBPS, KBPS_UNIT);
printk(")\n");
}
static void shell_tcp_upload_print_stats(zperf_results *results)
{
unsigned int client_rate_in_kbps;
printk("[%s] Upload completed!\n", CMD_STR_TCP_UPLOAD);
if (results->client_time_in_us != 0)
client_rate_in_kbps = (uint32_t) (((uint64_t) results->nb_packets_sent
* (uint64_t) results->packet_size * (uint64_t) 8
* (uint64_t) USEC_PER_SEC)
/ ((uint64_t) results->client_time_in_us * 1024));
else
client_rate_in_kbps = 0;
printk("[%s] duration:\t", CMD_STR_TCP_UPLOAD);
print_number(results->client_time_in_us, TIME_US, TIME_US_UNIT);
printk("\n");
printk("[%s] nb packets:\t%u\n", CMD_STR_UDP_UPLOAD,
results->nb_packets_sent);
printk("[%s] nb sending errors (retry or fail):\t%u\n", CMD_STR_UDP_UPLOAD,
results->nb_packets_errors);
printk("[%s] rate:\t", CMD_STR_UDP_UPLOAD);
print_number(client_rate_in_kbps, KBPS, KBPS_UNIT);
printk("\n");
}
static int shell_cmd_upload(int argc, char *argv[])
{
int value[IP_INDEX_MAX] = { 0 };
unsigned int duration_in_ms, packet_size, rate_in_kbps;
uint16_t port;
zperf_results results = { 0 };
struct net_context *net_context;
uint8_t is_udp = !strcmp(argv[0], CMD_STR_UDP_UPLOAD) ? 1 : 0;
if (argc == 1) {
is_udp ? shell_udp_upload_usage() : shell_tcp_upload_usage();
return -1;
}
if (argc > 1 && parseIpString(argv[1], value) == 0) {
printk("[%s] Remote IP address is ", argv[0]);
print_address(value);
printk("\n");
#ifdef CONFIG_NETWORKING_WITH_IPV6
for (int i = 0; i < IP_INDEX_MAX; i++) {
in_addr_dst.in6_addr.s6_addr[2*i] = (value[i] & 0xFF00) >> 8;
in_addr_dst.in6_addr.s6_addr[2*i+1] = value[i] & 0xFF;
}
#else
in_addr_dst.in_addr.s4_addr[0] = value[0];
in_addr_dst.in_addr.s4_addr[1] = value[1];
in_addr_dst.in_addr.s4_addr[2] = value[2];
in_addr_dst.in_addr.s4_addr[3] = value[3];
#endif
} else {
printk(
"[%s] ERROR! Please specify the IP address of the"
" remote server\n", argv[0]);
return -1;
}
if (argc > 2) {
port = strtoul(argv[2], NULL, 10);
printk("[%s] Remote port is %u\n", argv[0], port);
} else {
port = DEF_PORT;
}
net_context = net_context_get(is_udp ? IPPROTO_UDP : IPPROTO_TCP,
&in_addr_dst, port,
&in_addr_my, MY_SRC_PORT);
if (!net_context) {
printk("[%s] ERROR! Fail to retrieve a net context\n", argv[0]);
return -1;
}
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("[%s] duration:\t\t", argv[0]);
print_number(duration_in_ms * USEC_PER_MSEC, TIME_US, TIME_US_UNIT);
printk("\n");
printk("[%s] packet size:\t%u bytes\n", argv[0], packet_size);
printk("[%s] start...\n", argv[0]);
if (is_udp) {
printk("[%s] rate:\t\t", argv[0]);
print_number(rate_in_kbps, KBPS, KBPS_UNIT);
printk("\n");
zperf_udp_upload(net_context, duration_in_ms, packet_size, rate_in_kbps,
&results);
shell_udp_upload_print_stats(&results);
} else {
zperf_tcp_upload(net_context, duration_in_ms, packet_size, &results);
shell_tcp_upload_print_stats(&results);
}
/* release net context */
net_context_put(net_context);
return 0;
}
static int shell_cmd_connectap(int argc, char *argv[])
{
printk("[%s] Zephyr has not been built with Wi-Fi support.\n",
CMD_STR_CONNECTAP);
return 0;
}
static int shell_cmd_tcp_download(int argc, char *argv[])
{
static bool tcp_stopped = true;
int port;
if (argc == 1) {
/* Print usage */
printk("\n[%s]:\n", CMD_STR_TCP_DOWNLOAD);
printk("Usage:\t%s <port>\n", CMD_STR_TCP_DOWNLOAD);
printk("\nExample %s 5001\n", CMD_STR_TCP_DOWNLOAD);
return -1;
}
if (argc > 1) {
port = strtoul(argv[1], NULL, 10);
} else {
port = DEF_PORT;
}
if (tcp_stopped == false) {
printk("[%s] ERROR! TCP server already started!\n",
CMD_STR_TCP_DOWNLOAD);
return -1;
}
zperf_tcp_receiver_init(port);
tcp_stopped = false;
printk("[%s] TCP server started on port %u\n", CMD_STR_TCP_DOWNLOAD, port);
return 0;
}
static int shell_cmd_version(int argc, char *argv[])
{
printk("\nzperf [%s]: %s config: %s\n", CMD_STR_VERSION, VERSION, CONFIG);
return 0;
}
static void zperf_init(void)
{
zperf_session_init();
}
#define MY_SHELL_MODULE "zperf"
struct shell_cmd commands[] = {
{ CMD_STR_SETIP, shell_cmd_setip },
{ CMD_STR_CONNECTAP, shell_cmd_connectap },
{ CMD_STR_VERSION, shell_cmd_version },
{ CMD_STR_UDP_UPLOAD, shell_cmd_upload },
{ CMD_STR_UDP_DOWNLOAD, shell_cmd_udp_download },
#ifdef CONFIG_NETWORKING_WITH_TCP
{ CMD_STR_TCP_UPLOAD, shell_cmd_upload },
{ CMD_STR_TCP_DOWNLOAD, shell_cmd_tcp_download },
#endif
#ifdef PROFILER
PROF_CMD,
#endif
{ NULL, NULL } };
#ifdef CONFIG_MICROKERNEL
void mainloop(void)
{
#else
void main(void)
{
#endif
shell_cmd_version(0, NULL);
SHELL_REGISTER(MY_SHELL_MODULE, commands);
shell_register_default_module(MY_SHELL_MODULE);
net_init();
zperf_init();
#if PROFILER
while (1) {
task_sleep(5 * sys_clock_ticks_per_sec);
prof_flush();
}
#endif
}