From e27a26eb9d8d9cfa5ab8b0833737678299959eae Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 23 Jan 2018 12:26:14 +0200 Subject: [PATCH 01/40] Icetea support --- .gitignore | 3 + .mbedignore | 2 + .yotta_ignore | 0 Makefile | 5 + README.md | 105 + TEST_APPS/__init__.py | 0 TEST_APPS/device/exampleapp/main.cpp | 61 + TEST_APPS/device/socket_app/cmd_ifconfig.cpp | 126 ++ TEST_APPS/device/socket_app/cmd_ifconfig.h | 19 + TEST_APPS/device/socket_app/cmd_socket.cpp | 1058 ++++++++++ TEST_APPS/device/socket_app/cmd_socket.h | 10 + TEST_APPS/device/socket_app/main.cpp | 65 + TEST_APPS/device/socket_app/mbed_app.json | 22 + TEST_APPS/device/socket_app/strconv.c | 231 +++ TEST_APPS/device/socket_app/strconv.h | 87 + TEST_APPS/icetea_plugins/__init__.py | 0 TEST_APPS/icetea_plugins/ip_test_parsers.py | 146 ++ TEST_APPS/icetea_plugins/plugins_to_load.py | 8 + TEST_APPS/readme.md | 115 ++ TEST_APPS/testcases/__init__.py | 0 TEST_APPS/testcases/example/__init__.py | 0 TEST_APPS/testcases/example/test_cmdline.py | 50 + .../testcases/netsocket/SOCKET_BIND_PORT.py | 60 + .../testcases/netsocket/TCPSERVER_ACCEPT.py | 90 + .../TCPSOCKET_ECHOTEST_BURST_SHORT.py | 68 + TEST_APPS/testcases/netsocket/__init__.py | 0 .../testcases/netsocket/generate_test_data.py | 16 + TEST_APPS/testcases/netsocket/interface.py | 29 + features/frameworks/mbed-trace/mbed_lib.json | 10 +- mbed-client-cli/ns_cmdline.h | 450 ++++ module.json | 33 + source/CMakeLists.txt | 14 + source/ns_cmdline.c | 1829 +++++++++++++++++ source/ns_list_internal/ns_list.c | 23 + source/ns_list_internal/ns_list.h | 721 +++++++ source/ns_list_internal/ns_types.h | 373 ++++ target.json | 0 test/CMakeLists.txt | 27 + test/Test.cpp | 785 +++++++ test_suite.json | 27 + tools/build_api.py | 6 +- tools/resources/__init__.py | 1 + tools/run_icetea.py | 335 +++ tools/test.py | 54 +- tools/test/__init__.py | 0 tools/test/run_icetea/.gitignore | 1 + tools/test/run_icetea/TEST_DIR/__init__.py | 14 + tools/test/run_icetea/TEST_DIR/test_pass.py | 37 + tools/test/run_icetea/TEST_DIR/test_print.py | 37 + tools/test/run_icetea/TEST_DIR_HW/README.md | 1 + tools/test/run_icetea/TEST_DIR_HW/__init__.py | 14 + .../test/run_icetea/TEST_DIR_HW/test_K64F.py | 49 + .../TEST_DIR_HW/test_predefined_platforms.py | 58 + tools/test/run_icetea/__init__.py | 0 tools/test/run_icetea/empty_build_data.json | 3 + tools/test/run_icetea/run_icetea_test.py | 104 + tools/test/run_icetea/run_icetea_unittest.py | 146 ++ tools/test_api.py | 452 ++-- tools/toolchains/__init__.py | 2 +- tools/utils.py | 21 + 60 files changed, 7765 insertions(+), 238 deletions(-) create mode 100644 .mbedignore create mode 100644 .yotta_ignore create mode 100644 Makefile create mode 100644 TEST_APPS/__init__.py create mode 100644 TEST_APPS/device/exampleapp/main.cpp create mode 100644 TEST_APPS/device/socket_app/cmd_ifconfig.cpp create mode 100644 TEST_APPS/device/socket_app/cmd_ifconfig.h create mode 100644 TEST_APPS/device/socket_app/cmd_socket.cpp create mode 100644 TEST_APPS/device/socket_app/cmd_socket.h create mode 100644 TEST_APPS/device/socket_app/main.cpp create mode 100644 TEST_APPS/device/socket_app/mbed_app.json create mode 100644 TEST_APPS/device/socket_app/strconv.c create mode 100644 TEST_APPS/device/socket_app/strconv.h create mode 100644 TEST_APPS/icetea_plugins/__init__.py create mode 100644 TEST_APPS/icetea_plugins/ip_test_parsers.py create mode 100644 TEST_APPS/icetea_plugins/plugins_to_load.py create mode 100644 TEST_APPS/readme.md create mode 100644 TEST_APPS/testcases/__init__.py create mode 100644 TEST_APPS/testcases/example/__init__.py create mode 100644 TEST_APPS/testcases/example/test_cmdline.py create mode 100644 TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py create mode 100644 TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py create mode 100644 TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py create mode 100644 TEST_APPS/testcases/netsocket/__init__.py create mode 100644 TEST_APPS/testcases/netsocket/generate_test_data.py create mode 100644 TEST_APPS/testcases/netsocket/interface.py create mode 100644 mbed-client-cli/ns_cmdline.h create mode 100644 module.json create mode 100644 source/CMakeLists.txt create mode 100644 source/ns_cmdline.c create mode 100644 source/ns_list_internal/ns_list.c create mode 100644 source/ns_list_internal/ns_list.h create mode 100644 source/ns_list_internal/ns_types.h create mode 100644 target.json create mode 100644 test/CMakeLists.txt create mode 100644 test/Test.cpp create mode 100644 test_suite.json create mode 100644 tools/run_icetea.py create mode 100644 tools/test/__init__.py create mode 100644 tools/test/run_icetea/.gitignore create mode 100644 tools/test/run_icetea/TEST_DIR/__init__.py create mode 100644 tools/test/run_icetea/TEST_DIR/test_pass.py create mode 100644 tools/test/run_icetea/TEST_DIR/test_print.py create mode 100644 tools/test/run_icetea/TEST_DIR_HW/README.md create mode 100644 tools/test/run_icetea/TEST_DIR_HW/__init__.py create mode 100644 tools/test/run_icetea/TEST_DIR_HW/test_K64F.py create mode 100644 tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py create mode 100644 tools/test/run_icetea/__init__.py create mode 100644 tools/test/run_icetea/empty_build_data.json create mode 100644 tools/test/run_icetea/run_icetea_test.py create mode 100644 tools/test/run_icetea/run_icetea_unittest.py diff --git a/.gitignore b/.gitignore index 7b85cc34175..416944046df 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,6 @@ tags .vscode/ features/FEATURE_BLE/targets/TARGET_CORDIO/stack_backup/ + +.pytest_cache +log \ No newline at end of file diff --git a/.mbedignore b/.mbedignore new file mode 100644 index 00000000000..7bb56421f19 --- /dev/null +++ b/.mbedignore @@ -0,0 +1,2 @@ +test/* +source/ns_list_internal/* diff --git a/.yotta_ignore b/.yotta_ignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Makefile b/Makefile new file mode 100644 index 00000000000..856a59b4eff --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +SRCS := $(wildcard source/*.c) +LIB := libCmdline.a +EXPORT_HEADERS := mbed-client-cli + +include ../exported_rules.mk diff --git a/README.md b/README.md index 68b9acaaf0d..49f2458a77a 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,108 @@ We have a [developer website](https://os.mbed.com) for asking questions, engagin ## Getting started for contributors We also have a [contributing and publishing guide](https://os.mbed.com/contributing/) that covers licensing, contributor agreements and style guidelines. +======= +# mbed-client-cli + +This is the Command Line Library for a CLI application. This library provides methods for: + +* Adding commands to the interpreter. +* Deleting commands from the interpreter. +* Executing commands. +* Adding command aliases to the interpreter. +* Searching command arguments. + +## API + +Command Line Library API is described in the snipplet below: + +```c++ +// if thread safety for CLI terminal output is needed +// configure output mutex wait cb before initialization so it's available immediately +cmd_set_mutex_wait_func( (func)(void) ); +// configure output mutex release cb before initialization so it's available immediately +cmd_set_mutex_wait_func( (func)(void) ); +// initialize cmdline with print function +cmd_init( (func)(const char* fmt, va_list ap) ); +// configure ready cb +cmd_set_ready_cb( (func)(int retcode) ); +// register command for library +cmd_add( , (int func)(int argc, char *argv[]), , ); +//execute some existing commands +cmd_exe( ); +``` + +## Tracing + +Command Line Library has trace messages, which are disabled by default. +"MBED_CLIENT_CLI_TRACE_ENABLE" flag if defined, enables all the trace prints for debugging. + +## Usage example + +Adding new commands to the Command Line Library and executing the commands: + +```c++ +//example print function +void myprint(const char* fmt, va_list ap){ vprintf(fmt, ap); } + +// ready cb, calls next command to be executed +void cmd_ready_cb(int retcode) { cmd_next( retcode ); } + +// dummy command with some option +int cmd_dummy(int argc, char *argv[]){ + if( cmd_has_option(argc, argv, "o") ) { + cmd_printf("This is o option"); + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + return CMDLINE_RETCODE_SUCCESS; +} + +// timer cb (pseudo-timer-code) +void timer_ready_cb(void) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); +} + +// long command, that needs for example some events to complete the command execution +int cmd_long(int argc, char *argv[] ) { + timer_start( 5000, timer_ready_cb ); //pseudo timer code + return CMDLINE_RETCODE_EXCUTING_CONTINUE; +} +void main(void) { + cmd_init( &myprint ); // initialize cmdline with print function + cmd_set_ready_cb( cmd_ready_cb ); // configure ready cb + cmd_add("dummy", cmd_dummy, 0, 0); // add one dummy command + cmd_add("long", cmd_long, 0, 0); // add one dummy command + //execute dummy and long commands + cmd_exe( "dummy;long" ); +} +``` + +## Thread safety +The CLI library is not thread safe, but the CLI terminal output can be locked against other +output streams, for example if both traces and CLI output are using serial out. + +```c++ +static Mutex MyMutex; + +// mutex wait cb, acquires the mutex, waiting if necessary +static void mutex_wait(void) +{ + MyMutex.lock(); +} + +// mutex release cb, releases the mutex +static void my_mutex_release(void) +{ + MyMutex.unlock(); +} + +void main(void) { + cmd_mutex_wait_func( my_mutex_wait ); // configure mutex wait function before initializing + cmd_mutex_release_func( my_mutex_release ); // configure mutex release function before initializing + cmd_init( &myprint ); // initialize cmdline with print function + cmd_set_ready_cb( cmd_ready_cb ); // configure ready cb. + //CLI terminal output now locks against MyMutex +} + +``` diff --git a/TEST_APPS/__init__.py b/TEST_APPS/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/TEST_APPS/device/exampleapp/main.cpp b/TEST_APPS/device/exampleapp/main.cpp new file mode 100644 index 00000000000..c79b1a065fb --- /dev/null +++ b/TEST_APPS/device/exampleapp/main.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015-2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 +#include +#include "mbed.h" +#include "mbed-client-cli/ns_cmdline.h" + +/** + * Macros for setting console flow control. + */ +#define CONSOLE_FLOWCONTROL_RTS 1 +#define CONSOLE_FLOWCONTROL_CTS 2 +#define CONSOLE_FLOWCONTROL_RTSCTS 3 +#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x +#define mbed_console_concat(x) mbed_console_concat_(x) +#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL) + +void cmd_ready_cb(int retcode) +{ + cmd_next(retcode); +} + +void wrap_printf(const char *f, va_list a) { + vprintf(f, a); +} + +int main() +{ + cmd_init(&wrap_printf); + + int c; + while((c = getchar()) != EOF) { + cmd_char_input(c); + } + return 0; +} + +FileHandle* mbed::mbed_override_console(int) { + static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, 115200); +#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS + console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS + console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS + console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS); +#endif + return &console; +} diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp new file mode 100644 index 00000000000..d94a2580189 --- /dev/null +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp @@ -0,0 +1,126 @@ +#include "NetworkStack.h" +#include "NetworkInterface.h" + +#include "mbed-client-cli/ns_cmdline.h" +#include "mbed-trace/mbed_trace.h" + + #ifndef MBED_CONF_APP_CONNECT_STATEMENT + #error [NOT_SUPPORTED] No network configuration found for this target. + #endif + +#include +#include MBED_CONF_APP_HEADER_FILE + +#define TRACE_GROUP "Aifc" + +NetworkInterface* net; + +NetworkInterface* get_interface(void) +{ + return net; +} + +int cmd_ifup(int argc, char *argv[]); +int cmd_ifdown(int argc, char *argv[]); +int cmd_ifconfig(int argc, char *argv[]); + +const char* MAN_IFCONFIG = " ifup interface up\r\n"\ + " ifdown interface down\r\n"; + +static bool is_ipv4(const char *str) +{ + int dot_count = 0; + for (int i = 0; str[i]; i++) { + if (str[i] == '.' && ++dot_count == 3) { + return true; + } + } + return false; +} + +static void ifconfig_print() +{ + if(!net) + return; + const char *str = net->get_ip_address(); + + if (str) { + if (is_ipv4(str)) { + cmd_printf("IPv4 if addr: %s\r\n", str); + } else { + cmd_printf("IPv6 if addr:\r\n [0]: %s\r\n", str); + } + } else { + cmd_printf("No IP address\r\n"); + } + str = net->get_mac_address(); + if(str) { + cmd_printf("MAC-48: %s\r\n", str); + } else { + cmd_printf("MAC-48: unknown\r\n"); + } +} + + +void cmd_ifconfig_init(void) +{ + cmd_add("ifup", cmd_ifup, "ifconfig up", MAN_IFCONFIG); + cmd_add("ifdown", cmd_ifdown, "ifconfig down", MAN_IFCONFIG); + cmd_add("ifconfig", cmd_ifconfig, "ifconfig", MAN_IFCONFIG); +} + +int cmd_ifconfig(int argc, char *argv[]) +{ + ifconfig_print(); + return CMDLINE_RETCODE_SUCCESS; +} + +int cmd_ifup(int argc, char *argv[]) +{ + if(!net) + net = MBED_CONF_APP_OBJECT_CONSTRUCTION; + int err = MBED_CONF_APP_CONNECT_STATEMENT; + if(err != NSAPI_ERROR_OK) + return CMDLINE_RETCODE_FAIL; + + ifconfig_print(); + return CMDLINE_RETCODE_SUCCESS; +} + +int cmd_ifdown(int argc, char *argv[]) +{ + if(!net) + return CMDLINE_RETCODE_FAIL; + int err = net->disconnect(); + if(err != NSAPI_ERROR_OK) + return CMDLINE_RETCODE_FAIL; + + return CMDLINE_RETCODE_SUCCESS; +} + + + +const char* networkstack_error_to_str(int errorcode) +{ + switch(errorcode) { + case NSAPI_ERROR_OK: return "NSAPI_ERROR_OK"; + case NSAPI_ERROR_WOULD_BLOCK: return "NSAPI_ERROR_WOULD_BLOCK"; + case NSAPI_ERROR_UNSUPPORTED: return "NSAPI_ERROR_UNSUPPORTED"; + case NSAPI_ERROR_PARAMETER: return "NSAPI_ERROR_PARAMETER"; + case NSAPI_ERROR_NO_CONNECTION: return "NSAPI_ERROR_NO_CONNECTION"; + case NSAPI_ERROR_NO_SOCKET: return "NSAPI_ERROR_NO_SOCKET"; + case NSAPI_ERROR_NO_ADDRESS: return "NSAPI_ERROR_NO_ADDRESS"; + case NSAPI_ERROR_NO_MEMORY: return "NSAPI_ERROR_NO_MEMORY"; + case NSAPI_ERROR_NO_SSID: return "NSAPI_ERROR_NO_SSID"; + case NSAPI_ERROR_DNS_FAILURE: return "NSAPI_ERROR_DNS_FAILURE"; + case NSAPI_ERROR_DHCP_FAILURE: return "NSAPI_ERROR_DHCP_FAILURE"; + case NSAPI_ERROR_AUTH_FAILURE: return "NSAPI_ERROR_AUTH_FAILURE"; + case NSAPI_ERROR_DEVICE_ERROR: return "NSAPI_ERROR_DEVICE_ERROR"; + case NSAPI_ERROR_IN_PROGRESS: return "NSAPI_ERROR_IN_PROGRESS"; + case NSAPI_ERROR_ALREADY: return "NSAPI_ERROR_ALREADY"; + case NSAPI_ERROR_IS_CONNECTED: return "NSAPI_ERROR_IS_CONNECTED"; + case NSAPI_ERROR_CONNECTION_LOST: return "NSAPI_ERROR_CONNECTION_LOST"; + case NSAPI_ERROR_CONNECTION_TIMEOUT: return "NSAPI_ERROR_CONNECTION_TIMEOUT"; + default: return "unknown error code"; + } +} diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.h b/TEST_APPS/device/socket_app/cmd_ifconfig.h new file mode 100644 index 00000000000..bb93c05e0a2 --- /dev/null +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.h @@ -0,0 +1,19 @@ +#ifndef CMD_IFCONFIG_H +#define CMD_IFCONFIG_H + +#include "NetworkInterface.h" +#include "NetworkStack.h" + +/** Get a pointer to a network interface instance + * + * Allowed interface types (depend on application configurations): + * cell0, wlan0, eth0, mesh0 + * + * @return pointer to the network interface, or NULL if unrecognized or ambiguous + */ +NetworkInterface* get_interface(void); + +void cmd_ifconfig_init(void); +const char* networkstack_error_to_str(int errorcode); + +#endif diff --git a/TEST_APPS/device/socket_app/cmd_socket.cpp b/TEST_APPS/device/socket_app/cmd_socket.cpp new file mode 100644 index 00000000000..8ff6d2eeb06 --- /dev/null +++ b/TEST_APPS/device/socket_app/cmd_socket.cpp @@ -0,0 +1,1058 @@ +#include "mbed.h" +#include "NetworkStack.h" +#include "UDPSocket.h" +#include "TCPSocket.h" +#include "TCPServer.h" +#include "NetworkInterface.h" +#include "SocketAddress.h" +#include "Queue.h" + +#include +#include +#include +#include +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include + +#include "mbed-client-cli/ns_cmdline.h" +#include "mbed-trace/mbed_trace.h" +#include "strconv.h" + +#define TRACE_GROUP "Asck" + +#include "cmd_ifconfig.h" +#include "cmd_socket.h" + +#define SIGNAL_SIGIO 0x1 +#define PACKET_SIZE_ARRAY_LEN 5 + +#define MAN_SOCKET "\r\nSOCKET API\r\n"\ + "\r\n"\ + "socket [options]\r\n\r\n"\ + " new \r\n" \ + " type: UDPSocket|TCPSocket|TCPServer\r\n"\ + " return socket id\r\n"\ + " delete\r\n"\ + " remote the space allocated for Socket\r\n"\ + " open\r\n"\ + " close\r\n"\ + " bind [port] [addr ]\r\n"\ + " set_blocking \r\n"\ + " set_timeout \r\n"\ + " register_sigio_cb\r\n"\ + " set_RFC_864_pattern_check \r\n"\ + "\r\nFor UDPSocket\r\n"\ + " sendto (\"msg\" | --data_len )\r\n"\ + " \"msg\" Send packet with defined string content\r\n"\ + " --data_len Send packet with random content with size \r\n"\ + " recvfrom \r\n"\ + " start_udp_receiver_thread --max_data_len [--packets ]\r\n"\ + " --max_data_len Size of input buffer to fill up\r\n"\ + " --packets Receive N number of packets, default 1\r\n"\ + "\r\nFor TCPSocket\r\n"\ + " connect \r\n"\ + " send (\"msg\" | --data_len )\r\n"\ + " recv \r\n"\ + " start_tcp_receiver_thread --data_len [--max_recv_len ] [--repeat ]\r\n"\ + " --data_len Size of input buffer to fill up\r\n"\ + " --max_recv_len Read maximum of L bytes in at a time. Default full buffer \r\n"\ + " --repeat Repeat buffer filling N times, default 1\r\n"\ + " join_tcp_receiver_thread\r\n"\ + " start_bg_traffic_thread\r\n"\ + " join_bg_traffic_thread\r\n"\ + " setsockopt_keepalive \r\n"\ + " getsockopt_keepalive\r\n"\ + "\r\nFor TCPServer\r\n"\ + " listen [backlog]\r\n"\ + " accept \r\n"\ + " accept new connection into socket. Requires to be pre-allocated.\r\n"\ + "\r\nOther options\r\n"\ + " print-mode [--string|--hex|--disabled] [--col-width ]" + +class SInfo; +static Queue event_queue; +static int id_count=0; + +class SInfo +{ + public: + enum SocketType { + TCP_CLIENT, + TCP_SERVER, + UDP + }; + SInfo(TCPSocket* sock): + _id(id_count++), + _sock(sock), + _type(SInfo::TCP_CLIENT), + _blocking(true), + _dataLen(0), + _maxRecvLen(0), + _repeatBufferFill(1), + _receivedTotal(0), + _receiverThread(NULL), + _receiveBuffer(NULL), + _senderThreadId(NULL), + _receiverThreadId(NULL), + _packetSizes(NULL), + _check_pattern(false) + { + assert(sock); + } + SInfo(TCPServer* sock): + _id(id_count++), + _sock(sock), + _type(SInfo::TCP_SERVER), + _blocking(true), + _dataLen(0), + _maxRecvLen(0), + _repeatBufferFill(1), + _receivedTotal(0), + _receiverThread(NULL), + _receiveBuffer(NULL), + _senderThreadId(NULL), + _receiverThreadId(NULL), + _packetSizes(NULL), + _check_pattern(false) + { + assert(sock); + } + SInfo(UDPSocket* sock): + _id(id_count++), + _sock(sock), + _type(SInfo::UDP), + _blocking(true), + _dataLen(0), + _maxRecvLen(0), + _repeatBufferFill(1), + _receivedTotal(0), + _receiverThread(NULL), + _receiveBuffer(NULL), + _senderThreadId(NULL), + _receiverThreadId(NULL), + _packetSizes(NULL), + _check_pattern(false) + { + assert(sock); + } + ~SInfo() { + this->_sock->sigio(Callback()); + if (this->_receiverThread) { + this->_receiverThread->terminate(); + delete this->_receiverThread; + } + if (this->_receiveBuffer) { + delete this->_receiveBuffer; + } + delete this->_sock; + } + int id() const { return this->_id; } + Socket& socket() { return *(this->_sock); } + Socket& socket() const { return *(this->_sock); } + TCPSocket *tcp_socket() { return this->_type == SInfo::TCP_CLIENT ? static_cast(this->_sock) : NULL; } + TCPServer *tcp_server() { return this->_type == SInfo::TCP_SERVER ? static_cast(this->_sock) : NULL; } + UDPSocket *udp_socket() { return this->_type == SInfo::UDP ? static_cast(this->_sock) : NULL; } + SInfo::SocketType type() const { return this->_type; } + void setDataCount(int dataCount) { this->_dataLen = dataCount; } + int getDataCount() { return this->_dataLen; } + void setReceiverThread(Thread *receiverThread) { this->_receiverThread = receiverThread; } + Thread *getReceiverThread() { return this->_receiverThread; } + void setReceiveBuffer(uint8_t *receiveBuffer) { this->_receiveBuffer = receiveBuffer; } + uint8_t *getReceiveBuffer() { return this->_receiveBuffer; } + void setMaxRecvLen(int recvLen) { this->_maxRecvLen = recvLen; } + int getMaxRecvLen() { return this->_maxRecvLen; } + void setRepeatBufferFill(int n) { this->_repeatBufferFill = n; } + int getRepeatBufferFill() { return this->_repeatBufferFill; } + void setRecvTotal(int n) { this->_receivedTotal = n; } + int getRecvTotal() { return this->_receivedTotal; } + void setSenderThreadId(osThreadId threadID) { this->_senderThreadId = threadID; } + void setReceiverThreadId(osThreadId threadID) { this->_receiverThreadId = threadID; } + osThreadId getSenderThreadId() { return this->_senderThreadId; } + osThreadId getReceiverThreadId() { return this->_receiverThreadId; } + void setPacketSizeArray(int *ptr) { this->_packetSizes = ptr; } + int *getPacketSizeArray() { return this->_packetSizes; } + void setUnavailable() { this->_available = false;} + void setAvailable() {this->_available = true;} + bool available() { return this->_available; } + void set_pattern_check(bool enabled) { _check_pattern = enabled; }; + bool check_pattern(void *buffer, size_t len); + + const char *type_str() const + { + const char *str; + switch (this->_type) { + case SInfo::TCP_CLIENT: + str = "TCPSocket"; + break; + case SInfo::TCP_SERVER: + str = "TCPServer"; + break; + case SInfo::UDP: + str = "UDPSocket"; + break; + default: + assert(0); + break; + } + return str; + } + bool blocking() const { return this->_blocking; } + void set_blocking(bool blocking) { socket().set_blocking(blocking); this->_blocking = blocking; } + bool can_connect() { return (this->type() == SInfo::TCP_CLIENT); } + bool can_bind() { return (this->type() == SInfo::UDP || this->type() == SInfo::TCP_SERVER); } + bool can_send() { return (this->type() == SInfo::TCP_CLIENT); } + bool can_recv() { return (this->type() == SInfo::TCP_CLIENT); } + bool can_sendto() { return (this->type() == SInfo::UDP); } + bool can_recvfrom() { return (this->type() == SInfo::UDP); } + bool can_listen() { return (this->type() == SInfo::TCP_SERVER); } + bool can_accept() { return (this->type() == SInfo::TCP_SERVER); } + private: + const int _id; + Socket* _sock; + const SInfo::SocketType _type; + bool _blocking; + int _dataLen; + int _maxRecvLen; + int _repeatBufferFill; + int _receivedTotal; + Thread *_receiverThread; + uint8_t *_receiveBuffer; + osThreadId _senderThreadId; + osThreadId _receiverThreadId; + int *_packetSizes; + bool _available; + bool _check_pattern; + + SInfo(); +}; + +static std::vector m_sockets; + +static enum { + PRINT_DISABLED, + PRINT_STRING, + PRINT_HEX +} printing_mode = PRINT_STRING; +static int printing_col_width = 20; + +static int cmd_socket(int argc, char *argv[]); +static void print_data(const uint8_t *buf, int len); +static void print_data_as_string(const uint8_t *buf, int len, int col_width); +static void print_data_as_hex(const uint8_t *buf, int len, int col_width); + +/** Generate RFC 864 example pattern. + * + * Pattern is 72 chraracter lines of the ASCII printing characters ending with "\r\n". + * There are 95 printing characters in the ASCII character set. + * Example: `nc echo.mbedcloudtesting.com 19 | dd bs=1 count=222` + * !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg + * !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh + * "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi + * + * NOTE: Pattern starts with space, not ! + * + * \param offset Start pattern from offset + * \param len Length of pattern to generate. + */ +static void generate_RFC_864_pattern(size_t offset, uint8_t *buf, size_t len) +{ + while (len--) { + if (offset % 74 == 72) + *buf++ = '\r'; + else if (offset % 74 == 73) + *buf++ = '\n'; + else + *buf++ = ' ' + (offset%74 + offset/74) % 95 ; + offset++; + } +} + +bool SInfo::check_pattern(void *buffer, size_t len) +{ + if (!_check_pattern) { + return true; + } + void *buf = malloc(len); + if (!buf) + return false; + size_t offset = _receivedTotal; + generate_RFC_864_pattern(offset, (uint8_t*)buf, len); + bool match = memcmp(buf, buffer, len) == 0; + if (!match) { + cmd_printf("Pattern check failed\r\nWAS:%.*s\r\nREF:%.*s\r\n", len, (char*)buffer, len, (char*)buf); + } + free(buf); + return match; +} + +static void sigio_handler(SInfo *info) { + if (info->getReceiverThreadId()) { + osSignalSet(info->getReceiverThreadId(), SIGNAL_SIGIO); + } + if (info->getSenderThreadId()) { + osSignalSet(info->getSenderThreadId(), SIGNAL_SIGIO); + } +} + +void cmd_socket_init(void) +{ + cmd_add("socket", cmd_socket, "socket", MAN_SOCKET); + cmd_alias_add("socket help", "socket -h"); + cmd_alias_add("socket --help", "socket -h"); + cmd_alias_add("ping server start", "socket echo-server new start"); +} + +int handle_nsapi_error(const char *function, nsapi_error_t ret) +{ + if (ret != NSAPI_ERROR_OK) { + cmd_printf("%s returned: %d -> %s\r\n", function, ret, networkstack_error_to_str(ret)); + return CMDLINE_RETCODE_FAIL; + } + return CMDLINE_RETCODE_SUCCESS; +} + +int handle_nsapi_size_or_error(const char *function, nsapi_size_or_error_t ret) +{ + if (ret < 0) { + return handle_nsapi_error(function, ret); + } else { + cmd_printf("%s returned: %d\r\n", function, ret); + } + return CMDLINE_RETCODE_SUCCESS; +} + +static SInfo *get_sinfo(int id) { + + for (std::vector::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) { + if( (*it)->id() == id) { + return *it; + } + } + return NULL; +} + +static int del_sinfo(SInfo *info) { + for (std::vector::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) { + if( (*it) == info) { + delete info; + m_sockets.erase(it); + return CMDLINE_RETCODE_SUCCESS; + } + } + return CMDLINE_RETCODE_FAIL; +} + +static int cmd_socket_new(int argc, char *argv[]) +{ + const char *s; + SInfo *info; + + if (cmd_parameter_last(argc, argv)) { + s = cmd_parameter_last(argc, argv); + if (strcmp(s, "UDPSocket") == 0) { + tr_debug("Creating a new UDPSocket"); + info = new SInfo(new UDPSocket); + } else if (strcmp(s, "TCPSocket") == 0) { + tr_debug("Creating a new TCPSocket"); + info = new SInfo(new TCPSocket); + } else if (strcmp(s, "TCPServer") == 0) { + tr_debug("Creating a new TCPServer"); + info = new SInfo(new TCPServer); + } else { + cmd_printf("unsupported protocol: %s\r\n", s); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + cmd_printf("Must specify socket type\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + // Note, this cannot fail. We either succeed or "new" woud call exit(1) in failure. + // We did not ask no_throw version of it. + cmd_printf("new socket. sid: %d\r\n", info->id()); + m_sockets.push_back(info); + return CMDLINE_RETCODE_SUCCESS; +} + +static void udp_receiver_thread(SInfo *info) +{ + SocketAddress addr; + int i = 0, received = 0; + int n = info->getRepeatBufferFill(); + int *packetSizes = info->getPacketSizeArray(); + nsapi_size_or_error_t ret = 0; + + info->setReceiverThreadId(Thread::gettid()); + + while (i(info->socket()).recvfrom(&addr, info->getReceiveBuffer() + received, info->getDataCount() - received); + if (ret > 0) { + if (!info->check_pattern(info->getReceiveBuffer() + received, ret)) { + return; + } + received += ret; + packetSizes[i%PACKET_SIZE_ARRAY_LEN] = ret; + i++; + info->setRecvTotal(info->getRecvTotal() + ret); + } else if (ret == NSAPI_ERROR_WOULD_BLOCK) { + Thread::signal_wait(SIGNAL_SIGIO); + } else { + handle_nsapi_size_or_error("Thread: UDPSocket::recvfrom()", ret); + return; + } + } +} + +static nsapi_size_or_error_t start_udp_receiver_thread(SInfo *info, int argc, char *argv[]) +{ + int32_t max_size; + int32_t n = 1; + + if (!cmd_parameter_int(argc, argv, "--max_data_len", &max_size)) { + cmd_printf("Need data max data size\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + if (cmd_parameter_index(argc, argv, "--packets") > 0) { + if (!cmd_parameter_int(argc, argv, "--packets", &n)) { + cmd_printf("Need number of packets\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + uint8_t *dataIn = (uint8_t *)malloc(max_size+1); + if (!dataIn) { + cmd_printf("malloc() failed\r\n"); + return CMDLINE_RETCODE_FAIL; + } + int *packetSizes = new (nothrow) int[PACKET_SIZE_ARRAY_LEN]; + if (!packetSizes) { + cmd_printf("Allocation failed\r\n"); + return CMDLINE_RETCODE_FAIL; + } + for (int i=0; isetReceiveBuffer(dataIn); + info->setDataCount(max_size); + info->setRepeatBufferFill(n); + info->setPacketSizeArray(packetSizes); + info->setReceiverThread(new Thread()); + info->getReceiverThread()->start(callback(udp_receiver_thread, info)); + return CMDLINE_RETCODE_SUCCESS; +} + +static nsapi_size_or_error_t udp_sendto_command_handler(SInfo *info, int argc, char *argv[]) +{ + char *host; + int32_t port; + + if (!cmd_parameter_val(argc, argv, "sendto", &host)) { + cmd_printf("Need host name\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + if (!cmd_parameter_int(argc, argv, host, &port)) { + cmd_printf("Need port number\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + // Replace NULL-strings with NULL + host = strcmp(host, "NULL") ? host : NULL; + + int32_t len; + void *data; + if (cmd_parameter_int(argc, argv, "--data_len", &len)) { + data = malloc(len); + if (!data) { + cmd_printf("Failed to allocate memory\r\n"); + return CMDLINE_RETCODE_FAIL; + } + } else { + // Replace NULL-strings with NULL + if (strcmp(argv[5], "NULL") == 0) { + data = NULL; + len = 0; + } + else { + data = argv[5]; + len = strlen(argv[5]); + } + } + + nsapi_size_or_error_t ret = static_cast(info->socket()).sendto(host, port, data, len); + if (ret > 0) { + cmd_printf("sent: %d bytes\r\n", ret); + } + if (data != argv[5]) { + free(data); + } + + return handle_nsapi_size_or_error("UDPSocket::sendto()", ret); +} + +static nsapi_size_or_error_t udp_recvfrom_command_handler(SInfo *info, int argc, char *argv[]) +{ + SocketAddress addr; + int32_t len; + + if (!cmd_parameter_int(argc, argv, "recvfrom", &len)) { + cmd_printf("Need len\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + + void *data = malloc(len); + if (!data) { + cmd_printf("malloc() failed\r\n"); + return CMDLINE_RETCODE_FAIL; + } + nsapi_size_or_error_t ret = static_cast(info->socket()).recvfrom(&addr, data, len); + if (ret > 0) { + cmd_printf("UDPSocket::recvfrom, addr=%s port=%d\r\n", addr.get_ip_address(), addr.get_port()); + cmd_printf("received: %d bytes\r\n", ret); + print_data((const uint8_t *)data, len); + if(!info->check_pattern(data, len)) + ret=-1; + info->setRecvTotal(info->getRecvTotal() + ret); + } + free(data); + return handle_nsapi_size_or_error("UDPSocket::recvfrom()", ret); +} + +static void tcp_receiver_thread(SInfo *info) +{ + int i, received; + int n = info->getRepeatBufferFill(); + int recv_len = info->getMaxRecvLen(); + int bufferSize = info->getDataCount(); + nsapi_size_or_error_t ret = 0; + + info->setReceiverThreadId(Thread::gettid()); + + for (i=0; i(info->socket()).recv(info->getReceiveBuffer() + received, recv_len - received); + if (ret > 0) { + if(!info->check_pattern(info->getReceiveBuffer() + received, ret)) + return; + received += ret; + info->setRecvTotal(info->getRecvTotal() + ret); + } else if (ret == NSAPI_ERROR_WOULD_BLOCK){ + Thread::signal_wait(SIGNAL_SIGIO); + } else { + handle_nsapi_size_or_error("Thread: TCPSocket::recv()", ret); + return; + } + } + } +} + +static nsapi_size_or_error_t start_tcp_receiver_thread(SInfo *info, int argc, char *argv[]) +{ + int32_t len; + int32_t recv_len; + int32_t n = 1; + + if (!cmd_parameter_int(argc, argv, "--data_len", &len)) { + cmd_printf("Need data len\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + recv_len = len; + + if (cmd_parameter_index(argc, argv, "--max_recv_len") > 0) { + if (!cmd_parameter_int(argc, argv, "--max_recv_len", &recv_len)) { + cmd_printf("Need max recv len\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + + if (cmd_parameter_index(argc, argv, "--repeat") > 0) { + if (!cmd_parameter_int(argc, argv, "--repeat", &n)) { + cmd_printf("Need repeat number\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + + uint8_t *dataIn = (uint8_t *)malloc(len+1); + if (!dataIn) { + cmd_printf("malloc() failed\r\n"); + return CMDLINE_RETCODE_FAIL; + } + + info->setReceiveBuffer(dataIn); + info->setDataCount(len); + info->setMaxRecvLen(recv_len); + info->setRepeatBufferFill(n); + info->setReceiverThread(new Thread()); + info->getReceiverThread()->start(callback(tcp_receiver_thread, info)); + return CMDLINE_RETCODE_SUCCESS; +} + +static nsapi_size_or_error_t tcp_send_command_handler(SInfo *info, int argc, char *argv[]) +{ + int32_t len; + void *data; + nsapi_size_or_error_t ret = 0; + + info->setSenderThreadId(Thread::gettid()); + + if (cmd_parameter_int(argc, argv, "--data_len", &len)) { + data = malloc(len); + if (!data) { + cmd_printf("Failed to allocate memory\r\n"); + return CMDLINE_RETCODE_FAIL; + } + } else { + data = argv[3]; + len = strlen(argv[3]); + } + + ret = static_cast(info->socket()).send(data, len); + + if (ret > 0) { + cmd_printf("sent: %d bytes\r\n", ret); + } + if (data != argv[3]) { + free(data); + } + return handle_nsapi_size_or_error("TCPSocket::send()", ret); +} + +static nsapi_size_or_error_t tcp_recv_command_handler(SInfo *info, int argc, char *argv[]) +{ + int32_t len; + + if (!cmd_parameter_int(argc, argv, "recv", &len)) { + cmd_printf("Need length\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + + void *data = malloc(len); + if (!data) { + cmd_printf("malloc() failed\r\n"); + return CMDLINE_RETCODE_FAIL; + } + + nsapi_size_or_error_t ret = static_cast(info->socket()).recv(data, len); + if (ret > 0) { + cmd_printf("received: %d bytes\r\n", ret); + print_data((const uint8_t *)data, ret); + if(!info->check_pattern(data, ret)) + ret=-1; + info->setRecvTotal(info->getRecvTotal() + ret); + } + free(data); + return handle_nsapi_size_or_error("TCPSocket::recv()", ret); +} + +static nsapi_size_or_error_t recv_all(char *const rbuffer, const int expt_len, SInfo *const info) +{ + int rtotal, rbytes; + char *rhead; + + rtotal = 0; + rhead = rbuffer; + + while (rtotal < expt_len) { + rbytes = info->tcp_socket()->recv(rhead, expt_len); + if (rbytes <= 0) { // Connection closed abruptly + rbuffer[rtotal] = '\0'; + return rbytes; + } + rtotal += rbytes; + rhead = rbuffer + rtotal; + } + rbuffer[rtotal] = '\0'; + info->setRecvTotal(info->getRecvTotal() + rtotal); + return rtotal; +} + +static void bg_traffic_thread(SInfo *info) +{ + static const int data_len = 10; + char sbuffer[data_len+1] = "dummydata_"; + char rbuffer[data_len+1]; + int scount, rtotal = 0; + info->setSenderThreadId(Thread::gettid()); + + for(;;) { + if (!info->available()) { + (void)handle_nsapi_size_or_error(__func__, rtotal); + break; + } + sbuffer[data_len-1] = 'A' + (rand() % 26); + scount = info->tcp_socket()->send(sbuffer, data_len); + rtotal = recv_all(rbuffer, data_len, info); + + if(scount != rtotal || (strcmp(sbuffer, rbuffer) != 0)) { + info->setUnavailable(); + + tr_err("Background received data does not match to sent data"); + tr_err("Background sent: \"%s\"", sbuffer); + tr_err("Background received: \"%s\"", rbuffer); + } + wait_ms(10); + } +} + +static nsapi_size_or_error_t start_bg_traffic_thread(SInfo *info, int argc, char *argv[]) +{ + info->setReceiverThread(new Thread()); + info->setAvailable(); + info->getReceiverThread()->start(callback(bg_traffic_thread, info)); + return CMDLINE_RETCODE_SUCCESS; +} + +static void thread_clean_up(SInfo *info) +{ + if (info->getReceiverThread()) { + delete info->getReceiverThread(); + info->setReceiverThread(NULL); + } + if (info->getReceiveBuffer()) { + delete info->getReceiveBuffer(); + info->setReceiveBuffer(NULL); + } + if (info->getPacketSizeArray()) { + delete[] info->getPacketSizeArray(); + info->setPacketSizeArray(NULL); + } +} + +static int cmd_socket(int argc, char *argv[]) +{ + if(cmd_parameter_index(argc, argv, "new") == 1) { + return cmd_socket_new(argc, argv); + } else if (cmd_parameter_index(argc, argv, "print-mode") > 0) { + if (cmd_parameter_index(argc, argv, "--string") > 0) { + printing_mode = PRINT_STRING; + } else if (cmd_parameter_index(argc, argv, "--hex") > 0) { + printing_mode = PRINT_HEX; + } else if (cmd_parameter_index(argc, argv, "--disabled") > 0) { + printing_mode = PRINT_DISABLED; + } + int32_t parsed_col_width = 0; + if (cmd_parameter_int(argc, argv, "--col-width", &parsed_col_width)) { + if (parsed_col_width <= 0) { + cmd_printf("Printing column width must be > 0"); + return CMDLINE_RETCODE_FAIL; + } + if (printing_mode == PRINT_HEX && parsed_col_width > 42) { + cmd_printf("Maximum column width for hex data is 42 bytes"); + return CMDLINE_RETCODE_FAIL; + } + printing_col_width = (int)parsed_col_width; + } + // Allow print-mode to be used as a parameter to other commands + if (cmd_parameter_index(argc, argv, "print-mode") == 1) + return CMDLINE_RETCODE_SUCCESS; + } + + // Rest of the commands require Socket + SInfo *info = get_sinfo(strtol(argv[1], NULL, 10)); + if (!info) { + cmd_printf("Invalid socket id %s\r\n", argv[1]); + return CMDLINE_RETCODE_FAIL; + } + + bool enable_pattern_check; + if(cmd_parameter_bool(argc, argv, "set_RFC_864_pattern_check", &enable_pattern_check)) { + info->set_pattern_check(enable_pattern_check); + return CMDLINE_RETCODE_SUCCESS; + } + + // Helper macro for checking the which command was given + #define COMMAND_IS(cmd) (cmd_parameter_index(argc, argv, cmd) == 2) + + /* + * Generic Socket commands: + * delete, open, close, bind, set_blocking, set_timeout + */ + + if (COMMAND_IS("delete")) { + return del_sinfo(info); + + } else if (COMMAND_IS("open")) { + NetworkInterface *interface; + + interface = get_interface(); // get default interface + + if (!interface) { + cmd_printf("Invalid interface\r\n"); + return CMDLINE_RETCODE_FAIL; + } + + return handle_nsapi_error("Socket::open()", info->socket().open(interface)); + + } else if (COMMAND_IS("close")) { + return handle_nsapi_error("Socket::close()", info->socket().close()); + + } else if (COMMAND_IS("bind")) { + int32_t port = 0; + char *addr; + + if (!cmd_parameter_int(argc, argv, "port", &port) && !cmd_parameter_int(argc, argv, "bind", &port) && port <= 0 && port > 65535) { + printf("Missing or invalid port number\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + + if (cmd_parameter_val(argc, argv, "addr", &addr)) { + // Replace NULL-strings with NULL + addr = strcmp(addr, "NULL") ? addr : NULL; + cmd_printf("Socket::bind(%s, %" PRId32 ")\r\n", addr, port); + return handle_nsapi_error("Socket::bind(addr, port)", info->socket().bind(addr, port)); + } else { + cmd_printf("Socket::bind(%" PRId32 ")\r\n", port); + return handle_nsapi_error("Socket::bind(port)", info->socket().bind(port)); + } + + } else if (COMMAND_IS("set_blocking")) { + bool val; + if (!cmd_parameter_bool(argc, argv, "set_blocking", &val)) { + cmd_printf("Need boolean value"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + info->set_blocking(val); + return CMDLINE_RETCODE_SUCCESS; + + } else if (COMMAND_IS("set_timeout")) { + int32_t ms; + if (!cmd_parameter_int(argc, argv, "set_timeout", &ms)) { + cmd_printf("Need timeout value"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + if (ms == -1) + info->set_blocking(true); + else + info->set_blocking(false); + + info->socket().set_timeout(ms); + return CMDLINE_RETCODE_SUCCESS; + + } else if (COMMAND_IS("register_sigio_cb")) { + info->socket().sigio(callback(sigio_handler, info)); + return CMDLINE_RETCODE_SUCCESS; + } + + + /* + * Commands related to UDPSocket: + * sendto, recvfrom + */ + if ((COMMAND_IS("sendto") || COMMAND_IS("recvfrom") || COMMAND_IS("start_udp_receiver_thread") + || COMMAND_IS("last_data_received")) && info->type() != SInfo::UDP) { + cmd_printf("Not UDPSocket\r\n"); + return CMDLINE_RETCODE_FAIL; + } + + if (COMMAND_IS("sendto")) { + return udp_sendto_command_handler(info, argc, argv); + } else if (COMMAND_IS("recvfrom")) { + return udp_recvfrom_command_handler(info, argc, argv); + } else if (COMMAND_IS("start_udp_receiver_thread")) { + return start_udp_receiver_thread(info, argc, argv); + } else if (COMMAND_IS("last_data_received")) { + print_data((const uint8_t *)info->getReceiveBuffer(), info->getDataCount()); + if (info->getPacketSizeArray()) { + int *packetSizes = info->getPacketSizeArray(); + cmd_printf("packet_sizes: "); + for (int i=0; igetReceiverThread()) { + info->getReceiverThread()->terminate(); + } + thread_clean_up(info); + + return handle_nsapi_error("UDPSocket::last_data_received()", NSAPI_ERROR_OK); + } + + /* + * Commands related to TCPSocket + * connect, send, recv + */ + if ((COMMAND_IS("connect") || COMMAND_IS("recv") + || COMMAND_IS("start_tcp_receiver_thread") || COMMAND_IS("join_tcp_receiver_thread") + || COMMAND_IS("start_bg_traffic_thread") || COMMAND_IS("join_bg_traffic_thread") + || COMMAND_IS("setsockopt_keepalive") || COMMAND_IS("getsockopt_keepalive")) + && info->type() != SInfo::TCP_CLIENT) { + cmd_printf("Not TCPSocket\r\n"); + return CMDLINE_RETCODE_FAIL; + } + + if (COMMAND_IS("connect")) { + char *host; + int32_t port; + + if (!cmd_parameter_val(argc, argv, "connect", &host)) { + cmd_printf("Need host name\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + if (!cmd_parameter_int(argc, argv, host, &port)) { + cmd_printf("Need port number\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + if (strcmp(host, "NULL") == 0) { + host = NULL; + } + + cmd_printf("Host name: %s port: %" PRId32 "\r\n", host, port); + return handle_nsapi_error("TCPSocket::connect()", static_cast(info->socket()).connect(host, port)); + + } else if (COMMAND_IS("send")) { + return tcp_send_command_handler(info, argc, argv); + + } else if (COMMAND_IS("recv")) { + return tcp_recv_command_handler(info, argc, argv); + + } else if (COMMAND_IS("start_tcp_receiver_thread")) { + return start_tcp_receiver_thread(info, argc, argv); + + } else if (COMMAND_IS("join_tcp_receiver_thread")) { + info->getReceiverThread()->join(); + print_data((const uint8_t *)info->getReceiveBuffer(), info->getDataCount()); + cmd_printf("received: %d bytes\r\n", info->getRecvTotal()); + + thread_clean_up(info); + + return CMDLINE_RETCODE_SUCCESS; + + } else if (COMMAND_IS("start_bg_traffic_thread")) { + return start_bg_traffic_thread(info, argc, argv); + + } else if (COMMAND_IS("join_bg_traffic_thread")) { + int bg_thread_success = CMDLINE_RETCODE_SUCCESS; + + if( !info->available()) { // Tells that background thread stumbled to an issue and stopped prematurely + bg_thread_success = CMDLINE_RETCODE_FAIL; + } + + info->setUnavailable(); + info->getReceiverThread()->join(); + thread_clean_up(info); + + return bg_thread_success; + } else if (COMMAND_IS("setsockopt_keepalive")) { + int32_t seconds; + nsapi_error_t ret; + if (!cmd_parameter_int(argc, argv, "setsockopt_keepalive", &seconds)) { + cmd_printf("Need keep-alive value(0-7200seconds)"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + + ret = info->socket().setsockopt(NSAPI_SOCKET, NSAPI_KEEPALIVE, &seconds, sizeof(seconds)); + + return handle_nsapi_error("TCPSocket::setsockopt()", ret); + } else if (COMMAND_IS("getsockopt_keepalive")) { + int32_t optval; + unsigned optlen = sizeof(optval); + nsapi_error_t ret; + + ret = info->socket().getsockopt(NSAPI_SOCKET, NSAPI_KEEPALIVE, &optval, &optlen); + + if (optlen != sizeof(int)) { + return CMDLINE_RETCODE_FAIL; + } + if (ret < 0) { + return handle_nsapi_error("TCPSocket::getsockopt()", ret); + } + return handle_nsapi_size_or_error("TCPSocket::getsockopt()", optval); + } + + /* + * Commands for TCPServer + * listen, accept + */ + if ((COMMAND_IS("listen") || COMMAND_IS("accept")) && info->type() != SInfo::TCP_SERVER) { + cmd_printf("Not TCPServer\r\n"); + return CMDLINE_RETCODE_FAIL; + } + if (COMMAND_IS("listen")) { + int32_t backlog; + if (cmd_parameter_int(argc, argv, "listen", &backlog)) + return handle_nsapi_error("TCPServer::listen()", static_cast(info->socket()).listen(backlog)); + else + return handle_nsapi_error("TCPServer::listen()", static_cast(info->socket()).listen()); + + } else if (COMMAND_IS("accept")) { + SocketAddress addr; + int32_t id; + if (!cmd_parameter_int(argc, argv, "accept", &id)) { + cmd_printf("Need new socket id\r\n"); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + SInfo *new_info = get_sinfo(id); + if (!new_info) { + cmd_printf("Invalid socket id\r\n"); + return CMDLINE_RETCODE_FAIL; + } + TCPSocket *new_sock = static_cast(&new_info->socket()); + nsapi_error_t ret = static_cast(info->socket()).accept(new_sock, &addr); + if (ret == NSAPI_ERROR_OK) { + cmd_printf("TCPServer::accept() new socket sid: %d connection from %s port %d\r\n", + new_info->id(), addr.get_ip_address(), addr.get_port()); + } + return handle_nsapi_error("TCPServer::accept()", ret); + } + return CMDLINE_RETCODE_INVALID_PARAMETERS; +} + +void print_data(const uint8_t *buf, int len) +{ + switch (printing_mode) { + case PRINT_STRING: + print_data_as_string(buf, len, printing_col_width); + break; + case PRINT_HEX: + print_data_as_hex(buf, len, printing_col_width); + break; + case PRINT_DISABLED: + break; + default: + assert(0); + } +} + +void print_data_as_string(const uint8_t *buf, int len, int col_width) +{ + int printable_bytes; + for (printable_bytes = 0; printable_bytes < len; printable_bytes++) { + if (!isprint(buf[printable_bytes])) { + break; + } + } + + cmd_mutex_lock(); + cmd_printf("string data, printing %d bytes:\r\n", len); + for (int i = 0; i < printable_bytes; i += col_width) { + if (printable_bytes - i > col_width) { + cmd_printf("%04d: %.*s\r\n", i, col_width, buf + i); + } else { + cmd_printf("%04d: %.*s\r\n", i, printable_bytes - i, buf + i); + } + } + cmd_printf("Printed %d bytes\r\n", printable_bytes); + + if (len != printable_bytes) { + cmd_printf("Error! Couldn't print all data. " + "Unprintable character: 0x%02x found at index: %d\r\n", + buf[printable_bytes], printable_bytes); + } + cmd_mutex_unlock(); +} + +void print_data_as_hex(const uint8_t *buf, int len, int col_width) +{ + cmd_mutex_lock(); + cmd_printf("hex data, printing %d bytes:\r\n", len); + for (int i = 0; i < len; i += col_width) { + if (len - i > col_width) { + cmd_printf("%04d: %s:\r\n", i, print_array(buf + i, col_width)); + } else { + cmd_printf("%04d: %s\r\n", i, print_array(buf + i, len - i)); + } + } + cmd_printf("Printed %d bytes\r\n", len); + cmd_mutex_unlock(); +} diff --git a/TEST_APPS/device/socket_app/cmd_socket.h b/TEST_APPS/device/socket_app/cmd_socket.h new file mode 100644 index 00000000000..f63695f59a4 --- /dev/null +++ b/TEST_APPS/device/socket_app/cmd_socket.h @@ -0,0 +1,10 @@ +#ifndef CMD_SOCKET_H_ +#define CMD_SOCKET_H_ + +#include "nsapi_types.h" + +int handle_nsapi_error(const char *function, nsapi_error_t ret); +int handle_nsapi_size_or_error(const char *function, nsapi_size_or_error_t ret); +void cmd_socket_init(void); + +#endif diff --git a/TEST_APPS/device/socket_app/main.cpp b/TEST_APPS/device/socket_app/main.cpp new file mode 100644 index 00000000000..5bb53c57fdf --- /dev/null +++ b/TEST_APPS/device/socket_app/main.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015-2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 +#include +#include "mbed.h" +#include "mbed-client-cli/ns_cmdline.h" +#include "cmd_ifconfig.h" +#include "cmd_socket.h" + +/** + * Macros for setting console flow control. + */ +#define CONSOLE_FLOWCONTROL_RTS 1 +#define CONSOLE_FLOWCONTROL_CTS 2 +#define CONSOLE_FLOWCONTROL_RTSCTS 3 +#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x +#define mbed_console_concat(x) mbed_console_concat_(x) +#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL) + +void cmd_ready_cb(int retcode) +{ + cmd_next(retcode); +} + +void wrap_printf(const char *f, va_list a) { + vprintf(f, a); +} + +int main() +{ + cmd_init(&wrap_printf); + cmd_ifconfig_init(); + cmd_socket_init(); + + int c; + while((c = getchar()) != EOF) { + cmd_char_input(c); + } + return 0; +} + +FileHandle* mbed::mbed_override_console(int) { + static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, 115200); +#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS + console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS + console.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS); +#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS + console.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS); +#endif + return &console; +} diff --git a/TEST_APPS/device/socket_app/mbed_app.json b/TEST_APPS/device/socket_app/mbed_app.json new file mode 100644 index 00000000000..a1829b5e7e7 --- /dev/null +++ b/TEST_APPS/device/socket_app/mbed_app.json @@ -0,0 +1,22 @@ +{ + "macros": [ + "MEM_ALLOC=malloc", + "MEM_FREE=free", + "MBED_HEAP_STATS_ENABLED=1", + "MBED_MEM_TRACING_ENABLED" + ], + "target_overrides": { + "*": { + "target.features_add": ["LWIP", "COMMON_PAL"], + "mbed-trace.enable": 1, + "platform.stdio-baud-rate": 115200, + "platform.stdio-convert-newlines": true, + "platform.stdio-buffered-serial": true, + "platform.stdio-flush-at-exit": true, + "drivers.uart-serial-rxbuf-size": 768 + }, + "UBLOX_EVK_ODIN_W2" : { + "target.device_has_remove": ["EMAC"] + } + } +} diff --git a/TEST_APPS/device/socket_app/strconv.c b/TEST_APPS/device/socket_app/strconv.c new file mode 100644 index 00000000000..d1db5ae3bca --- /dev/null +++ b/TEST_APPS/device/socket_app/strconv.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + */ + +#include +#include +#include +#include + +#include "ip6string.h" +#include "strconv.h" + + +int8_t strtohex(uint8_t *out, const char *str, int8_t max_length) +{ + int8_t i = 0; + char *rd = (char *)str; + uint8_t *wr = out; + while (*rd != 0) { + if (i > max_length) { + break; + } + *wr++ = strtoul(rd, &rd, 16); + while (!isxdigit((int)*rd) && *rd != 0) { + rd++; //skip some invalid characters + } + i++; + } + return i; +} + +int hexstr_to_bytes_inplace(char *str) +{ + int16_t len, i, j; + if (str == NULL) { + return -1; + } + len = strlen(str); + if (len < 2 || (len+1)%3 != 0) { + return -1; + } + for (i = 0, j = 0; i < len; i += 3, ++j) { + str[j] = (char)strtol(str+i, 0, 16); + } + return j; +} + +// convert hex string (eg. "76 ab ff") to binary array +int string_to_bytes(const char *str, uint8_t *buf, int bytes) +{ + int len = strlen(str); + if (len <= (3 * bytes - 1)) { + int i; + for (i = 0; i < bytes; i++) { + if (i * 3 < len) { + buf[i] = (uint8_t)strtoul(str + i * 3, 0, 16); + } else { + buf[i] = 0; + } + } + return 0; + } + return -1; +} + +int16_t hextostr(const uint8_t *buf, uint16_t buf_length, char *out, int16_t out_length, char delimiter) +{ + int16_t outLeft = out_length; + int16_t arrLeft = buf_length; + const uint8_t *rd = buf; + int retcode = 0; + char *wr = out; + while (arrLeft > 0 && outLeft > 0) { + retcode = snprintf(wr, outLeft, "%02x", *rd); + + if (retcode <= 0 || retcode >= outLeft) { + break; + } + outLeft -= retcode; + wr += retcode; + arrLeft --; + rd++; + if (delimiter && arrLeft > 0 && outLeft > 0) { + *wr++ = delimiter; + outLeft--; + *wr = 0; + } + } + return (int16_t)(wr - out); +} +int replace_hexdata(char *str) +{ + char *ptr = str; + if (str == NULL) { + return 0; + } + while (*ptr) { + if (ptr[0] == '\\') { + if (ptr[1] == 'n') { + ptr[0] = 0x0a; + memmove(ptr + 1, ptr + 2, strlen(ptr + 2) + 1); + } else if (ptr[1] == 'r') { + ptr[0] = 0x0d; + memmove(ptr + 1, ptr + 2, strlen(ptr + 2) + 1); + } else if (ptr[1] == 'x') { + char *end; + ptr[0] = (char)strtoul(ptr + 2, &end, 16); + memmove(ptr + 1, end, strlen(end) + 1); + } else if (isdigit((int)ptr[1])) { + char *end; + ptr[0] = strtoul(ptr + 1, &end, 10); + memmove(ptr + 1, end, strlen(end) + 1); + } + } + ptr++; + } + return ptr - str; +} +bool is_visible(uint8_t *buf, int len) +{ + while (len--) { + if (!isalnum(*buf) && *buf != ' ') { + return false; + } + buf++; + } + return true; +} + +char *strdupl(const char *str) +{ + if (!str) { + return NULL; + } + char *p = malloc(strlen(str) + 1); + if (!p) { + return p; + } + strcpy(p, str); + return p; +} +int strnlen_(const char *s, int n) +{ + char *end = memchr(s, 0, n); + return end ? end - s : n; +} +char *strndupl(const char *s, int n) +{ + char *p = NULL; + int len = strnlen_(s, n); + p = malloc(len + 1); + if (!p) { + return p; + } + p[len] = 0; + return memcpy(p, s, len); +} +int strnicmp_(char const *a, char const *b, int n) +{ + for (; (n > 0 && *a != 0 && *b != 0) ; a++, b++, n--) { + int d = tolower((int) * a) - tolower((int) * b); + if (d != 0 || !*a) { + return d; + } + } + return 0; +} + + +/* HELPING PRINT FUNCTIONS for cmd_printf */ + +static inline uint8_t context_split_mask(uint_fast8_t split_value) +{ + return (uint8_t) - (0x100u >> split_value); +} + +static uint8_t *bitcopy(uint8_t *restrict dst, const uint8_t *restrict src, uint_fast8_t bits) +{ + uint_fast8_t bytes = bits / 8; + bits %= 8; + + if (bytes) { + dst = (uint8_t *) memcpy(dst, src, bytes) + bytes; + src += bytes; + } + + if (bits) { + uint_fast8_t split_bit = context_split_mask(bits); + *dst = (*src & split_bit) | (*dst & ~ split_bit); + } + + return dst; +} + +char tmp_print_buffer[128] = {0}; + +char *print_ipv6(const void *addr_ptr) +{ + ip6tos(addr_ptr, tmp_print_buffer); + return tmp_print_buffer; +} +char *print_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len) +{ + char *str = tmp_print_buffer; + int retval; + char tmp[40]; + uint8_t addr[16] = {0}; + + if (prefix_len != 0) { + if (prefix == NULL || prefix_len > 128) { + return ""; + } + bitcopy(addr, prefix, prefix_len); + } + + ip6tos(addr, tmp); + retval = snprintf(str, 128, "%s/%u", tmp, prefix_len); + if (retval <= 0) { + return ""; + } + return str; +} +char *print_array(const uint8_t *buf, uint16_t len) +{ + int16_t retcode = hextostr(buf, len, tmp_print_buffer, 128, ':'); + if (retcode > 0) { + //yeah, something is converted + } + return tmp_print_buffer; +} + diff --git a/TEST_APPS/device/socket_app/strconv.h b/TEST_APPS/device/socket_app/strconv.h new file mode 100644 index 00000000000..9e70bc280a1 --- /dev/null +++ b/TEST_APPS/device/socket_app/strconv.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + */ + +#ifndef STRCONVERSION_H +#define STRCONVERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ns_types.h" + +/** Convert hex string to binary array + * e.g. + * char str[] = {30, 31, 32, 33}; + * uint8_t buffer[4]; + * strtohex( buffer, str, 4 ); + */ +int8_t strtohex(uint8_t *out, const char *str, int8_t max_length); + +/** Convert space separated hex string (eg. "76 ab ff") to binary array. + */ +int string_to_bytes(const char *str, uint8_t *buf, int bytes); + +/** Convert a colon/space separated hex string (eg. "76:ab:ff") to binary + * array (inplace) returning the number of the bytes in the array. + */ +int hexstr_to_bytes_inplace(char *str); + +/** Convert hex array to string + */ +int16_t hextostr(const uint8_t *buf, uint16_t buf_length, char *out, int16_t out_length, char delimiter); +/** Replace hex characters from string + * e.g. + * "hello\\n" -> "hello\n" + * "hello\\r" -> "hello\r" + * "val: \\10" -> "val: \x0a" //integer + * "val: \\x10" -> "val: \x10" //hex value + * @param str string that will be replaced + * @return length + */ +int replace_hexdata(char *str); +/** + * check if array contains only visible characters + * = isalpha | isdigit + */ +bool is_visible(uint8_t *buf, int len); + +/** Convert ipv6 address to string format + */ +char *print_ipv6(const void *addr_ptr); +/** Convert ipv6 prefix to string format + */ +char *print_ipv6_prefix(const uint8_t *prefix, uint8_t prefix_len); +/** Convert binary array to string format and return static results + */ +char *print_array(const uint8_t *buf, uint16_t len); +/** The strdupl() function shall return a pointer to a new string, + * which is a duplicate of the string pointed to by s1. The returned pointer can be passed to free(). + * A null pointer is returned if the new string cannot be created. + * strdupl are same than linux strdup, but this way we avoid to duplicate reference in linux + */ +char *strdupl(const char *str); +/** The strdup() function returns a pointer to a new string which is a duplicate of the string. + * Memory for the new string is obtained with malloc(3), and can be freed with free(3). + * The strndup() function is similar, but only copies at most n bytes. If s is longer than n, + * only n bytes are copied, and a terminating null byte ('\0') is added. + */ +char *strndupl(const char *s, int n); +/** strnlen - determine the length of a fixed-size string + * The strnlen() function returns the number of bytes in the string pointed to by s, excluding the terminating null bye ('\0'), but at most maxlen. + * In doing this, strnlen() looks only at the first maxlen bytes at s and never beyond s+maxlen. + * The strnlen() function returns strlen(s), if that is less than maxlen, or maxlen if there is no null byte ('\0') + * among the first maxlen bytes pointed to by s. + */ +int strnlen_(const char *s, int n); +/** strnicmp compares a and b without sensitivity to case. + * All alphabetic characters in the two arguments a and b are converted to lowercase before the comparison. + */ +int strnicmp_(char const *a, char const *b, int n); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/TEST_APPS/icetea_plugins/__init__.py b/TEST_APPS/icetea_plugins/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/TEST_APPS/icetea_plugins/ip_test_parsers.py b/TEST_APPS/icetea_plugins/ip_test_parsers.py new file mode 100644 index 00000000000..f208dcbb6ca --- /dev/null +++ b/TEST_APPS/icetea_plugins/ip_test_parsers.py @@ -0,0 +1,146 @@ +# +# Copyright (c) 2018, Arm Limited and affiliates. +# +import re +import time +from collections import OrderedDict +from datetime import datetime + +from icetea_lib.Plugin.PluginBase import PluginBase + + +class IpTestParsers(PluginBase): + # constructor + def __init__(self): + super(IpTestParsers, self).__init__() + + def get_parsers(self): + return { + 'ifconfig': self.__ifconfigParser, + 'ifup': self.__ifconfigParser, + 'ethup': self.__ifconfigParser, + 'dut1': self.__ifconfigParser, + 'dut2': self.__ifconfigParser, + 'dut3': self.__ifconfigParser, + 'dut4': self.__ifconfigParser, + 'dut5': self.__ifconfigParser, + 'dut6': self.__ifconfigParser, + 'socket': self.__mbedossocketParser, + 'ticker': self.__ticker_parser + } + + # socket command for mbedos + def __mbedossocketParser(self, response): + results = {'socket_id': None, + 'data_type': None, + 'data': "", + 'printed_bytes': None, + 'sent_bytes': None, + 'received_bytes': None, + 'address': None, + 'port': None, + 'loop_id': None + } + respLines = response.lines + part = None + for line in respLines: + ret = PluginBase.find_one(line, ".*sid: ([0-9]+)") + if ret is not False: + results['socket_id'] = ret + + ret = PluginBase.find_one(line, ".*(hex|string) data, printing .* bytes:") + if ret is not False: + results['data_type'] = ret + + ret = PluginBase.find_one(line, ".*data, printing (.*) bytes:") + if ret is not False: + part = "data" + + ret = PluginBase.find_one(line, "^Printed (.*) bytes$") + if ret is not False: + results['printed_bytes'] = int(ret) + part = None + + if part == "data": + ret = PluginBase.find_one(line, "^\d{4}: (.*)$") + if ret is not False: + results['data'] = results['data'] + ret + + ret = PluginBase.find_one(line, ".*sent: ([0-9]+) bytes") + if ret is not False: + results['sent_bytes'] = int(ret) + + ret = PluginBase.find_one(line, ".*received: ([0-9]+) bytes") + if ret is not False: + results['received_bytes'] = int(ret) + + ret = PluginBase.find_one(line, ".*address: ([0-9a-fxA-F:.]+)") + if ret is not False: + results['address'] = ret + + ret = PluginBase.find_one(line, ".*port: ([0-9]+)") + if ret is not False: + results['port'] = ret + + ret = PluginBase.find_one(line, ".*lid: ([0-9]+)") + if ret is not False: + results['loop_id'] = ret + + return results + + # response parser for ifup + def __ifconfigParser(self, response): + results = {} + lines = response.traces + part = None + + results['address'] = { + 'll': '', + 'globals': [], + 'ipv4': None, + 'ipv6': [] + } + + for line in lines: + # print "ifcfgparser: %s" % line + match = re.search('IPv6 if addr', line) + if match: + part = "address" + match = re.search('IPv4 if addr', line) + if match: + part = "address" + match = re.search('MAC-48\:[\W]{1,}(.*)', line) + if match: + mac48 = match.group(1) + # Validate the MAC-48 syntax as well + match = re.search("([0-9a-fA-F]{2}:??){5}([0-9a-fA-F]{2})", mac48) + if match: + results['MAC'] = mac48 + + if part == "address": + match = re.search('.*\[(\d)\]:\W([abcdefg\d\:]{5,})', line) + if match: + addr = match.group(2) + if re.search('^fe80\:\:', addr): + results['address']['ll'] = addr + else: + results['address']["globals"].append(addr) + + match = re.search('\[(\d)\]:\W([a-fA-F\d\:]{5,})', line) + if match: + results['address']['ipv6'].append(match.group(2)) + + match = re.search('(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$', line) + if match: + results['address']['ipv4'] = match.group(1) + + return results + + def __ticker_parser(self, response): + results = {} + respLines = response.lines + for line in respLines: + ret = PluginBase.find_one(line, 'Ticker id: ([0-9]+)') + if ret is not False: + results['ticker_id'] = ret + return results diff --git a/TEST_APPS/icetea_plugins/plugins_to_load.py b/TEST_APPS/icetea_plugins/plugins_to_load.py new file mode 100644 index 00000000000..356547618ae --- /dev/null +++ b/TEST_APPS/icetea_plugins/plugins_to_load.py @@ -0,0 +1,8 @@ +# +# Copyright (c) 2018, Arm Limited and affiliates. +# +from ip_test_parsers import IpTestParsers + +plugins_to_load = { + "ip_test_parsers": IpTestParsers +} diff --git a/TEST_APPS/readme.md b/TEST_APPS/readme.md new file mode 100644 index 00000000000..b4fa79359bd --- /dev/null +++ b/TEST_APPS/readme.md @@ -0,0 +1,115 @@ +## Running IceTea tests located under mbed-os + +### Structure + +mbed-os has a folder called TEST_APPS that contains everything related to IceTea -testing. +There are currently 3 folders + +- device - contains all the different test applications you can flash to your board +- icetea-plugins - contains plugins that are being used by some of the testcases, needed for the test execution +- testcases - contains IceTea testcases written in Python + +The testcases and test applications have a dependency + +### Preparing your work environment + +#### Prerequisities + +You should have IceTea and forked mbed-cli that supports IceTea, installed. + +#### Selecting the network interface to use + +Depending on a device, there might be a default network interface type defined in the mbed-os/targets/targets.json, which is used to locate a test-config file by default. +If there is not, or you want to use a different interface than the default, you need to provide a relevant test-config -file to the mbed test with --test-config option. +The test-config file contains the necessary information for the test application, there are some test-config files located under mbed-os/tools/test-configs. + +### Running the tests + +Now that the interface has been selected you can run the icetea tests from the mbed-os root on your command line by + +`>mbed test -m -t --icetea` + +This command will compile the mbed-os, then compiles the test applications, creates a test suite and then starts running the tests. + +If you want only to run some specific tests, you can use the -n -option. You can list multiple tests by separating them by comma (,). + +`>mbed test -m -t --icetea -n test1,test2` + +#### Running the tests with specifig test-config + +Some devices may offer multiple network interfaces to operate with. For example, UBLOX_EVK_ODIN_W2 offers ethernet and Wi-Fi capabilities. +The tests can be run for either one of those using already existing test-config -files. + +To run the tests with Wi-Fi interface: +`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/OdinInterface.json` + +To run the tests with ethernet interface: +`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/Odin_EthernetInterface.json` + +#### Providing Wi-Fi access point information + +If you are using Wi-Fi interface for running the tests, you need to provide also information about the used access point. +The information can be provided in the used test-config -file. Depending on the used interface you might need to provide ssid, password and security. +You can either provide separate WIFI_SSID, WIFI_PASSWORD and WIFI_SECURITY macros, or provide the SSID, password and security in the connect statement provided in the test-config -file. + +Example of separate macros: +``` + "wifi-ssid": { + "help": "WiFi SSID", + "value": "\"ssid\"", + "macro_name": "WIFI_SSID" + }, + "wifi-password": { + "help": "WiFi Password", + "value": "\"password\"", + "macro_name": "WIFI_PASSWORD" + }, + "wifi-security": { + "help": "WiFi Security, values from nsapi_security from features/netsocket/nsapi_types.h" + "value": "NSAPI_SECURITY_WPA_WPA2" + "macro_name": "WIFI_SECURITY" +``` + +Example of modifying the connect statement +Original: +``` + "connect-statement" : { + "help" : "Must use 'net' variable name", + "value" : "net->wifiInterface()->connect(WIFI_SSID, WIFI_PASSWORD, WIFI_SECURITY)" + }, +``` +Modified: +``` + "connect-statement" : { + "help" : "Must use 'net' variable name", + "value" : "net->wifiInterface()->connect(\"ssid\", \"password\", NSAPI_SECURITY_WPA_WPA2)" + }, +``` + +### Test results + +IceTea prints the results from the test run to the command line, and the final result looks similar to this. + +``` ++--------------------------------+---------+-------------+-------------+-----------+----------+ +| Testcase | Verdict | Fail Reason | Skip Reason | platforms | duration | ++--------------------------------+---------+-------------+-------------+-----------+----------+ +| test_cmdline | pass | | | K64F | 8.555 | +| UDPSOCKET_BIND_PORT | pass | | | K64F | 19.614 | +| TCPSOCKET_BIND_PORT | pass | | | K64F | 15.852 | +| TCPSERVER_ACCEPT | pass | | | K64F | 41.629 | +| TCPSOCKET_ECHOTEST_BURST_SHORT | pass | | | K64F | 19.926 | ++--------------------------------+---------+-------------+-------------+-----------+----------+ ++---------------+----------------+ +| Summary | | ++---------------+----------------+ +| Final Verdict | PASS | +| count | 5 | +| passrate | 100.00 % | +| pass | 5 | +| Duration | 0:01:45.576000 | ++---------------+----------------+ +``` + +The results from the tests can also be found from mbed-os/log -folder. +You probably want to add the log -folder to your .mbedignore -file to prevent issues with build commands becoming too long over the time. diff --git a/TEST_APPS/testcases/__init__.py b/TEST_APPS/testcases/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/TEST_APPS/testcases/example/__init__.py b/TEST_APPS/testcases/example/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/TEST_APPS/testcases/example/test_cmdline.py b/TEST_APPS/testcases/example/test_cmdline.py new file mode 100644 index 00000000000..d1dbcd7ea0f --- /dev/null +++ b/TEST_APPS/testcases/example/test_cmdline.py @@ -0,0 +1,50 @@ +""" +Copyright 2018 ARM Limited +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. +""" + +from icetea_lib.bench import Bench + + +class Testcase(Bench): + def __init__(self): + self.device = None + Bench.__init__(self, + name="test_cmdline", + title="Smoke test for command line interface", + status="released", + purpose="Verify Command Line Interface", + component=["cmdline"], + type="smoke", + requirements={ + "duts": { + '*': { + "count": 1, + "type": "hardware", + "application": { + "name": "TEST_APPS-device-exampleapp" + } + } + } + } + ) + + def setup(self): + self.device = self.get_node_endpoint(1) + + def case(self): + self.command(1, "echo hello world", timeout=5) + self.device.command("help") + + def teardown(self): + pass diff --git a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py new file mode 100644 index 00000000000..51492862052 --- /dev/null +++ b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py @@ -0,0 +1,60 @@ +# +# Copyright (c) 2017-2018, Arm Limited and affiliates. +# +from icetea_lib.bench import Bench +from icetea_lib.tools import test_case + + +class MultipleTestcase(Bench): + def __init__(self, **kwargs): + testcase_args = { + 'status': "released", + 'component': ["mbed-os", "netsocket"], + 'type': "smoke", + 'subtype': "socket", + 'requirements': { + "duts": { + "*": { + "count": 1, + "type": "hardware", + "application": {"name": "TEST_APPS-device-socket_app"} + }, + "1": {"nick": "dut1"}, + } + } + } + + testcase_args.update(kwargs) + Bench.__init__(self, **testcase_args) + + def setup(self): + self.command("dut1", "ifup") + + def socket_bind_port(self, socket_type): + response = self.command("dut1", "socket new " + socket_type) + socket_id = int(response.parsed['socket_id']) + + self.command("dut1", "socket " + str(socket_id) + " open") + + self.command("dut1", "socket " + str(socket_id) + " bind port 1024") + + self.command("dut1", "socket " + str(socket_id) + " delete") + + def teardown(self): + self.command("dut1", "ifdown") + + +@test_case(MultipleTestcase, + name="TCPSOCKET_BIND_PORT", + title="tcpsocket open and bind port", + purpose="Verify TCPSocket can be created, opened and port binded") +def test1(self): + self.socket_bind_port("TCPSocket") + + +@test_case(MultipleTestcase, + name="UDPSOCKET_BIND_PORT", + title="udpsocket open and bind port", + purpose="Verify UDPSocket can be created, opened and port binded") +def test2(self): + self.socket_bind_port("UDPSocket") diff --git a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py new file mode 100644 index 00000000000..55d6e6d50e9 --- /dev/null +++ b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py @@ -0,0 +1,90 @@ +# +# Copyright (c) 2017-2018, Arm Limited and affiliates. +# +from icetea_lib.bench import Bench +from interface import interfaceUp, interfaceDown +from icetea_lib.tools import test_case +import threading +import time + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, + name="TCPSERVER_ACCEPT", + title = "TCPSERVER_ACCEPT", + purpose = "Test that TCPServer::bind(), TCPServer::listen() and TCPServer::accept() works", + status = "released", + component= ["mbed-os", "netsocket"], + author = "Juha Ylinen ", + type="smoke", + subtype="socket", + requirements={ + "duts": { + '*': { #requirements for all nodes + "count":2, + "type": "hardware", + "application": {"name": "TEST_APPS-device-socket_app"} + }, + "1": {"nick": "dut1"}, + "2": {"nick": "dut2"} + } + } + ) + + def setup(self): + interface = interfaceUp(self, ["dut1"]) + self.server_ip = interface["dut1"]["ip"] + interface = interfaceUp(self, ["dut2"]) + self.client_ip = interface["dut2"]["ip"] + + def clientThread(self): + self.logger.info("Starting") + time.sleep(5) #wait accept from server + self.command("dut2", "socket " + str(self.client_socket_id) + " open") + self.command("dut2", "socket " + str(self.client_socket_id) + " connect " + str(self.server_ip) + " " + str(self.used_port)) + + + def case(self): + self.used_port = 2000 + + response = self.command("dut1", "socket new TCPServer") + server_base_socket_id = int(response.parsed['socket_id']) + + self.command("dut1", "socket " + str(server_base_socket_id) + " open") + self.command("dut1", "socket " + str(server_base_socket_id) + " bind port " + str(self.used_port)) + self.command("dut1", "socket " + str(server_base_socket_id) + " listen") + + response = self.command("dut1", "socket new TCPSocket") + server_socket_id = int(response.parsed['socket_id']) + self.command("dut1", "socket " + str(server_socket_id) + " open") + + response = self.command("dut2", "socket new TCPSocket") + zero = response.timedelta + self.client_socket_id = int(response.parsed['socket_id']) + + #Create a thread which calls client connect() + t = threading.Thread(name='clientThread', target=self.clientThread) + t.start() + + wait = 5 + response = self.command("dut1", "socket " + str(server_base_socket_id) + " accept " + str(server_socket_id)) + response.verify_response_duration(expected = wait, zero = zero, threshold_percent = 10, break_in_fail = True) + socket_id = int(response.parsed['socket_id']) + + t.join() + self.command("dut1", "socket " + str(socket_id) + " send hello") + + response = self.command("dut2", "socket " + str(self.client_socket_id) + " recv 5") + data = response.parsed['data'].replace(":","") + + if data != "hello": + raise TestStepFail("Received data doesn't match the sent data") + + self.command("dut1", "socket " + str(server_socket_id) + " delete") + self.command("dut1", "socket " + str(server_base_socket_id) + " delete") + self.command("dut2", "socket " + str(self.client_socket_id) + " delete") + + + def teardown(self): + interfaceDown(self, ["dut1"]) + interfaceDown(self, ["dut2"]) diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py new file mode 100644 index 00000000000..d408a293203 --- /dev/null +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -0,0 +1,68 @@ +# +# Copyright (c) 2018, Arm Limited and affiliates. +# +from icetea_lib.bench import Bench, TestStepFail +from icetea_lib.tools import test_case +import random +import string + + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, + name="TCPSOCKET_ECHOTEST_BURST_SHORT", + title="TCPSOCKET_ECHOTEST_BURST_SHORT", + purpose="Verify that TCPSocket can send burst of packets to echo server and read incoming packets", + status="released", + component=["mbed-os", "netsocket"], + author="Juha Ylinen ", + type="smoke", + subtype="socket", + requirements={ + "duts": { + '*': { # requirements for all nodes + "count": 1, + "type": "hardware", + "application": {"name": "TEST_APPS-device-socket_app"} + }, + "1": {"nick": "dut1"}, + } + } + ) + + def setup(self): + self.command("dut1", "ifup") + + def randomString(self, length): + return ''.join(random.choice(string.ascii_uppercase) for i in range(length)) + + def case(self): + response = self.command("dut1", "socket new TCPSocket") + socket_id = int(response.parsed['socket_id']) + + self.command("dut1", "socket " + str(socket_id) + " open") + self.command("dut1", "socket " + str(socket_id) + " connect echo.mbedcloudtesting.com 7") + + for i in range(2): + sentData = "" + for size in (100, 200, 300, 120, 500): + packet = self.randomString(size) + sentData += packet + response = self.command("dut1", "socket " + str(socket_id) + " send " + str(packet)) + response.verify_trace("TCPSocket::send() returned: " + str(size)) + + received = 0 + data = "" + totalSize = 1220 + while received < totalSize: + response = self.command("dut1", "socket " + str(socket_id) + " recv " + str(totalSize)) + data += response.parsed['data'].replace(":", "") + received += int(response.parsed['received_bytes']) + + if data != sentData: + raise TestStepFail("Received data doesn't match the sent data") + + self.command("dut1", "socket " + str(socket_id) + " delete") + + def teardown(self): + self.command("dut1", "ifdown") diff --git a/TEST_APPS/testcases/netsocket/__init__.py b/TEST_APPS/testcases/netsocket/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/TEST_APPS/testcases/netsocket/generate_test_data.py b/TEST_APPS/testcases/netsocket/generate_test_data.py new file mode 100644 index 00000000000..3720bd81757 --- /dev/null +++ b/TEST_APPS/testcases/netsocket/generate_test_data.py @@ -0,0 +1,16 @@ +# +# Copyright (c) 2017, Arm Limited and affiliates. +# +import random, string + +''' +This script is intended to be a common test data generator. +Currently it implements randomUppercaseAsciiString, randomLowercaseAsciiString methods. +''' + +def randomUppercaseAsciiString(length): + return ''.join(random.choice(string.ascii_uppercase) for i in range(length)) + + +def randomLowercaseAsciiString(length): + return ''.join(random.choice(string.ascii_lowercase) for i in range(length)) diff --git a/TEST_APPS/testcases/netsocket/interface.py b/TEST_APPS/testcases/netsocket/interface.py new file mode 100644 index 00000000000..492794ea2bc --- /dev/null +++ b/TEST_APPS/testcases/netsocket/interface.py @@ -0,0 +1,29 @@ +# +# Copyright (c) 2016-2018, Arm Limited and affiliates. +# +from icetea_lib.TestStepError import TestStepFail +''' +This interface script is intended to be a common library to be used in testcase scripts by testers. +It delegates setUp and tearDown functions with different provided network interface types using setUp() and tearDown() methods. +''' + +def interfaceUp(tc, duts): + interfaces = {} + for dut in duts: + interface = {dut:{"ipv4": None, "ipv6": None}} + + resp = tc.command("%s" % dut, "ifup") + + ip = interface[dut]["ip"] = interface[dut]["ipv4"] = resp.parsed["address"]["ipv4"] + if not ip: + if resp.parsed["address"]["ipv6"] != []: + ip = interface[dut]["ip"] = interface[dut]["ipv6"] = resp.parsed["address"]["ipv6"][0] + if not ip: + raise TestStepFail("Failed to parse IP address") + + interfaces.update(interface) + return interfaces + +def interfaceDown(tc, duts): + for dut in duts: + tc.command(dut, "ifdown") diff --git a/features/frameworks/mbed-trace/mbed_lib.json b/features/frameworks/mbed-trace/mbed_lib.json index f51343717f4..9a2b49d7d57 100644 --- a/features/frameworks/mbed-trace/mbed_lib.json +++ b/features/frameworks/mbed-trace/mbed_lib.json @@ -8,7 +8,15 @@ "fea-ipv6": { "help": "Used to globally disable ipv6 tracing features.", "value": null + }, + "allocator": { + "value": "malloc", + "macro_name": "MEM_ALLOC" + }, + "deallocator": { + "value": "free", + "macro_name": "MEM_FREE" } - } + } } \ No newline at end of file diff --git a/mbed-client-cli/ns_cmdline.h b/mbed-client-cli/ns_cmdline.h new file mode 100644 index 00000000000..4741edc456f --- /dev/null +++ b/mbed-client-cli/ns_cmdline.h @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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. + */ + +/** + * \file ns_cmdline.h + * + * Command line library - mbedOS shell + * + * Usage example: + * + * \code + * //simple print function + * void myprint(const char* fmt, va_list ap){ vprintf(fmt, ap); } + * // simple ready cb, which call next command to be execute + * void cmd_ready_cb(int retcode) { cmd_next( retcode ); } + * + * // dummy command with some option + * int cmd_dummy(int argc, char *argv[]){ + * if( cmd_has_option(argc, argv, "o") ) { + * cmd_printf("This is o option"); + * } else { + * return CMDLINE_RETCODE_INVALID_PARAMETERS; + * } + * return CMDLINE_RETCODE_SUCCESS; + *} + * // timer cb ( pseudo-timer-code ) + * void timer_ready_cb(void) { + * cmd_ready(CMDLINE_RETCODE_SUCCESS); + * } + * // long command, which need e.g. some events to finalize command execution + * int cmd_long(int argc, char *argv[] ) { + timer_start( 5000, timer_ready_cb ); + * return CMDLINE_RETCODE_EXCUTING_CONTINUE; + * } + * void main(void) { + * cmd_init( &myprint ); // initialize cmdline with print function + * cmd_set_ready_cb( cmd_ready_cb ); // configure ready cb + * cmd_add("dummy", cmd_dummy, 0, 0); // add one dummy command + * cmd_add("long", cmd_long, 0, 0); // add one dummy command + * //execute dummy and long commands + * cmd_exe( "dymmy;long" ); + * } + * \endcode + * \startuml{cli_usecase.png} + actor user + participant app + participant cli + participant mycmd + + == Initialization == + * app -> cli: cmd_init( &default_cmd_response_out ) + note right : This initialize command line library + * mycmd -> cli: cmd_add( mycmd_command, "mycmd", NULL, NULL) + note right : All commands have to be register \nto cmdline library with cmd_add() function + + == command input characters== + * app -> cli: cmd_char_input("d") + * app -> cli: cmd_char_input("u") + * app -> cli: cmd_char_input("m") + * app -> cli: cmd_char_input("m") + * app -> cli: cmd_char_input("y") + * app -> cli: cmd_char_input("\\n") + note left : User write command to \nbe execute and press ENTER when \ncommand with all parameters are ready.\nCharacters can be come from serial port for example. + == command execution== + * mycmd <- cli: mycmd_command(argc, argv) + * mycmd -> cli: cmd_printf("hello world!\\n") + note right : cmd_printf() should \nbe used when command prints \nsomething to the console + * cli -> user: "hello world!\\n" + * mycmd -> cli: <> + == finish command and goto forward == + * app <- cli: cmd_ready_cb() + * app -> cli: cmd_next() + note left : this way application can \ndecide when/how to go forward.\nWhen using event-loop, \nyou probably want create tasklet where \ncommands are actually executed.\nif there are some commands in queue cmd_next()\nstart directly next command execution.\n + == command execution== + * app -> cli: cmd_exe("long\\n") + note left : input string can be \ngive also with cmd_exe() -function + * mycmd <- cli: long_command(argc, argv) + * mycmd -> cli: <> = CMDLINE_RETCODE_EXECUTING_CONTINUE + note right : When command continue in background, it should return\nCMDLINE_RETCODE_EXECUTING_CONTINUE.\nCommand interpreter not continue next command \nas long as cmd_ready() -function is not called. + ... Some ~~long delay~~ ... + * mycmd -> cli: cmd_ready( <> ) + note right : When command is finally finished,\nit should call cmd_ready() function. + == finish command and goto forward == + * app <- cli: cmd_ready_cb() + * app -> cli: cmd_next() + ... ... + * \enduml + */ +#ifndef _CMDLINE_H_ +#define _CMDLINE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#define CMDLINE_RETCODE_COMMAND_BUSY 2 //!< Command Busy +#define CMDLINE_RETCODE_EXCUTING_CONTINUE 1 //!< Execution continue in background +#define CMDLINE_RETCODE_SUCCESS 0 //!< Execution Success +#define CMDLINE_RETCODE_FAIL -1 //!< Execution Fail +#define CMDLINE_RETCODE_INVALID_PARAMETERS -2 //!< Command parameters was incorrect +#define CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED -3 //!< Command not implemented +#define CMDLINE_RETCODE_COMMAND_CB_MISSING -4 //!< Command callback function missing +#define CMDLINE_RETCODE_COMMAND_NOT_FOUND -5 //!< Command not found + +/** + * typedef for print functions + */ +typedef void (cmd_print_t)(const char *, va_list); +/** + * Initialize cmdline class. + * This is command line editor without any commands. Application + * needs to add commands that should be enabled. + * usage e.g. + * \code + cmd_init( &default_cmd_response_out ); + * \endcode + * \param outf console printing function (like vprintf) + */ +void cmd_init(cmd_print_t *outf); +/** Command ready function for __special__ cases. + * This need to be call if command implementation return CMDLINE_RETCODE_EXECUTING_CONTINUE + * because there is some background stuff ongoing before command is finally completed. + * Normally there is some event, which call cmd_ready(). + * \param retcode return code for command + */ +void cmd_ready(int retcode); +/** typedef for ready cb function */ +typedef void (cmd_ready_cb_f)(int); +/** + * Configure cb which will be called after commands are executed + * or cmd_ready is called + * \param cb callback function for command ready + */ +void cmd_set_ready_cb(cmd_ready_cb_f *cb); +/** + * execute next command if any + * \param retcode last command return value + */ +void cmd_next(int retcode); +/** Free cmd class */ +void cmd_free(void); +/** Reset cmdline to default values + * detach external commands, delete all variables and aliases + */ +void cmd_reset(void); +/** Configure command history size (default 32) + * \param max maximum history size + * max > 0 -> configure new value + * max = 0 -> just return current value + * \return current history max-size + */ +uint8_t cmd_history_size(uint8_t max); +/** command line print function + * This function should be used when user want to print something to the console + * \param fmt console print function (like printf) + */ +#if defined(__GNUC__) || defined(__CC_ARM) +void cmd_printf(const char *fmt, ...) __attribute__ ((__format__(__printf__, 1, 2))); +#else +void cmd_printf(const char *fmt, ...); +#endif +/** command line print function + * This function should be used when user want to print something to the console with vprintf functionality + * \param fmt The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of zero or more directives. + * \param ap list of parameters needed by format string. This must correspond properly with the conversion specifier. + */ +#if defined(__GNUC__) || defined(__CC_ARM) +void cmd_vprintf(const char *fmt, va_list ap) __attribute__ ((__format__(__printf__, 1, 0))); +#else +void cmd_vprintf(const char *fmt, va_list ap); +#endif +/** Reconfigure default cmdline out function (cmd_printf) + * \param outf select console print function + */ +void cmd_out_func(cmd_print_t *outf); +/** Configure function, which will be called when Ctrl+A is pressed + * \param sohf control function which called every time when user input control keys + */ +void cmd_ctrl_func(void (*sohf)(uint8_t c)); +/** + * Configure mutex wait function + * By default, cmd_printf calls may not be thread safe, depending on the implementation of the used output. + * This can be used to set a callback function that will be called before each cmd_printf call. + * The specific implementation is up to the application developer, but simple mutex locking is assumed. + */ +void cmd_mutex_wait_func(void (*mutex_wait_f)(void)); +/** + * Configure mutex wait function + * By default, cmd_printf calls may not be thread safe, depending on the implementation of the used output. + * This can be used to set a callback function that will be called after each cmd_printf call. + * The specific implementation is up to the application developer, but simple mutex locking is assumed. + */ +void cmd_mutex_release_func(void (*mutex_release_f)(void)); +/** + * Retrieve output mutex lock + * This can be used to retrieve the output mutex when multiple cmd_printf/cmd_vprintf calls must be + * guaranteed to be grouped together in a thread safe manner. Must be released by a following call to + * cmd_mutex_unlock() + * For example: + * * \code + * cmd_mutex_lock(); + for (i = 0; i < 10; i++) { + cmd_printf("%02x ", i); + } + // without locking a print from another thread could happen here + cmd_printf("\r\n); + cmd_mutex_unlock(); + * \endcode + * Exact behaviour depends on the implementation of the configured mutex, + * but counting mutexes are required. + */ +void cmd_mutex_lock(void); +/** + * Release output mutex lock + * This can be used to release the output mutex once it has been retrieved with cmd_mutex_lock() + * Exact behaviour depends on the implementation of the configured mutex, + * but counting mutexes are required. + */ +void cmd_mutex_unlock(void); +/** Refresh output */ +void cmd_output(void); +/** default cmd response function, use stdout + * \param fmt The format string is a character string, beginning and ending in its initial shift state, if any. The format string is composed of zero or more directives. + * \param ap list of parameters needed by format string. This must correspond properly with the conversion specifier. + */ +void default_cmd_response_out(const char *fmt, va_list ap); +/** Initialize screen */ +void cmd_init_screen(void); +/** Get echo state + * \return true if echo is on otherwise false + */ +bool cmd_echo_state(void); +/** Echo off */ +void cmd_echo_off(void); +/** Echo on */ +void cmd_echo_on(void); +/** Enter character to console. + * insert key pressess to cmdline called from main loop of application + * \param u_data char to be added to console + */ +void cmd_char_input(int16_t u_data); +/* + * Set the passthrough mode callback function. In passthrough mode normal command input handling is skipped and any + * received characters are passed to the passthrough callback function. Setting this to null will disable passthrough mode. + * \param passthrough_fnc The passthrough callback function + */ +typedef void (*input_passthrough_func_t)(uint8_t c); +void cmd_input_passthrough_func(input_passthrough_func_t passthrough_fnc); + +/* Methods used for adding and handling of commands and aliases + */ + +/** Callback called when your command is run. + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + */ +typedef int (cmd_run_cb)(int argc, char *argv[]); +/** Add command to intepreter + * \param name command string + * \param callback This function is called when command line start executing + * \param info Command short description which is visible in help command, or null if not in use + * \param man Help page for this command. This is shown when executing command with invalid parameters or command with --help parameter. Can be null if not in use. + */ +void cmd_add(const char *name, cmd_run_cb *callback, const char *info, const char *man); + +/** delete command from intepreter + * \param name command to be delete + */ +void cmd_delete(const char *name); +/** Command executer. + * Command executer, which split&push command(s) to the buffer and + * start executing commands in cmd tasklet. + * if not, execute command directly. + * If command implementation returns CMDLINE_RETCODE_EXCUTING_CONTINUE, + * executor will wait for cmd_ready() before continue to next command. + * \param str command string, e.g. "help" + */ +void cmd_exe(char *str); +/** Add alias to interpreter. + * Aliases are replaced with values before executing a command. All aliases must be started from beginning of line. + * null or empty value deletes alias. + * \code + cmd_alias_add("print", "echo"); + cmd_exe("print \"hello world!\""); // this is now same as "echo \"hello world!\"" . + * \endcode + * \param alias alias name + * \param value value for alias. Values can be any visible ASCII -characters. + */ +void cmd_alias_add(const char *alias, const char *value); +/** Add Variable to interpreter. + * Variables are replaced with values before executing a command. + * To use variables from cli, use dollar ($) -character so that interpreter knows user want to use variable in that place. + * null or empty value deletes variable. + * \code + cmd_variable_add("world", "hello world!"); + cmd_exe("echo $world"); // this is now same as echo "hello world!" . + * \endcode + * \param variable Variable name, which will be replaced in interpreter. + * \param value Value for variable. Values can contains white spaces and '"' or '"' characters. + */ +void cmd_variable_add(char *variable, char *value); + +/** find command parameter index by key. + * e.g. + * \code + int main(void){ + //..init cmd.. + //.. + cmd_exe("mycmd enable") + } + int mycmd_command(int argc, char *argv[]) { + bool found = cmd_parameter_index( argc, argv, "enable" ) > 0; + } + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key option key, which index you want to find out. + * \return index where parameter was or -1 when not found + */ +int cmd_parameter_index(int argc, char *argv[], const char *key); +/** check if command option is present. + * e.g. cmd: "mycmd -c" + * \code + * bool on = cmd_has_option( argc, argv, "p" ); + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key option key to be find + * \return true if option found otherwise false + */ +bool cmd_has_option(int argc, char *argv[], const char *key); +/** find command parameter by key. + * if exists, return true, otherwise false. + * e.g. cmd: "mycmd enable 1" + * \code + int mycmd_command(int argc, char *argv[]) { + bool value; + bool found = cmd_parameter_bool( argc, argv, "mykey", &value ); + if( found ) return CMDLINE_RETCODE_SUCCESS; + else return CMDLINE_RETCODE_FAIL; + } + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key parameter key to be find + * \param value parameter value to be fetch, if key not found value are untouched. "1" and "on" and "true" and "enable" and "allow" are True -value, all others false. + * \return true if parameter key and value found otherwise false + */ +bool cmd_parameter_bool(int argc, char *argv[], const char *key, bool *value); +/** find command parameter by key and return value (next parameter). + * if exists, return parameter pointer, otherwise null. + * e.g. cmd: "mycmd mykey myvalue" + * \code + int mycmd_command(int argc, char *argv[]) { + char *value; + bool found = cmd_parameter_val( argc, argv, "mykey", &value ); + if( found ) return CMDLINE_RETCODE_SUCCESS; + else return CMDLINE_RETCODE_FAIL; + } + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key parameter key to be find + * \param value pointer to pointer, which will point to cli input data when key and value found. if key or value not found this parameter are untouched. + * \return true if parameter key and value found otherwise false + */ +bool cmd_parameter_val(int argc, char *argv[], const char *key, char **value); +/** find command parameter by key and return value (next parameter) in integer. Only whitespaces are allowed in addition to the float to be read. + * e.g. cmd: "mycmd mykey myvalue" + * \code + int32_t value; + cmd_parameter_int( argc, argv, "key", &value ); + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the item 0 in the list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key parameter key to be found + * \param value A pointer to a variable where to write the converted number. If value cannot be converted, it is not touched. + * \return true if parameter key and an integer is found, otherwise return false + */ +bool cmd_parameter_int(int argc, char *argv[], const char *key, int32_t *value); +/** find command parameter by key and return value (next parameter) in float. Only whitespaces are allowed in addition to the float to be read. + * e.g. cmd: "mycmd mykey myvalue" + * \code + float value; + cmd_parameter_float( argc, argv, "key", &value ); + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. values begin from 1 and this means that the item 0 in the list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key parameter key to be found + * \param value A pointer to a variable where to write the converted number. If value cannot be converted, it is not touched. + * \return true if parameter key and a float found, otherwise return false + */ +bool cmd_parameter_float(int argc, char *argv[], const char *key, float *value); +/** Get last command line parameter as string. + * e.g. + * cmd: "mycmd hello world" + * cmd_parameter_last -> "world" + * cmd: "mycmd" + * cmd_parameter_last() -> NULL + * \code + cmd_parameter_last(argc, argv) + * \endcode + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \return pointer to last parameter or NULL when there is no any parameters. + */ +char *cmd_parameter_last(int argc, char *argv[]); + +/** find command parameter by key and return value (next parameter) in int64. + * e.g. cmd: "mycmd mykey myvalue" + * \code + uint32_t i; + cmd_parameter_timestamp( argc, argv, "mykey", &i ); + * \endcode + * + * Supports following formats: + * number -> direct conversion + * 11:22:33:44:55:66:77:88 -> converts to number + * seconds,tics -> converts thread type timestamp to int64 + * + * \param argc argc is the count of arguments given in argv pointer list. value begins from 1 and this means that the 0 item in list argv is a string to name of command. + * \param argv is list of arguments. List size is given in argc parameter. Value in argv[0] is string to name of command. + * \param key parameter key to be find + * \param value parameter value to be fetch, if key not found value are untouched. + * \return true if parameter key and value found otherwise false + */ +bool cmd_parameter_timestamp(int argc, char *argv[], const char *key, int64_t *value); + +#ifdef __cplusplus +} +#endif +#endif /*_CMDLINE_H_*/ diff --git a/module.json b/module.json new file mode 100644 index 00000000000..89d45561514 --- /dev/null +++ b/module.json @@ -0,0 +1,33 @@ +{ + "name": "mbed-client-cli", + "version": "0.3.0", + "description": "Command Line Library for mbedOS", + "keywords": [ + "cli", + "client", + "cmd" + ], + "author": "Jussi Vatjus-Anttila", + "repository": { + "url": "https://github.com/ARMmbed/mbed-client-cli.git", + "type": "git" + }, + "homepage": "https://github.com/ARMmbed/mbed-client-cli.git", + "licenses": [ + { + "url": "https://spdx.org/licenses/Apache-2.0", + "type": "Apache-2.0" + } + ], + "dependencies": { + "mbed-trace": "^1.1.2" + }, + "testTargetDependencies": { + "x86-linux-native": { + "cpputest": "ARMmbed/cpputest" + }, + "x86-windows-native": { + "cpputest": "ARMmbed/cpputest" + } + } +} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt new file mode 100644 index 00000000000..d0a7475b832 --- /dev/null +++ b/source/CMakeLists.txt @@ -0,0 +1,14 @@ +if(DEFINED TARGET_LIKE_X86_LINUX_NATIVE) + add_library( mbed-client-cli + ns_cmdline.c + ns_list_internal/ns_list.c + ) + add_definitions("-g -O0 -fprofile-arcs -ftest-coverage") + target_link_libraries(mbed-client-cli gcov) +else() + add_library( mbed-client-cli + ns_cmdline.c + ns_list_internal/ns_list.c + ) + target_link_libraries(mbed-client-cli) +endif() diff --git a/source/ns_cmdline.c b/source/ns_cmdline.c new file mode 100644 index 00000000000..55e4d000a12 --- /dev/null +++ b/source/ns_cmdline.c @@ -0,0 +1,1829 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 +#include +#include +#include +#include + +#if defined(_WIN32) || defined(__unix__) || defined(__unix) || defined(unix) || defined(YOTTA_CFG) +#include //malloc +#ifndef MEM_ALLOC +#define MEM_ALLOC malloc +#endif +#ifndef MEM_FREE +#define MEM_FREE free +#endif +#else +#include "nsdynmemLIB.h" +#ifndef MEM_ALLOC +#define MEM_ALLOC ns_dyn_mem_temporary_alloc +#endif +#ifndef MEM_FREE +#define MEM_FREE ns_dyn_mem_free +#endif +#endif + +// force traces for this module +//#define FEA_TRACE_SUPPORT + + +#ifdef YOTTA_CFG +#include "ns_list_internal/ns_list.h" +#include "mbed-client-cli/ns_cmdline.h" +#else +#include "ns_list.h" +#include "ns_cmdline.h" +#endif +#include "mbed-trace/mbed_trace.h" + +//#define TRACE_DEEP +//#define TRACE_PRINTF + +#ifdef TRACE_PRINTF +#undef tr_debug +#define tr_debug(...) printf( __VA_ARGS__);printf("\r\n") +#endif + +// MBED_CLIENT_CLI_TRACE_ENABLE is to enable the traces for debugging, +// default all debug traces are disabled. +#ifndef MBED_CLIENT_CLI_TRACE_ENABLE +#undef tr_error +#define tr_error(...) +#undef tr_warn +#define tr_warn(...) +#undef tr_debug +#define tr_debug(...) +#undef tr_info +#define tr_info(...) +#endif + +#ifdef TRACE_DEEP +#define tr_deep tr_debug +#else +#define tr_deep(...) +#endif + +#define TRACE_GROUP "cmdL" + +/*ASCII defines*/ +#define ESC 0x1B +#define DEL 0x7F +#define BS 0x08 +#define ETX 0x03 +#define TAB 0x09 +#define CAN 0x18 + +// Maximum length of input line +#ifdef MBED_CMDLINE_MAX_LINE_LENGTH +#define MAX_LINE MBED_CMDLINE_MAX_LINE_LENGTH +#else +#define MAX_LINE 2000 +#endif +// Maximum number of arguments in a single command +#ifdef MBED_CMDLINE_ARGUMENTS_MAX_COUNT +#define MAX_ARGUMENTS MBED_CMDLINE_ARGUMENTS_MAX_COUNT +#else +#define MAX_ARGUMENTS 30 +#endif +// Maximum number of commands saved in history +#ifdef MBED_CMDLINE_HISTORY_MAX_COUNT +#define HISTORY_MAX_COUNT MBED_CMDLINE_HISTORY_MAX_COUNT +#else +#define HISTORY_MAX_COUNT 32 +#endif + +//include manuals or not (save memory a little when not include) +#define INCLUDE_MAN + + +typedef struct cmd_history_s { + char *command_ptr; + ns_list_link_t link; +} cmd_history_t; + +typedef struct cmd_command_s { + const char *name_ptr; + const char *info_ptr; + const char *man_ptr; + cmd_run_cb *run_cb; + bool busy; + ns_list_link_t link; +} cmd_command_t; + +typedef struct cmd_alias_s { + char *name_ptr; + char *value_ptr; + ns_list_link_t link; +} cmd_alias_t; + +typedef struct cmd_variable_s { + char *name_ptr; + char *value_ptr; + ns_list_link_t link; +} cmd_variable_t; + +typedef enum operator_s { + OPERATOR_SEMI_COLON, //default + OPERATOR_AND, + OPERATOR_OR, + OPERATOR_BACKGROUND, + OPERATOR_PIPE +} operator_t; +typedef struct cmd_exe_s { + char *cmd_s; + operator_t operator; + ns_list_link_t link; +} cmd_exe_t; +typedef NS_LIST_HEAD(cmd_exe_t, link) cmd_list_t; +typedef NS_LIST_HEAD(cmd_history_t, link) history_list_t; +typedef NS_LIST_HEAD(cmd_command_t, link) command_list_t; +typedef NS_LIST_HEAD(cmd_alias_t, link) alias_list_t; +typedef NS_LIST_HEAD(cmd_variable_t, link) variable_list_t; + +typedef struct cmd_class_s { + char input[MAX_LINE]; // input data + char escape[10]; // escape data + int16_t history; // history position + int16_t cursor; // cursor position + int16_t escape_index; // escape index + history_list_t history_list; // input history + uint8_t history_max_count; // history max size + command_list_t command_list; // commands list + alias_list_t alias_list; // alias list + variable_list_t variable_list; // variables list + bool vt100_on; // control characters + bool init; // true when lists are initialized already + bool escaping; // escaping input + bool insert; // insert enabled + int tab_lookup; // originally lookup characters count + int tab_lookup_cmd_n; // index in command list + int tab_lookup_n; // + bool echo; // echo inputs + char *retcode_fmt; // retcode format + bool print_retcode; // print retcode after command is executed + cmd_ready_cb_f *ready_cb; // ready cb function + cmd_list_t cmd_buffer; + cmd_exe_t *cmd_buffer_ptr; + cmd_command_t *cmd_ptr; + int8_t tasklet_id; + int8_t network_tasklet_id; + bool idle; + + cmd_print_t *out; // print cb function + void (*ctrl_fnc)(uint8_t c); // control cb function + void (*mutex_wait_fnc)(void); // mutex wait cb function + void (*mutex_release_fnc)(void); // mutex release cb function + input_passthrough_func_t passthrough_fnc; // input passthrough cb function +} cmd_class_t; + +cmd_class_t cmd = { .init = false, .retcode_fmt = NULL, .cmd_ptr = NULL, .mutex_wait_fnc = NULL, .mutex_release_fnc = NULL, .passthrough_fnc = NULL }; + +/* Function prototypes + */ +static void cmd_init_base_commands(void); +static void cmd_replace_alias(char *input); +static void cmd_replace_variables(char *input); +static int cmd_parse_argv(char *string_ptr, char **argv); +static void cmd_execute(void); +static void cmd_line_clear(int from); +static void cmd_history_save(int16_t index); +static void cmd_history_get(uint16_t index); +static void cmd_history_clean_overflow(void); +static void cmd_history_clean(void); +static void cmd_echo(bool on); +static cmd_history_t *cmd_history_find(int16_t index); +static bool cmd_tab_lookup(void); +static const char *cmd_input_lookup(char *name, int namelength, int n); +static char *cmd_input_lookup_var(char *name, int namelength, int n); +static cmd_command_t *cmd_find(const char *name); +static cmd_command_t *cmd_find_n(char *name, int nameLength, int n); +static cmd_alias_t *alias_find(const char *alias); +static cmd_alias_t *alias_find_n(char *alias, int aliaslength, int n); +static cmd_variable_t *variable_find(char *variable); +static cmd_variable_t *variable_find_n(char *variable, int length, int n); +static void cmd_print_man(cmd_command_t *command_ptr); +static void goto_end_of_history(void); +static void goto_beginning_of_history(void); +static void cmd_set_input(const char *str, int cur); +static void cmd_set_retfmt(char *fmt); +static char *next_command(char *string_ptr, operator_t *mode); +/** Run single command through cmd intepreter + * \param string_ptr command string with parameters + * \ret command return code (CMDLINE_RETCODE_*) + */ +static int cmd_run(char *string_ptr); +static cmd_exe_t *cmd_next_ptr(int retcode); +static void cmd_split(char *string_ptr); +static void cmd_push(char *cmd_str, operator_t oper); + +/*internal shell commands + */ +int echo_command(int argc, char *argv[]); +int alias_command(int argc, char *argv[]); +int set_command(int argc, char *argv[]); +int clear_command(int argc, char *argv[]); +int help_command(int argc, char *argv[]); +int history_command(int argc, char *argv[]); + +void default_cmd_response_out(const char *fmt, va_list ap) +{ + vprintf(fmt, ap); + fflush(stdout); +} +void cmd_printf(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + cmd_vprintf(fmt, ap); + va_end(ap); +} +void cmd_vprintf(const char *fmt, va_list ap) +{ + if (cmd.mutex_wait_fnc) { + cmd.mutex_wait_fnc(); + } + cmd.out(fmt, ap); + if (cmd.mutex_release_fnc) { + cmd.mutex_release_fnc(); + } +} +/* Function definitions + */ +void cmd_init(cmd_print_t *outf) +{ + if (!cmd.init) { + ns_list_init(&cmd.alias_list); + ns_list_init(&cmd.history_list); + ns_list_init(&cmd.command_list); + ns_list_init(&cmd.variable_list); + ns_list_init(&cmd.cmd_buffer); + cmd.init = true; + } + cmd.out = outf ? outf : default_cmd_response_out; + cmd.ctrl_fnc = NULL; + cmd.echo = true; + cmd.print_retcode = false; + cmd.escaping = false; + cmd.insert = true; + cmd.cursor = 0; + cmd.vt100_on = true; + cmd.history_max_count = HISTORY_MAX_COUNT; + cmd.tab_lookup = 0; + cmd.tab_lookup_cmd_n = 0; + cmd.tab_lookup_n = 0; + cmd.cmd_buffer_ptr = 0; + cmd.idle = true; + cmd.ready_cb = cmd_next; + cmd.passthrough_fnc = NULL; + cmd_set_retfmt("retcode: %i\r\n"); + cmd_line_clear(0); // clear line + cmd_history_save(0); // the current line is the 0 item + //cmd_free(); + cmd_init_base_commands(); + cmd_init_screen(); + return; +} + +#ifdef INCLUDE_MAN +#define MAN_ECHO "Displays messages, or turns command echoing on or off\r\n"\ + "echo \r\n"\ + "some special parameters:\r\n"\ + " On/Off echo input characters\r\n" +#define MAN_ALIAS "alias \r\n" +#define MAN_SET "set \r\n"\ + "some special parameters\r\n"\ + "--vt100 On/Off vt100 controls\r\n"\ + "--retcode On/Off retcode print after execution\r\n"\ + "--retfmt Return print format. Default: \"retcode: %i\\n\"\r\n" + +#define MAN_CLEAR "Clears the display" +#define MAN_HISTORY "Show commands history\r\n"\ + "history ()\r\n"\ + "clear Clear history\r\n" +#else +#define MAN_ECHO NULL +#define MAN_ALIAS NULL +#define MAN_SET NULL +#define MAN_CLEAR NULL +#define MAN_HISTORY NULL +#endif + +static void cmd_init_base_commands(void) +{ + cmd_add("help", help_command, "This help", NULL); + cmd_add("echo", echo_command, "Echo controlling", MAN_ECHO); + cmd_add("alias", alias_command, "Handle aliases", MAN_ALIAS); + cmd_add("set", set_command, "Handle variables", MAN_SET); + cmd_add("clear", clear_command, "Clears the display", MAN_CLEAR); + cmd_add("history", history_command, "View your command Line History", MAN_HISTORY); +} +void cmd_reset(void) +{ + cmd_free(); + cmd_init_base_commands(); +} +void cmd_free(void) +{ + ns_list_foreach_safe(cmd_command_t, cur_ptr, &cmd.command_list) { + cmd_delete(cur_ptr->name_ptr); + } + ns_list_foreach_safe(cmd_alias_t, cur_ptr, &cmd.alias_list) { + cmd_alias_add(cur_ptr->name_ptr, NULL); + } + ns_list_foreach_safe(cmd_variable_t, cur_ptr, &cmd.variable_list) { + cmd_variable_add(cur_ptr->name_ptr, NULL); + } + ns_list_foreach_safe(cmd_history_t, cur_ptr, &cmd.history_list) { + MEM_FREE(cur_ptr->command_ptr); + ns_list_remove(&cmd.history_list, cur_ptr); + MEM_FREE(cur_ptr); + } + cmd.mutex_wait_fnc = NULL; + cmd.mutex_release_fnc = NULL; +} + +void cmd_input_passthrough_func(input_passthrough_func_t passthrough_fnc) +{ + cmd.passthrough_fnc = passthrough_fnc; +} + +void cmd_exe(char *str) +{ + cmd_split(str); + if (cmd.cmd_buffer_ptr == 0) { + //execution buffer is empty + cmd.idle = false; //not really, but fake it + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + tr_debug("previous cmd is still in progress"); + } +} +void cmd_set_ready_cb(cmd_ready_cb_f *cb) +{ + cmd.ready_cb = cb; +} +void cmd_ready(int retcode) +{ + if( cmd.cmd_ptr && cmd.cmd_ptr->busy ) + { + //execution finished + cmd.cmd_ptr->busy = false; + } + if (!cmd.idle) { + if (cmd.cmd_buffer_ptr == NULL) { + tr_debug("goto next command"); + } else { + tr_debug("cmd '%s' executed with retcode: %i", cmd.cmd_buffer_ptr->cmd_s, retcode); + } + if (cmd.ready_cb == NULL) { + tr_warn("Missing ready_cb! use cmd_set_ready_cb()"); + } else { + cmd.ready_cb(retcode); + } + } else { + tr_warn("Someone call cmd_ready(%i) even there shouldn't be any running cmd", retcode); + if (cmd.echo) { + cmd_output(); //refresh if this happens + } + } +} +void cmd_next(int retcode) +{ + cmd.idle = true; + //figure out next command + cmd.cmd_buffer_ptr = cmd_next_ptr(retcode); + if (cmd.cmd_buffer_ptr) { + cmd.idle = false; + //yep there was some -> lets execute it + retcode = cmd_run(cmd.cmd_buffer_ptr->cmd_s); + //check if execution goes to the backend or not + if (retcode == CMDLINE_RETCODE_EXCUTING_CONTINUE ) { + if( (NULL != cmd.cmd_buffer_ptr) && cmd.cmd_buffer_ptr->operator == OPERATOR_BACKGROUND ) + { + //execution continue in background, but operator say that it's "ready" + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + //command execution phase continuous in background + tr_debug("Command execution continuous in background.."); + } + } else { + //execution finished -> call ready function with retcode + cmd_ready(retcode); + } + } else { + if (cmd.print_retcode) { + cmd_printf(cmd.retcode_fmt, retcode); + } + cmd_line_clear(0); + if (cmd.echo) { + cmd_output(); //ready + } + } +} +static cmd_exe_t *cmd_pop(void) +{ + cmd_exe_t *cmd_ptr = ns_list_get_first(&cmd.cmd_buffer), + *next_cmd = ns_list_get_next(&cmd.cmd_buffer, cmd_ptr); + + if (cmd.cmd_buffer_ptr == 0) { + //was first in bool-- + next_cmd = ns_list_get_first(&cmd.cmd_buffer); + } else { + MEM_FREE(cmd_ptr->cmd_s); + ns_list_remove(&cmd.cmd_buffer, cmd_ptr); + MEM_FREE(cmd_ptr); + } + return next_cmd; +} + +static cmd_exe_t *cmd_next_ptr(int retcode) +{ + cmd_exe_t *next_cmd = cmd.cmd_buffer_ptr; + if (ns_list_is_empty(&cmd.cmd_buffer)) { + return NULL; + } + if (cmd.cmd_buffer_ptr == NULL) { + return cmd_pop(); + } + retcode = retcode; + switch (cmd.cmd_buffer_ptr->operator) { + case (OPERATOR_AND): + if (retcode != CMDLINE_RETCODE_SUCCESS) { + //if fails, go to next command, which not have AND operator + while ((next_cmd->operator == OPERATOR_AND) && ((next_cmd = cmd_pop()) != 0)); + } else { + next_cmd = cmd_pop(); + } + break; + case (OPERATOR_OR): + if (retcode == CMDLINE_RETCODE_SUCCESS) { + //if fails, go to next command, which not have OR operator + while ((next_cmd->operator == OPERATOR_OR) && ((next_cmd = cmd_pop()) != 0)); + } else { + next_cmd = cmd_pop(); + } + break; + case (OPERATOR_BACKGROUND): + next_cmd = cmd_pop(); + break; + case (OPERATOR_PIPE): + /// @TODO + case (OPERATOR_SEMI_COLON): + default: + //get next command to be execute (might be null if there is no more) + next_cmd = cmd_pop(); + break; + } + + //return next command if any + return next_cmd; +} +static void cmd_split(char *string_ptr) +{ + char *ptr = string_ptr, *next; + operator_t oper = OPERATOR_SEMI_COLON; + do { + next = next_command(ptr, &oper); + cmd_push(ptr, oper); + ptr = next; + if (next && !*next) { + break; + } + } while (ptr != 0); +} + +static void cmd_push(char *cmd_str, operator_t oper) +{ + //store this command to the stack + cmd_exe_t *cmd_ptr = MEM_ALLOC(sizeof(cmd_exe_t)); + cmd_ptr->cmd_s = MEM_ALLOC(strlen(cmd_str) + 1); + strcpy(cmd_ptr->cmd_s, cmd_str); + cmd_ptr->operator = oper; + ns_list_add_to_end(&cmd.cmd_buffer, cmd_ptr); +} +void cmd_out_func(cmd_print_t *outf) +{ + cmd.out = outf; +} +void cmd_ctrl_func(void (*sohf)(uint8_t c)) +{ + cmd.ctrl_fnc = sohf; +} + +void cmd_mutex_wait_func(void (*mutex_wait_f)(void)) +{ + cmd.mutex_wait_fnc = mutex_wait_f; +} +void cmd_mutex_release_func(void (*mutex_release_f)(void)) +{ + cmd.mutex_release_fnc = mutex_release_f; +} + +void cmd_mutex_lock() +{ + if (cmd.mutex_wait_fnc) { + cmd.mutex_wait_fnc(); + } +} + +void cmd_mutex_unlock() +{ + if (cmd.mutex_release_fnc) { + cmd.mutex_release_fnc(); + } +} + +void cmd_init_screen() +{ + if (cmd.vt100_on) { + cmd_printf("\r\x1b[2J"); /* Clear screen */ + cmd_printf("\x1b[7h"); /* enable line wrap */ + } + cmd_printf("ARM Ltd\r\n"); + cmd_output(); +} +uint8_t cmd_history_size(uint8_t max) +{ + if (max > 0) { + cmd.history_max_count = max; + cmd_history_clean_overflow(); + } + return cmd.history_max_count; +} +static void cmd_echo(bool on) +{ + cmd.echo = on; +} +bool cmd_echo_state(void) +{ + return cmd.echo; +} +static cmd_command_t *cmd_find_n(char *name, int nameLength, int n) +{ + cmd_command_t *cmd_ptr = NULL; + int i = 0; + if (name != NULL && nameLength != 0) { + ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) { + if (strncmp(name, cur_ptr->name_ptr, nameLength) == 0) { + if (i == n) { + cmd_ptr = cur_ptr; + break; + } + i++; + } + } + } + return cmd_ptr; +} +static const char *cmd_input_lookup(char *name, int namelength, int n) +{ + const char *str = NULL; + cmd_command_t *cmd_ptr = cmd_find_n(name, namelength, n); + if (cmd_ptr) { + str = cmd_ptr->name_ptr; + cmd.tab_lookup_n = n + 1; + } else { + n -= cmd.tab_lookup_n; + cmd_alias_t *alias = alias_find_n(name, namelength, n); + if (alias) { + str = (const char*)alias->name_ptr; + } + } + + return str; +} +static char *cmd_input_lookup_var(char *name, int namelength, int n) +{ + char *str = NULL; + cmd_variable_t *var = variable_find_n(name, namelength, n); + if (var) { + str = var->name_ptr; + } + return str; +} +static cmd_command_t *cmd_find(const char *name) +{ + cmd_command_t *cmd_ptr = NULL; + if (name == NULL || strlen(name) == 0) { + tr_error("cmd_find invalid parameters"); + return NULL; + } + + ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) { + if (strcmp(name, cur_ptr->name_ptr) == 0) { + cmd_ptr = cur_ptr; + break; + } + } + return cmd_ptr; +} + +void cmd_add(const char *name, cmd_run_cb *callback, const char *info, const char *man) +{ + cmd_command_t *cmd_ptr; + + if (name == NULL || callback == NULL || strlen(name) == 0) { + tr_warn("cmd_add invalid parameters"); + return; + } + cmd_ptr = (cmd_command_t *)MEM_ALLOC(sizeof(cmd_command_t)); + cmd_ptr->name_ptr = name; + cmd_ptr->info_ptr = info; + cmd_ptr->man_ptr = man; + cmd_ptr->run_cb = callback; + cmd_ptr->busy = false; + ns_list_add_to_end(&cmd.command_list, cmd_ptr); + return; +} + +void cmd_delete(const char *name) +{ + cmd_command_t *cmd_ptr; + cmd_ptr = cmd_find(name); + if (cmd_ptr == NULL) { + return; + } + ns_list_remove(&cmd.command_list, cmd_ptr); + MEM_FREE(cmd_ptr); + return; +} + +static int cmd_parse_argv(char *string_ptr, char **argv) +{ + int argc = 0; + char *str_ptr, *end_quote_ptr = NULL; + + if (string_ptr == NULL || strlen(string_ptr) == 0) { + tr_error("Invalid parameters"); + return 0; + } + str_ptr = string_ptr; + do { + argv[argc] = str_ptr; + if (*str_ptr == '"') { + // check if end quote + end_quote_ptr = strstr(str_ptr + 1, "\""); + } + if (*str_ptr == '"' && end_quote_ptr != NULL) { + // remove quotes give as one parameter + argv[argc]++; + str_ptr = end_quote_ptr; + } else { + str_ptr = strstr(str_ptr, " "); + } + argc++; + if (str_ptr == NULL) { + break; + } + if (argc > MAX_ARGUMENTS) { + break; + } + *str_ptr++ = 0; + while (*str_ptr == ' ') { + str_ptr++; // skip spaces + } + } while (*str_ptr != 0); + return argc; +} +static void cmd_print_man(cmd_command_t *command_ptr) +{ + if (command_ptr->man_ptr) { + cmd_printf("%s\r\n", command_ptr->man_ptr); + } +} +static void cmd_set_input(const char *str, int cur) +{ + cmd_line_clear(cur); + strcpy(cmd.input + cur, str); + cmd.cursor = strlen(cmd.input); +} +/** + * If oper is not null, function set null pointers + */ +static char *next_command(char *string_ptr, operator_t *oper) +{ + char *ptr = string_ptr; + bool quote = false; + while (*ptr != 0) { + if (quote) { + if (*ptr == '"') { + quote = false; + } + } else { + //skip if previous character is '\' + if ((ptr == string_ptr) || (*(ptr - 1) != '\\')) { + switch (*ptr) { + case ('"'): { + quote = true; + break; + } + case (';'): //default operator + if (oper) { + *oper = OPERATOR_SEMI_COLON; + *ptr = 0; + } + return ptr + 1; + case ('&'): + if (ptr[1] == '&') { + if (oper) { + *oper = OPERATOR_AND; + *ptr = 0; + } + return ptr + 2; + } else { + if (oper) { + *oper = OPERATOR_BACKGROUND; + *ptr = 0; + } + return ptr + 1; + } + case ('|'): + if (ptr[1] == '|') { + tr_warn("or operator not supported"); + if (oper) { + *oper = OPERATOR_OR; + *ptr = 0; + } + return ptr + 2; + } else { + tr_warn("pipe operator not supported"); + if (oper) { + *oper = OPERATOR_PIPE; + *ptr = 0; + } + return ptr + 1; + } + default: + break; + } + } + } + ptr++; + } + return 0; +} +static int cmd_run(char *string_ptr) +{ + char *argv[MAX_ARGUMENTS]; + int argc, ret; + + tr_info("Executing cmd: '%s'", string_ptr); + char *command_str = MEM_ALLOC(MAX_LINE); + while (isspace((unsigned char) *string_ptr) && + *string_ptr != '\n' && + *string_ptr != 0) { + string_ptr++; //skip white spaces + } + strcpy(command_str, string_ptr); + tr_deep("cmd_run('%s') ", command_str); + cmd_replace_alias(command_str); + cmd_replace_variables(command_str); + tr_debug("Parsed cmd: '%s'", command_str); + + argc = cmd_parse_argv(command_str, argv); + + cmd.cmd_ptr = cmd_find(argv[0]); + + if (cmd.cmd_ptr == NULL) { + cmd_printf("Command '%s' not found.\r\n", argv[0]); + MEM_FREE(command_str); + return CMDLINE_RETCODE_COMMAND_NOT_FOUND; + } + if (cmd.cmd_ptr->run_cb == NULL) { + tr_error("Command callback missing"); + MEM_FREE(command_str); + return CMDLINE_RETCODE_COMMAND_CB_MISSING; + } + + if (argc == 2 && + (cmd_has_option(argc, argv, "h") || cmd_parameter_index(argc, argv, "--help") > 0)) { + MEM_FREE(command_str); + cmd_print_man(cmd.cmd_ptr); + return CMDLINE_RETCODE_SUCCESS; + } + + if( cmd.cmd_ptr->busy ) { + MEM_FREE(command_str); + return CMDLINE_RETCODE_COMMAND_BUSY; + } + + // Run the actual callback + cmd.cmd_ptr->busy = true; + ret = cmd.cmd_ptr->run_cb(argc, argv); +#ifndef TEST + cmd_alias_add("_", command_str); // last executed command +#endif + MEM_FREE(command_str); + switch (ret) { + case (CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED): + tr_warn("Command not implemented"); + break; + case (CMDLINE_RETCODE_INVALID_PARAMETERS): + tr_warn("Command parameter was incorrect"); + cmd_printf("Invalid parameters!\r\n"); + cmd_print_man(cmd.cmd_ptr); + break; + default: + break; + } + return ret; +} + +void cmd_escape_start(void) +{ + cmd.escaping = true; + memset(cmd.escape, 0, sizeof(cmd.escape)); + cmd.escape_index = 0; +} +char *strrevchr(const char *begin, const char *end, char ch) +{ + char *ptr = (char *)end; + while (begin <= ptr) { + if (*ptr == ch) { + return ptr; + } + if (*ptr == 0) { + return ptr; + } + ptr--; + } + return 0; +} +void cmd_escape_read(int16_t u_data) +{ + int16_t old_entry; + + tr_debug("cmd_escape_read: %02x '%c'", u_data, (isprint(u_data) ? u_data : '?')); + + if (u_data == '[') { + /*first character for longer escape sequence ends in character*/ + cmd.escape[cmd.escape_index++] = '['; + return; + } + if (u_data == 'D') { + /* Arrow left*/ + if ((cmd.escape_index == 1 && cmd.escape[0] == 'O') || + (cmd.escape_index == 4 && strncmp(cmd.escape + 1, "1;5", 3) == 0)) { + + char *ptr = cmd.input + cmd.cursor; + if (*ptr == ' ' || *ptr == 0) { + ptr--; + } + ptr = strrevchr(cmd.input, ptr, ' '); //@todo not working way that we want + if (ptr) { + cmd.cursor = ptr - cmd.input; + } else { + cmd.cursor = 0; + } + } else { + cmd.cursor --; + } + if (cmd.cursor < 0) { + cmd.cursor = 0; + } + } else if (u_data == 'C') { + /* Arrow Right*/ + if ((cmd.escape_index == 1 && cmd.escape[0] == 'O') || + (cmd.escape_index == 4 && strncmp(cmd.escape + 1, "1;5", 3) == 0)) { + char *ptr = cmd.input + cmd.cursor; + if (*ptr == ' ') { + ptr++; + } + ptr = strchr(ptr, ' '); + if (ptr) { + cmd.cursor = ptr - cmd.input; + } else { + cmd.cursor = strlen(cmd.input); + } + } else { + cmd.cursor ++; + } + if ((int)cmd.cursor > (int)strlen(cmd.input)) { + cmd.cursor = strlen(cmd.input); + } + } else if (u_data == 'A') { + /* Arrow Up*/ + old_entry = cmd.history++; + if (NULL == cmd_history_find(cmd.history)) { + cmd.history = old_entry; + } + if (old_entry != cmd.history) { + cmd_history_save(old_entry); + cmd_history_get(cmd.history); + } + } else if (u_data == 'B') { + /* Arrow Down*/ + old_entry = cmd.history--; + if (cmd.history < 0) { + cmd.history = 0; + } + + if (old_entry != cmd.history) { + cmd_history_save(old_entry); + cmd_history_get(cmd.history); + } + } else if (u_data == 'Z') { + // Shift+TAB + if (cmd.tab_lookup > 0) { + cmd.cursor = cmd.tab_lookup; + cmd.input[cmd.tab_lookup] = 0; + if (cmd.tab_lookup_cmd_n > 0) { + cmd.tab_lookup_cmd_n--; + } + } + cmd_tab_lookup(); + } else if (u_data == 'H') { + // Xterm support + cmd.cursor = 0; + } else if (u_data == 'F') { + // Xterm support + cmd.cursor = strlen(cmd.input); + } else if (isdigit((int)cmd.escape[cmd.escape_index - 1]) && u_data == '~') { + switch (cmd.escape[cmd.escape_index - 1]) { + case ('1'): //beginning-of-line # Home key + cmd.cursor = 0; + break; + case ('2'): //quoted-insert # Insert key + cmd.insert = !cmd.insert; + break; + case ('3'): //delete-char # Delete key + if ((int)strlen(cmd.input) > (int)cmd.cursor) { + memmove(&cmd.input[cmd.cursor], &cmd.input[cmd.cursor + 1], strlen(&cmd.input[cmd.cursor + 1]) + 1); + } + break; + case ('4'): //end-of-line # End key + cmd.cursor = strlen(cmd.input); + break; + case ('5'): //beginning-of-history # PageUp key + goto_end_of_history(); + break; + case ('6'): //end-of-history # PageDown key + goto_beginning_of_history(); + break; + default: + break; + } + } else if (isprint(u_data)) { //IS_NUMBER || IS_CONTROL + cmd.escape[cmd.escape_index++] = u_data; + return; + } + cmd_output(); + cmd.escaping = false; + return; +} +static void goto_end_of_history(void) +{ + // handle new input if any and verify that + // it is not already in beginning of history or current position + bool allowStore = strlen(cmd.input) != 0; //avoid store empty lines to history + cmd_history_t *entry_ptr; + if (cmd.history > 0 && allowStore) { + entry_ptr = cmd_history_find(cmd.history); + if (entry_ptr) { + if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) { + // current history contains contains same text as input + allowStore = false; + } + } + } else if (allowStore && (entry_ptr = cmd_history_find(0)) != NULL) { + if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) { + //beginning of history was same text as input + allowStore = false; + } + } + if (allowStore) { + cmd_history_save(0); // new is saved to place 0 + cmd_history_save(-1); // new is created to the current one + } + + cmd_history_t *cmd_ptr = ns_list_get_last(&cmd.history_list); + cmd_set_input(cmd_ptr->command_ptr, 0); + cmd.history = ns_list_count(&cmd.history_list) - 1; +} +static void goto_beginning_of_history(void) +{ + cmd_history_t *cmd_ptr = ns_list_get_first(&cmd.history_list); + cmd_set_input(cmd_ptr->command_ptr, 0); + cmd.history = 0; +} +static void cmd_reset_tab(void) +{ + cmd.tab_lookup = 0; + cmd.tab_lookup_cmd_n = 0; + cmd.tab_lookup_n = 0; +} +void cmd_char_input(int16_t u_data) +{ + /*Handle passthrough*/ + if (cmd.passthrough_fnc != NULL) { + cmd.passthrough_fnc(u_data); + return; + } + + /*handle ecape command*/ + if (cmd.escaping == true) { + cmd_escape_read(u_data); + return; + } + tr_debug("input char: %02x '%c', cursor: %i, input: \"%s\"", u_data, (isprint(u_data) ? u_data : ' '), cmd.cursor, cmd.input); + + /*Normal character input*/ + if (u_data == '\r' || u_data == '\n') { + cmd_reset_tab(); + if (strlen(cmd.input) == 0) { + if (cmd.echo) { + cmd_printf("\r\n"); + cmd_output(); + } + } else { + if (cmd.echo) { + cmd_printf("\r\n"); + } + cmd_execute(); + } + } else if (u_data == ESC) { + cmd_escape_start(); + } else if (u_data == BS || u_data == DEL) { + cmd_reset_tab(); + cmd.cursor--; + if (cmd.cursor < 0) { + cmd.cursor = 0; + return; + } + memmove(&cmd.input[cmd.cursor], &cmd.input[cmd.cursor + 1], strlen(&cmd.input[cmd.cursor + 1]) + 1); + if (cmd.echo) { + cmd_output(); + } + } else if (u_data == ETX || u_data == CAN) { + //ctrl+c (End of text) or ctrl+x (cancel) + cmd_reset_tab(); + cmd_line_clear(0); + if (!cmd.idle) { + cmd_ready(CMDLINE_RETCODE_FAIL); + } + if (cmd.echo) { + cmd_output(); + } + } else if (u_data == TAB) { + bool inc = false; + if (cmd.tab_lookup > 0) { + cmd.cursor = cmd.tab_lookup; + cmd.input[cmd.tab_lookup] = 0; + cmd.tab_lookup_cmd_n++; + inc = true; + } else { + cmd.tab_lookup = strlen(cmd.input); + } + + if (!cmd_tab_lookup()) { + if (inc) { + cmd.tab_lookup_cmd_n--; + } + memset(cmd.input + cmd.tab_lookup, 0, MAX_LINE - cmd.tab_lookup); + } + if (cmd.echo) { + cmd_output(); + } + + } else if (iscntrl(u_data)) { + if (cmd.ctrl_fnc) { + cmd.ctrl_fnc(u_data); + } + } else { + cmd_reset_tab(); + tr_deep("cursor: %d, inputlen: %d, u_data: %c\r\n", cmd.cursor, strlen(cmd.input), u_data); + if ((strlen(cmd.input) >= MAX_LINE - 1) || (cmd.cursor >= MAX_LINE - 1)) { + tr_warn("input buffer full"); + if (cmd.echo) { + cmd_output(); + } + return; + } + if (cmd.insert) { + memmove(&cmd.input[cmd.cursor + 1], &cmd.input[cmd.cursor], strlen(&cmd.input[cmd.cursor]) + 1); + } + cmd.input[cmd.cursor++] = u_data; + if (cmd.echo) { + cmd_output(); + } + } +} +static int check_variable_keylookup_size(char **key, int *keysize) +{ + if (cmd.cursor > 0 && cmd.tab_lookup > 0) { + //printf("tab_lookup: %i\r\n", cmd.tab_lookup); + char *ptr = cmd.input + cmd.tab_lookup; + do { + //printf("varkey lookup: %c\r\n", *ptr); + if (*ptr == ' ') { + return 0; + } + if (*ptr == '$') { + int varlen = cmd.tab_lookup - (ptr - cmd.input) - 1; + *key = ptr; + *keysize = varlen; + //printf("varkey size: %i\r\n", varlen); + return (ptr - cmd.input); + } + ptr--; + } while (ptr > cmd.input); + } + return 0; +} +bool cmd_tab_lookup(void) +{ + int len = strlen(cmd.input); + if (len == 0) { + return false; + } + + char *variable_keypart; + int lookupSize; + int varpos = check_variable_keylookup_size(&variable_keypart, &lookupSize); + + const char *str = NULL; + if (varpos) { + str = cmd_input_lookup_var(variable_keypart + 1, lookupSize, cmd.tab_lookup_cmd_n); + if (str) { + cmd_set_input(str, varpos + 1); + return true; + } + } else { + str = cmd_input_lookup(cmd.input, len, cmd.tab_lookup_cmd_n); + if (str != NULL) { + cmd_set_input(str, 0); + return true; + } + } + + return false; +} +void cmd_output(void) +{ + if (cmd.vt100_on && cmd.idle) { + cmd_printf("\r\x1b[2K/>%s \x1b[%dD", cmd.input, (int)strlen(cmd.input) - cmd.cursor + 1); + } +} +void cmd_echo_off(void) +{ + cmd.echo = false; +} +void cmd_echo_on(void) +{ + cmd.echo = true; +} +// alias +#ifndef TEST +static +#endif +int replace_alias(char *str, const char *old_str, const char *new_str) +{ + int old_len = strlen(old_str), + new_len = strlen(new_str); + if ((strncmp(str, old_str, old_len) == 0) && + ((str[ old_len ] == ' ') || (str[ old_len ] == 0) || + (str[ old_len ] == ';') || (str[ old_len ] == '&'))) { + memmove(str + new_len, str + old_len, strlen(str + old_len) + 1); + memcpy(str, new_str, new_len); + return new_len - old_len; + } + return 0; +} +static void cmd_replace_alias(char *input) +{ + ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { + replace_alias(input, cur_ptr->name_ptr, cur_ptr->value_ptr); + } +} +//variable +#ifndef TEST +static +#endif +void replace_variable(char *str, const char *old_str, const char *new_str) +{ + char *ptr = str; + int old_len = strlen(old_str), + new_len = strlen(new_str); + if (old_len > 0) { + while ((ptr = strstr(ptr, old_str)) != 0) { + if (ptr > str && *(ptr - 1) == '$') { + memmove(ptr + new_len - 1, ptr + old_len, strlen(ptr + old_len) + 1); + memcpy(ptr - 1, new_str, new_len); + } + ptr++; + } + } +} +void cmd_replace_variables(char *input) +{ + ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { + replace_variable(input, cur_ptr->name_ptr, cur_ptr->value_ptr); + } +} +//history +static void cmd_history_item_delete(cmd_history_t *entry_ptr) +{ + ns_list_remove(&cmd.history_list, entry_ptr); + MEM_FREE(entry_ptr->command_ptr); + MEM_FREE(entry_ptr); +} + +static cmd_history_t *cmd_history_find(int16_t index) +{ + cmd_history_t *entry_ptr = NULL; + int16_t count = 0; + ns_list_foreach(cmd_history_t, cur_ptr, &cmd.history_list) { + if (count == index) { + entry_ptr = cur_ptr; + break; + } + count++; + } + return entry_ptr; +} + +static void cmd_history_clean_overflow(void) +{ + while (ns_list_count(&cmd.history_list) > cmd.history_max_count) { + cmd_history_t *cmd_ptr = ns_list_get_last(&cmd.history_list); + tr_debug("removing older history (%s)", cmd_ptr->command_ptr); + cmd_history_item_delete(cmd_ptr); + } +} +static void cmd_history_clean(void) +{ + while (ns_list_count(&cmd.history_list) > 0) { + tr_debug("removing older history"); + cmd_history_item_delete(ns_list_get_last(&cmd.history_list)); + } +} +static void cmd_history_save(int16_t index) +{ + /*if entry true save it to first item which is the one currently edited*/ + cmd_history_t *entry_ptr; + int16_t len; + + len = strlen(cmd.input); + + tr_debug("saving history item %d", index); + entry_ptr = cmd_history_find(index); + + if (entry_ptr == NULL) { + /*new entry*/ + entry_ptr = (cmd_history_t *)MEM_ALLOC(sizeof(cmd_history_t)); + entry_ptr->command_ptr = NULL; + ns_list_add_to_start(&cmd.history_list, entry_ptr); + } + + if (entry_ptr->command_ptr != NULL) { + MEM_FREE(entry_ptr->command_ptr); + } + entry_ptr->command_ptr = (char *)MEM_ALLOC(len + 1); + strcpy(entry_ptr->command_ptr, cmd.input); + + cmd_history_clean_overflow(); +} +static void cmd_history_get(uint16_t index) +{ + cmd_history_t *entry_ptr; + + tr_debug("getting history item %d", index); + + entry_ptr = cmd_history_find(index); + + if (entry_ptr != NULL) { + memset(cmd.input, 0, MAX_LINE); + cmd_set_input(entry_ptr->command_ptr, 0); + } +} + +static void cmd_line_clear(int from) +{ + memset(cmd.input + from, 0, MAX_LINE - from); + cmd.cursor = from; +} + +static void cmd_execute(void) +{ + if (strlen(cmd.input) != 0) { + bool noduplicate = true; + cmd_history_t *entry_ptr = cmd_history_find(0); + if (entry_ptr) { + if (strcmp(entry_ptr->command_ptr, cmd.input) == 0) { + noduplicate = false; + } + } + if (noduplicate) { + cmd_history_save(0); // new is saved to place 0 + cmd_history_save(-1); // new is created to the current one + } + } + cmd.history = 0; + + tr_deep("cmd_execute('%s') ", cmd.input); + cmd_exe(cmd.input); + cmd_line_clear(0); +} + + +static cmd_alias_t *alias_find(const char *alias) +{ + cmd_alias_t *alias_ptr = NULL; + if (alias == NULL || strlen(alias) == 0) { + tr_error("alias_find invalid parameters"); + return NULL; + } + + ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { + if (strcmp(alias, cur_ptr->name_ptr) == 0) { + alias_ptr = cur_ptr; + break; + } + } + return alias_ptr; +} + +static cmd_alias_t *alias_find_n(char *alias, int aliaslength, int n) +{ + cmd_alias_t *alias_ptr = NULL; + int i = 0; + if (alias == NULL || strlen(alias) == 0) { + tr_error("alias_find invalid parameters"); + return NULL; + } + + ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { + if (strncmp(alias, cur_ptr->name_ptr, aliaslength) == 0) { + if (i == n) { + alias_ptr = cur_ptr; + break; + } + i++; + } + } + return alias_ptr; +} +static cmd_variable_t *variable_find(char *variable) +{ + cmd_variable_t *variable_ptr = NULL; + if (variable == NULL || strlen(variable) == 0) { + tr_error("variable_find invalid parameters"); + return NULL; + } + + ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { + if (strcmp(variable, cur_ptr->name_ptr) == 0) { + variable_ptr = cur_ptr; + break; + } + } + return variable_ptr; +} +static cmd_variable_t *variable_find_n(char *variable, int length, int n) +{ + cmd_variable_t *variable_ptr = NULL; + if (variable == NULL || strlen(variable) == 0) { + tr_error("variable_find invalid parameters"); + return NULL; + } + int i = 0; + ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { + if (strncmp(variable, cur_ptr->name_ptr, length) == 0) { + if (i == n) { + variable_ptr = cur_ptr; + break; + } + i++; + } + } + return variable_ptr; +} +static void cmd_alias_print_all(void) +{ + ns_list_foreach(cmd_alias_t, cur_ptr, &cmd.alias_list) { + if (cur_ptr->name_ptr != NULL) { + cmd_printf("%-18s'%s'\r\n", cur_ptr->name_ptr, cur_ptr->value_ptr ? cur_ptr->value_ptr : ""); + } + } + return; +} +static void cmd_variable_print_all(void) +{ + ns_list_foreach(cmd_variable_t, cur_ptr, &cmd.variable_list) { + if (cur_ptr->name_ptr != NULL) { + cmd_printf("%-18s'%s'\r\n", cur_ptr->name_ptr, cur_ptr->value_ptr ? cur_ptr->value_ptr : ""); + } + } + return; +} + +void cmd_alias_add(const char *alias, const char *value) +{ + cmd_alias_t *alias_ptr; + if (alias == NULL || strlen(alias) == 0) { + tr_warn("cmd_alias_add invalid parameters"); + return; + } + alias_ptr = alias_find(alias); + if (alias_ptr == NULL) { + if (value == NULL) { + return; // no need to add new null one + } + if (strlen(value) == 0) { + return; // no need to add new empty one + } + alias_ptr = (cmd_alias_t *)MEM_ALLOC(sizeof(cmd_alias_t)); + ns_list_add_to_end(&cmd.alias_list, alias_ptr); + alias_ptr->name_ptr = (char *)MEM_ALLOC(strlen(alias) + 1); + strcpy(alias_ptr->name_ptr, alias); + alias_ptr->value_ptr = NULL; + } + if (value == NULL || strlen(value) == 0) { + // delete this one + ns_list_remove(&cmd.alias_list, alias_ptr); + MEM_FREE(alias_ptr->name_ptr); + MEM_FREE(alias_ptr->value_ptr); + MEM_FREE(alias_ptr); + } else { + // add new or modify + if (alias_ptr->value_ptr != NULL) { + MEM_FREE(alias_ptr->value_ptr); + } + alias_ptr->value_ptr = (char *)MEM_ALLOC(strlen(value) + 1); + strcpy(alias_ptr->value_ptr, value); + } + return; +} +void cmd_variable_add(char *variable, char *value) +{ + cmd_variable_t *variable_ptr; + + if (variable == NULL || strlen(variable) == 0) { + tr_warn("cmd_variable_add invalid parameters"); + return; + } + variable_ptr = variable_find(variable); + if (variable_ptr == NULL) { + if (value == NULL) { + return; // adding null variable + } + if (strlen(value) == 0) { + return; // no need to add new empty one + } + variable_ptr = (cmd_variable_t *)MEM_ALLOC(sizeof(cmd_variable_t)); + ns_list_add_to_end(&cmd.variable_list, variable_ptr); + variable_ptr->name_ptr = (char *)MEM_ALLOC(strlen(variable) + 1); + strcpy(variable_ptr->name_ptr, variable); + variable_ptr->value_ptr = NULL; + } + if (value == NULL || strlen(value) == 0) { + // delete this one + ns_list_remove(&cmd.variable_list, variable_ptr); + MEM_FREE(variable_ptr->name_ptr); + MEM_FREE(variable_ptr->value_ptr); + MEM_FREE(variable_ptr); + } else { + // add new or modify + if (variable_ptr->value_ptr != NULL) { + MEM_FREE(variable_ptr->value_ptr); + } + variable_ptr->value_ptr = (char *)MEM_ALLOC(strlen(value) + 1); + strcpy(variable_ptr->value_ptr, value); + } + return; +} + +static bool is_cmdline_commands(char *command) +{ + if ((strncmp(command, "alias", 5) == 0) || + (strcmp(command, "echo") == 0) || + (strcmp(command, "set") == 0) || + (strcmp(command, "clear") == 0) || + (strcmp(command, "help") == 0)) { + return true; + } + return false; +} +static void cmd_set_retfmt(char *fmt) +{ + if (cmd.retcode_fmt) { + MEM_FREE(cmd.retcode_fmt); + } + cmd.retcode_fmt = MEM_ALLOC(strlen(fmt) + 1); + strcpy(cmd.retcode_fmt, fmt); +} +/*Basic commands for cmd line + * alias + * echo + * set + * clear + * help + */ +int alias_command(int argc, char *argv[]) +{ + if (argc == 1) { + // print all alias + cmd_printf("alias:\r\n"); + cmd_alias_print_all(); + } else if (argc == 2) { + // print alias + if (is_cmdline_commands(argv[1])) { + cmd_printf("Cannot overwrite default commands with alias\r\n"); + return -1; + } + tr_debug("Deleting alias %s", argv[1]); + cmd_alias_add(argv[1], NULL); + } else { + // set alias + tr_debug("Setting alias %s = %s", argv[1], argv[2]); + cmd_alias_add(argv[1], argv[2]); + } + return 0; +} +int set_command(int argc, char *argv[]) +{ + if (argc == 1) { + // print all alias + cmd_printf("variables:\r\n"); + cmd_variable_print_all(); + } else if (argc == 2) { + // print alias + tr_debug("Deleting variable %s", argv[1]); + cmd_variable_add(argv[1], NULL); + } else { + // set alias + tr_debug("Setting variable %s = %s", argv[1], argv[2]); + //handle special cases: vt100 on|off + bool state; + if (cmd_parameter_bool(argc, argv, "--vt100", &state)) { + cmd.vt100_on = state; + return 0; + } + if (cmd_parameter_bool(argc, argv, "--retcode", &state)) { + cmd.print_retcode = state; + return 0; + } + char *str; + if (cmd_parameter_val(argc, argv, "--retfmt", &str)) { + cmd_set_retfmt(str); + return 0; + } + cmd_variable_add(argv[1], argv[2]); + } + return 0; +} +int echo_command(int argc, char *argv[]) +{ + bool printEcho = false; + if (argc == 1) { + printEcho = true; + } else if (argc == 2) { + if (strcmp(argv[1], "off") == 0) { + cmd_echo(false); + printEcho = true; + } else if (strcmp(argv[1], "on") == 0) { + cmd_echo(true); + printEcho = true; + } + } + if( printEcho ) { + cmd_printf("ECHO is %s\r\n", cmd.echo ? "on" : "off"); + } else { + for (int n = 1; n < argc; n++) { + tr_deep("ECHO: %s\r\n", argv[n]); + cmd_printf("%s ", argv[n]); + } + cmd_printf("\r\n"); + } + return 0; +} + +int clear_command(int argc, char *argv[]) +{ + (void)argc; + (void )argv; + + cmd_echo(true); + cmd_init_screen(); + return 0; +} +int help_command(int argc, char *argv[]) +{ + cmd_printf("Commands:\r\n"); + if (argc == 1) { + ns_list_foreach(cmd_command_t, cur_ptr, &cmd.command_list) { + cmd_printf("%-16s%s\r\n", cur_ptr->name_ptr, (cur_ptr->info_ptr ? cur_ptr->info_ptr : "")); + } + } else if (argc == 2) { + cmd_command_t *cmd_ptr = cmd_find(argv[1]); + if (cmd_ptr) { + cmd_printf("Command: %s\r\n", cmd_ptr->name_ptr); + if (cmd_ptr->man_ptr) { + cmd_printf("%s\r\n", cmd_ptr->man_ptr); + } else if (cmd_ptr->info_ptr) { + cmd_printf("%s\r\n", cmd_ptr->info_ptr); + } + } else { + cmd_printf("Command '%s' not found", argv[1]); + } + } + return 0; +} +int history_command(int argc, char *argv[]) +{ + if (argc == 1) { + cmd_printf("History [%i/%i]:\r\n", (int)ns_list_count(&cmd.history_list), cmd.history_max_count); + int i = 0; + ns_list_foreach_reverse(cmd_history_t, cur_ptr, &cmd.history_list) { + cmd_printf("[%i]: %s\r\n", i++, cur_ptr->command_ptr); + } + } else if (argc == 2) { + if (strcmp(argv[1], "clear") == 0) { + cmd_history_clean(); + } else { + cmd_history_size(strtoul(argv[1], 0, 10)); + } + } + return 0; +} + +/** Parameter helping functions + */ +int cmd_parameter_index(int argc, char *argv[], const char *key) +{ + int i = 0; + for (i = 1; i < argc; i++) { + if (strcmp(argv[i], key) == 0) { + return i; + } + } + return -1; +} +bool cmd_has_option(int argc, char *argv[], const char *key) +{ + int i = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] != '-') { + if (strstr(argv[i], key) != 0) { + return true; + } + } + } + return false; +} +bool cmd_parameter_bool(int argc, char *argv[], const char *key, bool *value) +{ + int i = cmd_parameter_index(argc, argv, key); + if (i > 0) { + if (argc > (i + 1)) { + if (strcmp(argv[i + 1], "on") == 0 || + strcmp(argv[i + 1], "1") == 0 || + strcmp(argv[i + 1], "true") == 0 || + strcmp(argv[i + 1], "enable") == 0 || + strcmp(argv[i + 1], "allow") == 0) { + *value = true; + } else { + *value = false; + } + return true; + } + } + return false; +} +bool cmd_parameter_val(int argc, char *argv[], const char *key, char **value) +{ + int i = cmd_parameter_index(argc, argv, key); + if (i > 0) { + if (argc > (i + 1)) { + *value = argv[i + 1]; + return true; + } + } + return false; +} +bool cmd_parameter_int(int argc, char *argv[], const char *key, int32_t *value) +{ + int i = cmd_parameter_index(argc, argv, key); + char* tailptr; + if (i > 0) { + if (argc > (i + 1)) { + *value = strtol(argv[i + 1], &tailptr, 10); + if (0 == *tailptr) { + return true; + } + if (!isspace((unsigned char) *tailptr)) { + return false; + } else { + return true; + } + } + } + return false; +} +bool cmd_parameter_float(int argc, char *argv[], const char *key, float *value) +{ + int i = cmd_parameter_index(argc, argv, key); + char* tailptr; + if (i > 0) { + if (argc > (i + 1)) { + *value = strtof(argv[i + 1], &tailptr); + if (0 == *tailptr) { + return true; //Should be correct read always + } + if (!isspace((unsigned char) *tailptr)) { + return false; //Garbage in tailptr + } else { + return true; //Spaces are fine after float + } + } + } + return false; +} +// convert hex string (eg. "76 ab ff") to binary array +static int string_to_bytes(const char *str, uint8_t *buf, int bytes) +{ + int len = strlen(str); + if( len <= (3*bytes - 1)) { + int i; + for(i=0;i 0) { + if (argc > (i + 1)) { + if (strchr(argv[i + 1],',') != 0) { + // Format seconds,tics + const char splitValue[] = ", "; + char *token; + token = strtok(argv[i + 1], splitValue); + if (token) { + *value = strtoul(token, 0, 10) << 16; + } + token = strtok(NULL, splitValue); + if (token) { + *value |= (0xffff & strtoul(token, 0, 10)); + } + } else if (strchr(argv[i + 1],':') != 0 ) { + // Format 00:00:00:00:00:00:00:00 + uint8_t buf[8]; + if (string_to_bytes(argv[i + 1], buf, 8) == 0) { + *value = read_64_bit(buf); + } else { + cmd_printf("timestamp should be 8 bytes long\r\n"); + } + } else { + // Format uint64 + *value = strtol(argv[i + 1], 0, 10); + } + return true; + } + } + return false; +} +char *cmd_parameter_last(int argc, char *argv[]) +{ + if (argc > 1) { + return argv[ argc - 1 ]; + } + return NULL; +} diff --git a/source/ns_list_internal/ns_list.c b/source/ns_list_internal/ns_list.c new file mode 100644 index 00000000000..81de7bdd55a --- /dev/null +++ b/source/ns_list_internal/ns_list.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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. + */ + +/* + * All functions can be inlined, and definitions are in ns_list.h. + * Define NS_LIST_FN before including it to generate external definitions. + */ +#define NS_LIST_FN extern + +#include "ns_list.h" diff --git a/source/ns_list_internal/ns_list.h b/source/ns_list_internal/ns_list.h new file mode 100644 index 00000000000..1c4071ce0bb --- /dev/null +++ b/source/ns_list_internal/ns_list.h @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 NS_LIST_H_ +#define NS_LIST_H_ + +#include "ns_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** \file + * \brief Linked list support library + * + * The ns_list.h file provides a doubly-linked list/queue, providing O(1) + * performance for all insertion/removal operations, and access to either + * end of the list. + * + * Memory footprint is two pointers for the list head, and two pointers in each + * list entry. It is similar in concept to BSD's TAILQ. + * + * Although the API is symmetrical and O(1) in both directions, due to internal + * pointer design, it is *slightly* more efficient to insert at the end when + * used as a queue, and to iterate forwards rather than backwards. + * + * Example of an entry type that can be stored to this list. + * ~~~ + * typedef struct example_entry + * { + * uint8_t *data; + * uint32_t data_count; + * ns_list_link_t link; + * } + * example_entry_t; + * + * static NS_LIST_HEAD(example_entry_t, link) my_list; + * ns_list_init(&my_list); + * ~~~ + * OR + * ~~~ + * NS_LIST_HEAD(example_entry_t, link) my_list = NS_LIST_INIT(my_list); + * ~~~ + * OR + * ~~~ + * static NS_LIST_DEFINE(my_list, example_entry_t, link); + * ~~~ + * OR + * ~~~ + * typedef NS_LIST_HEAD(example_entry_t, link) example_list_t; + * example_list_t NS_LIST_NAME_INIT(my_list); + * ~~~ + * NOTE: the link field SHALL NOT be accessed by the user. + * + * An entry can exist on multiple lists by having multiple link fields. + * + * All the list operations are implemented as macros, most of which are backed + * by optionally-inline functions. The macros do not evaluate any arguments more + * than once, unless documented. + * + * In macro documentation, `list_t` refers to a list type defined using + * NS_LIST_HEAD(), and `entry_t` to the entry type that was passed to it. + */ + +/** \brief Underlying generic linked list head. + * + * Users should not use this type directly, but use the NS_LIST_HEAD() macro. + */ +typedef struct ns_list { + void *first_entry; ///< Pointer to first entry, or NULL if list is empty + void **last_nextptr; ///< Pointer to last entry's `next` pointer, or + ///< to head's `first_entry` pointer if list is empty +} ns_list_t; + +/** \brief Declare a list head type + * + * This union stores the real list head, and also encodes as compile-time type + * information the offset of the link pointer, and the type of the entry. + * + * Note that type information is compiler-dependent; this means + * ns_list_get_first() could return either `void *`, or a pointer to the actual + * entry type. So `ns_list_get_first()->data` is not a portable construct - + * always assign returned entry pointers to a properly typed pointer variable. + * This assignment will be then type-checked where the compiler supports it, and + * will dereference correctly on compilers that don't support this extension. + * ~~~ + * NS_LIST_HEAD(example_entry_t, link) my_list; + * + * example_entry_t *entry = ns_list_get_first(&my_list); + * do_something(entry->data); + * ~~~ + * Each use of this macro generates a new anonymous union, so these two lists + * have different types: + * ~~~ + * NS_LIST_HEAD(example_entry_t, link) my_list1; + * NS_LIST_HEAD(example_entry_t, link) my_list2; + * ~~~ + * If you need to use a list type in multiple places, eg as a function + * parameter, use typedef: + * ~~~ + * typedef NS_LIST_HEAD(example_entry_t, link) example_list_t; + * + * void example_function(example_list_t *); + * ~~~ + */ +#define NS_LIST_HEAD(entry_type, field) \ +union \ +{ \ + ns_list_t slist; \ + NS_STATIC_ASSERT(offsetof(entry_type, field) <= UINT_FAST8_MAX, "link offset too large") \ + char (*offset)[offsetof(entry_type, field)]; \ + entry_type *type; \ +} + +/// \privatesection +/** \brief Get offset of link field in entry. + * \return `(ns_list_offset_t)` The offset of the link field for entries on the specified list + */ +#define NS_LIST_OFFSET_(list) ((ns_list_offset_t) sizeof *(list)->offset) + +/** \brief Get the entry type. + * \def NS_LIST_TYPE_ + * + * \return The type of entry on the specified list. + * + * Only available if the compiler provides a "typeof" operator. + */ +#if defined __cplusplus && __cplusplus >= 201103L +#define NS_LIST_TYPE_(list) decltype(*(list)->type) +#elif defined __GNUC__ +#define NS_LIST_TYPE_(list) __typeof__(*(list)->type) +#endif + +/** \brief Check for compatible pointer types + * + * Although this can be done portably, the GCC custom version is provided to + * produce a clearer diagnostic, and it produces an error rather than a warning. + * + * The portable version will produce a diagnostic about a pointer mismatch on + * the == inside the sizeof operator. For example ARM/Norcroft C gives the error: + * + * operand types are incompatible ("entry_t *" and "other_t *") + */ +#ifdef CPPCHECK +#define NS_PTR_MATCH_(a, b, str) ((void) 0) +#elif defined __GNUC__ +#define NS_PTR_MATCH_(a, b, str) __extension__ \ + ({ NS_STATIC_ASSERT(__builtin_types_compatible_p(__typeof__ (*(a)), __typeof__ (*(b))), \ + str) }) +#else +#define NS_PTR_MATCH_(a, b, str) ((void) sizeof ((a) == (b))) +#endif + +/** \brief Internal macro to cast returned entry pointers to correct type. + * + * Not portable in C, alas. With GCC or C++11, the "get entry" macros return + * correctly-typed pointers. Otherwise, the macros return `void *`. + * + * The attempt at a portable version would work if the C `?:` operator wasn't + * broken - `x ? (t *) : (void *)` should really have type `(t *)` in C, but + * it has type `(void *)`, which only makes sense for C++. The `?:` is left in, + * in case some day it works. Some compilers may still warn if this is + * assigned to a different type. + */ +#ifdef NS_LIST_TYPE_ +#define NS_LIST_TYPECAST_(list, val) ((NS_LIST_TYPE_(list) *) (val)) +#else +#define NS_LIST_TYPECAST_(list, val) (0 ? (list)->type : (val)) +#endif + +/** \brief Internal macro to check types of input entry pointer. */ +#define NS_LIST_TYPECHECK_(list, entry) \ + (NS_PTR_MATCH_((list)->type, (entry), "incorrect entry type for list"), (entry)) + +/** \brief Type used to pass link offset to underlying functions + * + * We could use size_t, but it would be unnecessarily large on 8-bit systems, + * where we can be (pretty) confident we won't have next pointers more than + * 256 bytes into a structure. + */ +typedef uint_fast8_t ns_list_offset_t; + +/// \publicsection +/** \brief The type for the link member in the user's entry structure. + * + * Users should not access this member directly - just pass its name to the + * list head macros. The funny prev pointer simplifies common operations + * (eg insertion, removal), at the expense of complicating rare reverse iteration. + * + * NB - the list implementation relies on next being the first member. + */ +typedef struct ns_list_link { + void *next; ///< Pointer to next entry, or NULL if none + void **prev; ///< Pointer to previous entry's (or head's) next pointer +} ns_list_link_t; + +/** \brief "Poison" value placed in unattached entries' link pointers. + * \internal What are good values for this? Platform dependent, maybe just NULL + */ +#define NS_LIST_POISON ((void *) 0xDEADBEEF) + +/** \brief Initialiser for an entry's link member + * + * This initialiser is not required by the library, but a user may want an + * initialiser to include in their own entry initialiser. See + * ns_list_link_init() for more discussion. + */ +#define NS_LIST_LINK_INIT(name) \ + NS_FUNNY_INTPTR_OK \ + { NS_LIST_POISON, NS_LIST_POISON } \ + NS_FUNNY_INTPTR_RESTORE + +/** \hideinitializer \brief Initialise an entry's list link + * + * This "initialises" an unattached entry's link by filling the fields with + * poison. This is optional, as unattached entries field pointers are not + * meaningful, and it is not valid to call ns_list_get_next or similar on + * an unattached entry. + * + * \param entry Pointer to an entry + * \param field The name of the link member to initialise + */ +#define ns_list_link_init(entry, field) ns_list_link_init_(&(entry)->field) + +/** \hideinitializer \brief Initialise a list + * + * Initialise a list head before use. A list head must be initialised using this + * function or one of the NS_LIST_INIT()-type macros before use. A zero-initialised + * list head is *not* valid. + * + * If used on a list containing existing entries, those entries will + * become detached. (They are not modified, but their links are now effectively + * undefined). + * + * \param list Pointer to a NS_LIST_HEAD() structure. + */ +#define ns_list_init(list) ns_list_init_(&(list)->slist) + +/** \brief Initialiser for an empty list + * + * Usage in an enclosing initialiser: + * ~~~ + * static my_type_including_list_t x = { + * "Something", + * 23, + * NS_LIST_INIT(x), + * }; + * ~~~ + * NS_LIST_DEFINE() or NS_LIST_NAME_INIT() may provide a shorter alternative + * in simpler cases. + */ +#define NS_LIST_INIT(name) { { NULL, &(name).slist.first_entry } } + +/** \brief Name and initialiser for an empty list + * + * Usage: + * ~~~ + * list_t NS_LIST_NAME_INIT(foo); + * ~~~ + * acts as + * ~~~ + * list_t foo = { empty list }; + * ~~~ + * Also useful with designated initialisers: + * ~~~ + * .NS_LIST_NAME_INIT(foo), + * ~~~ + * acts as + * ~~~ + * .foo = { empty list }, + * ~~~ + */ +#define NS_LIST_NAME_INIT(name) name = NS_LIST_INIT(name) + +/** \brief Define a list, and initialise to empty. + * + * Usage: + * ~~~ + * static NS_LIST_DEFINE(my_list, entry_t, link); + * ~~~ + * acts as + * ~~~ + * static list_type my_list = { empty list }; + * ~~~ + */ +#define NS_LIST_DEFINE(name, type, field) \ + NS_LIST_HEAD(type, field) NS_LIST_NAME_INIT(name) + +/** \hideinitializer \brief Add an entry to the start of the linked list. + * + * ns_list_add_to_end() is *slightly* more efficient than ns_list_add_to_start(). + * + * \param list `(list_t *)` Pointer to list. + * \param entry `(entry_t * restrict)` Pointer to new entry to add. + */ +#define ns_list_add_to_start(list, entry) \ + ns_list_add_to_start_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, entry)) + +/** \hideinitializer \brief Add an entry to the end of the linked list. + * + * \param list `(list_t *)` Pointer to list. + * \param entry `(entry_t * restrict)` Pointer to new entry to add. + */ +#define ns_list_add_to_end(list, entry) \ + ns_list_add_to_end_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, entry)) + +/** \hideinitializer \brief Add an entry before a specified entry. + * + * \param list `(list_t *)` Pointer to list. + * \param before `(entry_t *)` Existing entry before which to place the new entry. + * \param entry `(entry_t * restrict)` Pointer to new entry to add. + */ +#define ns_list_add_before(list, before, entry) \ + ns_list_add_before_(NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, before), NS_LIST_TYPECHECK_(list, entry)) + +/** \hideinitializer \brief Add an entry after a specified entry. + * + * ns_list_add_before() is *slightly* more efficient than ns_list_add_after(). + * + * \param list `(list_t *)` Pointer to list. + * \param after `(entry_t *)` Existing entry after which to place the new entry. + * \param entry `(entry_t * restrict)` Pointer to new entry to add. + */ +#define ns_list_add_after(list, after, entry) \ + ns_list_add_after_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, after), NS_LIST_TYPECHECK_(list, entry)) + +/** \brief Check if a list is empty. + * + * \param list `(const list_t *)` Pointer to list. + * + * \return `(bool)` true if the list is empty. + */ +#define ns_list_is_empty(list) ((bool) ((list)->slist.first_entry == NULL)) + +/** \brief Get the first entry. + * + * \param list `(const list_t *)` Pointer to list. + * + * \return `(entry_t *)` Pointer to first entry. + * \return NULL if list is empty. + */ +#define ns_list_get_first(list) NS_LIST_TYPECAST_(list, (list)->slist.first_entry) + +/** \hideinitializer \brief Get the previous entry. + * + * \param list `(const list_t *)` Pointer to list. + * \param current `(const entry_t *)` Pointer to current entry. + * + * \return `(entry_t *)` Pointer to previous entry. + * \return NULL if current entry is first. + */ +#define ns_list_get_previous(list, current) \ + NS_LIST_TYPECAST_(list, ns_list_get_previous_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, current))) + +/** \hideinitializer \brief Get the next entry. + * + * \param list `(const list_t *)` Pointer to list. + * \param current `(const entry_t *)` Pointer to current entry. + * + * \return `(entry_t *)` Pointer to next entry. + * \return NULL if current entry is last. + */ +#define ns_list_get_next(list, current) \ + NS_LIST_TYPECAST_(list, ns_list_get_next_(NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, current))) + +/** \hideinitializer \brief Get the last entry. + * + * \param list `(const list_t *)` Pointer to list. + * + * \return `(entry_t *)` Pointer to last entry. + * \return NULL if list is empty. + */ +#define ns_list_get_last(list) \ + NS_LIST_TYPECAST_(list, ns_list_get_last_(&(list)->slist, NS_LIST_OFFSET_(list))) + +/** \hideinitializer \brief Remove an entry. + * + * \param list `(list_t *)` Pointer to list. + * \param entry `(entry_t *)` Entry on list to be removed. + */ +#define ns_list_remove(list, entry) \ + ns_list_remove_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, entry)) + +/** \hideinitializer \brief Replace an entry. + * + * \param list `(list_t *)` Pointer to list. + * \param current `(entry_t *)` Existing entry on list to be replaced. + * \param replacement `(entry_t * restrict)` New entry to be the replacement. + */ +#define ns_list_replace(list, current, replacement) \ + ns_list_replace_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, current), NS_LIST_TYPECHECK_(list, replacement)) + +/** \hideinitializer \brief Concatenate two lists. + * + * Attach the entries on the source list to the end of the destination + * list, leaving the source list empty. + * + * \param dst `(list_t *)` Pointer to destination list. + * \param src `(list_t *)` Pointer to source list. + * + */ +#define ns_list_concatenate(dst, src) \ + (NS_PTR_MATCH_(dst, src, "concatenating different list types"), \ + ns_list_concatenate_(&(dst)->slist, &(src)->slist, NS_LIST_OFFSET_(src))) + +/** \brief Iterate forwards over a list. + * + * Example: + * ~~~ + * ns_list_foreach(const my_entry_t, cur, &my_list) + * { + * printf("%s\n", cur->name); + * } + * ~~~ + * Deletion of the current entry is not permitted as its next is checked after + * running user code. + * + * The iteration pointer is declared inside the loop, using C99/C++, so it + * is not accessible after the loop. This encourages good code style, and + * matches the semantics of C++11's "ranged for", which only provides the + * declaration form: + * ~~~ + * for (const my_entry_t cur : my_list) + * ~~~ + * If you need to see the value of the iteration pointer after a `break`, + * you will need to assign it to a variable declared outside the loop before + * breaking: + * ~~~ + * my_entry_t *match = NULL; + * ns_list_foreach(my_entry_t, cur, &my_list) + * { + * if (cur->id == id) + * { + * match = cur; + * break; + * } + * } + * ~~~ + * + * The user has to specify the entry type for the pointer definition, as type + * extraction from the list argument isn't portable. On the other hand, this + * also permits const qualifiers, as in the example above, and serves as + * documentation. The entry type will be checked against the list type where the + * compiler supports it. + * + * \param type Entry type `([const] entry_t)`. + * \param e Name for iteration pointer to be defined + * inside the loop. + * \param list `(const list_t *)` Pointer to list - evaluated multiple times. + */ +#define ns_list_foreach(type, e, list) \ + for (type *e = ns_list_get_first(list); e; e = ns_list_get_next(list, e)) + +/** \brief Iterate forwards over a list, where user may delete. + * + * As ns_list_foreach(), but deletion of current entry is permitted as its + * next pointer is recorded before running user code. + * + * Example: + * ~~~ + * ns_list_foreach_safe(my_entry_t, cur, &my_list) + * { + * ns_list_remove(cur); + * } + * ~~~ + * \param type Entry type `(entry_t)`. + * \param e Name for iteration pointer to be defined + * inside the loop. + * \param list `(list_t *)` Pointer to list - evaluated multiple times. + */ +#define ns_list_foreach_safe(type, e, list) \ + for (type *e = ns_list_get_first(list), *_next; \ + e && (_next = ns_list_get_next(list, e), true); e = _next) + +/** \brief Iterate backwards over a list. + * + * As ns_list_foreach(), but going backwards - see its documentation. + * Iterating forwards is *slightly* more efficient. + */ +#define ns_list_foreach_reverse(type, e, list) \ + for (type *e = ns_list_get_last(list); e; e = ns_list_get_previous(list, e)) + +/** \brief Iterate backwards over a list, where user may delete. + * + * As ns_list_foreach_safe(), but going backwards - see its documentation. + * Iterating forwards is *slightly* more efficient. + */ +#define ns_list_foreach_reverse_safe(type, e, list) \ + for (type *e = ns_list_get_last(list), *_next; \ + e && (_next = ns_list_get_previous(list, e), true); e = _next) + +/** \hideinitializer \brief Count entries on a list + * + * Unlike other operations, this is O(n). Note: if list might contain over + * 65535 entries, this function **must not** be used to get the entry count. + * + * \param list `(const list_t *)` Pointer to list. + + * \return `(uint_fast16_t)` Number of entries that are stored in list. + */ +#define ns_list_count(list) ns_list_count_(&(list)->slist, NS_LIST_OFFSET_(list)) + +/** \privatesection + * Internal functions - designed to be accessed using corresponding macros above + */ +NS_INLINE void ns_list_init_(ns_list_t *list); +NS_INLINE void ns_list_link_init_(ns_list_link_t *link); +NS_INLINE void ns_list_add_to_start_(ns_list_t *list, ns_list_offset_t link_offset, void *restrict entry); +NS_INLINE void ns_list_add_to_end_(ns_list_t *list, ns_list_offset_t link_offset, void *restrict entry); +NS_INLINE void ns_list_add_before_(ns_list_offset_t link_offset, void *before, void *restrict entry); +NS_INLINE void ns_list_add_after_(ns_list_t *list, ns_list_offset_t link_offset, void *after, void *restrict entry); +NS_INLINE void *ns_list_get_next_(ns_list_offset_t link_offset, const void *current); +NS_INLINE void *ns_list_get_previous_(const ns_list_t *list, ns_list_offset_t link_offset, const void *current); +NS_INLINE void *ns_list_get_last_(const ns_list_t *list, ns_list_offset_t offset); +NS_INLINE void ns_list_remove_(ns_list_t *list, ns_list_offset_t link_offset, void *entry); +NS_INLINE void ns_list_replace_(ns_list_t *list, ns_list_offset_t link_offset, void *current, void *restrict replacement); +NS_INLINE void ns_list_concatenate_(ns_list_t *dst, ns_list_t *src, ns_list_offset_t offset); +NS_INLINE uint_fast16_t ns_list_count_(const ns_list_t *list, ns_list_offset_t link_offset); + +/* Provide definitions, either for inlining, or for ns_list.c */ +#if defined NS_ALLOW_INLINING || defined NS_LIST_FN +#ifndef NS_LIST_FN +#define NS_LIST_FN NS_INLINE +#endif + +/* Pointer to the link member in entry e */ +#define NS_LIST_LINK_(e, offset) ((ns_list_link_t *)((char *)(e) + offset)) + +/* Lvalue of the next link pointer in entry e */ +#define NS_LIST_NEXT_(e, offset) (NS_LIST_LINK_(e, offset)->next) + +/* Lvalue of the prev link pointer in entry e */ +#define NS_LIST_PREV_(e, offset) (NS_LIST_LINK_(e, offset)->prev) + +/* Convert a pointer to a link member back to the entry; + * works for linkptr either being a ns_list_link_t pointer, or its next pointer, + * as the next pointer is first in the ns_list_link_t */ +#define NS_LIST_ENTRY_(linkptr, offset) ((void *)((char *)(linkptr) - offset)) + +NS_LIST_FN void ns_list_init_(ns_list_t *list) +{ + list->first_entry = NULL; + list->last_nextptr = &list->first_entry; +} + +NS_LIST_FN void ns_list_link_init_(ns_list_link_t *link) +{ + NS_FUNNY_INTPTR_OK + link->next = NS_LIST_POISON; + link->prev = NS_LIST_POISON; + NS_FUNNY_INTPTR_RESTORE +} + +NS_LIST_FN void ns_list_add_to_start_(ns_list_t *list, ns_list_offset_t offset, void *restrict entry) +{ + void *next; + + NS_LIST_PREV_(entry, offset) = &list->first_entry; + NS_LIST_NEXT_(entry, offset) = next = list->first_entry; + + if (next) { + NS_LIST_PREV_(next, offset) = &NS_LIST_NEXT_(entry, offset); + } else { + list->last_nextptr = &NS_LIST_NEXT_(entry, offset); + } + + list->first_entry = entry; +} + +NS_LIST_FN void ns_list_add_after_(ns_list_t *list, ns_list_offset_t offset, void *current, void *restrict entry) +{ + void *next; + + NS_LIST_PREV_(entry, offset) = &NS_LIST_NEXT_(current, offset); + NS_LIST_NEXT_(entry, offset) = next = NS_LIST_NEXT_(current, offset); + + if (next) { + NS_LIST_PREV_(next, offset) = &NS_LIST_NEXT_(entry, offset); + } else { + list->last_nextptr = &NS_LIST_NEXT_(entry, offset); + } + + NS_LIST_NEXT_(current, offset) = entry; +} + +NS_LIST_FN void ns_list_add_before_(ns_list_offset_t offset, void *current, void *restrict entry) +{ + void **prev_nextptr; + + NS_LIST_NEXT_(entry, offset) = current; + NS_LIST_PREV_(entry, offset) = prev_nextptr = NS_LIST_PREV_(current, offset); + *prev_nextptr = entry; + NS_LIST_PREV_(current, offset) = &NS_LIST_NEXT_(entry, offset); +} + +NS_LIST_FN void ns_list_add_to_end_(ns_list_t *list, ns_list_offset_t offset, void *restrict entry) +{ + void **prev_nextptr; + + NS_LIST_NEXT_(entry, offset) = NULL; + NS_LIST_PREV_(entry, offset) = prev_nextptr = list->last_nextptr; + *prev_nextptr = entry; + list->last_nextptr = &NS_LIST_NEXT_(entry, offset); +} + +NS_LIST_FN void *ns_list_get_next_(ns_list_offset_t offset, const void *current) +{ + return NS_LIST_NEXT_(current, offset); +} + +NS_LIST_FN void *ns_list_get_previous_(const ns_list_t *list, ns_list_offset_t offset, const void *current) +{ + if (current == list->first_entry) { + return NULL; + } + + // Tricky. We don't have a direct previous pointer, but a pointer to the + // pointer that points to us - ie &head->first_entry OR &{prev}->next. + // This makes life easier on insertion and removal, but this is where we + // pay the price. + + // We have to check manually for being the first entry above, so we know it's + // a real link's next pointer. Then next is the first field of + // ns_list_link_t, so we can use the normal offset value. + + return NS_LIST_ENTRY_(NS_LIST_PREV_(current, offset), offset); +} + +NS_LIST_FN void *ns_list_get_last_(const ns_list_t *list, ns_list_offset_t offset) +{ + if (!list->first_entry) { + return NULL; + } + + // See comments in ns_list_get_previous_() + return NS_LIST_ENTRY_(list->last_nextptr, offset); +} + +NS_LIST_FN void ns_list_remove_(ns_list_t *list, ns_list_offset_t offset, void *removed) +{ + void *next; + void **prev_nextptr; + + next = NS_LIST_NEXT_(removed, offset); + prev_nextptr = NS_LIST_PREV_(removed, offset); + if (next) { + NS_LIST_PREV_(next, offset) = prev_nextptr; + } else { + list->last_nextptr = prev_nextptr; + } + *prev_nextptr = next; + + ns_list_link_init_(NS_LIST_LINK_(removed, offset)); +} + +NS_LIST_FN void ns_list_replace_(ns_list_t *list, ns_list_offset_t offset, void *current, void *restrict replacement) +{ + void *next; + void **prev_nextptr; + + NS_LIST_PREV_(replacement, offset) = prev_nextptr = NS_LIST_PREV_(current, offset); + NS_LIST_NEXT_(replacement, offset) = next = NS_LIST_NEXT_(current, offset); + + if (next) { + NS_LIST_PREV_(next, offset) = &NS_LIST_NEXT_(replacement, offset); + } else { + list->last_nextptr = &NS_LIST_NEXT_(replacement, offset); + } + *prev_nextptr = replacement; + + ns_list_link_init_(NS_LIST_LINK_(current, offset)); +} + +NS_LIST_FN void ns_list_concatenate_(ns_list_t *dst, ns_list_t *src, ns_list_offset_t offset) +{ + ns_list_link_t *src_first; + + src_first = src->first_entry; + if (!src_first) { + return; + } + + *dst->last_nextptr = src_first; + NS_LIST_PREV_(src_first, offset) = dst->last_nextptr; + dst->last_nextptr = src->last_nextptr; + + ns_list_init_(src); +} + +NS_LIST_FN uint_fast16_t ns_list_count_(const ns_list_t *list, ns_list_offset_t offset) +{ + uint_fast16_t count = 0; + + for (void *p = list->first_entry; p; p = NS_LIST_NEXT_(p, offset)) { + count++; + } + + return count; +} +#endif /* defined NS_ALLOW_INLINING || defined NS_LIST_FN */ + +#ifdef __cplusplus +} +#endif + +#endif /* NS_LIST_H_ */ + diff --git a/source/ns_list_internal/ns_types.h b/source/ns_list_internal/ns_types.h new file mode 100644 index 00000000000..ee7ddf3567b --- /dev/null +++ b/source/ns_list_internal/ns_types.h @@ -0,0 +1,373 @@ +/* + * Copyright (c) 2014-2015 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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. + */ +/* + * ns_types.h - Basic compiler and type setup for Nanostack libraries. + */ +#ifndef NS_TYPES_H_ +#define NS_TYPES_H_ + +/** \file + * \brief Basic compiler and type setup + * + * We currently assume C99 or later. + * + * C99 features being relied on: + * + * - and + * - inline (with C99 semantics, not C++ as per default GCC); + * - designated initialisers; + * - compound literals; + * - restrict; + * - [static N] in array parameters; + * - declarations in for statements; + * - mixing declarations and statements + * + * Compilers should be set to C99 or later mode when building Nanomesh source. + * For GCC this means "-std=gnu99" (C99 with usual GNU extensions). + * + * Also, a little extra care is required for public header files that could be + * included from C++, especially as C++ lacks some C99 features. + * + * (TODO: as this is exposed to API users, do we need a predefine to distinguish + * internal and external use, for finer control? Not yet, but maybe...) + */ + +/* Make sure defines its macros if C++ */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include +#include // includes ; debugf() users need PRIu32 etc +#include + +/* + * Create the optional 24-bit types if they don't exist (worth trying + * to use them, as they could exist and be more efficient than 32-bit on 8-bit + * systems...) + */ +#ifndef UINT24_LEAST_MAX +typedef uint_least32_t uint_least24_t; +#define UINT_LEAST24_MAX UINT_LEAST32_MAX +#define UINT24_C(x) UINT32_C(x) +#define PRIoLEAST24 PRIoLEAST32 +#define PRIuLEAST24 PRIuLEAST32 +#define PRIxLEAST24 PRIxLEAST32 +#define PRIXLEAST24 PRIXLEAST32 +#endif + +#ifndef INT24_LEAST_MAX +typedef int_least32_t int_least24_t; +#define INT24_LEAST_MIN INT_LEAST32_MIN +#define INT24_LEAST_MAX INT_LEAST32_MAX +#define INT24_C(x) INT32_C(x) +#define PRIdLEAST24 PRIdLEAST32 +#define PRIiLEAST24 PRIiLEAST32 +#endif + +#ifndef UINT24_FAST_MAX +typedef uint_fast32_t uint_fast24_t; +#define UINT_FAST24_MAX UINT_FAST32_MAX +#define PRIoFAST24 PRIoFAST32 +#define PRIuFAST24 PRIuFAST32 +#define PRIxFAST24 PRIxFAST32 +#define PRIXFAST24 PRIXFAST32 +#endif + +#ifndef INT24_FAST_MAX +typedef int_fast32_t int_fast24_t; +#define INT_FAST24_MIN INT_FAST32_MIN +#define INT_FAST24_MAX INT_FAST32_MAX +#define PRIdFAST24 PRIdFAST32 +#define PRIiFAST24 PRIiFAST32 +#endif + +/* Function attribute - C11 "noreturn" or C++11 "[[noreturn]]" */ +#ifndef NS_NORETURN +#if defined __cplusplus && __cplusplus >= 201103L +#define NS_NORETURN [[noreturn]] +#elif !defined __cplusplus && __STDC_VERSION__ >= 201112L +#define NS_NORETURN _Noreturn +#elif defined __GNUC__ +#define NS_NORETURN __attribute__((__noreturn__)) +#elif defined __CC_ARM +#define NS_NORETURN __declspec(noreturn) +#elif defined __IAR_SYSTEMS_ICC__ +#define NS_NORETURN __noreturn +#else +#define NS_NORETURN +#endif +#endif + +/* C11's "alignas" macro, emulated for integer expressions if necessary */ +#ifndef __alignas_is_defined +#if defined __CC_ARM || defined __TASKING__ +#define alignas(n) __align(n) +#define __alignas_is_defined 1 +#elif (__STDC_VERSION__ >= 201112L) || (defined __cplusplus && __cplusplus >= 201103L) +#include +#elif defined __GNUC__ +#define alignas(n) __attribute__((__aligned__(n))) +#define __alignas_is_defined 1 +#elif defined __IAR_SYSTEMS_ICC__ +/* Does this really just apply to the next variable? */ +#define alignas(n) __Alignas(data_alignment=n) +#define __Alignas(x) _Pragma(#x) +#define __alignas_is_defined 1 +#endif +#endif + +/** + * Marker for functions or objects that may be unused, suppressing warnings. + * Place after the identifier: + * ~~~ + * static int X MAYBE_UNUSED = 3; + * static int foo(void) MAYBE_UNUSED; + * ~~~ + */ +#if defined __CC_ARM || defined __GNUC__ +#define MAYBE_UNUSED __attribute__((unused)) +#else +#define MAYBE_UNUSED +#endif + +/* + * C++ (even C++11) doesn't provide restrict: define away or provide + * alternative. + */ +#ifdef __cplusplus +#ifdef __GNUC__ +#define restrict __restrict +#else +#define restrict +#endif +#endif /* __cplusplus */ + + +/** + * C++ doesn't allow "static" in function parameter types: ie + * ~~~ + * entry_t *find_entry(const uint8_t address[static 16]) + * ~~~ + * If a header file may be included from C++, use this __static define instead. + * + * (Syntax introduced in C99 - `uint8_t address[16]` in a prototype was always + * equivalent to `uint8_t *address`, but the C99 addition of static tells the + * compiler that address is never NULL, and always points to at least 16 + * elements. This adds no new type-checking, but the information could aid + * compiler optimisation, and it can serve as documentation). + */ +#ifdef __cplusplus +#define __static +#else +#define __static static +#endif + +#ifdef __GNUC__ +#define NS_GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +#endif + +/** \brief Compile-time assertion + * + * C11 provides _Static_assert, as does GCC even in C99 mode (and + * as a freestanding implementation, we can't rely on to get + * the static_assert macro). + * C++11 provides static_assert as a keyword, as does G++ in C++0x mode. + * + * The assertion acts as a declaration that can be placed at file scope, in a + * code block (except after a label), or as a member of a struct/union. It + * produces a compiler error if "test" evaluates to 0. + * + * Note that this *includes* the required semicolon when defined, else it + * is totally empty, permitting use in structs. (If the user provided the `;`, + * it would leave an illegal stray `;` if unavailable). + */ +#ifdef __cplusplus +# if __cplusplus >= 201103L || __cpp_static_assert >= 200410 +# define NS_STATIC_ASSERT(test, str) static_assert(test, str); +# elif defined __GXX_EXPERIMENTAL_CXX0X__ && NS_GCC_VERSION >= 40300 +# define NS_STATIC_ASSERT(test, str) __extension__ static_assert(test, str); +# else +# define NS_STATIC_ASSERT(test, str) +# endif +#else /* C */ +# if __STDC_VERSION__ >= 201112L +# define NS_STATIC_ASSERT(test, str) _Static_assert(test, str); +# elif defined __GNUC__ && NS_GCC_VERSION >= 40600 && !defined __CC_ARM +# ifdef _Static_assert + /* + * Some versions of glibc cdefs.h (which comes in via above) + * attempt to define their own _Static_assert (if GCC < 4.6 or + * __STRICT_ANSI__) using an extern declaration, which doesn't work in a + * struct/union. + * + * For GCC >= 4.6 and __STRICT_ANSI__, we can do better - just use + * the built-in _Static_assert with __extension__. We have to do this, as + * ns_list.h needs to use it in a union. No way to get at it though, without + * overriding their define. + */ +# undef _Static_assert +# define _Static_assert(x, y) __extension__ _Static_assert(x, y) +# endif +# define NS_STATIC_ASSERT(test, str) __extension__ _Static_assert(test, str); +# else +# define NS_STATIC_ASSERT(test, str) +#endif +#endif + +/** \brief Pragma to suppress warnings about unusual pointer values. + * + * Useful if using "poison" values. + */ +#ifdef __IAR_SYSTEMS_ICC__ +#define NS_FUNNY_INTPTR_OK _Pragma("diag_suppress=Pe1053") +#define NS_FUNNY_INTPTR_RESTORE _Pragma("diag_default=Pe1053") +#else +#define NS_FUNNY_INTPTR_OK +#define NS_FUNNY_INTPTR_RESTORE +#endif + +/** \brief Convert pointer to member to pointer to containing structure */ +#define NS_CONTAINER_OF(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member))) + +/* + * Inlining could cause problems when mixing with C++; provide a mechanism to + * disable it. This could also be turned off for other reasons (although + * this can usually be done through a compiler flag, eg -O0 on gcc). + */ +#ifndef __cplusplus +#define NS_ALLOW_INLINING +#endif + +/* There is inlining problem in GCC version 4.1.x and we know it works in 4.6.3 */ +#if defined __GNUC__ && NS_GCC_VERSION < 40600 +#undef NS_ALLOW_INLINING +#endif + +/** \brief Mark a potentially-inlineable function. + * + * We follow C99 semantics, which requires precisely one external definition. + * To also allow inlining to be totally bypassed under control of + * NS_ALLOW_INLINING, code can be structured as per the example of ns_list: + * + * foo.h + * ----- + * ~~~ + * NS_INLINE int my_func(int); + * + * #if defined NS_ALLOW_INLINING || defined FOO_FN + * #ifndef FOO_FN + * #define FOO_FN NS_INLINE + * #endif + * FOO_FN int my_func(int a) + * { + * definition; + * } + * #endif + * ~~~ + * foo.c + * ----- + * ~~~ + * #define FOO_FN extern + * #include "foo.h" + * ~~~ + * Which generates: + * ~~~ + * NS_ALLOW_INLINING set NS_ALLOW_INLINING unset + * ===================== ======================= + * Include foo.h Include foo.h + * ------------- ------------- + * inline int my_func(int); int my_func(int); + * + * // inline definition + * inline int my_func(int a) + * { + * definition; + * } + * + * Compile foo.c Compile foo.c + * ------------- ------------- + * (from .h) inline int my_func(int); int my_func(int); + * + * // external definition + * // because of no "inline" // normal external definition + * extern int my_func(int a) extern int my_func(int a) + * { { + * definition; definition; + * } } + * ~~~ + * + * Note that even with inline keywords, whether the compiler inlines or not is + * up to it. For example, gcc at "-O0" will not inline at all, and will always + * call the real functions in foo.o, just as if NS_ALLOW_INLINING was unset. + * At "-O2", gcc could potentially inline everything, meaning that foo.o is not + * referenced at all. + * + * Alternatively, you could use "static inline", which gives every caller its + * own internal definition. This is compatible with C++ inlining (which expects + * the linker to eliminate duplicates), but in C it's less efficient if the code + * ends up non-inlined, and it's harder to breakpoint. I don't recommend it + * except for the most trivial functions (which could then probably be macros). + */ +#ifdef NS_ALLOW_INLINING +#define NS_INLINE inline +#else +#define NS_INLINE +#endif + +#if defined __SDCC_mcs51 || defined __ICC8051__ || defined __C51__ + +/* The 8051 environments: SDCC (historic), IAR (current), Keil (future?) */ + +#define NS_LARGE __xdata +#define NS_LARGE_PTR __xdata +#ifdef __ICC8051__ +#define NS_REENTRANT +#define NS_REENTRANT_PREFIX __idata_reentrant +#else +#define NS_REENTRANT __reentrant +#define NS_REENTRANT_PREFIX +#endif +#define NS_NEAR_FUNC __near_func + +#else + +/* "Normal" systems. Define it all away. */ +#define NS_LARGE +#define NS_LARGE_PTR +#define NS_REENTRANT +#define NS_REENTRANT_PREFIX +#define NS_NEAR_FUNC + +#endif + +/** \brief Scatter-gather descriptor + * + * Slightly optimised for small platforms - we assume we won't need any + * element bigger than 64K. + */ +typedef struct ns_iovec { + void *iov_base; + uint_fast16_t iov_len; +} ns_iovec_t; + +#endif /* NS_TYPES_H */ diff --git a/target.json b/target.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000000..6e18e19d462 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,27 @@ +# only build tests on targets that declare they are like posix +if(DEFINED TARGET_LIKE_X86_WINDOWS_NATIVE OR DEFINED TARGET_LIKE_X86_LINUX_NATIVE) + + # describe the test executable + add_executable(mbed_client_cli_test Test.cpp) + + include_directories("../yotta_modules/cpputest") + + # describe what the test executable needs to link with + target_link_libraries(mbed_client_cli_test + "mbed-client-cli" + "mbed-trace" + cpputest + ) + add_definitions("-Wno-write-strings") + + # describe what is actual test binary + if(DEFINED TARGET_LIKE_X86_WINDOWS_NATIVE) + SET(TEST_EXECUTABLE "build/x86-windows-native/test/mbed_client_cli_test") + endif() + if(DEFINED TARGET_LIKE_X86_LINUX_NATIVE) + SET(TEST_EXECUTABLE "../../../build/x86-linux-native/test/mbed_client_cli_test") + endif() + + add_test(mbed_client_cli_test ${TEST_EXECUTABLE}) + +endif() diff --git a/test/Test.cpp b/test/Test.cpp new file mode 100644 index 00000000000..0d735b2e0a1 --- /dev/null +++ b/test/Test.cpp @@ -0,0 +1,785 @@ +/* + * Copyright (c) 2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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. + */ + +/** + * \file \test\Test.c + * + * \brief Unit tests for mbed-client-cli + */ +#include +#include +#include +#include +#include + + +#include "mbed-cpputest/CppUTest/TestHarness.h" +#include "mbed-cpputest/CppUTest/SimpleString.h" +#include "mbed-cpputest/CppUTest/CommandLineTestRunner.h" + +#define YOTTA_CFG_MBED_TRACE 1 +#define YOTTA_CFG_MBED_TRACE_FEA_IPV6 0 +#include "mbed-trace/mbed_trace.h" +#include "mbed-client-cli/ns_cmdline.h" +#define MAX(x,y) (x>y?x:y) +#define ARRAY_CMP(x, y) \ + MEMCMP_EQUAL(x, y, MAX(strlen(x), strlen(y))) + +int main(int ac, char **av) +{ + return CommandLineTestRunner::RunAllTests(ac, av); +} + +#define BUFSIZE 1024 +char buf[BUFSIZE] = {0}; +#define INIT_BUF() memset(buf, 0, BUFSIZE) +int cmd_dummy(int argc, char *argv[]) +{ + return 0; +} + +int mutex_wait_count = 0; +int mutex_release_count = 0; +int mutex_count_expected_difference = 1; +bool check_mutex_lock_state = false; +void my_mutex_wait() +{ + mutex_wait_count++; +} +void my_mutex_release() +{ + mutex_release_count++; +} + +void myprint(const char *fmt, va_list ap) +{ + if (check_mutex_lock_state) { + CHECK((mutex_wait_count - mutex_release_count) == mutex_count_expected_difference); + } + vsnprintf(buf + strlen(buf), BUFSIZE - strlen(buf), fmt, ap); + //printf("\nMYPRINT: %s\n", buf); //for test test +} +void input(const char *str) +{ + while (*str != 0) { + cmd_char_input(*str++); + } +} + +#define REQUEST(x) input(x);INIT_BUF();cmd_char_input('\r'); +#define RESPONSE(x) "\r\n" x "\r\n\r\x1B[2K/> \x1B[1D" +#define CMDLINE(x) "\r\x1b[2K/>" x "\x1b[1D" + +#define FORWARD "C" +#define BACKWARD "D" +#define CMDLINE_CUR(x, cursor, dir) "\r\x1b[2K/>" x "\x1b[" cursor "" dir +#define CLEAN() cmd_char_input('\r');INIT_BUF(); + +//vt100 keycodes +#define HOME() input("\x1b[1~") +#define INSERT() input("\x1b[2~") +#define DELETE() input("\x1b[3~") +#define BACKSPACE() input("\x7f") +#define LEFT() input("\x1b[D") +#define LEFT_N(n) for(int i=0;i 20 ); +} +TEST(cli, hello) +{ + REQUEST("echo Hi!"); + ARRAY_CMP(RESPONSE("Hi! ") , buf); +} +TEST(cli, cmd_echo1) +{ + REQUEST(" echo Hi!"); + ARRAY_CMP(RESPONSE("Hi! ") , buf); +} +TEST(cli, cmd_echo2) +{ + REQUEST("echo foo faa"); + ARRAY_CMP(RESPONSE("foo faa ") , buf); +} +TEST(cli, cmd_echo3) +{ + REQUEST("echo foo faa"); + ARRAY_CMP(RESPONSE("foo faa ") , buf); +} +TEST(cli, cmd_echo4) +{ + REQUEST("echo foo faa"); + ARRAY_CMP(RESPONSE("foo faa ") , buf); +} +TEST(cli, cmd_echo5) +{ + REQUEST("echo \"foo faa\""); + ARRAY_CMP(RESPONSE("foo faa ") , buf); +} +TEST(cli, cmd_echo6) +{ + REQUEST("echo \"foo faa"); + ARRAY_CMP(RESPONSE("\"foo faa ") , buf); +} +TEST(cli, cmd_echo7) +{ + REQUEST("echo 'foo faa\""); + ARRAY_CMP(RESPONSE("'foo faa\" ") , buf); +} +TEST(cli, cmd_echo8) +{ + REQUEST("echof\x7f foo faa"); + ARRAY_CMP(RESPONSE("foo faa ") , buf); +} +TEST(cli, cmd_echo9) +{ + REQUEST("echo foo faa\x1b[D\x1b[D\x1b[D hello "); + ARRAY_CMP(RESPONSE("foo hello faa ") , buf); + CLEAN(); +} +TEST(cli, cmd_echo10) +{ + REQUEST("echo foo faa\x1b[D\x1b[C\x1b[C hello "); //echo foo hello faa + ARRAY_CMP(RESPONSE("foo faa hello ") , buf); + CLEAN(); +} +TEST(cli, cmd_echo11) +{ + REQUEST("echo off\r"); + INIT_BUF(); + input("echo test"); + ARRAY_CMP("" , buf); + input("\r"); + ARRAY_CMP("test \r\n" , buf); + INIT_BUF(); + REQUEST("echo on\r"); + INIT_BUF(); + input("e"); + ARRAY_CMP(CMDLINE("e ") , buf); + INIT_BUF(); + input("c"); + ARRAY_CMP(CMDLINE("ec ") , buf); + INIT_BUF(); + input("h"); + ARRAY_CMP(CMDLINE("ech ") , buf); + INIT_BUF(); + input("o"); + ARRAY_CMP(CMDLINE("echo ") , buf); + INIT_BUF(); + input(" "); + ARRAY_CMP(CMDLINE("echo ") , buf); + INIT_BUF(); + input("o"); + ARRAY_CMP(CMDLINE("echo o ") , buf); + INIT_BUF(); + input("k"); + ARRAY_CMP(CMDLINE("echo ok ") , buf); + CLEAN(); +} + +TEST(cli, cmd_arrows_up) +{ + REQUEST("echo foo-1"); + INIT_BUF(); + input("\x1b[A"); + ARRAY_CMP(CMDLINE("echo foo-1 ") , buf); + INIT_BUF(); + input("\x1b[A"); + ARRAY_CMP(CMDLINE("echo foo-1 ") , buf); + CLEAN(); +} +TEST(cli, cmd_arrows_up_down) +{ + REQUEST("echo test-1"); + ARRAY_CMP(RESPONSE("test-1 "), buf); + REQUEST("echo test-2"); + ARRAY_CMP(RESPONSE("test-2 "), buf); + REQUEST("echo test-3"); + ARRAY_CMP(RESPONSE("test-3 "), buf); + + INIT_BUF(); + UP(); + ARRAY_CMP(CMDLINE("echo test-3 "), buf); + INIT_BUF(); + UP(); + ARRAY_CMP(CMDLINE("echo test-2 "), buf); + INIT_BUF(); + UP(); + ARRAY_CMP(CMDLINE("echo test-1 "), buf); + INIT_BUF(); + UP(); + ARRAY_CMP(CMDLINE("echo test-1 "), buf); + INIT_BUF(); + DOWN(); + ARRAY_CMP(CMDLINE("echo test-2 "), buf); + INIT_BUF(); + DOWN(); + ARRAY_CMP(CMDLINE("echo test-3 "), buf); + INIT_BUF(); + DOWN(); + ARRAY_CMP(CMDLINE(" "), buf); + CLEAN(); +} +TEST(cli, cmd_pageup_page_down) +{ + //goto history beginning/end + REQUEST("echo test-1"); + REQUEST("echo test-2"); + REQUEST("echo test-3"); + REQUEST("echo test-4"); + INIT_BUF(); + PAGE_UP(); + ARRAY_CMP(CMDLINE("echo test-1 "), buf); + INIT_BUF(); + PAGE_DOWN(); + ARRAY_CMP(CMDLINE("echo test-4 "), buf); + CLEAN(); +} +TEST(cli, cmd_text_pageup) +{ + REQUEST("echo test-1"); + REQUEST("echo test-2"); + REQUEST("echo test-3"); + REQUEST("echo test-4"); + input("hello"); + INIT_BUF(); + PAGE_UP(); //goto end of history + ARRAY_CMP(CMDLINE("echo test-1 "), buf); + INIT_BUF(); + PAGE_DOWN(); //goto beginning of history - it should be just writted "hello" + ARRAY_CMP(CMDLINE("hello "), buf); + CLEAN(); +} +TEST(cli, cmd_text_pageup_up) +{ + REQUEST("echo test-1"); + REQUEST("echo test-2"); + REQUEST("echo test-3"); + REQUEST("echo test-4"); + input("hello"); + INIT_BUF(); + PAGE_UP(); //goto end of history + ARRAY_CMP(CMDLINE("echo test-1 "), buf); + INIT_BUF(); + DOWN(); + ARRAY_CMP(CMDLINE("echo test-2 "), buf); + INIT_BUF(); + PAGE_DOWN(); //goto beginning of history - it should be just writted "hello" + ARRAY_CMP(CMDLINE("hello "), buf); + CLEAN(); +} +TEST(cli, cmd_text_delete) +{ + input("hello world"); + LEFT_N(2); + DELETE(); + INIT_BUF(); + DELETE(); + ARRAY_CMP(CMDLINE("hello wor "), buf); + INIT_BUF(); + DELETE(); + ARRAY_CMP(CMDLINE("hello wor "), buf); + INIT_BUF(); + DELETE(); + ARRAY_CMP(CMDLINE("hello wor "), buf); + LEFT_N(2); + INIT_BUF(); + DELETE(); + ARRAY_CMP(CMDLINE_CUR("hello wr ", "2", BACKWARD), buf); + BACKSPACE(); + BACKSPACE(); + INIT_BUF(); + BACKSPACE(); + ARRAY_CMP(CMDLINE_CUR("hellr ", "2", BACKWARD), buf); + CLEAN(); +} +TEST(cli, cmd_insert) +{ + CLEAN(); + input("echo hello word"); + LEFT(); + INIT_BUF(); + input("l"); + ARRAY_CMP(CMDLINE_CUR("echo hello world ", "2", BACKWARD), buf); + LEFT_N(10); + INIT_BUF(); + LEFT(); + ARRAY_CMP(CMDLINE_CUR("echo hello world ", "13", BACKWARD), buf); + INIT_BUF(); + RIGHT(); + ARRAY_CMP(CMDLINE_CUR("echo hello world ", "12", BACKWARD), buf); + CLEAN(); +} +TEST(cli, cmd_tab_1) +{ + INIT_BUF(); + input("e"); + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); + + input("\rech"); + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); + + input("\r"); +} +TEST(cli, cmd_tab_2) +{ + INIT_BUF(); + + cmd_add("role", cmd_dummy, 0, 0); + cmd_add("route", cmd_dummy, 0, 0); + cmd_add("rile", cmd_dummy, 0, 0); + input("r"); + INIT_BUF(); + + input("\t"); + ARRAY_CMP(CMDLINE_CUR("role ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("route ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("rile ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\x1b[Z"); + ARRAY_CMP(CMDLINE_CUR("route ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\x1b[Z"); + ARRAY_CMP(CMDLINE_CUR("role ", "1", BACKWARD) , buf); + + input("\r"); +} +TEST(cli, cmd_tab_3) +{ + INIT_BUF(); + cmd_add("role", cmd_dummy, 0, 0); + cmd_alias_add("rose", "role"); + cmd_alias_add("rope", "rope"); + + input("r"); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("role ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("rose ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("rope ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("r ", "1", BACKWARD) , buf); + + INIT_BUF(); + input("o"); + ARRAY_CMP(CMDLINE_CUR("ro ", "1", BACKWARD) , buf); + + ESC(); + INIT_BUF(); +} +TEST(cli, cmd_tab_4) +{ + INIT_BUF(); + cmd_variable_add("dut1", "hello"); + + input("e"); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); + + input(" $d"); + INIT_BUF(); + input("u"); + ARRAY_CMP(CMDLINE_CUR("echo $du ", "1", BACKWARD), buf); + + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("echo $dut1 ", "1", BACKWARD) , buf); + + input("\re"); + INIT_BUF(); + input("\t"); + ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); + + input("\r"); + INIT_BUF(); +} +// // alias test +// extern void replace_alias(const char *str, const char *old_str, const char *new_str); +// TEST(cli, cmd_alias_1) +// { +// char str[] = "hello a men"; +// replace_alias(str, "a", "b"); +// ARRAY_CMP("hello a men", str); +// +// replace_alias(str, "hello", "echo"); +// ARRAY_CMP("echo a men", str); +// INIT_BUF(); +// } +/* @todo this not working yet +TEST(cli, cmd_alias_2) +{ + REQUEST("alias foo bar"); + INIT_BUF(); + REQUEST("alias"); + ARRAY_CMP("\r\nalias:\r\nfoo 'bar'\r\n\r\x1b[2K/> \x1b[1D", buf); + + REQUEST("alias foo"); + INIT_BUF(); + REQUEST("alias"); + ARRAY_CMP("\r\nalias:\r\n\r\x1b[2K/> \x1b[1D", buf); +} +*/ +TEST(cli, cmd_alias_3) +{ + cmd_alias_add("p", "echo"); + REQUEST("p toimii"); + ARRAY_CMP("\r\ntoimii \r\n\r\x1b[2K/> \x1b[1D", buf); + + cmd_alias_add("printtti", "echo"); + REQUEST("printtti toimii"); + ARRAY_CMP("\r\ntoimii \r\n\r\x1b[2K/> \x1b[1D", buf); +} +TEST(cli, cmd_alias_4) +{ + REQUEST("alias dut1 \"echo dut1\""); + REQUEST("alias dut2 \"echo dut2\""); + REQUEST("alias dut3 \"echo dut3\""); + REQUEST("dut1"); + ARRAY_CMP(RESPONSE("dut1 "), buf); +} +TEST(cli, cmd_series) +{ + REQUEST("alias dut1 \"echo dut1\""); + REQUEST("alias dut2 \"echo dut2\""); + REQUEST("alias dut3 \"echo dut3\""); + REQUEST("dut1;dut2;dut3"); + ARRAY_CMP(RESPONSE("dut1 \r\ndut2 \r\ndut3 "), buf); +} + +TEST(cli, cmd_var_1) +{ + REQUEST("set foo \"bar test\""); + INIT_BUF(); + REQUEST("set"); + ARRAY_CMP("\r\nvariables:\r\nfoo 'bar test'\r\n\r\x1b[2K/> \x1b[1D", buf); + + REQUEST("set foo"); + INIT_BUF(); + REQUEST("set"); + ARRAY_CMP("\r\nvariables:\r\n\r\x1b[2K/> \x1b[1D", buf); +} +TEST(cli, cmd_var_2) +{ + REQUEST("set foo \"hello world\""); + REQUEST("echo foo"); + ARRAY_CMP(RESPONSE("foo ") , buf); + + REQUEST("echo $foo"); + ARRAY_CMP(RESPONSE("hello world ") , buf); + + REQUEST("set faa !"); + REQUEST("echo $foo$faa"); + ARRAY_CMP(RESPONSE("hello world! ") , buf); +} +TEST(cli, multiple_cmd) +{ + REQUEST("set foo \"hello world\";echo $foo"); + ARRAY_CMP(RESPONSE("hello world ") , buf); + + REQUEST("setd faa \"hello world\";echo $faa"); + ARRAY_CMP("\r\nCommand 'setd' not found.\r\n$faa \r\n\r\x1B[2K/> \x1B[1D" , buf); + + REQUEST("setd foo \"hello guy\"&&echo $foo"); + ARRAY_CMP(RESPONSE("Command 'setd' not found.") , buf); +} +TEST(cli, maxlength) +{ + int i; + char test_data[600]; + char *ptr = test_data; + strcpy(test_data, "echo "); + for (i = 5; i < 600; i++) { + test_data[i] = 'A' + i % 26; + } + test_data[599] = 0; + REQUEST(ptr); + //ARRAY_CMP( RESPONSE((test_data+5)), buf); +} +TEST(cli, ampersand) +{ + REQUEST("echo hello world&"); + ARRAY_CMP(RESPONSE("hello world ") , buf); +} + +#define REDIR_DATA "echo Hi!" +#define PASSTHROUGH_BUF_LENGTH 10 +char passthrough_buffer[PASSTHROUGH_BUF_LENGTH]; +char* passthrough_ptr = NULL; +void passthrough_cb(uint8_t c) +{ + if (passthrough_ptr != NULL) { + *passthrough_ptr++ = c; + } +} +TEST(cli, passthrough_set) +{ + passthrough_ptr = passthrough_buffer; + memset(&passthrough_buffer, 0, PASSTHROUGH_BUF_LENGTH); + INIT_BUF(); + + cmd_input_passthrough_func(passthrough_cb); + input(REDIR_DATA); + + CHECK(strlen(buf) == 0); + ARRAY_CMP(REDIR_DATA, passthrough_buffer); + + cmd_input_passthrough_func(NULL); + + REQUEST(REDIR_DATA); + ARRAY_CMP(RESPONSE("Hi! ") , buf); +} + + +TEST(cli, cmd_out_func_set_null) +{ + cmd_out_func(NULL); +} + +static int outf_called = 0; +void outf(const char *fmt, va_list ap) { + outf_called++; +} +TEST(cli, cmd_out_func_set) +{ + outf_called = 0; + cmd_out_func(&outf); + cmd_vprintf(NULL, NULL); + CHECK_EQUAL(outf_called, 1); +} + +TEST(cli, cmd_ctrl_func_set_null) +{ + cmd_ctrl_func(NULL); +} + +TEST(cli, cmd_delete_null) +{ + cmd_delete(NULL); +} + +TEST(cli, cmd_history_size_set) +{ + cmd_history_size(0); + CHECK_EQUAL(cmd_history_size(1), 1); +} + +TEST(cli, cmd_add_invalid_params) +{ + cmd_add(NULL, cmd_dummy, NULL, NULL); + cmd_add("", cmd_dummy, NULL, NULL); + cmd_add("abc", NULL, NULL, NULL); +} diff --git a/test_suite.json b/test_suite.json new file mode 100644 index 00000000000..0447ba6001e --- /dev/null +++ b/test_suite.json @@ -0,0 +1,27 @@ +{ + "testcases": [ + { + "config": { + "requirements": { + "duts": { + "*": { + "count": 1, + "application": { + "bin": "/Users/ollpuo01/git/mbed-os-icetea-integration/BUILD/tests/K64F/GCC_ARM/./TEST_APPS/device/exampleapp/exampleapp.bin", + "name": "TEST_APPS-device-exampleapp" + }, + "type": "hardware", + "allowed_platforms": [ + "K64F" + ] + } + }, + "external": { + "apps": [] + } + } + }, + "name": "test_cmdline" + } + ] +} \ No newline at end of file diff --git a/tools/build_api.py b/tools/build_api.py index 601184944b3..c3140b455ea 100644 --- a/tools/build_api.py +++ b/tools/build_api.py @@ -1348,11 +1348,13 @@ def merge_build_data(filename, toolchain_report, app_type): for project in tc.values(): for build in project: try: + build[0]['bin_fullpath'] = build[0]['bin'] + build[0]['elf_fullpath'] = build[0]['elf'] build[0]['elf'] = relpath(build[0]['elf'], path_to_file) build[0]['bin'] = relpath(build[0]['bin'], path_to_file) except KeyError: pass if 'type' not in build[0]: build[0]['type'] = app_type - build_data['builds'].append(build[0]) - dump(build_data, open(filename, "w"), indent=4, separators=(',', ': ')) + build_data['builds'].insert(0, build[0]) + dump(build_data, open(filename, "wb"), indent=4, separators=(',', ': ')) diff --git a/tools/resources/__init__.py b/tools/resources/__init__.py index d19cabde5d1..f49af77259d 100644 --- a/tools/resources/__init__.py +++ b/tools/resources/__init__.py @@ -69,6 +69,7 @@ # Tests, here for simplicity 'TESTS', + 'TEST_APPS', ]) LEGACY_TOOLCHAIN_NAMES = { 'ARM_STD':'ARM', diff --git a/tools/run_icetea.py b/tools/run_icetea.py new file mode 100644 index 00000000000..94283ce7307 --- /dev/null +++ b/tools/run_icetea.py @@ -0,0 +1,335 @@ +#! /usr/bin/env python2 +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited + +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. + +""" + +from __future__ import print_function, division, absolute_import +import sys +import os +import re +from os.path import abspath, join, dirname, relpath, sep +import json +import traceback +from fnmatch import translate +from argparse import ArgumentParser + +ROOT = abspath(join(dirname(__file__), '..')) +sys.path.insert(0, ROOT) + +from tools.config import ConfigException +from tools.utils import cmd, run_cmd + +plugins_path = abspath(join(ROOT, 'TEST_APPS', 'icetea_plugins', 'plugins_to_load.py')) + + +def find_build_from_build_data(build_data, id, target, toolchain): + + if 'builds' not in build_data: + raise Exception("build data is in wrong format, does not include builds object") + + for build in build_data['builds']: + if 'id' in build.keys() \ + and id.upper() in build['id'].upper() \ + and 'target_name' in build.keys() \ + and target.upper() == build['target_name'].upper() \ + and 'toolchain_name' in build.keys() \ + and toolchain.upper() == build['toolchain_name'].upper() \ + and 'result' in build.keys() \ + and "OK" == build['result']: + return build + return None + + +def create_test_suite(target, tool, icetea_json_output, build_data, tests_by_name): + """ + Create test suite content + :param target: + :param tool: + :param icetea_json_output: + :param build_data: + :return: + """ + test_suite = dict() + test_suite['testcases'] = list() + + for test in icetea_json_output: + skip = False + + for dut in test['requirements']['duts'].values(): + # Set binary path based on application name + if 'application' in dut.keys() and 'name' in dut['application'].keys(): + build = find_build_from_build_data( + build_data=build_data, + id=dut['application']['name'], + target=target, + toolchain=tool) + if build: + try: + dut['application']['bin'] = build['bin_fullpath'] + except KeyError: + raise Exception('Full path is missing from build: {}'.format(build)) + else: + skip = True + + if not tests_by_name or is_test_in_test_by_name(test['name'], tests_by_name): + test_case = { + 'name': test['name'], + 'config': { + 'requirements': test['requirements'] + } + } + + # Skip test if not binary path + if skip: + test_case['config']['execution'] = { + 'skip': { + 'value': True, + 'reason': "Test requiring application binary not build" + } + } + + test_suite['testcases'].append(test_case) + + return test_suite + + +def get_applications(test): + ret = list() + for dut in test['requirements']['duts'].values(): + if 'application' in dut.keys() and 'name' in dut['application'].keys(): + ret.append(dut['application']['name']) + return ret + + +def filter_test_by_build_data(icetea_json_output, build_data, target, toolchain): + if not build_data: + return icetea_json_output + + ret = list() + for test in icetea_json_output: + for dut in test['requirements']['duts'].values(): + if 'application' in dut.keys() and 'name' in dut['application'].keys(): + id = dut['application']['name'] + if find_build_from_build_data(build_data, id, target, toolchain): + # Test requiring build found + ret.append(test) + return ret + + +def filter_test_by_name(icetea_json_output, test_by_name): + if not test_by_name: + return icetea_json_output + ret = list() + for test_temp in icetea_json_output: + if is_test_in_test_by_name(test_temp['name'], test_by_name) and test_temp not in ret: + ret.append(test_temp) + return ret + + +def get_applications_from_test(test): + ret = list() + if u'requirements' in test.keys() and u'duts' in test[u'requirements']: + for name, dut in test[u'requirements'][u'duts'].items(): + if u'application' in dut.keys() and u'name' in dut[u'application']: + ret.append(dut[u'application'][u'name']) + return ret + + +def get_application_list(icetea_json_output, tests_by_name): + """ Return comma separated list of application which are used in tests """ + ret = list() + for test in filter_test_by_name(icetea_json_output, tests_by_name): + ret.extend(get_applications_from_test(test)) + # Remove duplicates + return list(set(ret)) + + +def icetea_tests(target, tcdir, verbose): + command = ['icetea', '--tcdir', tcdir, '--list', '--json', '--platform_filter', target] \ + + (['-v'] if verbose else []) + + stdout, stderr, returncode = run_cmd(command) + + if returncode != 0: + raise Exception( + "Error when running icetea. \ncwd:{} \nCommand:'{}' \noutput:{}".format(os.getcwd(), ' '.join(command), + stderr.decode())) + + return json.loads(stdout) + + +def is_test_in_test_by_name(test_name, test_by_name): + for tbn_temp in test_by_name: + if re.search(translate(tbn_temp), test_name): + return True + return False + + +def check_tests(icetea_json_output): + """ + Check that all tests have all necessary information + :return: + """ + for test in icetea_json_output: + if not get_applications_from_test(test): + raise Exception('Test {} does not have application with correct name'.format(test['name'])) + + +def load_build_data(build_data_path): + """ + :return: build_data.json content as dict and None if build data is not available + """ + if not os.path.isfile(build_data_path): + return None + return json.load(open(build_data_path)) + + +if __name__ == '__main__': + try: + # Parse Options + parser = ArgumentParser() + + parser.add_argument('-m', '--mcu', + dest='target', + default=None, + help='Test target MCU', + required=True) + + parser.add_argument('-t', '--toolchain', + dest='toolchain', + default=None, + help='Toolchain', + required=True) + + parser.add_argument('--build-data', + dest='build_data', + default=None, + help='Detail data from build') + + parser.add_argument('--test-suite', + dest='test_suite', + default=None, + help='Path used for test suite file') + + parser.add_argument('-n', '--tests-by-name', + dest='tests_by_name', + default=None, + help='Limit the tests to a list (ex. test1,test2,test3)') + + parser.add_argument('--tcdir', + dest='tcdir', + default='TEST_APPS', + help='Test case directory', + required=False) + + parser.add_argument('--compile-list', + action='store_true', + dest='compile_list', + default=False, + help='List tests, which applications can be compiled') + + parser.add_argument('--run-list', + action='store_true', + dest='run_list', + default=False, + help='List tests, which applications are compiled and ready for run') + + parser.add_argument('--application-list', + action='store_true', + dest='application_list', + default=False, + help='List application that need to be build') + + parser.add_argument('--ignore-checks', + action='store_true', + dest='ignore_checks', + default=False, + help='Ignore data validation checks') + + parser.add_argument('-v', '--verbose', + action='store_true', + dest='verbose', + default=False, + help='Verbose diagnostic output') + + options = parser.parse_args() + + icetea_json_output = icetea_tests(options.target, options.tcdir, options.verbose) + tests_by_name = options.tests_by_name.split(',') if options.tests_by_name else None + build_data = load_build_data(options.build_data) if options.build_data else None + + if not options.ignore_checks: + check_tests(icetea_json_output) + + if options.compile_list: + print('Available icetea tests for build \'{}-{}\', location \'{}\''.format( + options.target, options.toolchain, options.tcdir)) + for test in icetea_json_output: + print( + 'Test Case:\n Name: {name}\n Path: .{sep}{filepath}\n Test applications: .{sep}{apps}'.format( + name=test['name'], + sep=sep, + filepath=relpath(test['filepath'], ROOT), + apps=''.join(get_applications(test)).replace('-', os.path.sep))) + + elif options.run_list: + print('Available icetea tests for build \'{}-{}\', location \'{}\''.format( + options.target, options.toolchain, options.tcdir)) + + # Filters + tests = filter_test_by_name(icetea_json_output, tests_by_name) + if build_data: + tests = filter_test_by_build_data(tests, build_data, options.target, options.toolchain) + + for test in tests: + print(' test \'{name}\''.format(name=test['name'])) + + elif options.application_list: + print(','.join(get_application_list(icetea_json_output, tests_by_name))) + + else: + if not build_data: + raise Exception("Build data file does not exist: {}".format(options.build_data)) + + test_suite = create_test_suite(options.target, options.toolchain, icetea_json_output, build_data, + tests_by_name) + + if not test_suite['testcases']: + raise Exception("Test suite is empty. Check that --tcdir and --tests-by-name have correct values") + + if not options.test_suite: + raise Exception('--test-suite is required when running tests') + + with open(options.test_suite, 'w') as f: + json.dump(test_suite, f, indent=2) + + # List just for debug + if options.verbose: + cmd(['icetea', '--tcdir', options.tcdir, '--list'] + (['-v'] if options.verbose else [])) + + cmd(['icetea', '--tcdir', options.tcdir, '--suite', options.test_suite, '--clean', '--plugin_path', + plugins_path] + (['-v'] if options.verbose else [])) + + except KeyboardInterrupt as e: + print('\n[CTRL+c] exit') + except ConfigException as e: + # Catching ConfigException here to prevent a traceback + print('[ERROR] {}'.format(e)) + except Exception as e: + traceback.print_exc(file=sys.stdout) + print('[ERROR] {}'.format(e)) + sys.exit(1) diff --git a/tools/test.py b/tools/test.py index 179c7fb30b2..27b2a86f009 100644 --- a/tools/test.py +++ b/tools/test.py @@ -16,27 +16,29 @@ limitations under the License. -TEST BUILD & RUN +TEST BUILD """ from __future__ import print_function, division, absolute_import import sys import os -import json +import subprocess import fnmatch ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) sys.path.insert(0, ROOT) from tools.config import ConfigException, Config -from tools.test_api import test_path_to_name, find_tests, get_test_config, print_tests, build_tests, test_spec_from_test_builds from tools.test_configs import get_default_config +from tools.config import ConfigException +from tools.test_api import find_tests, get_test_config, print_tests, build_tests, test_spec_from_test_builds +import tools.test_configs as TestConfig from tools.options import get_default_options_parser, extract_profile, extract_mcus from tools.build_api import build_project, build_library from tools.build_api import print_build_memory_usage from tools.build_api import merge_build_data from tools.targets import TARGET_MAP from tools.notifier.term import TerminalNotifier -from tools.utils import mkdir, ToolException, NotSupportedException, args_error +from tools.utils import mkdir, ToolException, NotSupportedException, args_error, write_json_to_file from tools.test_exporters import ReportExporter, ResultExporterType from tools.utils import argparse_filestring_type, argparse_lowercase_type, argparse_many from tools.utils import argparse_dir_not_parent @@ -111,9 +113,19 @@ dest="stats_depth", default=2, help="Depth level for static memory report") - parser.add_argument("--ignore", dest="ignore", type=argparse_many(str), default=None, help="Comma separated list of patterns to add to mbedignore (eg. ./main.cpp)") + parser.add_argument("--icetea", + action="store_true", + dest="icetea", + default=False, + help="Only icetea tests") + + parser.add_argument("--greentea", + action="store_true", + dest="greentea", + default=False, + help="Only greentea tests") options = parser.parse_args() @@ -126,8 +138,13 @@ all_tests = {} tests = {} + # As default both test tools are enabled + if not (options.greentea or options.icetea): + options.greentea = True + options.icetea = True + # Target - if options.mcu is None : + if options.mcu is None: args_error(parser, "argument -m/--mcu is required") mcu = extract_mcus(parser, options)[0] @@ -159,8 +176,13 @@ # Find all tests in the relevant paths for path in all_paths: - all_tests.update(find_tests(path, mcu, toolchain, - app_config=config)) + all_tests.update(find_tests( + base_dir=path, + target_name=mcu, + toolchain_name=toolchain, + icetea=options.icetea, + greentea=options.greentea, + app_config=config)) # Filter tests by name if specified if options.names: @@ -251,20 +273,7 @@ # If a path to a test spec is provided, write it to a file if options.test_spec: - test_spec_data = test_spec_from_test_builds(test_build) - - # Create the target dir for the test spec if necessary - # mkdir will not create the dir if it already exists - test_spec_dir = os.path.dirname(options.test_spec) - if test_spec_dir: - mkdir(test_spec_dir) - - try: - with open(options.test_spec, 'w') as f: - f.write(json.dumps(test_spec_data, indent=2)) - except IOError as e: - print("[ERROR] Error writing test spec to file") - print(e) + write_json_to_file(test_spec_from_test_builds(test_build), options.test_spec) # If a path to a JUnit build report spec is provided, write it to a file if options.build_report_junit: @@ -296,3 +305,4 @@ traceback.print_exc(file=sys.stdout) print("[ERROR] %s" % str(e)) sys.exit(1) + diff --git a/tools/test/__init__.py b/tools/test/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/test/run_icetea/.gitignore b/tools/test/run_icetea/.gitignore new file mode 100644 index 00000000000..7dfa3a841fb --- /dev/null +++ b/tools/test/run_icetea/.gitignore @@ -0,0 +1 @@ +test_suite.json \ No newline at end of file diff --git a/tools/test/run_icetea/TEST_DIR/__init__.py b/tools/test/run_icetea/TEST_DIR/__init__.py new file mode 100644 index 00000000000..ecb93799a76 --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR/__init__.py @@ -0,0 +1,14 @@ +""" +Copyright 2017 ARM Limited +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. +""" \ No newline at end of file diff --git a/tools/test/run_icetea/TEST_DIR/test_pass.py b/tools/test/run_icetea/TEST_DIR/test_pass.py new file mode 100644 index 00000000000..4e1f646a9f7 --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR/test_pass.py @@ -0,0 +1,37 @@ +""" +Copyright 2017 ARM Limited +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. +""" + +from icetea_lib.bench import Bench + + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, + name="test_pass", + title="Test icetea integration", + status="released", + purpose="Just for testing scripts", + component=[], + type="smoke" + ) + + def setup(self): + pass + + def case(self): + print("Test2 running") + + def teardown(self): + pass diff --git a/tools/test/run_icetea/TEST_DIR/test_print.py b/tools/test/run_icetea/TEST_DIR/test_print.py new file mode 100644 index 00000000000..04654acbeff --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR/test_print.py @@ -0,0 +1,37 @@ +""" +Copyright 2017 ARM Limited +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. +""" + +from icetea_lib.bench import Bench + + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, + name="test_print", + title="Test icetea integration", + status="released", + purpose="Just for testing scripts", + component=[], + type="smoke" + ) + + def setup(self): + pass + + def case(self): + print("Test running") + + def teardown(self): + pass diff --git a/tools/test/run_icetea/TEST_DIR_HW/README.md b/tools/test/run_icetea/TEST_DIR_HW/README.md new file mode 100644 index 00000000000..6f59e5747bf --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR_HW/README.md @@ -0,0 +1 @@ +This folder contains hardware test data for icetea integration \ No newline at end of file diff --git a/tools/test/run_icetea/TEST_DIR_HW/__init__.py b/tools/test/run_icetea/TEST_DIR_HW/__init__.py new file mode 100644 index 00000000000..ecb93799a76 --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR_HW/__init__.py @@ -0,0 +1,14 @@ +""" +Copyright 2017 ARM Limited +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. +""" \ No newline at end of file diff --git a/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py b/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py new file mode 100644 index 00000000000..778af71a849 --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py @@ -0,0 +1,49 @@ +""" +Copyright 2017 ARM Limited +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. +""" + +from icetea_lib.bench import Bench + + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, + name="test_K64F_only", + title="Test a test case which have onely K64F support", + status="released", + purpose="Just for testing scripts", + component=[], + type="smoke", + requirements={ + "duts": { + '*': { + "count": 1, + "type": "hardware", + "allowed_platforms": ['K64F'], + "application": { + "name": "TEST_APPS-device-exampleapp" + } + } + } + } + ) + + def setup(self): + pass + + def case(self): + pass + + def teardown(self): + pass diff --git a/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py b/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py new file mode 100644 index 00000000000..15735913d4c --- /dev/null +++ b/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py @@ -0,0 +1,58 @@ +""" +Copyright 2017 ARM Limited +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. +""" + +from icetea_lib.bench import Bench + + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, + name="test_predefined_platforms", + title="Test a test case which have support for multiple platforms", + status="released", + purpose="Just for testing scripts", + component=[], + type="regression", + requirements={ + "duts": { + '*': { + "count": 1, + "type": "hardware", + "allowed_platforms": [ + "LPC1768", "KL25Z", "K64F", "K66F", "K22F", "LPC4088", "LPC1549", + "NUCLEO_F072RB", "NUCLEO_F091RC", "NUCLEO_F302R8", "NUCLEO_F303K8", + "NUCLEO_F303RE", "NUCLEO_F207ZG", "NUCLEO_F334R8", "NUCLEO_F303ZE", + "NUCLEO_L053R8", "DISCO_L072CZ_LRWAN1", "NUCLEO_L073RZ", "NUCLEO_L152RE", + "NUCLEO_F410RB", "NUCLEO_F446RE", "NUCLEO_F446ZE", "NUCLEO_F429ZI", + "DISCO_F407VG", "NUCLEO_F746ZG", "NUCLEO_L476RG", "DISCO_L053C8", "DISCO_F334C8", + "DISCO_L475VG_IOT01A", "DISCO_L476VG", "DISCO_F469NI", "DISCO_F429ZI", + "DISCO_F769NI", "ARCH_MAX", "MAX32600MBED", "MOTE_L152RC", "B96B_F446VE" + ], + "application": { + "name": "TEST_APPS-device-exampleapp" + } + } + } + } + ) + + def setup(self): + pass + + def case(self): + pass + + def teardown(self): + pass diff --git a/tools/test/run_icetea/__init__.py b/tools/test/run_icetea/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/test/run_icetea/empty_build_data.json b/tools/test/run_icetea/empty_build_data.json new file mode 100644 index 00000000000..7292e11b6cc --- /dev/null +++ b/tools/test/run_icetea/empty_build_data.json @@ -0,0 +1,3 @@ +{ + "builds": [] +} \ No newline at end of file diff --git a/tools/test/run_icetea/run_icetea_test.py b/tools/test/run_icetea/run_icetea_test.py new file mode 100644 index 00000000000..ad1e29def4b --- /dev/null +++ b/tools/test/run_icetea/run_icetea_test.py @@ -0,0 +1,104 @@ +""" +mbed SDK +Copyright (c) 2016 ARM Limited + +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. +""" + +from os.path import realpath, join, dirname, isfile +import subprocess + +""" +Tests for run_icetea.py +""" +this_file_dir = dirname(realpath(__file__)) +hw_test_dir = join(this_file_dir, 'TEST_DIR_HW') +test_dir = join(this_file_dir, 'TEST_DIR') +empty_build_data = join(this_file_dir, 'empty_build_data.json') +test_suite = join(this_file_dir, 'test_suite.json') +run_icetea_py = join(dirname(dirname(this_file_dir)), 'run_icetea.py') +assert isfile(run_icetea_py) + + +def _execute_icetea(*params): + command = ["python", run_icetea_py] + list(params) + process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stout, sterr = process.communicate() + status = process.poll() + if status != 0: + raise Exception("Error with {}, \nreturn code: {}, \nerror message: {}, \noutput:{}".format( + " ".join(command), status, sterr, stout + )) + return stout.decode() + + +def test_help(): + """ + Just test that something works + :return: + """ + _execute_icetea('--help') + + +def test_list_tests_k64f(): + out = _execute_icetea('--compile-list', '--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', hw_test_dir) + assert 'test_K64F_only' in out + assert 'test_predefined_platforms' in out + + +def test_list_tests_nucleo_l073rz(): + out = _execute_icetea('--compile-list', '--mcu', 'NUCLEO_L073RZ', '--toolchain', 'GCC_ARM', '--tcdir', hw_test_dir) + assert 'test_predefined_platforms' in out + assert 'test_K64F_only' not in out + + +def test_run(): + out = _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', test_dir, '--build-data', + empty_build_data, '--test-suite', test_suite, '--ignore-checks') + assert 'test_print' in out + assert 'test_pass' in out + + +def test_run_by_name(): + out = _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', test_dir, '--build-data', + empty_build_data, '--test-suite', test_suite, '--tests-by-name', 'test_pass', + '--ignore-checks') + assert 'test_pass' in out + assert 'test_print' not in out + + +def test_run_hw_with_not_build_tests(): + """ + When test binaries are not found tests will be skipped + :return: + """ + out = _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', hw_test_dir, '--build-data', + empty_build_data, '--test-suite', test_suite) + output_lines = out.split('\n') + + # Assert that + temp = list(filter(lambda x: 'test_K64F_only' in x, output_lines))[0] + assert 'skip' in temp + + temp = list(filter(lambda x: 'test_predefined_platforms' in x, output_lines))[0] + assert 'skip' in temp + + +def test_data_validation(): + exception_happened = False + try: + _execute_icetea('--mcu', 'K64F', '--toolchain', 'GCC_ARM', '--tcdir', test_dir, '--build-data', + empty_build_data, '--test-suite', test_suite) + except BaseException: + exception_happened = True + assert exception_happened diff --git a/tools/test/run_icetea/run_icetea_unittest.py b/tools/test/run_icetea/run_icetea_unittest.py new file mode 100644 index 00000000000..bb94c3efeab --- /dev/null +++ b/tools/test/run_icetea/run_icetea_unittest.py @@ -0,0 +1,146 @@ +""" +mbed SDK +Copyright (c) 2016 ARM Limited + +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. +""" + +import os +import sys + +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", + "..")) +sys.path.insert(0, ROOT) + +from tools.run_icetea import find_build_from_build_data, filter_test_by_build_data, filter_test_by_name, \ + get_application_list + +""" +Unit tests for run_icetea.py +""" + +test_build_data = { + 'builds': [ + { + "id": "TEST_APPS-DEVICE-SOCKET_APP", + "target_name": "K64F", + "toolchain_name": "GCC_ARM" + } + ] +} + + +def test_find_build_from_build_data_empty(): + assert find_build_from_build_data(build_data={'builds': []}, id="something", target="K64F", + toolchain="GCC_ARM") is None + + +def test_find_build_from_build_data_wrong_target(): + assert find_build_from_build_data(build_data=test_build_data, id="TEST_APPS-DEVICE-SOCKET_APP", target="AAAA", + toolchain="GCC_ARM") is None + + +def test_find_build_from_build_data(): + assert find_build_from_build_data(build_data=test_build_data, id="TEST_APPS-DEVICE-SOCKET_APP", target="K64F", + toolchain="GCC_ARM") is not None + + +icetea_json_output = [ + { + "status": "released", + "requirements": { + "duts": { + "1": { + "nick": "dut1" + }, + "*": { + "count": 1, + "application": { + "bin": None, + "name": "TEST_APPS-device-socket_app" + }, + "type": "hardware" + } + }, + "external": { + "apps": [] + } + }, + "name": "UDPSOCKET_BIND_PORT", + "filepath": "/Users/test/mbed-os/TEST_APPS/testcases/SOCKET_BIND_PORT.py", + "title": "udpsocket open and bind port", + "component": [ + "mbed-os", + "netsocket" + ], + "compatible": { + "framework": { + "version": ">=1.0.0", + "name": "Icetea" + }, + "hw": { + "value": True + }, + "automation": { + "value": True + } + }, + "subtype": "socket", + "purpose": "Verify UDPSocket can be created, opened and port binded", + "type": "smoke", + "sub_type": None + } +] + + +def test_filter_test_by_build_data_when_data_is_empty(): + assert filter_test_by_build_data( + icetea_json_output=icetea_json_output, + build_data=None, + target="K64F", + toolchain="GCC_ARM" + ) == icetea_json_output + + +def test_filter_test_by_build_data(): + temp = filter_test_by_build_data( + icetea_json_output=icetea_json_output, + build_data=test_build_data, + target="K64F", + toolchain="GCC_ARM" + ) + assert len(temp) > 0 + + +def test_filter_test_by_name(): + assert len(filter_test_by_name(icetea_json_output, ['UDPSOCKET_BIND_PORT'])) > 0 + + +def test_filter_test_by_name_when_not_found(): + assert filter_test_by_name(icetea_json_output, ['AAA']) == list() + + +def test_filter_test_by_name_when_name_is_empty(): + assert filter_test_by_name(icetea_json_output, None) == icetea_json_output + + +def test_get_application_list(): + assert 'TEST_APPS-device-socket_app' in get_application_list(icetea_json_output, ['UDPSOCKET_BIND_PORT']) + + +def test_get_application_list_not_found(): + assert 'TEST_APPS-device-socket_app' not in get_application_list(icetea_json_output, ['SOMETHING_ELSE']) + + +def test_get_application_list_none(): + assert 'TEST_APPS-device-socket_app' in get_application_list(icetea_json_output, None) diff --git a/tools/test_api.py b/tools/test_api.py index 121b18016c4..2fe0794976c 100644 --- a/tools/test_api.py +++ b/tools/test_api.py @@ -28,14 +28,17 @@ import random import argparse import datetime +import errno import threading import ctypes import functools +import subprocess from colorama import Fore, Back, Style from prettytable import PrettyTable from copy import copy, deepcopy from time import sleep, time + try: from Queue import Queue, Empty except ImportError: @@ -114,6 +117,7 @@ def stop(self): class SingleTestExecutor(threading.Thread): """ Example: Single test class in separate thread usage """ + def __init__(self, single_test): self.single_test = single_test threading.Thread.__init__(self) @@ -134,7 +138,7 @@ def run(self): # table shows text x toolchain test result matrix print(self.single_test.generate_test_summary_by_target( test_summary, shuffle_seed)) - print("Completed in %.2f sec"% (elapsed_time)) + print("Completed in %.2f sec" % (elapsed_time)) class SingleTestRunner(object): @@ -157,26 +161,26 @@ class SingleTestRunner(object): TEST_RESULT_NOT_SUPPORTED = "NOT_SUPPORTED" GLOBAL_LOOPS_COUNT = 1 # How many times each test should be repeated - TEST_LOOPS_LIST = [] # We redefine no.of loops per test_id - TEST_LOOPS_DICT = {} # TEST_LOOPS_LIST in dict format: { test_id : test_loop_count} + TEST_LOOPS_LIST = [] # We redefine no.of loops per test_id + TEST_LOOPS_DICT = {} # TEST_LOOPS_LIST in dict format: { test_id : test_loop_count} - muts = {} # MUTs descriptor (from external file) - test_spec = {} # Test specification (from external file) + muts = {} # MUTs descriptor (from external file) + test_spec = {} # Test specification (from external file) # mbed test suite -> SingleTestRunner - TEST_RESULT_MAPPING = {"success" : TEST_RESULT_OK, - "failure" : TEST_RESULT_FAIL, - "error" : TEST_RESULT_ERROR, - "ioerr_copy" : TEST_RESULT_IOERR_COPY, - "ioerr_disk" : TEST_RESULT_IOERR_DISK, - "ioerr_serial" : TEST_RESULT_IOERR_SERIAL, - "timeout" : TEST_RESULT_TIMEOUT, - "no_image" : TEST_RESULT_NO_IMAGE, - "end" : TEST_RESULT_UNDEF, - "mbed_assert" : TEST_RESULT_MBED_ASSERT, - "build_failed" : TEST_RESULT_BUILD_FAILED, - "not_supproted" : TEST_RESULT_NOT_SUPPORTED - } + TEST_RESULT_MAPPING = {"success": TEST_RESULT_OK, + "failure": TEST_RESULT_FAIL, + "error": TEST_RESULT_ERROR, + "ioerr_copy": TEST_RESULT_IOERR_COPY, + "ioerr_disk": TEST_RESULT_IOERR_DISK, + "ioerr_serial": TEST_RESULT_IOERR_SERIAL, + "timeout": TEST_RESULT_TIMEOUT, + "no_image": TEST_RESULT_NO_IMAGE, + "end": TEST_RESULT_UNDEF, + "mbed_assert": TEST_RESULT_MBED_ASSERT, + "build_failed": TEST_RESULT_BUILD_FAILED, + "not_supproted": TEST_RESULT_NOT_SUPPORTED + } def __init__(self, _global_loops_count=1, @@ -286,17 +290,18 @@ def __init__(self, # Database related initializations self.db_logger = factory_db_logger(self.opts_db_url) - self.db_logger_build_id = None # Build ID (database index of build_id table) + self.db_logger_build_id = None # Build ID (database index of build_id table) # Let's connect to database to set up credentials and confirm database is ready if self.db_logger: - self.db_logger.connect_url(self.opts_db_url) # Save db access info inside db_logger object + self.db_logger.connect_url(self.opts_db_url) # Save db access info inside db_logger object if self.db_logger.is_connected(): # Get hostname and uname so we can use it as build description # when creating new build_id in external database (_hostname, _uname) = self.db_logger.get_hostname() _host_location = os.path.dirname(os.path.abspath(__file__)) build_id_type = None if self.opts_only_build_tests is None else self.db_logger.BUILD_ID_TYPE_BUILD_ONLY - self.db_logger_build_id = self.db_logger.get_next_build_id(_hostname, desc=_uname, location=_host_location, type=build_id_type) + self.db_logger_build_id = self.db_logger.get_next_build_id(_hostname, desc=_uname, + location=_host_location, type=build_id_type) self.db_logger.disconnect() def dump_options(self): @@ -307,23 +312,23 @@ def dump_options(self): or data_str = json.dumps(self.dump_options()) """ - result = {"db_url" : str(self.opts_db_url), - "log_file_name" : str(self.opts_log_file_name), - "shuffle_test_order" : str(self.opts_shuffle_test_order), - "shuffle_test_seed" : str(self.opts_shuffle_test_seed), - "test_by_names" : str(self.opts_test_by_names), - "peripheral_by_names" : str(self.opts_peripheral_by_names), - "test_only_peripheral" : str(self.opts_test_only_peripheral), - "test_only_common" : str(self.opts_test_only_common), - "verbose" : str(self.opts_verbose), - "firmware_global_name" : str(self.opts_firmware_global_name), - "only_build_tests" : str(self.opts_only_build_tests), - "copy_method" : str(self.opts_copy_method), - "mut_reset_type" : str(self.opts_mut_reset_type), - "jobs" : str(self.opts_jobs), - "extend_test_timeout" : str(self.opts_extend_test_timeout), - "_dummy" : '' - } + result = {"db_url": str(self.opts_db_url), + "log_file_name": str(self.opts_log_file_name), + "shuffle_test_order": str(self.opts_shuffle_test_order), + "shuffle_test_seed": str(self.opts_shuffle_test_seed), + "test_by_names": str(self.opts_test_by_names), + "peripheral_by_names": str(self.opts_peripheral_by_names), + "test_only_peripheral": str(self.opts_test_only_peripheral), + "test_only_common": str(self.opts_test_only_common), + "verbose": str(self.opts_verbose), + "firmware_global_name": str(self.opts_firmware_global_name), + "only_build_tests": str(self.opts_only_build_tests), + "copy_method": str(self.opts_copy_method), + "mut_reset_type": str(self.opts_mut_reset_type), + "jobs": str(self.opts_jobs), + "extend_test_timeout": str(self.opts_extend_test_timeout), + "_dummy": '' + } return result def shuffle_random_func(self): @@ -365,7 +370,6 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep 'shuffle_random_seed': self.shuffle_random_seed } - # print '=== %s::%s ===' % (target, toolchain) # Let's build our test if target not in TARGET_MAP: @@ -395,7 +399,7 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep print(self.logger.log_line( self.logger.LogType.NOTIF, 'Skipped tests for %s target. Toolchain %s is not ' - 'supported for this target'% (T.name, toolchain))) + 'supported for this target' % (T.name, toolchain))) continue except ToolException: @@ -438,13 +442,13 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep _extra=json.dumps(self.dump_options())) self.db_logger.disconnect(); - valid_test_map_keys = self.get_valid_tests(test_map_keys, target, toolchain, test_ids, self.opts_include_non_automated) + valid_test_map_keys = self.get_valid_tests(test_map_keys, target, toolchain, test_ids, + self.opts_include_non_automated) skipped_test_map_keys = self.get_skipped_tests(test_map_keys, valid_test_map_keys) for skipped_test_id in skipped_test_map_keys: test_suite_properties['skipped'].append(skipped_test_id) - # First pass through all tests and determine which libraries need to be built libraries = [] for test_id in valid_test_map_keys: @@ -456,7 +460,6 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep if lib['build_dir'] in test.dependencies and lib['id'] not in libraries: libraries.append(lib['id']) - clean_project_options = True if self.opts_goanna_for_tests or clean or self.opts_clean else None # Build all required libraries @@ -478,11 +481,10 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep 'There were errors while building library %s' % lib_id)) continue - for test_id in valid_test_map_keys: test = TEST_MAP[test_id] - test_suite_properties['test.libs.%s.%s.%s'% (target, toolchain, test_id)] = ', '.join(libraries) + test_suite_properties['test.libs.%s.%s.%s' % (target, toolchain, test_id)] = ', '.join(libraries) # TODO: move this 2 below loops to separate function INC_DIRS = [] @@ -494,18 +496,18 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep for lib_id in libraries: if 'macros' in LIBRARY_MAP[lib_id] and LIBRARY_MAP[lib_id]['macros']: MACROS.extend(LIBRARY_MAP[lib_id]['macros']) - MACROS.append('TEST_SUITE_TARGET_NAME="%s"'% target) - MACROS.append('TEST_SUITE_TEST_ID="%s"'% test_id) + MACROS.append('TEST_SUITE_TARGET_NAME="%s"' % target) + MACROS.append('TEST_SUITE_TEST_ID="%s"' % test_id) test_uuid = uuid.uuid4() - MACROS.append('TEST_SUITE_UUID="%s"'% str(test_uuid)) + MACROS.append('TEST_SUITE_UUID="%s"' % str(test_uuid)) # Prepare extended test results data structure (it can be used to generate detailed test report) if target not in self.test_summary_ext: self.test_summary_ext[target] = {} # test_summary_ext : toolchain if toolchain not in self.test_summary_ext[target]: - self.test_summary_ext[target][toolchain] = {} # test_summary_ext : toolchain : target + self.test_summary_ext[target][toolchain] = {} # test_summary_ext : toolchain : target - tt_test_id = "%s::%s::%s" % (toolchain, target, test_id) # For logging only + tt_test_id = "%s::%s::%s" % (toolchain, target, test_id) # For logging only project_name = self.opts_firmware_global_name if self.opts_firmware_global_name else None try: @@ -523,7 +525,6 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep except Exception as e: project_name_str = project_name if project_name is not None else test_id - test_result = self.TEST_RESULT_FAIL if isinstance(e, ToolException): @@ -538,7 +539,6 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep 'Project %s is not supported' % project_name_str)) test_result = self.TEST_RESULT_NOT_SUPPORTED - # Append test results to global test summary self.test_summary.append( (test_result, target, toolchain, test_id, @@ -549,17 +549,17 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep if test_id not in self.test_summary_ext[target][toolchain]: self.test_summary_ext[target][toolchain][test_id] = [] - self.test_summary_ext[target][toolchain][test_id].append({ 0: { - 'result' : test_result, - 'output' : '', - 'target_name' : target, + self.test_summary_ext[target][toolchain][test_id].append({0: { + 'result': test_result, + 'output': '', + 'target_name': target, 'target_name_unique': target, - 'toolchain_name' : toolchain, - 'id' : test_id, - 'description' : test.get_description(), - 'elapsed_time' : 0, - 'duration' : 0, - 'copy_method' : None + 'toolchain_name': toolchain, + 'id': test_id, + 'description': test.get_description(), + 'elapsed_time': 0, + 'duration': 0, + 'copy_method': None }}) continue @@ -577,9 +577,9 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep test_spec = self.shape_test_request(target, path, test_id, test_duration) test_loops = self.get_test_loop_count(test_id) - test_suite_properties['test.duration.%s.%s.%s'% (target, toolchain, test_id)] = test_duration - test_suite_properties['test.loops.%s.%s.%s'% (target, toolchain, test_id)] = test_loops - test_suite_properties['test.path.%s.%s.%s'% (target, toolchain, test_id)] = path + test_suite_properties['test.duration.%s.%s.%s' % (target, toolchain, test_id)] = test_duration + test_suite_properties['test.loops.%s.%s.%s' % (target, toolchain, test_id)] = test_loops + test_suite_properties['test.path.%s.%s.%s' % (target, toolchain, test_id)] = path # read MUTs, test specification and perform tests handle_results = self.handle(test_spec, target, toolchain, test_loops=test_loops) @@ -627,12 +627,11 @@ def execute(self): if self.opts_shuffle_test_seed is not None and self.is_shuffle_seed_float(): self.shuffle_random_seed = round(float(self.opts_shuffle_test_seed), self.SHUFFLE_SEED_ROUND) - if self.opts_parallel_test_exec: ################################################################### # Experimental, parallel test execution per singletest instance. ################################################################### - execute_threads = [] # Threads used to build mbed SDL, libs, test cases and execute tests + execute_threads = [] # Threads used to build mbed SDL, libs, test cases and execute tests # Note: We are building here in parallel for each target separately! # So we are not building the same thing multiple times and compilers # in separate threads do not collide. @@ -640,26 +639,29 @@ def execute(self): # get information about available MUTs (per target). for target, toolchains in self.test_spec['targets'].items(): self.test_suite_properties_ext[target] = {} - t = threading.Thread(target=self.execute_thread_slice, args = (q, target, toolchains, clean, test_ids, self.build_report, self.build_properties)) + t = threading.Thread(target=self.execute_thread_slice, args=( + q, target, toolchains, clean, test_ids, self.build_report, self.build_properties)) t.daemon = True t.start() execute_threads.append(t) for t in execute_threads: - q.get() # t.join() would block some threads because we should not wait in any order for thread end + q.get() # t.join() would block some threads because we should not wait in any order for thread end else: # Serialized (not parallel) test execution for target, toolchains in self.test_spec['targets'].items(): if target not in self.test_suite_properties_ext: self.test_suite_properties_ext[target] = {} - self.execute_thread_slice(q, target, toolchains, clean, test_ids, self.build_report, self.build_properties) + self.execute_thread_slice(q, target, toolchains, clean, test_ids, self.build_report, + self.build_properties) q.get() if self.db_logger: self.db_logger.reconnect(); if self.db_logger.is_connected(): - self.db_logger.update_build_id_info(self.db_logger_build_id, _status_fk=self.db_logger.BUILD_ID_STATUS_COMPLETED) + self.db_logger.update_build_id_info(self.db_logger_build_id, + _status_fk=self.db_logger.BUILD_ID_STATUS_COMPLETED) self.db_logger.disconnect(); return self.test_summary, self.shuffle_random_seed, self.test_summary_ext, self.test_suite_properties_ext, self.build_report, self.build_properties @@ -683,8 +685,8 @@ def get_valid_tests(self, test_map_keys, target, toolchain, test_ids, include_no continue if (self.opts_peripheral_by_names and test.peripherals and - not any((i in self.opts_peripheral_by_names) - for i in test.peripherals)): + not any((i in self.opts_peripheral_by_names) + for i in test.peripherals)): # We will skip tests not forced with -p option if self.opts_verbose_skipped_tests: print(self.logger.log_line( @@ -755,7 +757,7 @@ def generate_test_summary_by_target(self, test_summary, shuffle_seed=None): result = "Test summary:\n" for target in unique_targets: - result_dict = {} # test : { toolchain : result } + result_dict = {} # test : { toolchain : result } unique_target_toolchains = [] for test in test_summary: if test[TARGET_INDEX] == target: @@ -769,7 +771,7 @@ def generate_test_summary_by_target(self, test_summary, shuffle_seed=None): pt = PrettyTable(pt_cols) for col in pt_cols: pt.align[col] = "l" - pt.padding_width = 1 # One space between column edges and contents (default) + pt.padding_width = 1 # One space between column edges and contents (default) for test in unique_tests: if test in result_dict: @@ -781,40 +783,40 @@ def generate_test_summary_by_target(self, test_summary, shuffle_seed=None): row.append(test_results[toolchain]) pt.add_row(row) result += pt.get_string() - shuffle_seed_text = "Shuffle Seed: %.*f"% (self.SHUFFLE_SEED_ROUND, - shuffle_seed if shuffle_seed else self.shuffle_random_seed) - result += "\n%s"% (shuffle_seed_text if self.opts_shuffle_test_order else '') + shuffle_seed_text = "Shuffle Seed: %.*f" % (self.SHUFFLE_SEED_ROUND, + shuffle_seed if shuffle_seed else self.shuffle_random_seed) + result += "\n%s" % (shuffle_seed_text if self.opts_shuffle_test_order else '') return result def generate_test_summary(self, test_summary, shuffle_seed=None): """ Prints well-formed summary with results (SQL table like) table shows target x test results matrix across """ - success_code = 0 # Success code that can be leter returned to + success_code = 0 # Success code that can be leter returned to result = "Test summary:\n" # Pretty table package is used to print results pt = PrettyTable(["Result", "Target", "Toolchain", "Test ID", "Test Description", "Elapsed Time (sec)", "Timeout (sec)", "Loops"]) - pt.align["Result"] = "l" # Left align - pt.align["Target"] = "l" # Left align - pt.align["Toolchain"] = "l" # Left align - pt.align["Test ID"] = "l" # Left align - pt.align["Test Description"] = "l" # Left align - pt.padding_width = 1 # One space between column edges and contents (default) - - result_dict = {self.TEST_RESULT_OK : 0, - self.TEST_RESULT_FAIL : 0, - self.TEST_RESULT_ERROR : 0, - self.TEST_RESULT_UNDEF : 0, - self.TEST_RESULT_IOERR_COPY : 0, - self.TEST_RESULT_IOERR_DISK : 0, - self.TEST_RESULT_IOERR_SERIAL : 0, - self.TEST_RESULT_NO_IMAGE : 0, - self.TEST_RESULT_TIMEOUT : 0, - self.TEST_RESULT_MBED_ASSERT : 0, - self.TEST_RESULT_BUILD_FAILED : 0, - self.TEST_RESULT_NOT_SUPPORTED : 0 - } + pt.align["Result"] = "l" # Left align + pt.align["Target"] = "l" # Left align + pt.align["Toolchain"] = "l" # Left align + pt.align["Test ID"] = "l" # Left align + pt.align["Test Description"] = "l" # Left align + pt.padding_width = 1 # One space between column edges and contents (default) + + result_dict = {self.TEST_RESULT_OK: 0, + self.TEST_RESULT_FAIL: 0, + self.TEST_RESULT_ERROR: 0, + self.TEST_RESULT_UNDEF: 0, + self.TEST_RESULT_IOERR_COPY: 0, + self.TEST_RESULT_IOERR_DISK: 0, + self.TEST_RESULT_IOERR_SERIAL: 0, + self.TEST_RESULT_NO_IMAGE: 0, + self.TEST_RESULT_TIMEOUT: 0, + self.TEST_RESULT_MBED_ASSERT: 0, + self.TEST_RESULT_BUILD_FAILED: 0, + self.TEST_RESULT_NOT_SUPPORTED: 0 + } for test in test_summary: if test[0] in result_dict: @@ -824,10 +826,11 @@ def generate_test_summary(self, test_summary, shuffle_seed=None): result += "\n" # Print result count - result += "Result: " + ' / '.join(['%s %s' % (value, key) for (key, value) in {k: v for k, v in result_dict.items() if v != 0}.items()]) - shuffle_seed_text = "Shuffle Seed: %.*f\n"% (self.SHUFFLE_SEED_ROUND, - shuffle_seed if shuffle_seed else self.shuffle_random_seed) - result += "\n%s"% (shuffle_seed_text if self.opts_shuffle_test_order else '') + result += "Result: " + ' / '.join( + ['%s %s' % (value, key) for (key, value) in {k: v for k, v in result_dict.items() if v != 0}.items()]) + shuffle_seed_text = "Shuffle Seed: %.*f\n" % (self.SHUFFLE_SEED_ROUND, + shuffle_seed if shuffle_seed else self.shuffle_random_seed) + result += "\n%s" % (shuffle_seed_text if self.opts_shuffle_test_order else '') return result def test_loop_list_to_dict(self, test_loops_str): @@ -883,7 +886,7 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): return None mcu = mut['mcu'] - copy_method = mut.get('copy_method') # Available board configuration selection e.g. core selection etc. + copy_method = mut.get('copy_method') # Available board configuration selection e.g. core selection etc. if self.db_logger: self.db_logger.reconnect() @@ -939,13 +942,14 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): # Host test execution start_host_exec_time = time() - single_test_result = self.TEST_RESULT_UNDEF # single test run result + single_test_result = self.TEST_RESULT_UNDEF # single test run result _copy_method = selected_copy_method if not exists(image_path): single_test_result = self.TEST_RESULT_NO_IMAGE elapsed_time = 0 - single_test_output = self.logger.log_line(self.logger.LogType.ERROR, 'Image file does not exist: %s'% image_path) + single_test_output = self.logger.log_line(self.logger.LogType.ERROR, + 'Image file does not exist: %s' % image_path) print(single_test_output) else: # Host test execution @@ -965,20 +969,20 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): # Store test result test_all_result.append(single_test_result) - total_elapsed_time = time() - start_host_exec_time # Test time with copy (flashing) / reset + total_elapsed_time = time() - start_host_exec_time # Test time with copy (flashing) / reset elapsed_time = single_testduration # TIme of single test case execution after reset detailed_test_results[test_index] = { - 'result' : single_test_result, - 'output' : single_test_output, - 'target_name' : target_name, - 'target_name_unique' : target_name_unique, - 'toolchain_name' : toolchain_name, - 'id' : test_id, - 'description' : test_description, - 'elapsed_time' : round(elapsed_time, 2), - 'duration' : single_timeout, - 'copy_method' : _copy_method, + 'result': single_test_result, + 'output': single_test_output, + 'target_name': target_name, + 'target_name_unique': target_name_unique, + 'toolchain_name': toolchain_name, + 'id': test_id, + 'description': test_description, + 'elapsed_time': round(elapsed_time, 2), + 'duration': single_timeout, + 'copy_method': _copy_method, } print(self.print_test_result( @@ -1006,7 +1010,8 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): if self.db_logger: self.db_logger.disconnect() - return (self.shape_global_test_loop_result(test_all_result, self.opts_waterfall_test and self.opts_consolidate_waterfall_test), + return (self.shape_global_test_loop_result(test_all_result, + self.opts_waterfall_test and self.opts_consolidate_waterfall_test), target_name_unique, toolchain_name, test_id, @@ -1044,7 +1049,7 @@ def print_test_result(self, test_result, target_name, toolchain_name, tokens.append(test_description) separator = "::" time_info = " in %.2f of %d sec" % (round(elapsed_time, 2), duration) - result = separator.join(tokens) + " [" + test_result +"]" + time_info + result = separator.join(tokens) + " [" + test_result + "]" + time_info return Fore.MAGENTA + result + Fore.RESET def shape_test_loop_ok_result_count(self, test_all_result): @@ -1052,7 +1057,7 @@ def shape_test_loop_ok_result_count(self, test_all_result): """ test_loop_count = len(test_all_result) test_loop_ok_result = test_all_result.count(self.TEST_RESULT_OK) - return "%d/%d"% (test_loop_ok_result, test_loop_count) + return "%d/%d" % (test_loop_ok_result, test_loop_count) def shape_global_test_loop_result(self, test_all_result, waterfall_and_consolidate): """ Reformats list of results to simple string @@ -1106,16 +1111,16 @@ def get_auto_property_value(property_name, line): Returns string """ result = None - if re.search("HOST: Property '%s'"% property_name, line) is not None: - property = re.search("HOST: Property '%s' = '([\w\d _]+)'"% property_name, line) + if re.search("HOST: Property '%s'" % property_name, line) is not None: + property = re.search("HOST: Property '%s' = '([\w\d _]+)'" % property_name, line) if property is not None and len(property.groups()) == 1: result = property.groups()[0] return result cmd = ["python", - '%s.py'% name, + '%s.py' % name, '-d', disk, - '-f', '"%s"'% image_path, + '-f', '"%s"' % image_path, '-p', port, '-t', str(duration), '-C', str(program_cycle_s)] @@ -1139,7 +1144,7 @@ def get_auto_property_value(property_name, line): proc = Popen(cmd, stdout=PIPE, cwd=HOST_TESTS) obs = ProcessObserver(proc) - update_once_flag = {} # Stores flags checking if some auto-parameter was already set + update_once_flag = {} # Stores flags checking if some auto-parameter was already set line = '' output = [] start_time = time() @@ -1178,7 +1183,7 @@ def get_auto_property_value(property_name, line): else: line += c end_time = time() - testcase_duration = end_time - start_time # Test case duration from reset to {end} + testcase_duration = end_time - start_time # Test case duration from reset to {end} c = get_char_from_queue(obs) @@ -1255,11 +1260,11 @@ def show_json_file_format_error(json_spec_filename, line, column): with open(json_spec_filename) as data_file: line_no = 1 for json_line in data_file: - if line_no + 5 >= line: # Print last few lines before error - print('Line %d:\t'%line_no + json_line) + if line_no + 5 >= line: # Print last few lines before error + print('Line %d:\t' % line_no + json_line) if line_no == line: - print('%s\t%s^' (' ' * len('Line %d:' % line_no), - '-' * (column - 1))) + print('%s\t%s^'(' ' * len('Line %d:' % line_no), + '-' * (column - 1))) break line_no += 1 @@ -1307,7 +1312,7 @@ def get_json_data_from_file(json_spec_filename, verbose=False): show_json_file_format_error(json_spec_filename, line, column) except IOError as fileopen_error_msg: - print('JSON file %s not opened. Reason: %s\n'% + print('JSON file %s not opened. Reason: %s\n' % (json_spec_filename, fileopen_error_msg)) if verbose and result: pp = pprint.PrettyPrinter(indent=4) @@ -1380,7 +1385,7 @@ def print_test_configuration_from_json(json_data, join_delim=", "): target_supported_toolchains = get_target_supported_toolchains(target) if not target_supported_toolchains: target_supported_toolchains = [] - target_name = target if target in TARGET_MAP else "%s*"% target + target_name = target if target in TARGET_MAP else "%s*" % target row = [target_name] toolchains = targets[target] @@ -1411,27 +1416,27 @@ def print_test_configuration_from_json(json_data, join_delim=", "): pt.add_row(row) # generate result string - result = pt.get_string() # Test specification table + result = pt.get_string() # Test specification table if toolchain_conflicts or toolchain_path_conflicts: result += "\n" result += "Toolchain conflicts:\n" for target in toolchain_conflicts: if target not in TARGET_MAP: - result += "\t* Target %s unknown\n"% (target) + result += "\t* Target %s unknown\n" % (target) conflict_target_list = join_delim.join(toolchain_conflicts[target]) sufix = 's' if len(toolchain_conflicts[target]) > 1 else '' - result += "\t* Target %s does not support %s toolchain%s\n"% (target, conflict_target_list, sufix) + result += "\t* Target %s does not support %s toolchain%s\n" % (target, conflict_target_list, sufix) for toolchain in toolchain_path_conflicts: - # Let's check toolchain configuration + # Let's check toolchain configuration if toolchain in TOOLCHAIN_PATHS: toolchain_path = TOOLCHAIN_PATHS[toolchain] if not os.path.isdir(toolchain_path): - result += "\t# Toolchain %s path not found: %s\n"% (toolchain, toolchain_path) + result += "\t# Toolchain %s path not found: %s\n" % (toolchain, toolchain_path) return result -def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',',platform_filter=None): +def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',', platform_filter=None): """ Generates table summary with all test cases and additional test cases information using pretty print functionality. Allows test suite user to see test cases @@ -1462,7 +1467,7 @@ def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',' counter_all = 0 counter_automated = 0 - pt.padding_width = 1 # One space between column edges and contents (default) + pt.padding_width = 1 # One space between column edges and contents (default) for test_id in sorted(TEST_MAP.keys()): if platform_filter is not None: @@ -1516,7 +1521,8 @@ def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',' pt.align['percent [%]'] = "r" for unique_id in unique_test_id: # print "\t\t%s: %d / %d" % (unique_id, counter_dict_test_id_types[unique_id], counter_dict_test_id_types_all[unique_id]) - percent_progress = round(100.0 * counter_dict_test_id_types[unique_id] / float(counter_dict_test_id_types_all[unique_id]), 1) + percent_progress = round( + 100.0 * counter_dict_test_id_types[unique_id] / float(counter_dict_test_id_types_all[unique_id]), 1) str_progress = progress_bar(percent_progress, 75) row = [unique_id, counter_dict_test_id_types[unique_id], @@ -1533,7 +1539,7 @@ def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',' def progress_bar(percent_progress, saturation=0): """ This function creates progress bar with optional simple saturation mark """ - step = int(percent_progress / 2) # Scale by to (scale: 1 - 50) + step = int(percent_progress / 2) # Scale by to (scale: 1 - 50) str_progress = '#' * step + '.' * int(50 - step) c = '!' if str_progress[38] == '.' else '|' if saturation > 0: @@ -1573,26 +1579,32 @@ def singletest_in_cli_mode(single_test): if single_test.opts_report_html_file_name: # Export results in form of HTML report to separate file report_exporter = ReportExporter(ResultExporterType.HTML) - report_exporter.report_to_file(test_summary_ext, single_test.opts_report_html_file_name, test_suite_properties=test_suite_properties_ext) + report_exporter.report_to_file(test_summary_ext, single_test.opts_report_html_file_name, + test_suite_properties=test_suite_properties_ext) if single_test.opts_report_junit_file_name: # Export results in form of JUnit XML report to separate file report_exporter = ReportExporter(ResultExporterType.JUNIT) - report_exporter.report_to_file(test_summary_ext, single_test.opts_report_junit_file_name, test_suite_properties=test_suite_properties_ext) + report_exporter.report_to_file(test_summary_ext, single_test.opts_report_junit_file_name, + test_suite_properties=test_suite_properties_ext) if single_test.opts_report_text_file_name: # Export results in form of a text file report_exporter = ReportExporter(ResultExporterType.TEXT) - report_exporter.report_to_file(test_summary_ext, single_test.opts_report_text_file_name, test_suite_properties=test_suite_properties_ext) + report_exporter.report_to_file(test_summary_ext, single_test.opts_report_text_file_name, + test_suite_properties=test_suite_properties_ext) if single_test.opts_report_build_file_name: # Export build results as html report to sparate file report_exporter = ReportExporter(ResultExporterType.JUNIT, package="build") - report_exporter.report_to_file(build_report, single_test.opts_report_build_file_name, test_suite_properties=build_properties) + report_exporter.report_to_file(build_report, single_test.opts_report_build_file_name, + test_suite_properties=build_properties) # Returns True if no build failures of the test projects or their dependencies return status + class TestLogger(): """ Super-class for logging and printing ongoing events for test suite pass """ + def __init__(self, store_log=True): """ We can control if logger actually stores log in memory or just handled all log entries immediately @@ -1608,18 +1620,18 @@ def __init__(self, store_log=True): ERROR='Error', EXCEPT='Exception') - self.LogToFileAttr = construct_enum(CREATE=1, # Create or overwrite existing log file - APPEND=2) # Append to existing log file + self.LogToFileAttr = construct_enum(CREATE=1, # Create or overwrite existing log file + APPEND=2) # Append to existing log file def log_line(self, LogType, log_line, timestamp=True, line_delim='\n'): """ Log one line of text """ log_timestamp = time() - log_entry = {'log_type' : LogType, - 'log_timestamp' : log_timestamp, - 'log_line' : log_line, - '_future' : None - } + log_entry = {'log_type': LogType, + 'log_timestamp': log_timestamp, + 'log_line': log_line, + '_future': None + } # Store log in memory if self.store_log: self.log.append(log_entry) @@ -1629,18 +1641,20 @@ def log_line(self, LogType, log_line, timestamp=True, line_delim='\n'): class CLITestLogger(TestLogger): """ Logger used with CLI (Command line interface) test suite. Logs on screen and to file if needed """ + def __init__(self, store_log=True, file_name=None): TestLogger.__init__(self) self.log_file_name = file_name - #self.TIMESTAMP_FORMAT = '%y-%m-%d %H:%M:%S' # Full date and time - self.TIMESTAMP_FORMAT = '%H:%M:%S' # Time only + # self.TIMESTAMP_FORMAT = '%y-%m-%d %H:%M:%S' # Full date and time + self.TIMESTAMP_FORMAT = '%H:%M:%S' # Time only def log_print(self, log_entry, timestamp=True): """ Prints on screen formatted log entry """ ts = log_entry['log_timestamp'] - timestamp_str = datetime.datetime.fromtimestamp(ts).strftime("[%s] "% self.TIMESTAMP_FORMAT) if timestamp else '' - log_line_str = "%(log_type)s: %(log_line)s"% (log_entry) + timestamp_str = datetime.datetime.fromtimestamp(ts).strftime( + "[%s] " % self.TIMESTAMP_FORMAT) if timestamp else '' + log_line_str = "%(log_type)s: %(log_line)s" % (log_entry) return timestamp_str + log_line_str def log_line(self, LogType, log_line, timestamp=True, line_delim='\n'): @@ -1678,7 +1692,7 @@ def detect_database_verbose(db_url): if result is not None: # Parsing passed (db_type, username, password, host, db_name) = result - #print "DB type '%s', user name '%s', password '%s', host '%s', db name '%s'"% result + # print "DB type '%s', user name '%s', password '%s', host '%s', db name '%s'"% result # Let's try to connect db_ = factory_db_logger(db_url) if db_ is not None: @@ -1702,11 +1716,12 @@ def get_module_avail(module_name): """ return module_name in sys.modules.keys() + def get_autodetected_MUTS_list(platform_name_filter=None): oldError = None if os.name == 'nt': # Disable Windows error box temporarily - oldError = ctypes.windll.kernel32.SetErrorMode(1) #note that SEM_FAILCRITICALERRORS = 1 + oldError = ctypes.windll.kernel32.SetErrorMode(1) # note that SEM_FAILCRITICALERRORS = 1 mbeds = mbed_lstools.create() detect_muts_list = mbeds.list_mbeds() @@ -1716,6 +1731,7 @@ def get_autodetected_MUTS_list(platform_name_filter=None): return get_autodetected_MUTS(detect_muts_list, platform_name_filter=platform_name_filter) + def get_autodetected_MUTS(mbeds_list, platform_name_filter=None): """ Function detects all connected to host mbed-enabled devices and generates artificial MUTS file. If function fails to auto-detect devices it will return empty dictionary. @@ -1727,7 +1743,7 @@ def get_autodetected_MUTS(mbeds_list, platform_name_filter=None): @param mbeds_list list of mbeds captured from mbed_lstools @param platform_name You can filter 'platform_name' with list of filtered targets from 'platform_name_filter' """ - result = {} # Should be in muts_all.json format + result = {} # Should be in muts_all.json format # Align mbeds_list from mbed_lstools to MUT file format (JSON dictionary with muts) # mbeds_list = [{'platform_name': 'NUCLEO_F302R8', 'mount_point': 'E:', 'target_id': '07050200623B61125D5EF72A', 'serial_port': u'COM34'}] index = 1 @@ -1740,10 +1756,11 @@ def get_autodetected_MUTS(mbeds_list, platform_name_filter=None): # For mcu_unique - we are assigning 'platform_name_unique' value from mbedls output (if its existing) # if not we are creating our own unique value (last few chars from platform's target_id). m = {'mcu': mut['platform_name'], - 'mcu_unique' : mut['platform_name_unique'] if 'platform_name_unique' in mut else "%s[%s]" % (mut['platform_name'], mut['target_id'][-4:]), + 'mcu_unique': mut['platform_name_unique'] if 'platform_name_unique' in mut else "%s[%s]" % ( + mut['platform_name'], mut['target_id'][-4:]), 'port': mut['serial_port'], 'disk': mut['mount_point'], - 'peripherals': [] # No peripheral detection + 'peripherals': [] # No peripheral detection } if index not in result: result[index] = {} @@ -1764,7 +1781,7 @@ def get_autodetected_TEST_SPEC(mbeds_list, use_supported_toolchains - if True add all supported toolchains to test_spec toolchain_filter - if [...list of toolchains...] add from all toolchains only those in filter to test_spec """ - result = {'targets': {} } + result = {'targets': {}} for mut in mbeds_list: mcu = mut['mcu'] @@ -1822,7 +1839,7 @@ def get_default_test_options_parser(): toolchain_list = list(TOOLCHAINS) + ["DEFAULT", "ALL"] parser.add_argument('--tc', dest='toolchains_filter', - type=argparse_many(argparse_uppercase_type(toolchain_list, "toolchains")), + type=argparse_many(argparse_uppercase_type(toolchain_list, "toolchains")), help="Toolchain filter for --auto argument. Use toolchains names separated by comma, 'default' or 'all' to select toolchains") test_scopes = ','.join(["'%s'" % n for n in get_available_oper_test_scopes()]) @@ -1858,9 +1875,9 @@ def get_default_test_options_parser(): help='Runs only test enumerated it this switch. Use comma to separate test case names') parser.add_argument('-p', '--peripheral-by-names', - dest='peripheral_by_names', - type=argparse_many(str), - help='Forces discovery of particular peripherals. Use comma to separate peripheral names') + dest='peripheral_by_names', + type=argparse_many(str), + help='Forces discovery of particular peripherals. Use comma to separate peripheral names') copy_methods = host_tests_plugins.get_plugin_caps('CopyMethod') copy_methods_str = "Plugin support: " + ', '.join(copy_methods) @@ -1868,7 +1885,7 @@ def get_default_test_options_parser(): parser.add_argument('-c', '--copy-method', dest='copy_method', type=argparse_uppercase_type(copy_methods, "flash method"), - help="Select binary copy (flash) method. Default is Python's shutil.copy() method. %s"% copy_methods_str) + help="Select binary copy (flash) method. Default is Python's shutil.copy() method. %s" % copy_methods_str) reset_methods = host_tests_plugins.get_plugin_caps('ResetMethod') reset_methods_str = "Plugin support: " + ', '.join(reset_methods) @@ -1877,7 +1894,7 @@ def get_default_test_options_parser(): dest='mut_reset_type', default=None, type=argparse_uppercase_type(reset_methods, "reset method"), - help='Extra reset method used to reset MUT by host test script. %s'% reset_methods_str) + help='Extra reset method used to reset MUT by host test script. %s' % reset_methods_str) parser.add_argument('-g', '--goanna-for-tests', dest='goanna_for_tests', @@ -2041,18 +2058,20 @@ def get_default_test_options_parser(): help="Depth level for static memory report") return parser + def test_path_to_name(path, base): """Change all slashes in a path into hyphens This creates a unique cross-platform test name based on the path This can eventually be overriden by a to-be-determined meta-data mechanism""" name_parts = [] - head, tail = os.path.split(relpath(path,base)) + head, tail = os.path.split(relpath(path, base)) while (tail and tail != "."): name_parts.insert(0, tail) head, tail = os.path.split(head) return "-".join(name_parts).lower() + def get_test_config(config_name, target_name): """Finds the path to a test configuration file config_name: path to a custom configuration file OR mbed OS interface "ethernet, wifi_odin, etc" @@ -2066,13 +2085,15 @@ def get_test_config(config_name, target_name): # Otherwise find the path to configuration file based on mbed OS interface return TestConfig.get_config_path(config_name, target_name) -def find_tests(base_dir, target_name, toolchain_name, app_config=None): + +def find_tests(base_dir, target_name, toolchain_name, icetea, greentea, app_config=None): """ Finds all tests in a directory recursively - base_dir: path to the directory to scan for tests (ex. 'path/to/project') - target_name: name of the target to use for scanning (ex. 'K64F') - toolchain_name: name of the toolchain to use for scanning (ex. 'GCC_ARM') - options: Compile options to pass to the toolchain (ex. ['debug-info']) - app_config - location of a chosen mbed_app.json file + :param base_dir: path to the directory to scan for tests (ex. 'path/to/project') + :param target_name: name of the target to use for scanning (ex. 'K64F') + :param toolchain_name: name of the toolchain to use for scanning (ex. 'GCC_ARM') + :param icetea: icetea enabled + :param greentea: greentea enabled + :param app_config - location of a chosen mbed_app.json file returns a dictionary where keys are the test name, and the values are lists of paths needed to biuld the test. @@ -2089,38 +2110,39 @@ def find_tests(base_dir, target_name, toolchain_name, app_config=None): base_resources = Resources(MockNotifier(), collect_ignores=True) base_resources.scan_with_config(base_dir, config) - dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TESTS'] - ignoreset = MbedIgnoreSet() - - for directory in dirs: - ignorefile = join(directory, IGNORE_FILENAME) - if isfile(ignorefile): - ignoreset.add_mbedignore(directory, ignorefile) - for test_group_directory in os.listdir(directory): - grp_dir = join(directory, test_group_directory) - if not isdir(grp_dir) or ignoreset.is_ignored(grp_dir): - continue - grpignorefile = join(grp_dir, IGNORE_FILENAME) - if isfile(grpignorefile): - ignoreset.add_mbedignore(grp_dir, grpignorefile) - for test_case_directory in os.listdir(grp_dir): - d = join(directory, test_group_directory, test_case_directory) - if not isdir(d) or ignoreset.is_ignored(d): + if greentea: + dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TESTS'] + ignoreset = MbedIgnoreSet() + + for directory in dirs: + ignorefile = join(directory, IGNORE_FILENAME) + if isfile(ignorefile): + ignoreset.add_mbedignore(directory, ignorefile) + for test_group_directory in os.listdir(directory): + grp_dir = join(directory, test_group_directory) + if not isdir(grp_dir) or ignoreset.is_ignored(grp_dir): continue - special_dirs = ['host_tests', 'COMMON'] - if test_group_directory not in special_dirs and test_case_directory not in special_dirs: - test_name = test_path_to_name(d, base_dir) - tests[(test_name, directory, test_group_directory, test_case_directory)] = [d] - if test_case_directory == 'COMMON': - def predicate(base_pred, group_pred, name_base_group_case): - (name, base, group, case) = name_base_group_case - return base == base_pred and group == group_pred - commons.append((functools.partial(predicate, directory, test_group_directory), d)) - if test_group_directory == 'COMMON': - def predicate(base_pred, name_base_group_case): - (name, base, group, case) = name_base_group_case - return base == base_pred - commons.append((functools.partial(predicate, directory), grp_dir)) + grpignorefile = join(grp_dir, IGNORE_FILENAME) + if isfile(grpignorefile): + ignoreset.add_mbedignore(grp_dir, grpignorefile) + for test_case_directory in os.listdir(grp_dir): + d = join(directory, test_group_directory, test_case_directory) + if not isdir(d) or ignoreset.is_ignored(d): + continue + if 'device' == subdir: + for test_dir in os.listdir(d): + test_dir_path = join(d, test_dir) + test_name = test_path_to_name(test_dir_path, base_dir) + tests[(test_name, directory, subdir, test_dir)] = [test_dir_path] + + if icetea: + dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TEST_APPS'] + for directory in dirs: + if not isdir(directory): + continue + for subdir in os.listdir(directory): + d = join(directory, subdir) + if not isdir(d): # Apply common directories for pred, path in commons: @@ -2131,6 +2153,7 @@ def predicate(base_pred, name_base_group_case): # Drop identity besides name return {name: paths for (name, _, _, _), paths in six.iteritems(tests)} + def print_tests(tests, format="list", sort=True): """Given a dictionary of tests (as returned from "find_tests"), print them in the specified format""" @@ -2147,6 +2170,7 @@ def print_tests(tests, format="list", sort=True): print("Unknown format '%s'" % format) sys.exit(1) + def norm_relative_path(path, start): """This function will create a normalized, relative path. It mimics the python os.path.relpath function, but also normalizes a Windows-syle path @@ -2235,7 +2259,8 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name, "base_path": base_path, "baud_rate": baud_rate, "binary_type": "bootable", - "tests": {} + "tests": {}, + "test_apps": {} } result = True @@ -2302,19 +2327,20 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name, # Set the overall result to a failure if a build failure occurred if ('reason' in worker_result and - not worker_result['reason'] and - not isinstance(worker_result['reason'], NotSupportedException)): + not worker_result['reason'] and + not isinstance(worker_result['reason'], NotSupportedException)): result = False break # Adding binary path to test build result if ('result' in worker_result and - worker_result['result'] and - 'bin_file' in worker_result): + worker_result['result'] and + 'bin_file' in worker_result): bin_file = norm_relative_path(worker_result['bin_file'], execution_directory) - test_build['tests'][worker_result['kwargs']['project_id']] = { + test_key = 'test_apps' if 'test_apps-' in worker_result['kwargs']['project_id'] else 'tests' + test_build[test_key][worker_result['kwargs']['project_id']] = { "binaries": [ { "path": bin_file @@ -2358,3 +2384,5 @@ def test_spec_from_test_builds(test_builds): return { "builds": test_builds } + + diff --git a/tools/toolchains/__init__.py b/tools/toolchains/__init__.py index edf901bcabc..5b905d0e076 100644 --- a/tools/toolchains/__init__.py +++ b/tools/toolchains/__init__.py @@ -1117,4 +1117,4 @@ def report(self): u'IAR': IAR } -TOOLCHAINS = set(TOOLCHAIN_CLASSES.keys()) +TOOLCHAINS = set(TOOLCHAIN_CLASSES.keys()) \ No newline at end of file diff --git a/tools/utils.py b/tools/utils.py index 640403fe008..93e70430a0a 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -179,6 +179,27 @@ def mkdir(path): makedirs(path) +def write_json_to_file(json_data, file_name): + """ + Write json content in file + :param json_data: + :param file_name: + :return: + """ + # Create the target dir for file if necessary + test_spec_dir = os.path.dirname(file_name) + + if test_spec_dir: + mkdir(test_spec_dir) + + try: + with open(file_name, 'w') as f: + f.write(json.dumps(json_data, indent=2)) + except IOError as e: + print("[ERROR] Error writing test spec to file") + print(e) + + def copy_file(src, dst): """ Implement the behaviour of "shutil.copy(src, dst)" without copying the permissions (this was causing errors with directories mounted with samba) From 9f3704e7b925fd014c19d92ac9ef611bff549c26 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Fri, 10 Aug 2018 14:36:22 +0300 Subject: [PATCH 02/40] Add icetea in requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 48ee33d6ae6..0470934ec2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,3 +17,4 @@ future>=0.16.0 six>=1.11.0 git+https://github.com/armmbed/manifest-tool.git@v1.4.5 mbed-cloud-sdk==2.0.0 +icetea==1.0.0rc2 From 75400ed31b6f70c3d13cae783215ecb4641fed1d Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Fri, 10 Aug 2018 15:16:45 +0300 Subject: [PATCH 03/40] Fix mbed-client-cli part --- .mbedignore | 2 - .yotta_ignore | 0 Makefile | 5 - README.md | 105 ----- module.json | 33 -- source/CMakeLists.txt | 14 - source/ns_list_internal/ns_list.c | 23 - source/ns_list_internal/ns_list.h | 721 ----------------------------- source/ns_list_internal/ns_types.h | 373 --------------- target.json | 0 10 files changed, 1276 deletions(-) delete mode 100644 .mbedignore delete mode 100644 .yotta_ignore delete mode 100644 Makefile delete mode 100644 module.json delete mode 100644 source/CMakeLists.txt delete mode 100644 source/ns_list_internal/ns_list.c delete mode 100644 source/ns_list_internal/ns_list.h delete mode 100644 source/ns_list_internal/ns_types.h delete mode 100644 target.json diff --git a/.mbedignore b/.mbedignore deleted file mode 100644 index 7bb56421f19..00000000000 --- a/.mbedignore +++ /dev/null @@ -1,2 +0,0 @@ -test/* -source/ns_list_internal/* diff --git a/.yotta_ignore b/.yotta_ignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/Makefile b/Makefile deleted file mode 100644 index 856a59b4eff..00000000000 --- a/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -SRCS := $(wildcard source/*.c) -LIB := libCmdline.a -EXPORT_HEADERS := mbed-client-cli - -include ../exported_rules.mk diff --git a/README.md b/README.md index 49f2458a77a..68b9acaaf0d 100644 --- a/README.md +++ b/README.md @@ -33,108 +33,3 @@ We have a [developer website](https://os.mbed.com) for asking questions, engagin ## Getting started for contributors We also have a [contributing and publishing guide](https://os.mbed.com/contributing/) that covers licensing, contributor agreements and style guidelines. -======= -# mbed-client-cli - -This is the Command Line Library for a CLI application. This library provides methods for: - -* Adding commands to the interpreter. -* Deleting commands from the interpreter. -* Executing commands. -* Adding command aliases to the interpreter. -* Searching command arguments. - -## API - -Command Line Library API is described in the snipplet below: - -```c++ -// if thread safety for CLI terminal output is needed -// configure output mutex wait cb before initialization so it's available immediately -cmd_set_mutex_wait_func( (func)(void) ); -// configure output mutex release cb before initialization so it's available immediately -cmd_set_mutex_wait_func( (func)(void) ); -// initialize cmdline with print function -cmd_init( (func)(const char* fmt, va_list ap) ); -// configure ready cb -cmd_set_ready_cb( (func)(int retcode) ); -// register command for library -cmd_add( , (int func)(int argc, char *argv[]), , ); -//execute some existing commands -cmd_exe( ); -``` - -## Tracing - -Command Line Library has trace messages, which are disabled by default. -"MBED_CLIENT_CLI_TRACE_ENABLE" flag if defined, enables all the trace prints for debugging. - -## Usage example - -Adding new commands to the Command Line Library and executing the commands: - -```c++ -//example print function -void myprint(const char* fmt, va_list ap){ vprintf(fmt, ap); } - -// ready cb, calls next command to be executed -void cmd_ready_cb(int retcode) { cmd_next( retcode ); } - -// dummy command with some option -int cmd_dummy(int argc, char *argv[]){ - if( cmd_has_option(argc, argv, "o") ) { - cmd_printf("This is o option"); - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - return CMDLINE_RETCODE_SUCCESS; -} - -// timer cb (pseudo-timer-code) -void timer_ready_cb(void) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); -} - -// long command, that needs for example some events to complete the command execution -int cmd_long(int argc, char *argv[] ) { - timer_start( 5000, timer_ready_cb ); //pseudo timer code - return CMDLINE_RETCODE_EXCUTING_CONTINUE; -} -void main(void) { - cmd_init( &myprint ); // initialize cmdline with print function - cmd_set_ready_cb( cmd_ready_cb ); // configure ready cb - cmd_add("dummy", cmd_dummy, 0, 0); // add one dummy command - cmd_add("long", cmd_long, 0, 0); // add one dummy command - //execute dummy and long commands - cmd_exe( "dummy;long" ); -} -``` - -## Thread safety -The CLI library is not thread safe, but the CLI terminal output can be locked against other -output streams, for example if both traces and CLI output are using serial out. - -```c++ -static Mutex MyMutex; - -// mutex wait cb, acquires the mutex, waiting if necessary -static void mutex_wait(void) -{ - MyMutex.lock(); -} - -// mutex release cb, releases the mutex -static void my_mutex_release(void) -{ - MyMutex.unlock(); -} - -void main(void) { - cmd_mutex_wait_func( my_mutex_wait ); // configure mutex wait function before initializing - cmd_mutex_release_func( my_mutex_release ); // configure mutex release function before initializing - cmd_init( &myprint ); // initialize cmdline with print function - cmd_set_ready_cb( cmd_ready_cb ); // configure ready cb. - //CLI terminal output now locks against MyMutex -} - -``` diff --git a/module.json b/module.json deleted file mode 100644 index 89d45561514..00000000000 --- a/module.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "mbed-client-cli", - "version": "0.3.0", - "description": "Command Line Library for mbedOS", - "keywords": [ - "cli", - "client", - "cmd" - ], - "author": "Jussi Vatjus-Anttila", - "repository": { - "url": "https://github.com/ARMmbed/mbed-client-cli.git", - "type": "git" - }, - "homepage": "https://github.com/ARMmbed/mbed-client-cli.git", - "licenses": [ - { - "url": "https://spdx.org/licenses/Apache-2.0", - "type": "Apache-2.0" - } - ], - "dependencies": { - "mbed-trace": "^1.1.2" - }, - "testTargetDependencies": { - "x86-linux-native": { - "cpputest": "ARMmbed/cpputest" - }, - "x86-windows-native": { - "cpputest": "ARMmbed/cpputest" - } - } -} diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt deleted file mode 100644 index d0a7475b832..00000000000 --- a/source/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -if(DEFINED TARGET_LIKE_X86_LINUX_NATIVE) - add_library( mbed-client-cli - ns_cmdline.c - ns_list_internal/ns_list.c - ) - add_definitions("-g -O0 -fprofile-arcs -ftest-coverage") - target_link_libraries(mbed-client-cli gcov) -else() - add_library( mbed-client-cli - ns_cmdline.c - ns_list_internal/ns_list.c - ) - target_link_libraries(mbed-client-cli) -endif() diff --git a/source/ns_list_internal/ns_list.c b/source/ns_list_internal/ns_list.c deleted file mode 100644 index 81de7bdd55a..00000000000 --- a/source/ns_list_internal/ns_list.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2016 ARM Limited. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * 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. - */ - -/* - * All functions can be inlined, and definitions are in ns_list.h. - * Define NS_LIST_FN before including it to generate external definitions. - */ -#define NS_LIST_FN extern - -#include "ns_list.h" diff --git a/source/ns_list_internal/ns_list.h b/source/ns_list_internal/ns_list.h deleted file mode 100644 index 1c4071ce0bb..00000000000 --- a/source/ns_list_internal/ns_list.h +++ /dev/null @@ -1,721 +0,0 @@ -/* - * Copyright (c) 2016 ARM Limited. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * 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 NS_LIST_H_ -#define NS_LIST_H_ - -#include "ns_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/** \file - * \brief Linked list support library - * - * The ns_list.h file provides a doubly-linked list/queue, providing O(1) - * performance for all insertion/removal operations, and access to either - * end of the list. - * - * Memory footprint is two pointers for the list head, and two pointers in each - * list entry. It is similar in concept to BSD's TAILQ. - * - * Although the API is symmetrical and O(1) in both directions, due to internal - * pointer design, it is *slightly* more efficient to insert at the end when - * used as a queue, and to iterate forwards rather than backwards. - * - * Example of an entry type that can be stored to this list. - * ~~~ - * typedef struct example_entry - * { - * uint8_t *data; - * uint32_t data_count; - * ns_list_link_t link; - * } - * example_entry_t; - * - * static NS_LIST_HEAD(example_entry_t, link) my_list; - * ns_list_init(&my_list); - * ~~~ - * OR - * ~~~ - * NS_LIST_HEAD(example_entry_t, link) my_list = NS_LIST_INIT(my_list); - * ~~~ - * OR - * ~~~ - * static NS_LIST_DEFINE(my_list, example_entry_t, link); - * ~~~ - * OR - * ~~~ - * typedef NS_LIST_HEAD(example_entry_t, link) example_list_t; - * example_list_t NS_LIST_NAME_INIT(my_list); - * ~~~ - * NOTE: the link field SHALL NOT be accessed by the user. - * - * An entry can exist on multiple lists by having multiple link fields. - * - * All the list operations are implemented as macros, most of which are backed - * by optionally-inline functions. The macros do not evaluate any arguments more - * than once, unless documented. - * - * In macro documentation, `list_t` refers to a list type defined using - * NS_LIST_HEAD(), and `entry_t` to the entry type that was passed to it. - */ - -/** \brief Underlying generic linked list head. - * - * Users should not use this type directly, but use the NS_LIST_HEAD() macro. - */ -typedef struct ns_list { - void *first_entry; ///< Pointer to first entry, or NULL if list is empty - void **last_nextptr; ///< Pointer to last entry's `next` pointer, or - ///< to head's `first_entry` pointer if list is empty -} ns_list_t; - -/** \brief Declare a list head type - * - * This union stores the real list head, and also encodes as compile-time type - * information the offset of the link pointer, and the type of the entry. - * - * Note that type information is compiler-dependent; this means - * ns_list_get_first() could return either `void *`, or a pointer to the actual - * entry type. So `ns_list_get_first()->data` is not a portable construct - - * always assign returned entry pointers to a properly typed pointer variable. - * This assignment will be then type-checked where the compiler supports it, and - * will dereference correctly on compilers that don't support this extension. - * ~~~ - * NS_LIST_HEAD(example_entry_t, link) my_list; - * - * example_entry_t *entry = ns_list_get_first(&my_list); - * do_something(entry->data); - * ~~~ - * Each use of this macro generates a new anonymous union, so these two lists - * have different types: - * ~~~ - * NS_LIST_HEAD(example_entry_t, link) my_list1; - * NS_LIST_HEAD(example_entry_t, link) my_list2; - * ~~~ - * If you need to use a list type in multiple places, eg as a function - * parameter, use typedef: - * ~~~ - * typedef NS_LIST_HEAD(example_entry_t, link) example_list_t; - * - * void example_function(example_list_t *); - * ~~~ - */ -#define NS_LIST_HEAD(entry_type, field) \ -union \ -{ \ - ns_list_t slist; \ - NS_STATIC_ASSERT(offsetof(entry_type, field) <= UINT_FAST8_MAX, "link offset too large") \ - char (*offset)[offsetof(entry_type, field)]; \ - entry_type *type; \ -} - -/// \privatesection -/** \brief Get offset of link field in entry. - * \return `(ns_list_offset_t)` The offset of the link field for entries on the specified list - */ -#define NS_LIST_OFFSET_(list) ((ns_list_offset_t) sizeof *(list)->offset) - -/** \brief Get the entry type. - * \def NS_LIST_TYPE_ - * - * \return The type of entry on the specified list. - * - * Only available if the compiler provides a "typeof" operator. - */ -#if defined __cplusplus && __cplusplus >= 201103L -#define NS_LIST_TYPE_(list) decltype(*(list)->type) -#elif defined __GNUC__ -#define NS_LIST_TYPE_(list) __typeof__(*(list)->type) -#endif - -/** \brief Check for compatible pointer types - * - * Although this can be done portably, the GCC custom version is provided to - * produce a clearer diagnostic, and it produces an error rather than a warning. - * - * The portable version will produce a diagnostic about a pointer mismatch on - * the == inside the sizeof operator. For example ARM/Norcroft C gives the error: - * - * operand types are incompatible ("entry_t *" and "other_t *") - */ -#ifdef CPPCHECK -#define NS_PTR_MATCH_(a, b, str) ((void) 0) -#elif defined __GNUC__ -#define NS_PTR_MATCH_(a, b, str) __extension__ \ - ({ NS_STATIC_ASSERT(__builtin_types_compatible_p(__typeof__ (*(a)), __typeof__ (*(b))), \ - str) }) -#else -#define NS_PTR_MATCH_(a, b, str) ((void) sizeof ((a) == (b))) -#endif - -/** \brief Internal macro to cast returned entry pointers to correct type. - * - * Not portable in C, alas. With GCC or C++11, the "get entry" macros return - * correctly-typed pointers. Otherwise, the macros return `void *`. - * - * The attempt at a portable version would work if the C `?:` operator wasn't - * broken - `x ? (t *) : (void *)` should really have type `(t *)` in C, but - * it has type `(void *)`, which only makes sense for C++. The `?:` is left in, - * in case some day it works. Some compilers may still warn if this is - * assigned to a different type. - */ -#ifdef NS_LIST_TYPE_ -#define NS_LIST_TYPECAST_(list, val) ((NS_LIST_TYPE_(list) *) (val)) -#else -#define NS_LIST_TYPECAST_(list, val) (0 ? (list)->type : (val)) -#endif - -/** \brief Internal macro to check types of input entry pointer. */ -#define NS_LIST_TYPECHECK_(list, entry) \ - (NS_PTR_MATCH_((list)->type, (entry), "incorrect entry type for list"), (entry)) - -/** \brief Type used to pass link offset to underlying functions - * - * We could use size_t, but it would be unnecessarily large on 8-bit systems, - * where we can be (pretty) confident we won't have next pointers more than - * 256 bytes into a structure. - */ -typedef uint_fast8_t ns_list_offset_t; - -/// \publicsection -/** \brief The type for the link member in the user's entry structure. - * - * Users should not access this member directly - just pass its name to the - * list head macros. The funny prev pointer simplifies common operations - * (eg insertion, removal), at the expense of complicating rare reverse iteration. - * - * NB - the list implementation relies on next being the first member. - */ -typedef struct ns_list_link { - void *next; ///< Pointer to next entry, or NULL if none - void **prev; ///< Pointer to previous entry's (or head's) next pointer -} ns_list_link_t; - -/** \brief "Poison" value placed in unattached entries' link pointers. - * \internal What are good values for this? Platform dependent, maybe just NULL - */ -#define NS_LIST_POISON ((void *) 0xDEADBEEF) - -/** \brief Initialiser for an entry's link member - * - * This initialiser is not required by the library, but a user may want an - * initialiser to include in their own entry initialiser. See - * ns_list_link_init() for more discussion. - */ -#define NS_LIST_LINK_INIT(name) \ - NS_FUNNY_INTPTR_OK \ - { NS_LIST_POISON, NS_LIST_POISON } \ - NS_FUNNY_INTPTR_RESTORE - -/** \hideinitializer \brief Initialise an entry's list link - * - * This "initialises" an unattached entry's link by filling the fields with - * poison. This is optional, as unattached entries field pointers are not - * meaningful, and it is not valid to call ns_list_get_next or similar on - * an unattached entry. - * - * \param entry Pointer to an entry - * \param field The name of the link member to initialise - */ -#define ns_list_link_init(entry, field) ns_list_link_init_(&(entry)->field) - -/** \hideinitializer \brief Initialise a list - * - * Initialise a list head before use. A list head must be initialised using this - * function or one of the NS_LIST_INIT()-type macros before use. A zero-initialised - * list head is *not* valid. - * - * If used on a list containing existing entries, those entries will - * become detached. (They are not modified, but their links are now effectively - * undefined). - * - * \param list Pointer to a NS_LIST_HEAD() structure. - */ -#define ns_list_init(list) ns_list_init_(&(list)->slist) - -/** \brief Initialiser for an empty list - * - * Usage in an enclosing initialiser: - * ~~~ - * static my_type_including_list_t x = { - * "Something", - * 23, - * NS_LIST_INIT(x), - * }; - * ~~~ - * NS_LIST_DEFINE() or NS_LIST_NAME_INIT() may provide a shorter alternative - * in simpler cases. - */ -#define NS_LIST_INIT(name) { { NULL, &(name).slist.first_entry } } - -/** \brief Name and initialiser for an empty list - * - * Usage: - * ~~~ - * list_t NS_LIST_NAME_INIT(foo); - * ~~~ - * acts as - * ~~~ - * list_t foo = { empty list }; - * ~~~ - * Also useful with designated initialisers: - * ~~~ - * .NS_LIST_NAME_INIT(foo), - * ~~~ - * acts as - * ~~~ - * .foo = { empty list }, - * ~~~ - */ -#define NS_LIST_NAME_INIT(name) name = NS_LIST_INIT(name) - -/** \brief Define a list, and initialise to empty. - * - * Usage: - * ~~~ - * static NS_LIST_DEFINE(my_list, entry_t, link); - * ~~~ - * acts as - * ~~~ - * static list_type my_list = { empty list }; - * ~~~ - */ -#define NS_LIST_DEFINE(name, type, field) \ - NS_LIST_HEAD(type, field) NS_LIST_NAME_INIT(name) - -/** \hideinitializer \brief Add an entry to the start of the linked list. - * - * ns_list_add_to_end() is *slightly* more efficient than ns_list_add_to_start(). - * - * \param list `(list_t *)` Pointer to list. - * \param entry `(entry_t * restrict)` Pointer to new entry to add. - */ -#define ns_list_add_to_start(list, entry) \ - ns_list_add_to_start_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, entry)) - -/** \hideinitializer \brief Add an entry to the end of the linked list. - * - * \param list `(list_t *)` Pointer to list. - * \param entry `(entry_t * restrict)` Pointer to new entry to add. - */ -#define ns_list_add_to_end(list, entry) \ - ns_list_add_to_end_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, entry)) - -/** \hideinitializer \brief Add an entry before a specified entry. - * - * \param list `(list_t *)` Pointer to list. - * \param before `(entry_t *)` Existing entry before which to place the new entry. - * \param entry `(entry_t * restrict)` Pointer to new entry to add. - */ -#define ns_list_add_before(list, before, entry) \ - ns_list_add_before_(NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, before), NS_LIST_TYPECHECK_(list, entry)) - -/** \hideinitializer \brief Add an entry after a specified entry. - * - * ns_list_add_before() is *slightly* more efficient than ns_list_add_after(). - * - * \param list `(list_t *)` Pointer to list. - * \param after `(entry_t *)` Existing entry after which to place the new entry. - * \param entry `(entry_t * restrict)` Pointer to new entry to add. - */ -#define ns_list_add_after(list, after, entry) \ - ns_list_add_after_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, after), NS_LIST_TYPECHECK_(list, entry)) - -/** \brief Check if a list is empty. - * - * \param list `(const list_t *)` Pointer to list. - * - * \return `(bool)` true if the list is empty. - */ -#define ns_list_is_empty(list) ((bool) ((list)->slist.first_entry == NULL)) - -/** \brief Get the first entry. - * - * \param list `(const list_t *)` Pointer to list. - * - * \return `(entry_t *)` Pointer to first entry. - * \return NULL if list is empty. - */ -#define ns_list_get_first(list) NS_LIST_TYPECAST_(list, (list)->slist.first_entry) - -/** \hideinitializer \brief Get the previous entry. - * - * \param list `(const list_t *)` Pointer to list. - * \param current `(const entry_t *)` Pointer to current entry. - * - * \return `(entry_t *)` Pointer to previous entry. - * \return NULL if current entry is first. - */ -#define ns_list_get_previous(list, current) \ - NS_LIST_TYPECAST_(list, ns_list_get_previous_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, current))) - -/** \hideinitializer \brief Get the next entry. - * - * \param list `(const list_t *)` Pointer to list. - * \param current `(const entry_t *)` Pointer to current entry. - * - * \return `(entry_t *)` Pointer to next entry. - * \return NULL if current entry is last. - */ -#define ns_list_get_next(list, current) \ - NS_LIST_TYPECAST_(list, ns_list_get_next_(NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, current))) - -/** \hideinitializer \brief Get the last entry. - * - * \param list `(const list_t *)` Pointer to list. - * - * \return `(entry_t *)` Pointer to last entry. - * \return NULL if list is empty. - */ -#define ns_list_get_last(list) \ - NS_LIST_TYPECAST_(list, ns_list_get_last_(&(list)->slist, NS_LIST_OFFSET_(list))) - -/** \hideinitializer \brief Remove an entry. - * - * \param list `(list_t *)` Pointer to list. - * \param entry `(entry_t *)` Entry on list to be removed. - */ -#define ns_list_remove(list, entry) \ - ns_list_remove_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, entry)) - -/** \hideinitializer \brief Replace an entry. - * - * \param list `(list_t *)` Pointer to list. - * \param current `(entry_t *)` Existing entry on list to be replaced. - * \param replacement `(entry_t * restrict)` New entry to be the replacement. - */ -#define ns_list_replace(list, current, replacement) \ - ns_list_replace_(&(list)->slist, NS_LIST_OFFSET_(list), NS_LIST_TYPECHECK_(list, current), NS_LIST_TYPECHECK_(list, replacement)) - -/** \hideinitializer \brief Concatenate two lists. - * - * Attach the entries on the source list to the end of the destination - * list, leaving the source list empty. - * - * \param dst `(list_t *)` Pointer to destination list. - * \param src `(list_t *)` Pointer to source list. - * - */ -#define ns_list_concatenate(dst, src) \ - (NS_PTR_MATCH_(dst, src, "concatenating different list types"), \ - ns_list_concatenate_(&(dst)->slist, &(src)->slist, NS_LIST_OFFSET_(src))) - -/** \brief Iterate forwards over a list. - * - * Example: - * ~~~ - * ns_list_foreach(const my_entry_t, cur, &my_list) - * { - * printf("%s\n", cur->name); - * } - * ~~~ - * Deletion of the current entry is not permitted as its next is checked after - * running user code. - * - * The iteration pointer is declared inside the loop, using C99/C++, so it - * is not accessible after the loop. This encourages good code style, and - * matches the semantics of C++11's "ranged for", which only provides the - * declaration form: - * ~~~ - * for (const my_entry_t cur : my_list) - * ~~~ - * If you need to see the value of the iteration pointer after a `break`, - * you will need to assign it to a variable declared outside the loop before - * breaking: - * ~~~ - * my_entry_t *match = NULL; - * ns_list_foreach(my_entry_t, cur, &my_list) - * { - * if (cur->id == id) - * { - * match = cur; - * break; - * } - * } - * ~~~ - * - * The user has to specify the entry type for the pointer definition, as type - * extraction from the list argument isn't portable. On the other hand, this - * also permits const qualifiers, as in the example above, and serves as - * documentation. The entry type will be checked against the list type where the - * compiler supports it. - * - * \param type Entry type `([const] entry_t)`. - * \param e Name for iteration pointer to be defined - * inside the loop. - * \param list `(const list_t *)` Pointer to list - evaluated multiple times. - */ -#define ns_list_foreach(type, e, list) \ - for (type *e = ns_list_get_first(list); e; e = ns_list_get_next(list, e)) - -/** \brief Iterate forwards over a list, where user may delete. - * - * As ns_list_foreach(), but deletion of current entry is permitted as its - * next pointer is recorded before running user code. - * - * Example: - * ~~~ - * ns_list_foreach_safe(my_entry_t, cur, &my_list) - * { - * ns_list_remove(cur); - * } - * ~~~ - * \param type Entry type `(entry_t)`. - * \param e Name for iteration pointer to be defined - * inside the loop. - * \param list `(list_t *)` Pointer to list - evaluated multiple times. - */ -#define ns_list_foreach_safe(type, e, list) \ - for (type *e = ns_list_get_first(list), *_next; \ - e && (_next = ns_list_get_next(list, e), true); e = _next) - -/** \brief Iterate backwards over a list. - * - * As ns_list_foreach(), but going backwards - see its documentation. - * Iterating forwards is *slightly* more efficient. - */ -#define ns_list_foreach_reverse(type, e, list) \ - for (type *e = ns_list_get_last(list); e; e = ns_list_get_previous(list, e)) - -/** \brief Iterate backwards over a list, where user may delete. - * - * As ns_list_foreach_safe(), but going backwards - see its documentation. - * Iterating forwards is *slightly* more efficient. - */ -#define ns_list_foreach_reverse_safe(type, e, list) \ - for (type *e = ns_list_get_last(list), *_next; \ - e && (_next = ns_list_get_previous(list, e), true); e = _next) - -/** \hideinitializer \brief Count entries on a list - * - * Unlike other operations, this is O(n). Note: if list might contain over - * 65535 entries, this function **must not** be used to get the entry count. - * - * \param list `(const list_t *)` Pointer to list. - - * \return `(uint_fast16_t)` Number of entries that are stored in list. - */ -#define ns_list_count(list) ns_list_count_(&(list)->slist, NS_LIST_OFFSET_(list)) - -/** \privatesection - * Internal functions - designed to be accessed using corresponding macros above - */ -NS_INLINE void ns_list_init_(ns_list_t *list); -NS_INLINE void ns_list_link_init_(ns_list_link_t *link); -NS_INLINE void ns_list_add_to_start_(ns_list_t *list, ns_list_offset_t link_offset, void *restrict entry); -NS_INLINE void ns_list_add_to_end_(ns_list_t *list, ns_list_offset_t link_offset, void *restrict entry); -NS_INLINE void ns_list_add_before_(ns_list_offset_t link_offset, void *before, void *restrict entry); -NS_INLINE void ns_list_add_after_(ns_list_t *list, ns_list_offset_t link_offset, void *after, void *restrict entry); -NS_INLINE void *ns_list_get_next_(ns_list_offset_t link_offset, const void *current); -NS_INLINE void *ns_list_get_previous_(const ns_list_t *list, ns_list_offset_t link_offset, const void *current); -NS_INLINE void *ns_list_get_last_(const ns_list_t *list, ns_list_offset_t offset); -NS_INLINE void ns_list_remove_(ns_list_t *list, ns_list_offset_t link_offset, void *entry); -NS_INLINE void ns_list_replace_(ns_list_t *list, ns_list_offset_t link_offset, void *current, void *restrict replacement); -NS_INLINE void ns_list_concatenate_(ns_list_t *dst, ns_list_t *src, ns_list_offset_t offset); -NS_INLINE uint_fast16_t ns_list_count_(const ns_list_t *list, ns_list_offset_t link_offset); - -/* Provide definitions, either for inlining, or for ns_list.c */ -#if defined NS_ALLOW_INLINING || defined NS_LIST_FN -#ifndef NS_LIST_FN -#define NS_LIST_FN NS_INLINE -#endif - -/* Pointer to the link member in entry e */ -#define NS_LIST_LINK_(e, offset) ((ns_list_link_t *)((char *)(e) + offset)) - -/* Lvalue of the next link pointer in entry e */ -#define NS_LIST_NEXT_(e, offset) (NS_LIST_LINK_(e, offset)->next) - -/* Lvalue of the prev link pointer in entry e */ -#define NS_LIST_PREV_(e, offset) (NS_LIST_LINK_(e, offset)->prev) - -/* Convert a pointer to a link member back to the entry; - * works for linkptr either being a ns_list_link_t pointer, or its next pointer, - * as the next pointer is first in the ns_list_link_t */ -#define NS_LIST_ENTRY_(linkptr, offset) ((void *)((char *)(linkptr) - offset)) - -NS_LIST_FN void ns_list_init_(ns_list_t *list) -{ - list->first_entry = NULL; - list->last_nextptr = &list->first_entry; -} - -NS_LIST_FN void ns_list_link_init_(ns_list_link_t *link) -{ - NS_FUNNY_INTPTR_OK - link->next = NS_LIST_POISON; - link->prev = NS_LIST_POISON; - NS_FUNNY_INTPTR_RESTORE -} - -NS_LIST_FN void ns_list_add_to_start_(ns_list_t *list, ns_list_offset_t offset, void *restrict entry) -{ - void *next; - - NS_LIST_PREV_(entry, offset) = &list->first_entry; - NS_LIST_NEXT_(entry, offset) = next = list->first_entry; - - if (next) { - NS_LIST_PREV_(next, offset) = &NS_LIST_NEXT_(entry, offset); - } else { - list->last_nextptr = &NS_LIST_NEXT_(entry, offset); - } - - list->first_entry = entry; -} - -NS_LIST_FN void ns_list_add_after_(ns_list_t *list, ns_list_offset_t offset, void *current, void *restrict entry) -{ - void *next; - - NS_LIST_PREV_(entry, offset) = &NS_LIST_NEXT_(current, offset); - NS_LIST_NEXT_(entry, offset) = next = NS_LIST_NEXT_(current, offset); - - if (next) { - NS_LIST_PREV_(next, offset) = &NS_LIST_NEXT_(entry, offset); - } else { - list->last_nextptr = &NS_LIST_NEXT_(entry, offset); - } - - NS_LIST_NEXT_(current, offset) = entry; -} - -NS_LIST_FN void ns_list_add_before_(ns_list_offset_t offset, void *current, void *restrict entry) -{ - void **prev_nextptr; - - NS_LIST_NEXT_(entry, offset) = current; - NS_LIST_PREV_(entry, offset) = prev_nextptr = NS_LIST_PREV_(current, offset); - *prev_nextptr = entry; - NS_LIST_PREV_(current, offset) = &NS_LIST_NEXT_(entry, offset); -} - -NS_LIST_FN void ns_list_add_to_end_(ns_list_t *list, ns_list_offset_t offset, void *restrict entry) -{ - void **prev_nextptr; - - NS_LIST_NEXT_(entry, offset) = NULL; - NS_LIST_PREV_(entry, offset) = prev_nextptr = list->last_nextptr; - *prev_nextptr = entry; - list->last_nextptr = &NS_LIST_NEXT_(entry, offset); -} - -NS_LIST_FN void *ns_list_get_next_(ns_list_offset_t offset, const void *current) -{ - return NS_LIST_NEXT_(current, offset); -} - -NS_LIST_FN void *ns_list_get_previous_(const ns_list_t *list, ns_list_offset_t offset, const void *current) -{ - if (current == list->first_entry) { - return NULL; - } - - // Tricky. We don't have a direct previous pointer, but a pointer to the - // pointer that points to us - ie &head->first_entry OR &{prev}->next. - // This makes life easier on insertion and removal, but this is where we - // pay the price. - - // We have to check manually for being the first entry above, so we know it's - // a real link's next pointer. Then next is the first field of - // ns_list_link_t, so we can use the normal offset value. - - return NS_LIST_ENTRY_(NS_LIST_PREV_(current, offset), offset); -} - -NS_LIST_FN void *ns_list_get_last_(const ns_list_t *list, ns_list_offset_t offset) -{ - if (!list->first_entry) { - return NULL; - } - - // See comments in ns_list_get_previous_() - return NS_LIST_ENTRY_(list->last_nextptr, offset); -} - -NS_LIST_FN void ns_list_remove_(ns_list_t *list, ns_list_offset_t offset, void *removed) -{ - void *next; - void **prev_nextptr; - - next = NS_LIST_NEXT_(removed, offset); - prev_nextptr = NS_LIST_PREV_(removed, offset); - if (next) { - NS_LIST_PREV_(next, offset) = prev_nextptr; - } else { - list->last_nextptr = prev_nextptr; - } - *prev_nextptr = next; - - ns_list_link_init_(NS_LIST_LINK_(removed, offset)); -} - -NS_LIST_FN void ns_list_replace_(ns_list_t *list, ns_list_offset_t offset, void *current, void *restrict replacement) -{ - void *next; - void **prev_nextptr; - - NS_LIST_PREV_(replacement, offset) = prev_nextptr = NS_LIST_PREV_(current, offset); - NS_LIST_NEXT_(replacement, offset) = next = NS_LIST_NEXT_(current, offset); - - if (next) { - NS_LIST_PREV_(next, offset) = &NS_LIST_NEXT_(replacement, offset); - } else { - list->last_nextptr = &NS_LIST_NEXT_(replacement, offset); - } - *prev_nextptr = replacement; - - ns_list_link_init_(NS_LIST_LINK_(current, offset)); -} - -NS_LIST_FN void ns_list_concatenate_(ns_list_t *dst, ns_list_t *src, ns_list_offset_t offset) -{ - ns_list_link_t *src_first; - - src_first = src->first_entry; - if (!src_first) { - return; - } - - *dst->last_nextptr = src_first; - NS_LIST_PREV_(src_first, offset) = dst->last_nextptr; - dst->last_nextptr = src->last_nextptr; - - ns_list_init_(src); -} - -NS_LIST_FN uint_fast16_t ns_list_count_(const ns_list_t *list, ns_list_offset_t offset) -{ - uint_fast16_t count = 0; - - for (void *p = list->first_entry; p; p = NS_LIST_NEXT_(p, offset)) { - count++; - } - - return count; -} -#endif /* defined NS_ALLOW_INLINING || defined NS_LIST_FN */ - -#ifdef __cplusplus -} -#endif - -#endif /* NS_LIST_H_ */ - diff --git a/source/ns_list_internal/ns_types.h b/source/ns_list_internal/ns_types.h deleted file mode 100644 index ee7ddf3567b..00000000000 --- a/source/ns_list_internal/ns_types.h +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (c) 2014-2015 ARM Limited. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * 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. - */ -/* - * ns_types.h - Basic compiler and type setup for Nanostack libraries. - */ -#ifndef NS_TYPES_H_ -#define NS_TYPES_H_ - -/** \file - * \brief Basic compiler and type setup - * - * We currently assume C99 or later. - * - * C99 features being relied on: - * - * - and - * - inline (with C99 semantics, not C++ as per default GCC); - * - designated initialisers; - * - compound literals; - * - restrict; - * - [static N] in array parameters; - * - declarations in for statements; - * - mixing declarations and statements - * - * Compilers should be set to C99 or later mode when building Nanomesh source. - * For GCC this means "-std=gnu99" (C99 with usual GNU extensions). - * - * Also, a little extra care is required for public header files that could be - * included from C++, especially as C++ lacks some C99 features. - * - * (TODO: as this is exposed to API users, do we need a predefine to distinguish - * internal and external use, for finer control? Not yet, but maybe...) - */ - -/* Make sure defines its macros if C++ */ -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif -#ifndef __STDC_CONSTANT_MACROS -#define __STDC_CONSTANT_MACROS -#endif - -#include -#include // includes ; debugf() users need PRIu32 etc -#include - -/* - * Create the optional 24-bit types if they don't exist (worth trying - * to use them, as they could exist and be more efficient than 32-bit on 8-bit - * systems...) - */ -#ifndef UINT24_LEAST_MAX -typedef uint_least32_t uint_least24_t; -#define UINT_LEAST24_MAX UINT_LEAST32_MAX -#define UINT24_C(x) UINT32_C(x) -#define PRIoLEAST24 PRIoLEAST32 -#define PRIuLEAST24 PRIuLEAST32 -#define PRIxLEAST24 PRIxLEAST32 -#define PRIXLEAST24 PRIXLEAST32 -#endif - -#ifndef INT24_LEAST_MAX -typedef int_least32_t int_least24_t; -#define INT24_LEAST_MIN INT_LEAST32_MIN -#define INT24_LEAST_MAX INT_LEAST32_MAX -#define INT24_C(x) INT32_C(x) -#define PRIdLEAST24 PRIdLEAST32 -#define PRIiLEAST24 PRIiLEAST32 -#endif - -#ifndef UINT24_FAST_MAX -typedef uint_fast32_t uint_fast24_t; -#define UINT_FAST24_MAX UINT_FAST32_MAX -#define PRIoFAST24 PRIoFAST32 -#define PRIuFAST24 PRIuFAST32 -#define PRIxFAST24 PRIxFAST32 -#define PRIXFAST24 PRIXFAST32 -#endif - -#ifndef INT24_FAST_MAX -typedef int_fast32_t int_fast24_t; -#define INT_FAST24_MIN INT_FAST32_MIN -#define INT_FAST24_MAX INT_FAST32_MAX -#define PRIdFAST24 PRIdFAST32 -#define PRIiFAST24 PRIiFAST32 -#endif - -/* Function attribute - C11 "noreturn" or C++11 "[[noreturn]]" */ -#ifndef NS_NORETURN -#if defined __cplusplus && __cplusplus >= 201103L -#define NS_NORETURN [[noreturn]] -#elif !defined __cplusplus && __STDC_VERSION__ >= 201112L -#define NS_NORETURN _Noreturn -#elif defined __GNUC__ -#define NS_NORETURN __attribute__((__noreturn__)) -#elif defined __CC_ARM -#define NS_NORETURN __declspec(noreturn) -#elif defined __IAR_SYSTEMS_ICC__ -#define NS_NORETURN __noreturn -#else -#define NS_NORETURN -#endif -#endif - -/* C11's "alignas" macro, emulated for integer expressions if necessary */ -#ifndef __alignas_is_defined -#if defined __CC_ARM || defined __TASKING__ -#define alignas(n) __align(n) -#define __alignas_is_defined 1 -#elif (__STDC_VERSION__ >= 201112L) || (defined __cplusplus && __cplusplus >= 201103L) -#include -#elif defined __GNUC__ -#define alignas(n) __attribute__((__aligned__(n))) -#define __alignas_is_defined 1 -#elif defined __IAR_SYSTEMS_ICC__ -/* Does this really just apply to the next variable? */ -#define alignas(n) __Alignas(data_alignment=n) -#define __Alignas(x) _Pragma(#x) -#define __alignas_is_defined 1 -#endif -#endif - -/** - * Marker for functions or objects that may be unused, suppressing warnings. - * Place after the identifier: - * ~~~ - * static int X MAYBE_UNUSED = 3; - * static int foo(void) MAYBE_UNUSED; - * ~~~ - */ -#if defined __CC_ARM || defined __GNUC__ -#define MAYBE_UNUSED __attribute__((unused)) -#else -#define MAYBE_UNUSED -#endif - -/* - * C++ (even C++11) doesn't provide restrict: define away or provide - * alternative. - */ -#ifdef __cplusplus -#ifdef __GNUC__ -#define restrict __restrict -#else -#define restrict -#endif -#endif /* __cplusplus */ - - -/** - * C++ doesn't allow "static" in function parameter types: ie - * ~~~ - * entry_t *find_entry(const uint8_t address[static 16]) - * ~~~ - * If a header file may be included from C++, use this __static define instead. - * - * (Syntax introduced in C99 - `uint8_t address[16]` in a prototype was always - * equivalent to `uint8_t *address`, but the C99 addition of static tells the - * compiler that address is never NULL, and always points to at least 16 - * elements. This adds no new type-checking, but the information could aid - * compiler optimisation, and it can serve as documentation). - */ -#ifdef __cplusplus -#define __static -#else -#define __static static -#endif - -#ifdef __GNUC__ -#define NS_GCC_VERSION (__GNUC__ * 10000 \ - + __GNUC_MINOR__ * 100 \ - + __GNUC_PATCHLEVEL__) -#endif - -/** \brief Compile-time assertion - * - * C11 provides _Static_assert, as does GCC even in C99 mode (and - * as a freestanding implementation, we can't rely on to get - * the static_assert macro). - * C++11 provides static_assert as a keyword, as does G++ in C++0x mode. - * - * The assertion acts as a declaration that can be placed at file scope, in a - * code block (except after a label), or as a member of a struct/union. It - * produces a compiler error if "test" evaluates to 0. - * - * Note that this *includes* the required semicolon when defined, else it - * is totally empty, permitting use in structs. (If the user provided the `;`, - * it would leave an illegal stray `;` if unavailable). - */ -#ifdef __cplusplus -# if __cplusplus >= 201103L || __cpp_static_assert >= 200410 -# define NS_STATIC_ASSERT(test, str) static_assert(test, str); -# elif defined __GXX_EXPERIMENTAL_CXX0X__ && NS_GCC_VERSION >= 40300 -# define NS_STATIC_ASSERT(test, str) __extension__ static_assert(test, str); -# else -# define NS_STATIC_ASSERT(test, str) -# endif -#else /* C */ -# if __STDC_VERSION__ >= 201112L -# define NS_STATIC_ASSERT(test, str) _Static_assert(test, str); -# elif defined __GNUC__ && NS_GCC_VERSION >= 40600 && !defined __CC_ARM -# ifdef _Static_assert - /* - * Some versions of glibc cdefs.h (which comes in via above) - * attempt to define their own _Static_assert (if GCC < 4.6 or - * __STRICT_ANSI__) using an extern declaration, which doesn't work in a - * struct/union. - * - * For GCC >= 4.6 and __STRICT_ANSI__, we can do better - just use - * the built-in _Static_assert with __extension__. We have to do this, as - * ns_list.h needs to use it in a union. No way to get at it though, without - * overriding their define. - */ -# undef _Static_assert -# define _Static_assert(x, y) __extension__ _Static_assert(x, y) -# endif -# define NS_STATIC_ASSERT(test, str) __extension__ _Static_assert(test, str); -# else -# define NS_STATIC_ASSERT(test, str) -#endif -#endif - -/** \brief Pragma to suppress warnings about unusual pointer values. - * - * Useful if using "poison" values. - */ -#ifdef __IAR_SYSTEMS_ICC__ -#define NS_FUNNY_INTPTR_OK _Pragma("diag_suppress=Pe1053") -#define NS_FUNNY_INTPTR_RESTORE _Pragma("diag_default=Pe1053") -#else -#define NS_FUNNY_INTPTR_OK -#define NS_FUNNY_INTPTR_RESTORE -#endif - -/** \brief Convert pointer to member to pointer to containing structure */ -#define NS_CONTAINER_OF(ptr, type, member) \ - ((type *) ((char *) (ptr) - offsetof(type, member))) - -/* - * Inlining could cause problems when mixing with C++; provide a mechanism to - * disable it. This could also be turned off for other reasons (although - * this can usually be done through a compiler flag, eg -O0 on gcc). - */ -#ifndef __cplusplus -#define NS_ALLOW_INLINING -#endif - -/* There is inlining problem in GCC version 4.1.x and we know it works in 4.6.3 */ -#if defined __GNUC__ && NS_GCC_VERSION < 40600 -#undef NS_ALLOW_INLINING -#endif - -/** \brief Mark a potentially-inlineable function. - * - * We follow C99 semantics, which requires precisely one external definition. - * To also allow inlining to be totally bypassed under control of - * NS_ALLOW_INLINING, code can be structured as per the example of ns_list: - * - * foo.h - * ----- - * ~~~ - * NS_INLINE int my_func(int); - * - * #if defined NS_ALLOW_INLINING || defined FOO_FN - * #ifndef FOO_FN - * #define FOO_FN NS_INLINE - * #endif - * FOO_FN int my_func(int a) - * { - * definition; - * } - * #endif - * ~~~ - * foo.c - * ----- - * ~~~ - * #define FOO_FN extern - * #include "foo.h" - * ~~~ - * Which generates: - * ~~~ - * NS_ALLOW_INLINING set NS_ALLOW_INLINING unset - * ===================== ======================= - * Include foo.h Include foo.h - * ------------- ------------- - * inline int my_func(int); int my_func(int); - * - * // inline definition - * inline int my_func(int a) - * { - * definition; - * } - * - * Compile foo.c Compile foo.c - * ------------- ------------- - * (from .h) inline int my_func(int); int my_func(int); - * - * // external definition - * // because of no "inline" // normal external definition - * extern int my_func(int a) extern int my_func(int a) - * { { - * definition; definition; - * } } - * ~~~ - * - * Note that even with inline keywords, whether the compiler inlines or not is - * up to it. For example, gcc at "-O0" will not inline at all, and will always - * call the real functions in foo.o, just as if NS_ALLOW_INLINING was unset. - * At "-O2", gcc could potentially inline everything, meaning that foo.o is not - * referenced at all. - * - * Alternatively, you could use "static inline", which gives every caller its - * own internal definition. This is compatible with C++ inlining (which expects - * the linker to eliminate duplicates), but in C it's less efficient if the code - * ends up non-inlined, and it's harder to breakpoint. I don't recommend it - * except for the most trivial functions (which could then probably be macros). - */ -#ifdef NS_ALLOW_INLINING -#define NS_INLINE inline -#else -#define NS_INLINE -#endif - -#if defined __SDCC_mcs51 || defined __ICC8051__ || defined __C51__ - -/* The 8051 environments: SDCC (historic), IAR (current), Keil (future?) */ - -#define NS_LARGE __xdata -#define NS_LARGE_PTR __xdata -#ifdef __ICC8051__ -#define NS_REENTRANT -#define NS_REENTRANT_PREFIX __idata_reentrant -#else -#define NS_REENTRANT __reentrant -#define NS_REENTRANT_PREFIX -#endif -#define NS_NEAR_FUNC __near_func - -#else - -/* "Normal" systems. Define it all away. */ -#define NS_LARGE -#define NS_LARGE_PTR -#define NS_REENTRANT -#define NS_REENTRANT_PREFIX -#define NS_NEAR_FUNC - -#endif - -/** \brief Scatter-gather descriptor - * - * Slightly optimised for small platforms - we assume we won't need any - * element bigger than 64K. - */ -typedef struct ns_iovec { - void *iov_base; - uint_fast16_t iov_len; -} ns_iovec_t; - -#endif /* NS_TYPES_H */ diff --git a/target.json b/target.json deleted file mode 100644 index e69de29bb2d..00000000000 From fbbbc0d77fcee55795236d8f59471c94552e58a6 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 13 Aug 2018 09:50:29 +0300 Subject: [PATCH 04/40] Move mbed-client-cli in right place --- .../frameworks/mbed-client-cli/mbed-client-cli}/ns_cmdline.h | 0 .../frameworks/mbed-client-cli/source}/ns_cmdline.c | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {mbed-client-cli => features/frameworks/mbed-client-cli/mbed-client-cli}/ns_cmdline.h (100%) rename {source => features/frameworks/mbed-client-cli/source}/ns_cmdline.c (100%) diff --git a/mbed-client-cli/ns_cmdline.h b/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h similarity index 100% rename from mbed-client-cli/ns_cmdline.h rename to features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h diff --git a/source/ns_cmdline.c b/features/frameworks/mbed-client-cli/source/ns_cmdline.c similarity index 100% rename from source/ns_cmdline.c rename to features/frameworks/mbed-client-cli/source/ns_cmdline.c From db1b53e6f4390613155a2747bee4f5778035d204 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 13 Aug 2018 10:54:43 +0300 Subject: [PATCH 05/40] Limit mbed-ls less than 2 because of icetea and mbed-flashed support --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0470934ec2c..91f86d53cd1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ IntelHex>=1.3 junit-xml pyYAML requests -mbed-ls>=0.2.13 +mbed-ls>=0.2.13,<2.0 mbed-host-tests>=1.1.2 mbed-greentea>=0.2.24 beautifulsoup4>=4 From 1e883c560b58d2677ee41a1c38e4f6c80e308f34 Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Mon, 13 Aug 2018 11:39:53 +0300 Subject: [PATCH 06/40] removed mbed-client-cli tests folder from root --- test/CMakeLists.txt | 27 -- test/Test.cpp | 785 -------------------------------------------- 2 files changed, 812 deletions(-) delete mode 100644 test/CMakeLists.txt delete mode 100644 test/Test.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 6e18e19d462..00000000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# only build tests on targets that declare they are like posix -if(DEFINED TARGET_LIKE_X86_WINDOWS_NATIVE OR DEFINED TARGET_LIKE_X86_LINUX_NATIVE) - - # describe the test executable - add_executable(mbed_client_cli_test Test.cpp) - - include_directories("../yotta_modules/cpputest") - - # describe what the test executable needs to link with - target_link_libraries(mbed_client_cli_test - "mbed-client-cli" - "mbed-trace" - cpputest - ) - add_definitions("-Wno-write-strings") - - # describe what is actual test binary - if(DEFINED TARGET_LIKE_X86_WINDOWS_NATIVE) - SET(TEST_EXECUTABLE "build/x86-windows-native/test/mbed_client_cli_test") - endif() - if(DEFINED TARGET_LIKE_X86_LINUX_NATIVE) - SET(TEST_EXECUTABLE "../../../build/x86-linux-native/test/mbed_client_cli_test") - endif() - - add_test(mbed_client_cli_test ${TEST_EXECUTABLE}) - -endif() diff --git a/test/Test.cpp b/test/Test.cpp deleted file mode 100644 index 0d735b2e0a1..00000000000 --- a/test/Test.cpp +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (c) 2016 ARM Limited. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * 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. - */ - -/** - * \file \test\Test.c - * - * \brief Unit tests for mbed-client-cli - */ -#include -#include -#include -#include -#include - - -#include "mbed-cpputest/CppUTest/TestHarness.h" -#include "mbed-cpputest/CppUTest/SimpleString.h" -#include "mbed-cpputest/CppUTest/CommandLineTestRunner.h" - -#define YOTTA_CFG_MBED_TRACE 1 -#define YOTTA_CFG_MBED_TRACE_FEA_IPV6 0 -#include "mbed-trace/mbed_trace.h" -#include "mbed-client-cli/ns_cmdline.h" -#define MAX(x,y) (x>y?x:y) -#define ARRAY_CMP(x, y) \ - MEMCMP_EQUAL(x, y, MAX(strlen(x), strlen(y))) - -int main(int ac, char **av) -{ - return CommandLineTestRunner::RunAllTests(ac, av); -} - -#define BUFSIZE 1024 -char buf[BUFSIZE] = {0}; -#define INIT_BUF() memset(buf, 0, BUFSIZE) -int cmd_dummy(int argc, char *argv[]) -{ - return 0; -} - -int mutex_wait_count = 0; -int mutex_release_count = 0; -int mutex_count_expected_difference = 1; -bool check_mutex_lock_state = false; -void my_mutex_wait() -{ - mutex_wait_count++; -} -void my_mutex_release() -{ - mutex_release_count++; -} - -void myprint(const char *fmt, va_list ap) -{ - if (check_mutex_lock_state) { - CHECK((mutex_wait_count - mutex_release_count) == mutex_count_expected_difference); - } - vsnprintf(buf + strlen(buf), BUFSIZE - strlen(buf), fmt, ap); - //printf("\nMYPRINT: %s\n", buf); //for test test -} -void input(const char *str) -{ - while (*str != 0) { - cmd_char_input(*str++); - } -} - -#define REQUEST(x) input(x);INIT_BUF();cmd_char_input('\r'); -#define RESPONSE(x) "\r\n" x "\r\n\r\x1B[2K/> \x1B[1D" -#define CMDLINE(x) "\r\x1b[2K/>" x "\x1b[1D" - -#define FORWARD "C" -#define BACKWARD "D" -#define CMDLINE_CUR(x, cursor, dir) "\r\x1b[2K/>" x "\x1b[" cursor "" dir -#define CLEAN() cmd_char_input('\r');INIT_BUF(); - -//vt100 keycodes -#define HOME() input("\x1b[1~") -#define INSERT() input("\x1b[2~") -#define DELETE() input("\x1b[3~") -#define BACKSPACE() input("\x7f") -#define LEFT() input("\x1b[D") -#define LEFT_N(n) for(int i=0;i 20 ); -} -TEST(cli, hello) -{ - REQUEST("echo Hi!"); - ARRAY_CMP(RESPONSE("Hi! ") , buf); -} -TEST(cli, cmd_echo1) -{ - REQUEST(" echo Hi!"); - ARRAY_CMP(RESPONSE("Hi! ") , buf); -} -TEST(cli, cmd_echo2) -{ - REQUEST("echo foo faa"); - ARRAY_CMP(RESPONSE("foo faa ") , buf); -} -TEST(cli, cmd_echo3) -{ - REQUEST("echo foo faa"); - ARRAY_CMP(RESPONSE("foo faa ") , buf); -} -TEST(cli, cmd_echo4) -{ - REQUEST("echo foo faa"); - ARRAY_CMP(RESPONSE("foo faa ") , buf); -} -TEST(cli, cmd_echo5) -{ - REQUEST("echo \"foo faa\""); - ARRAY_CMP(RESPONSE("foo faa ") , buf); -} -TEST(cli, cmd_echo6) -{ - REQUEST("echo \"foo faa"); - ARRAY_CMP(RESPONSE("\"foo faa ") , buf); -} -TEST(cli, cmd_echo7) -{ - REQUEST("echo 'foo faa\""); - ARRAY_CMP(RESPONSE("'foo faa\" ") , buf); -} -TEST(cli, cmd_echo8) -{ - REQUEST("echof\x7f foo faa"); - ARRAY_CMP(RESPONSE("foo faa ") , buf); -} -TEST(cli, cmd_echo9) -{ - REQUEST("echo foo faa\x1b[D\x1b[D\x1b[D hello "); - ARRAY_CMP(RESPONSE("foo hello faa ") , buf); - CLEAN(); -} -TEST(cli, cmd_echo10) -{ - REQUEST("echo foo faa\x1b[D\x1b[C\x1b[C hello "); //echo foo hello faa - ARRAY_CMP(RESPONSE("foo faa hello ") , buf); - CLEAN(); -} -TEST(cli, cmd_echo11) -{ - REQUEST("echo off\r"); - INIT_BUF(); - input("echo test"); - ARRAY_CMP("" , buf); - input("\r"); - ARRAY_CMP("test \r\n" , buf); - INIT_BUF(); - REQUEST("echo on\r"); - INIT_BUF(); - input("e"); - ARRAY_CMP(CMDLINE("e ") , buf); - INIT_BUF(); - input("c"); - ARRAY_CMP(CMDLINE("ec ") , buf); - INIT_BUF(); - input("h"); - ARRAY_CMP(CMDLINE("ech ") , buf); - INIT_BUF(); - input("o"); - ARRAY_CMP(CMDLINE("echo ") , buf); - INIT_BUF(); - input(" "); - ARRAY_CMP(CMDLINE("echo ") , buf); - INIT_BUF(); - input("o"); - ARRAY_CMP(CMDLINE("echo o ") , buf); - INIT_BUF(); - input("k"); - ARRAY_CMP(CMDLINE("echo ok ") , buf); - CLEAN(); -} - -TEST(cli, cmd_arrows_up) -{ - REQUEST("echo foo-1"); - INIT_BUF(); - input("\x1b[A"); - ARRAY_CMP(CMDLINE("echo foo-1 ") , buf); - INIT_BUF(); - input("\x1b[A"); - ARRAY_CMP(CMDLINE("echo foo-1 ") , buf); - CLEAN(); -} -TEST(cli, cmd_arrows_up_down) -{ - REQUEST("echo test-1"); - ARRAY_CMP(RESPONSE("test-1 "), buf); - REQUEST("echo test-2"); - ARRAY_CMP(RESPONSE("test-2 "), buf); - REQUEST("echo test-3"); - ARRAY_CMP(RESPONSE("test-3 "), buf); - - INIT_BUF(); - UP(); - ARRAY_CMP(CMDLINE("echo test-3 "), buf); - INIT_BUF(); - UP(); - ARRAY_CMP(CMDLINE("echo test-2 "), buf); - INIT_BUF(); - UP(); - ARRAY_CMP(CMDLINE("echo test-1 "), buf); - INIT_BUF(); - UP(); - ARRAY_CMP(CMDLINE("echo test-1 "), buf); - INIT_BUF(); - DOWN(); - ARRAY_CMP(CMDLINE("echo test-2 "), buf); - INIT_BUF(); - DOWN(); - ARRAY_CMP(CMDLINE("echo test-3 "), buf); - INIT_BUF(); - DOWN(); - ARRAY_CMP(CMDLINE(" "), buf); - CLEAN(); -} -TEST(cli, cmd_pageup_page_down) -{ - //goto history beginning/end - REQUEST("echo test-1"); - REQUEST("echo test-2"); - REQUEST("echo test-3"); - REQUEST("echo test-4"); - INIT_BUF(); - PAGE_UP(); - ARRAY_CMP(CMDLINE("echo test-1 "), buf); - INIT_BUF(); - PAGE_DOWN(); - ARRAY_CMP(CMDLINE("echo test-4 "), buf); - CLEAN(); -} -TEST(cli, cmd_text_pageup) -{ - REQUEST("echo test-1"); - REQUEST("echo test-2"); - REQUEST("echo test-3"); - REQUEST("echo test-4"); - input("hello"); - INIT_BUF(); - PAGE_UP(); //goto end of history - ARRAY_CMP(CMDLINE("echo test-1 "), buf); - INIT_BUF(); - PAGE_DOWN(); //goto beginning of history - it should be just writted "hello" - ARRAY_CMP(CMDLINE("hello "), buf); - CLEAN(); -} -TEST(cli, cmd_text_pageup_up) -{ - REQUEST("echo test-1"); - REQUEST("echo test-2"); - REQUEST("echo test-3"); - REQUEST("echo test-4"); - input("hello"); - INIT_BUF(); - PAGE_UP(); //goto end of history - ARRAY_CMP(CMDLINE("echo test-1 "), buf); - INIT_BUF(); - DOWN(); - ARRAY_CMP(CMDLINE("echo test-2 "), buf); - INIT_BUF(); - PAGE_DOWN(); //goto beginning of history - it should be just writted "hello" - ARRAY_CMP(CMDLINE("hello "), buf); - CLEAN(); -} -TEST(cli, cmd_text_delete) -{ - input("hello world"); - LEFT_N(2); - DELETE(); - INIT_BUF(); - DELETE(); - ARRAY_CMP(CMDLINE("hello wor "), buf); - INIT_BUF(); - DELETE(); - ARRAY_CMP(CMDLINE("hello wor "), buf); - INIT_BUF(); - DELETE(); - ARRAY_CMP(CMDLINE("hello wor "), buf); - LEFT_N(2); - INIT_BUF(); - DELETE(); - ARRAY_CMP(CMDLINE_CUR("hello wr ", "2", BACKWARD), buf); - BACKSPACE(); - BACKSPACE(); - INIT_BUF(); - BACKSPACE(); - ARRAY_CMP(CMDLINE_CUR("hellr ", "2", BACKWARD), buf); - CLEAN(); -} -TEST(cli, cmd_insert) -{ - CLEAN(); - input("echo hello word"); - LEFT(); - INIT_BUF(); - input("l"); - ARRAY_CMP(CMDLINE_CUR("echo hello world ", "2", BACKWARD), buf); - LEFT_N(10); - INIT_BUF(); - LEFT(); - ARRAY_CMP(CMDLINE_CUR("echo hello world ", "13", BACKWARD), buf); - INIT_BUF(); - RIGHT(); - ARRAY_CMP(CMDLINE_CUR("echo hello world ", "12", BACKWARD), buf); - CLEAN(); -} -TEST(cli, cmd_tab_1) -{ - INIT_BUF(); - input("e"); - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); - - input("\rech"); - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); - - input("\r"); -} -TEST(cli, cmd_tab_2) -{ - INIT_BUF(); - - cmd_add("role", cmd_dummy, 0, 0); - cmd_add("route", cmd_dummy, 0, 0); - cmd_add("rile", cmd_dummy, 0, 0); - input("r"); - INIT_BUF(); - - input("\t"); - ARRAY_CMP(CMDLINE_CUR("role ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("route ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("rile ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\x1b[Z"); - ARRAY_CMP(CMDLINE_CUR("route ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\x1b[Z"); - ARRAY_CMP(CMDLINE_CUR("role ", "1", BACKWARD) , buf); - - input("\r"); -} -TEST(cli, cmd_tab_3) -{ - INIT_BUF(); - cmd_add("role", cmd_dummy, 0, 0); - cmd_alias_add("rose", "role"); - cmd_alias_add("rope", "rope"); - - input("r"); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("role ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("rose ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("rope ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("r ", "1", BACKWARD) , buf); - - INIT_BUF(); - input("o"); - ARRAY_CMP(CMDLINE_CUR("ro ", "1", BACKWARD) , buf); - - ESC(); - INIT_BUF(); -} -TEST(cli, cmd_tab_4) -{ - INIT_BUF(); - cmd_variable_add("dut1", "hello"); - - input("e"); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); - - input(" $d"); - INIT_BUF(); - input("u"); - ARRAY_CMP(CMDLINE_CUR("echo $du ", "1", BACKWARD), buf); - - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("echo $dut1 ", "1", BACKWARD) , buf); - - input("\re"); - INIT_BUF(); - input("\t"); - ARRAY_CMP(CMDLINE_CUR("echo ", "1", BACKWARD) , buf); - - input("\r"); - INIT_BUF(); -} -// // alias test -// extern void replace_alias(const char *str, const char *old_str, const char *new_str); -// TEST(cli, cmd_alias_1) -// { -// char str[] = "hello a men"; -// replace_alias(str, "a", "b"); -// ARRAY_CMP("hello a men", str); -// -// replace_alias(str, "hello", "echo"); -// ARRAY_CMP("echo a men", str); -// INIT_BUF(); -// } -/* @todo this not working yet -TEST(cli, cmd_alias_2) -{ - REQUEST("alias foo bar"); - INIT_BUF(); - REQUEST("alias"); - ARRAY_CMP("\r\nalias:\r\nfoo 'bar'\r\n\r\x1b[2K/> \x1b[1D", buf); - - REQUEST("alias foo"); - INIT_BUF(); - REQUEST("alias"); - ARRAY_CMP("\r\nalias:\r\n\r\x1b[2K/> \x1b[1D", buf); -} -*/ -TEST(cli, cmd_alias_3) -{ - cmd_alias_add("p", "echo"); - REQUEST("p toimii"); - ARRAY_CMP("\r\ntoimii \r\n\r\x1b[2K/> \x1b[1D", buf); - - cmd_alias_add("printtti", "echo"); - REQUEST("printtti toimii"); - ARRAY_CMP("\r\ntoimii \r\n\r\x1b[2K/> \x1b[1D", buf); -} -TEST(cli, cmd_alias_4) -{ - REQUEST("alias dut1 \"echo dut1\""); - REQUEST("alias dut2 \"echo dut2\""); - REQUEST("alias dut3 \"echo dut3\""); - REQUEST("dut1"); - ARRAY_CMP(RESPONSE("dut1 "), buf); -} -TEST(cli, cmd_series) -{ - REQUEST("alias dut1 \"echo dut1\""); - REQUEST("alias dut2 \"echo dut2\""); - REQUEST("alias dut3 \"echo dut3\""); - REQUEST("dut1;dut2;dut3"); - ARRAY_CMP(RESPONSE("dut1 \r\ndut2 \r\ndut3 "), buf); -} - -TEST(cli, cmd_var_1) -{ - REQUEST("set foo \"bar test\""); - INIT_BUF(); - REQUEST("set"); - ARRAY_CMP("\r\nvariables:\r\nfoo 'bar test'\r\n\r\x1b[2K/> \x1b[1D", buf); - - REQUEST("set foo"); - INIT_BUF(); - REQUEST("set"); - ARRAY_CMP("\r\nvariables:\r\n\r\x1b[2K/> \x1b[1D", buf); -} -TEST(cli, cmd_var_2) -{ - REQUEST("set foo \"hello world\""); - REQUEST("echo foo"); - ARRAY_CMP(RESPONSE("foo ") , buf); - - REQUEST("echo $foo"); - ARRAY_CMP(RESPONSE("hello world ") , buf); - - REQUEST("set faa !"); - REQUEST("echo $foo$faa"); - ARRAY_CMP(RESPONSE("hello world! ") , buf); -} -TEST(cli, multiple_cmd) -{ - REQUEST("set foo \"hello world\";echo $foo"); - ARRAY_CMP(RESPONSE("hello world ") , buf); - - REQUEST("setd faa \"hello world\";echo $faa"); - ARRAY_CMP("\r\nCommand 'setd' not found.\r\n$faa \r\n\r\x1B[2K/> \x1B[1D" , buf); - - REQUEST("setd foo \"hello guy\"&&echo $foo"); - ARRAY_CMP(RESPONSE("Command 'setd' not found.") , buf); -} -TEST(cli, maxlength) -{ - int i; - char test_data[600]; - char *ptr = test_data; - strcpy(test_data, "echo "); - for (i = 5; i < 600; i++) { - test_data[i] = 'A' + i % 26; - } - test_data[599] = 0; - REQUEST(ptr); - //ARRAY_CMP( RESPONSE((test_data+5)), buf); -} -TEST(cli, ampersand) -{ - REQUEST("echo hello world&"); - ARRAY_CMP(RESPONSE("hello world ") , buf); -} - -#define REDIR_DATA "echo Hi!" -#define PASSTHROUGH_BUF_LENGTH 10 -char passthrough_buffer[PASSTHROUGH_BUF_LENGTH]; -char* passthrough_ptr = NULL; -void passthrough_cb(uint8_t c) -{ - if (passthrough_ptr != NULL) { - *passthrough_ptr++ = c; - } -} -TEST(cli, passthrough_set) -{ - passthrough_ptr = passthrough_buffer; - memset(&passthrough_buffer, 0, PASSTHROUGH_BUF_LENGTH); - INIT_BUF(); - - cmd_input_passthrough_func(passthrough_cb); - input(REDIR_DATA); - - CHECK(strlen(buf) == 0); - ARRAY_CMP(REDIR_DATA, passthrough_buffer); - - cmd_input_passthrough_func(NULL); - - REQUEST(REDIR_DATA); - ARRAY_CMP(RESPONSE("Hi! ") , buf); -} - - -TEST(cli, cmd_out_func_set_null) -{ - cmd_out_func(NULL); -} - -static int outf_called = 0; -void outf(const char *fmt, va_list ap) { - outf_called++; -} -TEST(cli, cmd_out_func_set) -{ - outf_called = 0; - cmd_out_func(&outf); - cmd_vprintf(NULL, NULL); - CHECK_EQUAL(outf_called, 1); -} - -TEST(cli, cmd_ctrl_func_set_null) -{ - cmd_ctrl_func(NULL); -} - -TEST(cli, cmd_delete_null) -{ - cmd_delete(NULL); -} - -TEST(cli, cmd_history_size_set) -{ - cmd_history_size(0); - CHECK_EQUAL(cmd_history_size(1), 1); -} - -TEST(cli, cmd_add_invalid_params) -{ - cmd_add(NULL, cmd_dummy, NULL, NULL); - cmd_add("", cmd_dummy, NULL, NULL); - cmd_add("abc", NULL, NULL, NULL); -} From 2ddaed3a836aa022fceb0c917d8ba7c58d2769f2 Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Mon, 13 Aug 2018 11:41:48 +0300 Subject: [PATCH 07/40] Changes needed for abstract socket interface --- TEST_APPS/device/socket_app/cmd_socket.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/TEST_APPS/device/socket_app/cmd_socket.cpp b/TEST_APPS/device/socket_app/cmd_socket.cpp index 8ff6d2eeb06..59bbc9fb6e5 100644 --- a/TEST_APPS/device/socket_app/cmd_socket.cpp +++ b/TEST_APPS/device/socket_app/cmd_socket.cpp @@ -781,7 +781,14 @@ static int cmd_socket(int argc, char *argv[]) return CMDLINE_RETCODE_FAIL; } - return handle_nsapi_error("Socket::open()", info->socket().open(interface)); + switch(info->type()) { + case SInfo::TCP_CLIENT: + return handle_nsapi_error("Socket::open()", info->tcp_socket()->open(interface)); + case SInfo::UDP: + return handle_nsapi_error("Socket::open()", info->udp_socket()->open(interface)); + case SInfo::TCP_SERVER: + return handle_nsapi_error("Socket::open()", info->tcp_server()->open(interface)); + } } else if (COMMAND_IS("close")) { return handle_nsapi_error("Socket::close()", info->socket().close()); @@ -799,10 +806,12 @@ static int cmd_socket(int argc, char *argv[]) // Replace NULL-strings with NULL addr = strcmp(addr, "NULL") ? addr : NULL; cmd_printf("Socket::bind(%s, %" PRId32 ")\r\n", addr, port); - return handle_nsapi_error("Socket::bind(addr, port)", info->socket().bind(addr, port)); + SocketAddress tmp(addr, port); + return handle_nsapi_error("Socket::bind(addr, port)", info->socket().bind(tmp)); } else { cmd_printf("Socket::bind(%" PRId32 ")\r\n", port); - return handle_nsapi_error("Socket::bind(port)", info->socket().bind(port)); + SocketAddress tmp(NULL, port); + return handle_nsapi_error("Socket::bind(port)", info->socket().bind(tmp)); } } else if (COMMAND_IS("set_blocking")) { From 60911876cd8ea146911fbd4272d3afb02b069321 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 14 Aug 2018 13:33:58 +0300 Subject: [PATCH 08/40] Icetea version update --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 91f86d53cd1..ca3f57d3e05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,4 @@ future>=0.16.0 six>=1.11.0 git+https://github.com/armmbed/manifest-tool.git@v1.4.5 mbed-cloud-sdk==2.0.0 -icetea==1.0.0rc2 +icetea==1.0.* From 3b3bed2406c5732d9ec9559d8599e4407f5ff367 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 14 Aug 2018 15:00:08 +0300 Subject: [PATCH 09/40] Small fixes based on github comments --- TEST_APPS/testcases/example/test_cmdline.py | 2 +- TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py | 2 +- TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py | 4 ++-- .../testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py | 3 +-- requirements.txt | 2 +- tools/test.py | 1 - tools/test_api.py | 1 - tools/toolchains/__init__.py | 2 +- 8 files changed, 7 insertions(+), 10 deletions(-) diff --git a/TEST_APPS/testcases/example/test_cmdline.py b/TEST_APPS/testcases/example/test_cmdline.py index d1dbcd7ea0f..8796e13a7b3 100644 --- a/TEST_APPS/testcases/example/test_cmdline.py +++ b/TEST_APPS/testcases/example/test_cmdline.py @@ -19,7 +19,7 @@ class Testcase(Bench): def __init__(self): self.device = None - Bench.__init__(self, + super(Testcase, self).__init__(self, name="test_cmdline", title="Smoke test for command line interface", status="released", diff --git a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py index 51492862052..eb166d4caef 100644 --- a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py +++ b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py @@ -25,7 +25,7 @@ def __init__(self, **kwargs): } testcase_args.update(kwargs) - Bench.__init__(self, **testcase_args) + super(MultipleTestcase, self).__init__(self, **testcase_args) def setup(self): self.command("dut1", "ifup") diff --git a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py index 55d6e6d50e9..cf69b27002b 100644 --- a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py +++ b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py @@ -3,13 +3,13 @@ # from icetea_lib.bench import Bench from interface import interfaceUp, interfaceDown -from icetea_lib.tools import test_case import threading import time + class Testcase(Bench): def __init__(self): - Bench.__init__(self, + super(Testcase, self).__init__(self, name="TCPSERVER_ACCEPT", title = "TCPSERVER_ACCEPT", purpose = "Test that TCPServer::bind(), TCPServer::listen() and TCPServer::accept() works", diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py index d408a293203..8e142b67e04 100644 --- a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -2,14 +2,13 @@ # Copyright (c) 2018, Arm Limited and affiliates. # from icetea_lib.bench import Bench, TestStepFail -from icetea_lib.tools import test_case import random import string class Testcase(Bench): def __init__(self): - Bench.__init__(self, + super(Testcase, self).__init__(self, name="TCPSOCKET_ECHOTEST_BURST_SHORT", title="TCPSOCKET_ECHOTEST_BURST_SHORT", purpose="Verify that TCPSocket can send burst of packets to echo server and read incoming packets", diff --git a/requirements.txt b/requirements.txt index ca3f57d3e05..0d4130656a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ IntelHex>=1.3 junit-xml pyYAML requests -mbed-ls>=0.2.13,<2.0 +mbed-ls>=1.4.2,==1.* mbed-host-tests>=1.1.2 mbed-greentea>=0.2.24 beautifulsoup4>=4 diff --git a/tools/test.py b/tools/test.py index 27b2a86f009..57af4cf3032 100644 --- a/tools/test.py +++ b/tools/test.py @@ -21,7 +21,6 @@ from __future__ import print_function, division, absolute_import import sys import os -import subprocess import fnmatch ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) diff --git a/tools/test_api.py b/tools/test_api.py index 2fe0794976c..fd2b4c760aa 100644 --- a/tools/test_api.py +++ b/tools/test_api.py @@ -28,7 +28,6 @@ import random import argparse import datetime -import errno import threading import ctypes import functools diff --git a/tools/toolchains/__init__.py b/tools/toolchains/__init__.py index 5b905d0e076..edf901bcabc 100644 --- a/tools/toolchains/__init__.py +++ b/tools/toolchains/__init__.py @@ -1117,4 +1117,4 @@ def report(self): u'IAR': IAR } -TOOLCHAINS = set(TOOLCHAIN_CLASSES.keys()) \ No newline at end of file +TOOLCHAINS = set(TOOLCHAIN_CLASSES.keys()) From bf1781b00527ebafb2bb7c24f025f8fc7015de39 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Wed, 15 Aug 2018 08:56:26 +0300 Subject: [PATCH 10/40] Remove autoformatting --- tools/test_api.py | 369 ++++++++++++++++++++++------------------------ 1 file changed, 174 insertions(+), 195 deletions(-) diff --git a/tools/test_api.py b/tools/test_api.py index fd2b4c760aa..2d5561d873e 100644 --- a/tools/test_api.py +++ b/tools/test_api.py @@ -31,13 +31,11 @@ import threading import ctypes import functools -import subprocess from colorama import Fore, Back, Style from prettytable import PrettyTable from copy import copy, deepcopy from time import sleep, time - try: from Queue import Queue, Empty except ImportError: @@ -116,7 +114,6 @@ def stop(self): class SingleTestExecutor(threading.Thread): """ Example: Single test class in separate thread usage """ - def __init__(self, single_test): self.single_test = single_test threading.Thread.__init__(self) @@ -137,7 +134,7 @@ def run(self): # table shows text x toolchain test result matrix print(self.single_test.generate_test_summary_by_target( test_summary, shuffle_seed)) - print("Completed in %.2f sec" % (elapsed_time)) + print("Completed in %.2f sec"% (elapsed_time)) class SingleTestRunner(object): @@ -160,26 +157,26 @@ class SingleTestRunner(object): TEST_RESULT_NOT_SUPPORTED = "NOT_SUPPORTED" GLOBAL_LOOPS_COUNT = 1 # How many times each test should be repeated - TEST_LOOPS_LIST = [] # We redefine no.of loops per test_id - TEST_LOOPS_DICT = {} # TEST_LOOPS_LIST in dict format: { test_id : test_loop_count} + TEST_LOOPS_LIST = [] # We redefine no.of loops per test_id + TEST_LOOPS_DICT = {} # TEST_LOOPS_LIST in dict format: { test_id : test_loop_count} - muts = {} # MUTs descriptor (from external file) - test_spec = {} # Test specification (from external file) + muts = {} # MUTs descriptor (from external file) + test_spec = {} # Test specification (from external file) # mbed test suite -> SingleTestRunner - TEST_RESULT_MAPPING = {"success": TEST_RESULT_OK, - "failure": TEST_RESULT_FAIL, - "error": TEST_RESULT_ERROR, - "ioerr_copy": TEST_RESULT_IOERR_COPY, - "ioerr_disk": TEST_RESULT_IOERR_DISK, - "ioerr_serial": TEST_RESULT_IOERR_SERIAL, - "timeout": TEST_RESULT_TIMEOUT, - "no_image": TEST_RESULT_NO_IMAGE, - "end": TEST_RESULT_UNDEF, - "mbed_assert": TEST_RESULT_MBED_ASSERT, - "build_failed": TEST_RESULT_BUILD_FAILED, - "not_supproted": TEST_RESULT_NOT_SUPPORTED - } + TEST_RESULT_MAPPING = {"success" : TEST_RESULT_OK, + "failure" : TEST_RESULT_FAIL, + "error" : TEST_RESULT_ERROR, + "ioerr_copy" : TEST_RESULT_IOERR_COPY, + "ioerr_disk" : TEST_RESULT_IOERR_DISK, + "ioerr_serial" : TEST_RESULT_IOERR_SERIAL, + "timeout" : TEST_RESULT_TIMEOUT, + "no_image" : TEST_RESULT_NO_IMAGE, + "end" : TEST_RESULT_UNDEF, + "mbed_assert" : TEST_RESULT_MBED_ASSERT, + "build_failed" : TEST_RESULT_BUILD_FAILED, + "not_supproted" : TEST_RESULT_NOT_SUPPORTED + } def __init__(self, _global_loops_count=1, @@ -289,18 +286,17 @@ def __init__(self, # Database related initializations self.db_logger = factory_db_logger(self.opts_db_url) - self.db_logger_build_id = None # Build ID (database index of build_id table) + self.db_logger_build_id = None # Build ID (database index of build_id table) # Let's connect to database to set up credentials and confirm database is ready if self.db_logger: - self.db_logger.connect_url(self.opts_db_url) # Save db access info inside db_logger object + self.db_logger.connect_url(self.opts_db_url) # Save db access info inside db_logger object if self.db_logger.is_connected(): # Get hostname and uname so we can use it as build description # when creating new build_id in external database (_hostname, _uname) = self.db_logger.get_hostname() _host_location = os.path.dirname(os.path.abspath(__file__)) build_id_type = None if self.opts_only_build_tests is None else self.db_logger.BUILD_ID_TYPE_BUILD_ONLY - self.db_logger_build_id = self.db_logger.get_next_build_id(_hostname, desc=_uname, - location=_host_location, type=build_id_type) + self.db_logger_build_id = self.db_logger.get_next_build_id(_hostname, desc=_uname, location=_host_location, type=build_id_type) self.db_logger.disconnect() def dump_options(self): @@ -311,23 +307,23 @@ def dump_options(self): or data_str = json.dumps(self.dump_options()) """ - result = {"db_url": str(self.opts_db_url), - "log_file_name": str(self.opts_log_file_name), - "shuffle_test_order": str(self.opts_shuffle_test_order), - "shuffle_test_seed": str(self.opts_shuffle_test_seed), - "test_by_names": str(self.opts_test_by_names), - "peripheral_by_names": str(self.opts_peripheral_by_names), - "test_only_peripheral": str(self.opts_test_only_peripheral), - "test_only_common": str(self.opts_test_only_common), - "verbose": str(self.opts_verbose), - "firmware_global_name": str(self.opts_firmware_global_name), - "only_build_tests": str(self.opts_only_build_tests), - "copy_method": str(self.opts_copy_method), - "mut_reset_type": str(self.opts_mut_reset_type), - "jobs": str(self.opts_jobs), - "extend_test_timeout": str(self.opts_extend_test_timeout), - "_dummy": '' - } + result = {"db_url" : str(self.opts_db_url), + "log_file_name" : str(self.opts_log_file_name), + "shuffle_test_order" : str(self.opts_shuffle_test_order), + "shuffle_test_seed" : str(self.opts_shuffle_test_seed), + "test_by_names" : str(self.opts_test_by_names), + "peripheral_by_names" : str(self.opts_peripheral_by_names), + "test_only_peripheral" : str(self.opts_test_only_peripheral), + "test_only_common" : str(self.opts_test_only_common), + "verbose" : str(self.opts_verbose), + "firmware_global_name" : str(self.opts_firmware_global_name), + "only_build_tests" : str(self.opts_only_build_tests), + "copy_method" : str(self.opts_copy_method), + "mut_reset_type" : str(self.opts_mut_reset_type), + "jobs" : str(self.opts_jobs), + "extend_test_timeout" : str(self.opts_extend_test_timeout), + "_dummy" : '' + } return result def shuffle_random_func(self): @@ -369,6 +365,7 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep 'shuffle_random_seed': self.shuffle_random_seed } + # print '=== %s::%s ===' % (target, toolchain) # Let's build our test if target not in TARGET_MAP: @@ -398,7 +395,7 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep print(self.logger.log_line( self.logger.LogType.NOTIF, 'Skipped tests for %s target. Toolchain %s is not ' - 'supported for this target' % (T.name, toolchain))) + 'supported for this target'% (T.name, toolchain))) continue except ToolException: @@ -441,13 +438,13 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep _extra=json.dumps(self.dump_options())) self.db_logger.disconnect(); - valid_test_map_keys = self.get_valid_tests(test_map_keys, target, toolchain, test_ids, - self.opts_include_non_automated) + valid_test_map_keys = self.get_valid_tests(test_map_keys, target, toolchain, test_ids, self.opts_include_non_automated) skipped_test_map_keys = self.get_skipped_tests(test_map_keys, valid_test_map_keys) for skipped_test_id in skipped_test_map_keys: test_suite_properties['skipped'].append(skipped_test_id) + # First pass through all tests and determine which libraries need to be built libraries = [] for test_id in valid_test_map_keys: @@ -459,6 +456,7 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep if lib['build_dir'] in test.dependencies and lib['id'] not in libraries: libraries.append(lib['id']) + clean_project_options = True if self.opts_goanna_for_tests or clean or self.opts_clean else None # Build all required libraries @@ -480,10 +478,11 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep 'There were errors while building library %s' % lib_id)) continue + for test_id in valid_test_map_keys: test = TEST_MAP[test_id] - test_suite_properties['test.libs.%s.%s.%s' % (target, toolchain, test_id)] = ', '.join(libraries) + test_suite_properties['test.libs.%s.%s.%s'% (target, toolchain, test_id)] = ', '.join(libraries) # TODO: move this 2 below loops to separate function INC_DIRS = [] @@ -495,18 +494,18 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep for lib_id in libraries: if 'macros' in LIBRARY_MAP[lib_id] and LIBRARY_MAP[lib_id]['macros']: MACROS.extend(LIBRARY_MAP[lib_id]['macros']) - MACROS.append('TEST_SUITE_TARGET_NAME="%s"' % target) - MACROS.append('TEST_SUITE_TEST_ID="%s"' % test_id) + MACROS.append('TEST_SUITE_TARGET_NAME="%s"'% target) + MACROS.append('TEST_SUITE_TEST_ID="%s"'% test_id) test_uuid = uuid.uuid4() - MACROS.append('TEST_SUITE_UUID="%s"' % str(test_uuid)) + MACROS.append('TEST_SUITE_UUID="%s"'% str(test_uuid)) # Prepare extended test results data structure (it can be used to generate detailed test report) if target not in self.test_summary_ext: self.test_summary_ext[target] = {} # test_summary_ext : toolchain if toolchain not in self.test_summary_ext[target]: - self.test_summary_ext[target][toolchain] = {} # test_summary_ext : toolchain : target + self.test_summary_ext[target][toolchain] = {} # test_summary_ext : toolchain : target - tt_test_id = "%s::%s::%s" % (toolchain, target, test_id) # For logging only + tt_test_id = "%s::%s::%s" % (toolchain, target, test_id) # For logging only project_name = self.opts_firmware_global_name if self.opts_firmware_global_name else None try: @@ -524,6 +523,7 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep except Exception as e: project_name_str = project_name if project_name is not None else test_id + test_result = self.TEST_RESULT_FAIL if isinstance(e, ToolException): @@ -538,6 +538,7 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep 'Project %s is not supported' % project_name_str)) test_result = self.TEST_RESULT_NOT_SUPPORTED + # Append test results to global test summary self.test_summary.append( (test_result, target, toolchain, test_id, @@ -548,17 +549,17 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep if test_id not in self.test_summary_ext[target][toolchain]: self.test_summary_ext[target][toolchain][test_id] = [] - self.test_summary_ext[target][toolchain][test_id].append({0: { - 'result': test_result, - 'output': '', - 'target_name': target, + self.test_summary_ext[target][toolchain][test_id].append({ 0: { + 'result' : test_result, + 'output' : '', + 'target_name' : target, 'target_name_unique': target, - 'toolchain_name': toolchain, - 'id': test_id, - 'description': test.get_description(), - 'elapsed_time': 0, - 'duration': 0, - 'copy_method': None + 'toolchain_name' : toolchain, + 'id' : test_id, + 'description' : test.get_description(), + 'elapsed_time' : 0, + 'duration' : 0, + 'copy_method' : None }}) continue @@ -576,9 +577,9 @@ def execute_thread_slice(self, q, target, toolchains, clean, test_ids, build_rep test_spec = self.shape_test_request(target, path, test_id, test_duration) test_loops = self.get_test_loop_count(test_id) - test_suite_properties['test.duration.%s.%s.%s' % (target, toolchain, test_id)] = test_duration - test_suite_properties['test.loops.%s.%s.%s' % (target, toolchain, test_id)] = test_loops - test_suite_properties['test.path.%s.%s.%s' % (target, toolchain, test_id)] = path + test_suite_properties['test.duration.%s.%s.%s'% (target, toolchain, test_id)] = test_duration + test_suite_properties['test.loops.%s.%s.%s'% (target, toolchain, test_id)] = test_loops + test_suite_properties['test.path.%s.%s.%s'% (target, toolchain, test_id)] = path # read MUTs, test specification and perform tests handle_results = self.handle(test_spec, target, toolchain, test_loops=test_loops) @@ -626,11 +627,12 @@ def execute(self): if self.opts_shuffle_test_seed is not None and self.is_shuffle_seed_float(): self.shuffle_random_seed = round(float(self.opts_shuffle_test_seed), self.SHUFFLE_SEED_ROUND) + if self.opts_parallel_test_exec: ################################################################### # Experimental, parallel test execution per singletest instance. ################################################################### - execute_threads = [] # Threads used to build mbed SDL, libs, test cases and execute tests + execute_threads = [] # Threads used to build mbed SDL, libs, test cases and execute tests # Note: We are building here in parallel for each target separately! # So we are not building the same thing multiple times and compilers # in separate threads do not collide. @@ -638,29 +640,26 @@ def execute(self): # get information about available MUTs (per target). for target, toolchains in self.test_spec['targets'].items(): self.test_suite_properties_ext[target] = {} - t = threading.Thread(target=self.execute_thread_slice, args=( - q, target, toolchains, clean, test_ids, self.build_report, self.build_properties)) + t = threading.Thread(target=self.execute_thread_slice, args = (q, target, toolchains, clean, test_ids, self.build_report, self.build_properties)) t.daemon = True t.start() execute_threads.append(t) for t in execute_threads: - q.get() # t.join() would block some threads because we should not wait in any order for thread end + q.get() # t.join() would block some threads because we should not wait in any order for thread end else: # Serialized (not parallel) test execution for target, toolchains in self.test_spec['targets'].items(): if target not in self.test_suite_properties_ext: self.test_suite_properties_ext[target] = {} - self.execute_thread_slice(q, target, toolchains, clean, test_ids, self.build_report, - self.build_properties) + self.execute_thread_slice(q, target, toolchains, clean, test_ids, self.build_report, self.build_properties) q.get() if self.db_logger: self.db_logger.reconnect(); if self.db_logger.is_connected(): - self.db_logger.update_build_id_info(self.db_logger_build_id, - _status_fk=self.db_logger.BUILD_ID_STATUS_COMPLETED) + self.db_logger.update_build_id_info(self.db_logger_build_id, _status_fk=self.db_logger.BUILD_ID_STATUS_COMPLETED) self.db_logger.disconnect(); return self.test_summary, self.shuffle_random_seed, self.test_summary_ext, self.test_suite_properties_ext, self.build_report, self.build_properties @@ -684,8 +683,8 @@ def get_valid_tests(self, test_map_keys, target, toolchain, test_ids, include_no continue if (self.opts_peripheral_by_names and test.peripherals and - not any((i in self.opts_peripheral_by_names) - for i in test.peripherals)): + not any((i in self.opts_peripheral_by_names) + for i in test.peripherals)): # We will skip tests not forced with -p option if self.opts_verbose_skipped_tests: print(self.logger.log_line( @@ -756,7 +755,7 @@ def generate_test_summary_by_target(self, test_summary, shuffle_seed=None): result = "Test summary:\n" for target in unique_targets: - result_dict = {} # test : { toolchain : result } + result_dict = {} # test : { toolchain : result } unique_target_toolchains = [] for test in test_summary: if test[TARGET_INDEX] == target: @@ -770,7 +769,7 @@ def generate_test_summary_by_target(self, test_summary, shuffle_seed=None): pt = PrettyTable(pt_cols) for col in pt_cols: pt.align[col] = "l" - pt.padding_width = 1 # One space between column edges and contents (default) + pt.padding_width = 1 # One space between column edges and contents (default) for test in unique_tests: if test in result_dict: @@ -782,40 +781,40 @@ def generate_test_summary_by_target(self, test_summary, shuffle_seed=None): row.append(test_results[toolchain]) pt.add_row(row) result += pt.get_string() - shuffle_seed_text = "Shuffle Seed: %.*f" % (self.SHUFFLE_SEED_ROUND, - shuffle_seed if shuffle_seed else self.shuffle_random_seed) - result += "\n%s" % (shuffle_seed_text if self.opts_shuffle_test_order else '') + shuffle_seed_text = "Shuffle Seed: %.*f"% (self.SHUFFLE_SEED_ROUND, + shuffle_seed if shuffle_seed else self.shuffle_random_seed) + result += "\n%s"% (shuffle_seed_text if self.opts_shuffle_test_order else '') return result def generate_test_summary(self, test_summary, shuffle_seed=None): """ Prints well-formed summary with results (SQL table like) table shows target x test results matrix across """ - success_code = 0 # Success code that can be leter returned to + success_code = 0 # Success code that can be leter returned to result = "Test summary:\n" # Pretty table package is used to print results pt = PrettyTable(["Result", "Target", "Toolchain", "Test ID", "Test Description", "Elapsed Time (sec)", "Timeout (sec)", "Loops"]) - pt.align["Result"] = "l" # Left align - pt.align["Target"] = "l" # Left align - pt.align["Toolchain"] = "l" # Left align - pt.align["Test ID"] = "l" # Left align - pt.align["Test Description"] = "l" # Left align - pt.padding_width = 1 # One space between column edges and contents (default) - - result_dict = {self.TEST_RESULT_OK: 0, - self.TEST_RESULT_FAIL: 0, - self.TEST_RESULT_ERROR: 0, - self.TEST_RESULT_UNDEF: 0, - self.TEST_RESULT_IOERR_COPY: 0, - self.TEST_RESULT_IOERR_DISK: 0, - self.TEST_RESULT_IOERR_SERIAL: 0, - self.TEST_RESULT_NO_IMAGE: 0, - self.TEST_RESULT_TIMEOUT: 0, - self.TEST_RESULT_MBED_ASSERT: 0, - self.TEST_RESULT_BUILD_FAILED: 0, - self.TEST_RESULT_NOT_SUPPORTED: 0 - } + pt.align["Result"] = "l" # Left align + pt.align["Target"] = "l" # Left align + pt.align["Toolchain"] = "l" # Left align + pt.align["Test ID"] = "l" # Left align + pt.align["Test Description"] = "l" # Left align + pt.padding_width = 1 # One space between column edges and contents (default) + + result_dict = {self.TEST_RESULT_OK : 0, + self.TEST_RESULT_FAIL : 0, + self.TEST_RESULT_ERROR : 0, + self.TEST_RESULT_UNDEF : 0, + self.TEST_RESULT_IOERR_COPY : 0, + self.TEST_RESULT_IOERR_DISK : 0, + self.TEST_RESULT_IOERR_SERIAL : 0, + self.TEST_RESULT_NO_IMAGE : 0, + self.TEST_RESULT_TIMEOUT : 0, + self.TEST_RESULT_MBED_ASSERT : 0, + self.TEST_RESULT_BUILD_FAILED : 0, + self.TEST_RESULT_NOT_SUPPORTED : 0 + } for test in test_summary: if test[0] in result_dict: @@ -825,11 +824,10 @@ def generate_test_summary(self, test_summary, shuffle_seed=None): result += "\n" # Print result count - result += "Result: " + ' / '.join( - ['%s %s' % (value, key) for (key, value) in {k: v for k, v in result_dict.items() if v != 0}.items()]) - shuffle_seed_text = "Shuffle Seed: %.*f\n" % (self.SHUFFLE_SEED_ROUND, - shuffle_seed if shuffle_seed else self.shuffle_random_seed) - result += "\n%s" % (shuffle_seed_text if self.opts_shuffle_test_order else '') + result += "Result: " + ' / '.join(['%s %s' % (value, key) for (key, value) in {k: v for k, v in result_dict.items() if v != 0}.items()]) + shuffle_seed_text = "Shuffle Seed: %.*f\n"% (self.SHUFFLE_SEED_ROUND, + shuffle_seed if shuffle_seed else self.shuffle_random_seed) + result += "\n%s"% (shuffle_seed_text if self.opts_shuffle_test_order else '') return result def test_loop_list_to_dict(self, test_loops_str): @@ -885,7 +883,7 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): return None mcu = mut['mcu'] - copy_method = mut.get('copy_method') # Available board configuration selection e.g. core selection etc. + copy_method = mut.get('copy_method') # Available board configuration selection e.g. core selection etc. if self.db_logger: self.db_logger.reconnect() @@ -941,14 +939,13 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): # Host test execution start_host_exec_time = time() - single_test_result = self.TEST_RESULT_UNDEF # single test run result + single_test_result = self.TEST_RESULT_UNDEF # single test run result _copy_method = selected_copy_method if not exists(image_path): single_test_result = self.TEST_RESULT_NO_IMAGE elapsed_time = 0 - single_test_output = self.logger.log_line(self.logger.LogType.ERROR, - 'Image file does not exist: %s' % image_path) + single_test_output = self.logger.log_line(self.logger.LogType.ERROR, 'Image file does not exist: %s'% image_path) print(single_test_output) else: # Host test execution @@ -968,20 +965,20 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): # Store test result test_all_result.append(single_test_result) - total_elapsed_time = time() - start_host_exec_time # Test time with copy (flashing) / reset + total_elapsed_time = time() - start_host_exec_time # Test time with copy (flashing) / reset elapsed_time = single_testduration # TIme of single test case execution after reset detailed_test_results[test_index] = { - 'result': single_test_result, - 'output': single_test_output, - 'target_name': target_name, - 'target_name_unique': target_name_unique, - 'toolchain_name': toolchain_name, - 'id': test_id, - 'description': test_description, - 'elapsed_time': round(elapsed_time, 2), - 'duration': single_timeout, - 'copy_method': _copy_method, + 'result' : single_test_result, + 'output' : single_test_output, + 'target_name' : target_name, + 'target_name_unique' : target_name_unique, + 'toolchain_name' : toolchain_name, + 'id' : test_id, + 'description' : test_description, + 'elapsed_time' : round(elapsed_time, 2), + 'duration' : single_timeout, + 'copy_method' : _copy_method, } print(self.print_test_result( @@ -1009,8 +1006,7 @@ def handle_mut(self, mut, data, target_name, toolchain_name, test_loops=1): if self.db_logger: self.db_logger.disconnect() - return (self.shape_global_test_loop_result(test_all_result, - self.opts_waterfall_test and self.opts_consolidate_waterfall_test), + return (self.shape_global_test_loop_result(test_all_result, self.opts_waterfall_test and self.opts_consolidate_waterfall_test), target_name_unique, toolchain_name, test_id, @@ -1048,7 +1044,7 @@ def print_test_result(self, test_result, target_name, toolchain_name, tokens.append(test_description) separator = "::" time_info = " in %.2f of %d sec" % (round(elapsed_time, 2), duration) - result = separator.join(tokens) + " [" + test_result + "]" + time_info + result = separator.join(tokens) + " [" + test_result +"]" + time_info return Fore.MAGENTA + result + Fore.RESET def shape_test_loop_ok_result_count(self, test_all_result): @@ -1056,7 +1052,7 @@ def shape_test_loop_ok_result_count(self, test_all_result): """ test_loop_count = len(test_all_result) test_loop_ok_result = test_all_result.count(self.TEST_RESULT_OK) - return "%d/%d" % (test_loop_ok_result, test_loop_count) + return "%d/%d"% (test_loop_ok_result, test_loop_count) def shape_global_test_loop_result(self, test_all_result, waterfall_and_consolidate): """ Reformats list of results to simple string @@ -1110,16 +1106,16 @@ def get_auto_property_value(property_name, line): Returns string """ result = None - if re.search("HOST: Property '%s'" % property_name, line) is not None: - property = re.search("HOST: Property '%s' = '([\w\d _]+)'" % property_name, line) + if re.search("HOST: Property '%s'"% property_name, line) is not None: + property = re.search("HOST: Property '%s' = '([\w\d _]+)'"% property_name, line) if property is not None and len(property.groups()) == 1: result = property.groups()[0] return result cmd = ["python", - '%s.py' % name, + '%s.py'% name, '-d', disk, - '-f', '"%s"' % image_path, + '-f', '"%s"'% image_path, '-p', port, '-t', str(duration), '-C', str(program_cycle_s)] @@ -1143,7 +1139,7 @@ def get_auto_property_value(property_name, line): proc = Popen(cmd, stdout=PIPE, cwd=HOST_TESTS) obs = ProcessObserver(proc) - update_once_flag = {} # Stores flags checking if some auto-parameter was already set + update_once_flag = {} # Stores flags checking if some auto-parameter was already set line = '' output = [] start_time = time() @@ -1182,7 +1178,7 @@ def get_auto_property_value(property_name, line): else: line += c end_time = time() - testcase_duration = end_time - start_time # Test case duration from reset to {end} + testcase_duration = end_time - start_time # Test case duration from reset to {end} c = get_char_from_queue(obs) @@ -1259,11 +1255,11 @@ def show_json_file_format_error(json_spec_filename, line, column): with open(json_spec_filename) as data_file: line_no = 1 for json_line in data_file: - if line_no + 5 >= line: # Print last few lines before error - print('Line %d:\t' % line_no + json_line) + if line_no + 5 >= line: # Print last few lines before error + print('Line %d:\t'%line_no + json_line) if line_no == line: - print('%s\t%s^'(' ' * len('Line %d:' % line_no), - '-' * (column - 1))) + print('%s\t%s^' (' ' * len('Line %d:' % line_no), + '-' * (column - 1))) break line_no += 1 @@ -1311,7 +1307,7 @@ def get_json_data_from_file(json_spec_filename, verbose=False): show_json_file_format_error(json_spec_filename, line, column) except IOError as fileopen_error_msg: - print('JSON file %s not opened. Reason: %s\n' % + print('JSON file %s not opened. Reason: %s\n'% (json_spec_filename, fileopen_error_msg)) if verbose and result: pp = pprint.PrettyPrinter(indent=4) @@ -1384,7 +1380,7 @@ def print_test_configuration_from_json(json_data, join_delim=", "): target_supported_toolchains = get_target_supported_toolchains(target) if not target_supported_toolchains: target_supported_toolchains = [] - target_name = target if target in TARGET_MAP else "%s*" % target + target_name = target if target in TARGET_MAP else "%s*"% target row = [target_name] toolchains = targets[target] @@ -1415,27 +1411,27 @@ def print_test_configuration_from_json(json_data, join_delim=", "): pt.add_row(row) # generate result string - result = pt.get_string() # Test specification table + result = pt.get_string() # Test specification table if toolchain_conflicts or toolchain_path_conflicts: result += "\n" result += "Toolchain conflicts:\n" for target in toolchain_conflicts: if target not in TARGET_MAP: - result += "\t* Target %s unknown\n" % (target) + result += "\t* Target %s unknown\n"% (target) conflict_target_list = join_delim.join(toolchain_conflicts[target]) sufix = 's' if len(toolchain_conflicts[target]) > 1 else '' - result += "\t* Target %s does not support %s toolchain%s\n" % (target, conflict_target_list, sufix) + result += "\t* Target %s does not support %s toolchain%s\n"% (target, conflict_target_list, sufix) for toolchain in toolchain_path_conflicts: - # Let's check toolchain configuration + # Let's check toolchain configuration if toolchain in TOOLCHAIN_PATHS: toolchain_path = TOOLCHAIN_PATHS[toolchain] if not os.path.isdir(toolchain_path): - result += "\t# Toolchain %s path not found: %s\n" % (toolchain, toolchain_path) + result += "\t# Toolchain %s path not found: %s\n"% (toolchain, toolchain_path) return result -def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',', platform_filter=None): +def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',',platform_filter=None): """ Generates table summary with all test cases and additional test cases information using pretty print functionality. Allows test suite user to see test cases @@ -1466,7 +1462,7 @@ def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',' counter_all = 0 counter_automated = 0 - pt.padding_width = 1 # One space between column edges and contents (default) + pt.padding_width = 1 # One space between column edges and contents (default) for test_id in sorted(TEST_MAP.keys()): if platform_filter is not None: @@ -1520,8 +1516,7 @@ def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',' pt.align['percent [%]'] = "r" for unique_id in unique_test_id: # print "\t\t%s: %d / %d" % (unique_id, counter_dict_test_id_types[unique_id], counter_dict_test_id_types_all[unique_id]) - percent_progress = round( - 100.0 * counter_dict_test_id_types[unique_id] / float(counter_dict_test_id_types_all[unique_id]), 1) + percent_progress = round(100.0 * counter_dict_test_id_types[unique_id] / float(counter_dict_test_id_types_all[unique_id]), 1) str_progress = progress_bar(percent_progress, 75) row = [unique_id, counter_dict_test_id_types[unique_id], @@ -1538,7 +1533,7 @@ def get_avail_tests_summary_table(cols=None, result_summary=True, join_delim=',' def progress_bar(percent_progress, saturation=0): """ This function creates progress bar with optional simple saturation mark """ - step = int(percent_progress / 2) # Scale by to (scale: 1 - 50) + step = int(percent_progress / 2) # Scale by to (scale: 1 - 50) str_progress = '#' * step + '.' * int(50 - step) c = '!' if str_progress[38] == '.' else '|' if saturation > 0: @@ -1578,32 +1573,26 @@ def singletest_in_cli_mode(single_test): if single_test.opts_report_html_file_name: # Export results in form of HTML report to separate file report_exporter = ReportExporter(ResultExporterType.HTML) - report_exporter.report_to_file(test_summary_ext, single_test.opts_report_html_file_name, - test_suite_properties=test_suite_properties_ext) + report_exporter.report_to_file(test_summary_ext, single_test.opts_report_html_file_name, test_suite_properties=test_suite_properties_ext) if single_test.opts_report_junit_file_name: # Export results in form of JUnit XML report to separate file report_exporter = ReportExporter(ResultExporterType.JUNIT) - report_exporter.report_to_file(test_summary_ext, single_test.opts_report_junit_file_name, - test_suite_properties=test_suite_properties_ext) + report_exporter.report_to_file(test_summary_ext, single_test.opts_report_junit_file_name, test_suite_properties=test_suite_properties_ext) if single_test.opts_report_text_file_name: # Export results in form of a text file report_exporter = ReportExporter(ResultExporterType.TEXT) - report_exporter.report_to_file(test_summary_ext, single_test.opts_report_text_file_name, - test_suite_properties=test_suite_properties_ext) + report_exporter.report_to_file(test_summary_ext, single_test.opts_report_text_file_name, test_suite_properties=test_suite_properties_ext) if single_test.opts_report_build_file_name: # Export build results as html report to sparate file report_exporter = ReportExporter(ResultExporterType.JUNIT, package="build") - report_exporter.report_to_file(build_report, single_test.opts_report_build_file_name, - test_suite_properties=build_properties) + report_exporter.report_to_file(build_report, single_test.opts_report_build_file_name, test_suite_properties=build_properties) # Returns True if no build failures of the test projects or their dependencies return status - class TestLogger(): """ Super-class for logging and printing ongoing events for test suite pass """ - def __init__(self, store_log=True): """ We can control if logger actually stores log in memory or just handled all log entries immediately @@ -1619,18 +1608,18 @@ def __init__(self, store_log=True): ERROR='Error', EXCEPT='Exception') - self.LogToFileAttr = construct_enum(CREATE=1, # Create or overwrite existing log file - APPEND=2) # Append to existing log file + self.LogToFileAttr = construct_enum(CREATE=1, # Create or overwrite existing log file + APPEND=2) # Append to existing log file def log_line(self, LogType, log_line, timestamp=True, line_delim='\n'): """ Log one line of text """ log_timestamp = time() - log_entry = {'log_type': LogType, - 'log_timestamp': log_timestamp, - 'log_line': log_line, - '_future': None - } + log_entry = {'log_type' : LogType, + 'log_timestamp' : log_timestamp, + 'log_line' : log_line, + '_future' : None + } # Store log in memory if self.store_log: self.log.append(log_entry) @@ -1640,20 +1629,18 @@ def log_line(self, LogType, log_line, timestamp=True, line_delim='\n'): class CLITestLogger(TestLogger): """ Logger used with CLI (Command line interface) test suite. Logs on screen and to file if needed """ - def __init__(self, store_log=True, file_name=None): TestLogger.__init__(self) self.log_file_name = file_name - # self.TIMESTAMP_FORMAT = '%y-%m-%d %H:%M:%S' # Full date and time - self.TIMESTAMP_FORMAT = '%H:%M:%S' # Time only + #self.TIMESTAMP_FORMAT = '%y-%m-%d %H:%M:%S' # Full date and time + self.TIMESTAMP_FORMAT = '%H:%M:%S' # Time only def log_print(self, log_entry, timestamp=True): """ Prints on screen formatted log entry """ ts = log_entry['log_timestamp'] - timestamp_str = datetime.datetime.fromtimestamp(ts).strftime( - "[%s] " % self.TIMESTAMP_FORMAT) if timestamp else '' - log_line_str = "%(log_type)s: %(log_line)s" % (log_entry) + timestamp_str = datetime.datetime.fromtimestamp(ts).strftime("[%s] "% self.TIMESTAMP_FORMAT) if timestamp else '' + log_line_str = "%(log_type)s: %(log_line)s"% (log_entry) return timestamp_str + log_line_str def log_line(self, LogType, log_line, timestamp=True, line_delim='\n'): @@ -1691,7 +1678,7 @@ def detect_database_verbose(db_url): if result is not None: # Parsing passed (db_type, username, password, host, db_name) = result - # print "DB type '%s', user name '%s', password '%s', host '%s', db name '%s'"% result + #print "DB type '%s', user name '%s', password '%s', host '%s', db name '%s'"% result # Let's try to connect db_ = factory_db_logger(db_url) if db_ is not None: @@ -1715,12 +1702,11 @@ def get_module_avail(module_name): """ return module_name in sys.modules.keys() - def get_autodetected_MUTS_list(platform_name_filter=None): oldError = None if os.name == 'nt': # Disable Windows error box temporarily - oldError = ctypes.windll.kernel32.SetErrorMode(1) # note that SEM_FAILCRITICALERRORS = 1 + oldError = ctypes.windll.kernel32.SetErrorMode(1) #note that SEM_FAILCRITICALERRORS = 1 mbeds = mbed_lstools.create() detect_muts_list = mbeds.list_mbeds() @@ -1730,7 +1716,6 @@ def get_autodetected_MUTS_list(platform_name_filter=None): return get_autodetected_MUTS(detect_muts_list, platform_name_filter=platform_name_filter) - def get_autodetected_MUTS(mbeds_list, platform_name_filter=None): """ Function detects all connected to host mbed-enabled devices and generates artificial MUTS file. If function fails to auto-detect devices it will return empty dictionary. @@ -1742,7 +1727,7 @@ def get_autodetected_MUTS(mbeds_list, platform_name_filter=None): @param mbeds_list list of mbeds captured from mbed_lstools @param platform_name You can filter 'platform_name' with list of filtered targets from 'platform_name_filter' """ - result = {} # Should be in muts_all.json format + result = {} # Should be in muts_all.json format # Align mbeds_list from mbed_lstools to MUT file format (JSON dictionary with muts) # mbeds_list = [{'platform_name': 'NUCLEO_F302R8', 'mount_point': 'E:', 'target_id': '07050200623B61125D5EF72A', 'serial_port': u'COM34'}] index = 1 @@ -1755,11 +1740,10 @@ def get_autodetected_MUTS(mbeds_list, platform_name_filter=None): # For mcu_unique - we are assigning 'platform_name_unique' value from mbedls output (if its existing) # if not we are creating our own unique value (last few chars from platform's target_id). m = {'mcu': mut['platform_name'], - 'mcu_unique': mut['platform_name_unique'] if 'platform_name_unique' in mut else "%s[%s]" % ( - mut['platform_name'], mut['target_id'][-4:]), + 'mcu_unique' : mut['platform_name_unique'] if 'platform_name_unique' in mut else "%s[%s]" % (mut['platform_name'], mut['target_id'][-4:]), 'port': mut['serial_port'], 'disk': mut['mount_point'], - 'peripherals': [] # No peripheral detection + 'peripherals': [] # No peripheral detection } if index not in result: result[index] = {} @@ -1780,7 +1764,7 @@ def get_autodetected_TEST_SPEC(mbeds_list, use_supported_toolchains - if True add all supported toolchains to test_spec toolchain_filter - if [...list of toolchains...] add from all toolchains only those in filter to test_spec """ - result = {'targets': {}} + result = {'targets': {} } for mut in mbeds_list: mcu = mut['mcu'] @@ -1838,7 +1822,7 @@ def get_default_test_options_parser(): toolchain_list = list(TOOLCHAINS) + ["DEFAULT", "ALL"] parser.add_argument('--tc', dest='toolchains_filter', - type=argparse_many(argparse_uppercase_type(toolchain_list, "toolchains")), + type=argparse_many(argparse_uppercase_type(toolchain_list, "toolchains")), help="Toolchain filter for --auto argument. Use toolchains names separated by comma, 'default' or 'all' to select toolchains") test_scopes = ','.join(["'%s'" % n for n in get_available_oper_test_scopes()]) @@ -1874,9 +1858,9 @@ def get_default_test_options_parser(): help='Runs only test enumerated it this switch. Use comma to separate test case names') parser.add_argument('-p', '--peripheral-by-names', - dest='peripheral_by_names', - type=argparse_many(str), - help='Forces discovery of particular peripherals. Use comma to separate peripheral names') + dest='peripheral_by_names', + type=argparse_many(str), + help='Forces discovery of particular peripherals. Use comma to separate peripheral names') copy_methods = host_tests_plugins.get_plugin_caps('CopyMethod') copy_methods_str = "Plugin support: " + ', '.join(copy_methods) @@ -1884,7 +1868,7 @@ def get_default_test_options_parser(): parser.add_argument('-c', '--copy-method', dest='copy_method', type=argparse_uppercase_type(copy_methods, "flash method"), - help="Select binary copy (flash) method. Default is Python's shutil.copy() method. %s" % copy_methods_str) + help="Select binary copy (flash) method. Default is Python's shutil.copy() method. %s"% copy_methods_str) reset_methods = host_tests_plugins.get_plugin_caps('ResetMethod') reset_methods_str = "Plugin support: " + ', '.join(reset_methods) @@ -1893,7 +1877,7 @@ def get_default_test_options_parser(): dest='mut_reset_type', default=None, type=argparse_uppercase_type(reset_methods, "reset method"), - help='Extra reset method used to reset MUT by host test script. %s' % reset_methods_str) + help='Extra reset method used to reset MUT by host test script. %s'% reset_methods_str) parser.add_argument('-g', '--goanna-for-tests', dest='goanna_for_tests', @@ -2057,20 +2041,18 @@ def get_default_test_options_parser(): help="Depth level for static memory report") return parser - def test_path_to_name(path, base): """Change all slashes in a path into hyphens This creates a unique cross-platform test name based on the path This can eventually be overriden by a to-be-determined meta-data mechanism""" name_parts = [] - head, tail = os.path.split(relpath(path, base)) + head, tail = os.path.split(relpath(path,base)) while (tail and tail != "."): name_parts.insert(0, tail) head, tail = os.path.split(head) return "-".join(name_parts).lower() - def get_test_config(config_name, target_name): """Finds the path to a test configuration file config_name: path to a custom configuration file OR mbed OS interface "ethernet, wifi_odin, etc" @@ -2169,7 +2151,6 @@ def print_tests(tests, format="list", sort=True): print("Unknown format '%s'" % format) sys.exit(1) - def norm_relative_path(path, start): """This function will create a normalized, relative path. It mimics the python os.path.relpath function, but also normalizes a Windows-syle path @@ -2326,16 +2307,16 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name, # Set the overall result to a failure if a build failure occurred if ('reason' in worker_result and - not worker_result['reason'] and - not isinstance(worker_result['reason'], NotSupportedException)): + not worker_result['reason'] and + not isinstance(worker_result['reason'], NotSupportedException)): result = False break # Adding binary path to test build result if ('result' in worker_result and - worker_result['result'] and - 'bin_file' in worker_result): + worker_result['result'] and + 'bin_file' in worker_result): bin_file = norm_relative_path(worker_result['bin_file'], execution_directory) test_key = 'test_apps' if 'test_apps-' in worker_result['kwargs']['project_id'] else 'tests' @@ -2382,6 +2363,4 @@ def build_tests(tests, base_source_paths, build_path, target, toolchain_name, def test_spec_from_test_builds(test_builds): return { "builds": test_builds - } - - + } \ No newline at end of file From b35aea255abf194e7175b6f9e820831e0f41f428 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Wed, 15 Aug 2018 08:58:36 +0300 Subject: [PATCH 11/40] Roll back initialization with super because it does not work with icetea for some reason --- TEST_APPS/testcases/example/test_cmdline.py | 2 +- TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py | 2 +- TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py | 2 +- TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TEST_APPS/testcases/example/test_cmdline.py b/TEST_APPS/testcases/example/test_cmdline.py index 8796e13a7b3..d1dbcd7ea0f 100644 --- a/TEST_APPS/testcases/example/test_cmdline.py +++ b/TEST_APPS/testcases/example/test_cmdline.py @@ -19,7 +19,7 @@ class Testcase(Bench): def __init__(self): self.device = None - super(Testcase, self).__init__(self, + Bench.__init__(self, name="test_cmdline", title="Smoke test for command line interface", status="released", diff --git a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py index eb166d4caef..51492862052 100644 --- a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py +++ b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py @@ -25,7 +25,7 @@ def __init__(self, **kwargs): } testcase_args.update(kwargs) - super(MultipleTestcase, self).__init__(self, **testcase_args) + Bench.__init__(self, **testcase_args) def setup(self): self.command("dut1", "ifup") diff --git a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py index cf69b27002b..414823a6e8c 100644 --- a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py +++ b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py @@ -9,7 +9,7 @@ class Testcase(Bench): def __init__(self): - super(Testcase, self).__init__(self, + Bench.__init__(self, name="TCPSERVER_ACCEPT", title = "TCPSERVER_ACCEPT", purpose = "Test that TCPServer::bind(), TCPServer::listen() and TCPServer::accept() works", diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py index 8e142b67e04..c705e69ea61 100644 --- a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -8,7 +8,7 @@ class Testcase(Bench): def __init__(self): - super(Testcase, self).__init__(self, + Bench.__init__(self, name="TCPSOCKET_ECHOTEST_BURST_SHORT", title="TCPSOCKET_ECHOTEST_BURST_SHORT", purpose="Verify that TCPSocket can send burst of packets to echo server and read incoming packets", From e4e6ddc424dba13875992d902cce36e1d9899392 Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Wed, 15 Aug 2018 10:13:08 +0300 Subject: [PATCH 12/40] Fix doxygen from ns_cmdline.h --- .../frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h b/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h index 4741edc456f..37c006a749d 100644 --- a/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h +++ b/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h @@ -54,7 +54,7 @@ * cmd_exe( "dymmy;long" ); * } * \endcode - * \startuml{cli_usecase.png} + * \startuml {cli_usecase.png} actor user participant app participant cli From 664d98a2b8c056f6472caccc8a8cd1a6548b14fa Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Wed, 15 Aug 2018 10:27:47 +0300 Subject: [PATCH 13/40] Remove startuml doxygen-command --- .../mbed-client-cli/ns_cmdline.h | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h b/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h index 37c006a749d..09a0e68606e 100644 --- a/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h +++ b/features/frameworks/mbed-client-cli/mbed-client-cli/ns_cmdline.h @@ -54,50 +54,6 @@ * cmd_exe( "dymmy;long" ); * } * \endcode - * \startuml {cli_usecase.png} - actor user - participant app - participant cli - participant mycmd - - == Initialization == - * app -> cli: cmd_init( &default_cmd_response_out ) - note right : This initialize command line library - * mycmd -> cli: cmd_add( mycmd_command, "mycmd", NULL, NULL) - note right : All commands have to be register \nto cmdline library with cmd_add() function - - == command input characters== - * app -> cli: cmd_char_input("d") - * app -> cli: cmd_char_input("u") - * app -> cli: cmd_char_input("m") - * app -> cli: cmd_char_input("m") - * app -> cli: cmd_char_input("y") - * app -> cli: cmd_char_input("\\n") - note left : User write command to \nbe execute and press ENTER when \ncommand with all parameters are ready.\nCharacters can be come from serial port for example. - == command execution== - * mycmd <- cli: mycmd_command(argc, argv) - * mycmd -> cli: cmd_printf("hello world!\\n") - note right : cmd_printf() should \nbe used when command prints \nsomething to the console - * cli -> user: "hello world!\\n" - * mycmd -> cli: <> - == finish command and goto forward == - * app <- cli: cmd_ready_cb() - * app -> cli: cmd_next() - note left : this way application can \ndecide when/how to go forward.\nWhen using event-loop, \nyou probably want create tasklet where \ncommands are actually executed.\nif there are some commands in queue cmd_next()\nstart directly next command execution.\n - == command execution== - * app -> cli: cmd_exe("long\\n") - note left : input string can be \ngive also with cmd_exe() -function - * mycmd <- cli: long_command(argc, argv) - * mycmd -> cli: <> = CMDLINE_RETCODE_EXECUTING_CONTINUE - note right : When command continue in background, it should return\nCMDLINE_RETCODE_EXECUTING_CONTINUE.\nCommand interpreter not continue next command \nas long as cmd_ready() -function is not called. - ... Some ~~long delay~~ ... - * mycmd -> cli: cmd_ready( <> ) - note right : When command is finally finished,\nit should call cmd_ready() function. - == finish command and goto forward == - * app <- cli: cmd_ready_cb() - * app -> cli: cmd_next() - ... ... - * \enduml */ #ifndef _CMDLINE_H_ #define _CMDLINE_H_ From 773904000d906b5f81a9958bc3b33548e51abc0a Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Wed, 15 Aug 2018 15:44:03 +0300 Subject: [PATCH 14/40] Update copyright texts --- .../testcases/netsocket/SOCKET_BIND_PORT.py | 17 ++++++++++++++--- .../testcases/netsocket/TCPSERVER_ACCEPT.py | 17 ++++++++++++++--- .../netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py | 17 ++++++++++++++--- .../testcases/netsocket/generate_test_data.py | 17 ++++++++++++++--- TEST_APPS/testcases/netsocket/interface.py | 17 ++++++++++++++--- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py index 51492862052..4c601e770d6 100644 --- a/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py +++ b/TEST_APPS/testcases/netsocket/SOCKET_BIND_PORT.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2017-2018, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" from icetea_lib.bench import Bench from icetea_lib.tools import test_case diff --git a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py index 414823a6e8c..ac46657b8d1 100644 --- a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py +++ b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2017-2018, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" from icetea_lib.bench import Bench from interface import interfaceUp, interfaceDown import threading diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py index c705e69ea61..f2af3453116 100644 --- a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2018, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" from icetea_lib.bench import Bench, TestStepFail import random import string diff --git a/TEST_APPS/testcases/netsocket/generate_test_data.py b/TEST_APPS/testcases/netsocket/generate_test_data.py index 3720bd81757..283f7325bf7 100644 --- a/TEST_APPS/testcases/netsocket/generate_test_data.py +++ b/TEST_APPS/testcases/netsocket/generate_test_data.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2017, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" import random, string ''' diff --git a/TEST_APPS/testcases/netsocket/interface.py b/TEST_APPS/testcases/netsocket/interface.py index 492794ea2bc..80a3b3cd286 100644 --- a/TEST_APPS/testcases/netsocket/interface.py +++ b/TEST_APPS/testcases/netsocket/interface.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2016-2018, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" from icetea_lib.TestStepError import TestStepFail ''' This interface script is intended to be a common library to be used in testcase scripts by testers. From 912c4f5c05e4034584291f87f754ca088aa3d469 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 09:43:06 +0300 Subject: [PATCH 15/40] Remove not needed test_suite.json --- test_suite.json | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 test_suite.json diff --git a/test_suite.json b/test_suite.json deleted file mode 100644 index 0447ba6001e..00000000000 --- a/test_suite.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "testcases": [ - { - "config": { - "requirements": { - "duts": { - "*": { - "count": 1, - "application": { - "bin": "/Users/ollpuo01/git/mbed-os-icetea-integration/BUILD/tests/K64F/GCC_ARM/./TEST_APPS/device/exampleapp/exampleapp.bin", - "name": "TEST_APPS-device-exampleapp" - }, - "type": "hardware", - "allowed_platforms": [ - "K64F" - ] - } - }, - "external": { - "apps": [] - } - } - }, - "name": "test_cmdline" - } - ] -} \ No newline at end of file From 54ff956461c831475d760b64f91250e32fd4581b Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 09:50:55 +0300 Subject: [PATCH 16/40] Autoformatting files --- .gitignore | 2 +- .../testcases/netsocket/TCPSERVER_ACCEPT.py | 45 ++++++++++--------- .../TCPSOCKET_ECHOTEST_BURST_SHORT.py | 9 ++-- .../testcases/netsocket/generate_test_data.py | 1 + TEST_APPS/testcases/netsocket/interface.py | 5 ++- 5 files changed, 34 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index 416944046df..9d4ca57a6c6 100644 --- a/.gitignore +++ b/.gitignore @@ -90,4 +90,4 @@ tags features/FEATURE_BLE/targets/TARGET_CORDIO/stack_backup/ .pytest_cache -log \ No newline at end of file +log diff --git a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py index ac46657b8d1..a1711a5669a 100644 --- a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py +++ b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py @@ -12,35 +12,37 @@ See the License for the specific language governing permissions and limitations under the License. """ -from icetea_lib.bench import Bench -from interface import interfaceUp, interfaceDown + import threading import time +from icetea_lib.bench import Bench +from interface import interfaceUp, interfaceDown + class Testcase(Bench): def __init__(self): Bench.__init__(self, name="TCPSERVER_ACCEPT", - title = "TCPSERVER_ACCEPT", - purpose = "Test that TCPServer::bind(), TCPServer::listen() and TCPServer::accept() works", - status = "released", - component= ["mbed-os", "netsocket"], - author = "Juha Ylinen ", + title="TCPSERVER_ACCEPT", + purpose="Test that TCPServer::bind(), TCPServer::listen() and TCPServer::accept() works", + status="released", + component=["mbed-os", "netsocket"], + author="Juha Ylinen ", type="smoke", subtype="socket", requirements={ "duts": { - '*': { #requirements for all nodes - "count":2, - "type": "hardware", - "application": {"name": "TEST_APPS-device-socket_app"} - }, - "1": {"nick": "dut1"}, - "2": {"nick": "dut2"} + '*': { # requirements for all nodes + "count": 2, + "type": "hardware", + "application": {"name": "TEST_APPS-device-socket_app"} + }, + "1": {"nick": "dut1"}, + "2": {"nick": "dut2"} } } - ) + ) def setup(self): interface = interfaceUp(self, ["dut1"]) @@ -50,10 +52,10 @@ def setup(self): def clientThread(self): self.logger.info("Starting") - time.sleep(5) #wait accept from server + time.sleep(5) # wait accept from server self.command("dut2", "socket " + str(self.client_socket_id) + " open") - self.command("dut2", "socket " + str(self.client_socket_id) + " connect " + str(self.server_ip) + " " + str(self.used_port)) - + self.command("dut2", "socket " + str(self.client_socket_id) + " connect " + str(self.server_ip) + " " + str( + self.used_port)) def case(self): self.used_port = 2000 @@ -73,20 +75,20 @@ def case(self): zero = response.timedelta self.client_socket_id = int(response.parsed['socket_id']) - #Create a thread which calls client connect() + # Create a thread which calls client connect() t = threading.Thread(name='clientThread', target=self.clientThread) t.start() wait = 5 response = self.command("dut1", "socket " + str(server_base_socket_id) + " accept " + str(server_socket_id)) - response.verify_response_duration(expected = wait, zero = zero, threshold_percent = 10, break_in_fail = True) + response.verify_response_duration(expected=wait, zero=zero, threshold_percent=10, break_in_fail=True) socket_id = int(response.parsed['socket_id']) t.join() self.command("dut1", "socket " + str(socket_id) + " send hello") response = self.command("dut2", "socket " + str(self.client_socket_id) + " recv 5") - data = response.parsed['data'].replace(":","") + data = response.parsed['data'].replace(":", "") if data != "hello": raise TestStepFail("Received data doesn't match the sent data") @@ -95,7 +97,6 @@ def case(self): self.command("dut1", "socket " + str(server_base_socket_id) + " delete") self.command("dut2", "socket " + str(self.client_socket_id) + " delete") - def teardown(self): interfaceDown(self, ["dut1"]) interfaceDown(self, ["dut2"]) diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py index f2af3453116..d4f2df0bf13 100644 --- a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -12,10 +12,11 @@ See the License for the specific language governing permissions and limitations under the License. """ -from icetea_lib.bench import Bench, TestStepFail import random import string +from icetea_lib.bench import Bench, TestStepFail + class Testcase(Bench): def __init__(self): @@ -31,9 +32,9 @@ def __init__(self): requirements={ "duts": { '*': { # requirements for all nodes - "count": 1, - "type": "hardware", - "application": {"name": "TEST_APPS-device-socket_app"} + "count": 1, + "type": "hardware", + "application": {"name": "TEST_APPS-device-socket_app"} }, "1": {"nick": "dut1"}, } diff --git a/TEST_APPS/testcases/netsocket/generate_test_data.py b/TEST_APPS/testcases/netsocket/generate_test_data.py index 283f7325bf7..38400fe487a 100644 --- a/TEST_APPS/testcases/netsocket/generate_test_data.py +++ b/TEST_APPS/testcases/netsocket/generate_test_data.py @@ -19,6 +19,7 @@ Currently it implements randomUppercaseAsciiString, randomLowercaseAsciiString methods. ''' + def randomUppercaseAsciiString(length): return ''.join(random.choice(string.ascii_uppercase) for i in range(length)) diff --git a/TEST_APPS/testcases/netsocket/interface.py b/TEST_APPS/testcases/netsocket/interface.py index 80a3b3cd286..82cc2acbb2f 100644 --- a/TEST_APPS/testcases/netsocket/interface.py +++ b/TEST_APPS/testcases/netsocket/interface.py @@ -13,15 +13,17 @@ limitations under the License. """ from icetea_lib.TestStepError import TestStepFail + ''' This interface script is intended to be a common library to be used in testcase scripts by testers. It delegates setUp and tearDown functions with different provided network interface types using setUp() and tearDown() methods. ''' + def interfaceUp(tc, duts): interfaces = {} for dut in duts: - interface = {dut:{"ipv4": None, "ipv6": None}} + interface = {dut: {"ipv4": None, "ipv6": None}} resp = tc.command("%s" % dut, "ifup") @@ -35,6 +37,7 @@ def interfaceUp(tc, duts): interfaces.update(interface) return interfaces + def interfaceDown(tc, duts): for dut in duts: tc.command(dut, "ifdown") From 49f842b4f6b2377d6831fd91bfae7bbc3d79c41d Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 09:58:32 +0300 Subject: [PATCH 17/40] Use icetea random string generator --- .../testcases/netsocket/TCPSERVER_ACCEPT.py | 1 + .../TCPSOCKET_ECHOTEST_BURST_SHORT.py | 7 ++--- .../testcases/netsocket/generate_test_data.py | 28 ------------------- TEST_APPS/testcases/netsocket/interface.py | 2 +- 4 files changed, 4 insertions(+), 34 deletions(-) delete mode 100644 TEST_APPS/testcases/netsocket/generate_test_data.py diff --git a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py index a1711a5669a..e975d683596 100644 --- a/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py +++ b/TEST_APPS/testcases/netsocket/TCPSERVER_ACCEPT.py @@ -16,6 +16,7 @@ import threading import time +from icetea_lib.TestStepError import TestStepFail from icetea_lib.bench import Bench from interface import interfaceUp, interfaceDown diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py index d4f2df0bf13..0d7eb932639 100644 --- a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -12,9 +12,9 @@ See the License for the specific language governing permissions and limitations under the License. """ -import random import string +from icetea_lib.Randomize.randomize import Randomize from icetea_lib.bench import Bench, TestStepFail @@ -44,9 +44,6 @@ def __init__(self): def setup(self): self.command("dut1", "ifup") - def randomString(self, length): - return ''.join(random.choice(string.ascii_uppercase) for i in range(length)) - def case(self): response = self.command("dut1", "socket new TCPSocket") socket_id = int(response.parsed['socket_id']) @@ -57,7 +54,7 @@ def case(self): for i in range(2): sentData = "" for size in (100, 200, 300, 120, 500): - packet = self.randomString(size) + packet = Randomize.random_string(max_len=size, min_len=size, chars=string.ascii_uppercase) sentData += packet response = self.command("dut1", "socket " + str(socket_id) + " send " + str(packet)) response.verify_trace("TCPSocket::send() returned: " + str(size)) diff --git a/TEST_APPS/testcases/netsocket/generate_test_data.py b/TEST_APPS/testcases/netsocket/generate_test_data.py deleted file mode 100644 index 38400fe487a..00000000000 --- a/TEST_APPS/testcases/netsocket/generate_test_data.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Copyright 2018 ARM Limited -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. -""" -import random, string - -''' -This script is intended to be a common test data generator. -Currently it implements randomUppercaseAsciiString, randomLowercaseAsciiString methods. -''' - - -def randomUppercaseAsciiString(length): - return ''.join(random.choice(string.ascii_uppercase) for i in range(length)) - - -def randomLowercaseAsciiString(length): - return ''.join(random.choice(string.ascii_lowercase) for i in range(length)) diff --git a/TEST_APPS/testcases/netsocket/interface.py b/TEST_APPS/testcases/netsocket/interface.py index 82cc2acbb2f..db81636ec3c 100644 --- a/TEST_APPS/testcases/netsocket/interface.py +++ b/TEST_APPS/testcases/netsocket/interface.py @@ -29,7 +29,7 @@ def interfaceUp(tc, duts): ip = interface[dut]["ip"] = interface[dut]["ipv4"] = resp.parsed["address"]["ipv4"] if not ip: - if resp.parsed["address"]["ipv6"] != []: + if resp.parsed["address"]["ipv6"]: ip = interface[dut]["ip"] = interface[dut]["ipv6"] = resp.parsed["address"]["ipv6"][0] if not ip: raise TestStepFail("Failed to parse IP address") From ccf4416c70f9fb5c0d870a9662e7583cd585e544 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 10:04:50 +0300 Subject: [PATCH 18/40] Move test_suite.json ignore in project level --- .gitignore | 3 +++ tools/test/run_icetea/.gitignore | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 tools/test/run_icetea/.gitignore diff --git a/.gitignore b/.gitignore index 9d4ca57a6c6..a00b0a4f139 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,6 @@ features/FEATURE_BLE/targets/TARGET_CORDIO/stack_backup/ .pytest_cache log + +# Icetea related file +test_suite.json diff --git a/tools/test/run_icetea/.gitignore b/tools/test/run_icetea/.gitignore deleted file mode 100644 index 7dfa3a841fb..00000000000 --- a/tools/test/run_icetea/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test_suite.json \ No newline at end of file From c38769b4996af14fc6bf4f0b47b27c4bdf638b2e Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Mon, 20 Aug 2018 10:38:44 +0300 Subject: [PATCH 19/40] review corrections --- TEST_APPS/device/exampleapp/main.cpp | 16 +++++++++------- TEST_APPS/device/socket_app/cmd_ifconfig.cpp | 16 ++++++++++++++++ TEST_APPS/device/socket_app/cmd_ifconfig.h | 15 +++++++++++++++ TEST_APPS/device/socket_app/cmd_socket.cpp | 15 +++++++++++++++ TEST_APPS/device/socket_app/cmd_socket.h | 15 +++++++++++++++ TEST_APPS/device/socket_app/main.cpp | 8 +++++--- TEST_APPS/device/socket_app/strconv.c | 15 +++++++++++++-- TEST_APPS/device/socket_app/strconv.h | 15 +++++++++++++-- 8 files changed, 101 insertions(+), 14 deletions(-) diff --git a/TEST_APPS/device/exampleapp/main.cpp b/TEST_APPS/device/exampleapp/main.cpp index c79b1a065fb..36d1aeb6850 100644 --- a/TEST_APPS/device/exampleapp/main.cpp +++ b/TEST_APPS/device/exampleapp/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 ARM Limited. All rights reserved. + * Copyright (c) 2018 ARM Limited. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. @@ -28,28 +28,30 @@ #define mbed_console_concat(x) mbed_console_concat_(x) #define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL) +#define SERIAL_CONSOLE_BAUD_RATE 115200 + void cmd_ready_cb(int retcode) { cmd_next(retcode); } void wrap_printf(const char *f, va_list a) { - vprintf(f, a); + vprintf(f, a); } int main() { - cmd_init(&wrap_printf); + cmd_init(&wrap_printf); - int c; - while((c = getchar()) != EOF) { + int c; + while((c = getchar()) != EOF) { cmd_char_input(c); } - return 0; + return 0; } FileHandle* mbed::mbed_override_console(int) { - static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, 115200); + static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp index d94a2580189..955756bad14 100644 --- a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 "NetworkStack.h" #include "NetworkInterface.h" @@ -41,6 +56,7 @@ static bool is_ipv4(const char *str) static void ifconfig_print() { if(!net) + cmd_printf("No interface configured\r\n"); return; const char *str = net->get_ip_address(); diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.h b/TEST_APPS/device/socket_app/cmd_ifconfig.h index bb93c05e0a2..54c6124f806 100644 --- a/TEST_APPS/device/socket_app/cmd_ifconfig.h +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.h @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 CMD_IFCONFIG_H #define CMD_IFCONFIG_H diff --git a/TEST_APPS/device/socket_app/cmd_socket.cpp b/TEST_APPS/device/socket_app/cmd_socket.cpp index 59bbc9fb6e5..739ca898562 100644 --- a/TEST_APPS/device/socket_app/cmd_socket.cpp +++ b/TEST_APPS/device/socket_app/cmd_socket.cpp @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 "mbed.h" #include "NetworkStack.h" #include "UDPSocket.h" diff --git a/TEST_APPS/device/socket_app/cmd_socket.h b/TEST_APPS/device/socket_app/cmd_socket.h index f63695f59a4..9e2239c790f 100644 --- a/TEST_APPS/device/socket_app/cmd_socket.h +++ b/TEST_APPS/device/socket_app/cmd_socket.h @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 CMD_SOCKET_H_ #define CMD_SOCKET_H_ diff --git a/TEST_APPS/device/socket_app/main.cpp b/TEST_APPS/device/socket_app/main.cpp index 5bb53c57fdf..e64eb295c28 100644 --- a/TEST_APPS/device/socket_app/main.cpp +++ b/TEST_APPS/device/socket_app/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2016 ARM Limited. All rights reserved. + * Copyright (c) 2018 ARM Limited. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. @@ -30,13 +30,15 @@ #define mbed_console_concat(x) mbed_console_concat_(x) #define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL) +#define SERIAL_CONSOLE_BAUD_RATE 115200 + void cmd_ready_cb(int retcode) { cmd_next(retcode); } void wrap_printf(const char *f, va_list a) { - vprintf(f, a); + vprintf(f, a); } int main() @@ -53,7 +55,7 @@ int main() } FileHandle* mbed::mbed_override_console(int) { - static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, 115200); + static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); #elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS diff --git a/TEST_APPS/device/socket_app/strconv.c b/TEST_APPS/device/socket_app/strconv.c index d1db5ae3bca..38dc207880b 100644 --- a/TEST_APPS/device/socket_app/strconv.c +++ b/TEST_APPS/device/socket_app/strconv.c @@ -1,7 +1,18 @@ /* - * Copyright (c) 2016 ARM Limited. All rights reserved. + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 #include #include diff --git a/TEST_APPS/device/socket_app/strconv.h b/TEST_APPS/device/socket_app/strconv.h index 9e70bc280a1..4d0511c086f 100644 --- a/TEST_APPS/device/socket_app/strconv.h +++ b/TEST_APPS/device/socket_app/strconv.h @@ -1,7 +1,18 @@ /* - * Copyright (c) 2016 ARM Limited. All rights reserved. + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * 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 STRCONVERSION_H #define STRCONVERSION_H From d2c736ba58fee7f32e4e2262a6aa4f7d91e9ce64 Mon Sep 17 00:00:00 2001 From: Juhani Puurula Date: Mon, 13 Aug 2018 11:24:39 +0300 Subject: [PATCH 20/40] Added nanostack mac tester and testcases --- .../device/nanostack_mac_tester/README.md | 149 ++ .../device/nanostack_mac_tester/main.cpp | 160 ++ .../device/nanostack_mac_tester/mbed_app.json | 19 + .../source/mac_commands.cpp | 1642 +++++++++++++++++ .../source/mac_commands.h | 65 + .../nanostack_mac_tester/source/util.cpp | 90 + .../device/nanostack_mac_tester/source/util.h | 30 + .../testcases/nanostack_mac_tester/ED_scan.py | 110 ++ .../nanostack_mac_tester/__init__.py | 1 + .../address_read_and_write.py | 68 + .../create_and_join_PAN.py | 89 + .../nanostack_mac_tester/send_data.py | 69 + .../send_data_indirect.py | 99 + .../send_large_payloads.py | 82 + .../testcases/nanostack_mac_tester/template | 65 + 15 files changed, 2738 insertions(+) create mode 100644 TEST_APPS/device/nanostack_mac_tester/README.md create mode 100644 TEST_APPS/device/nanostack_mac_tester/main.cpp create mode 100644 TEST_APPS/device/nanostack_mac_tester/mbed_app.json create mode 100644 TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp create mode 100644 TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h create mode 100644 TEST_APPS/device/nanostack_mac_tester/source/util.cpp create mode 100644 TEST_APPS/device/nanostack_mac_tester/source/util.h create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/__init__.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/send_data.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py create mode 100644 TEST_APPS/testcases/nanostack_mac_tester/template diff --git a/TEST_APPS/device/nanostack_mac_tester/README.md b/TEST_APPS/device/nanostack_mac_tester/README.md new file mode 100644 index 00000000000..92b21c2d85f --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/README.md @@ -0,0 +1,149 @@ +# Nanostack MAC test application + +You can use this application to test the Nanostack RF driver implementations that follow the [Nanostack RF driver porting instructions](https://os.mbed.com/docs/v5.6/reference/contributing-connectivity.html#porting-new-rf-driver-for-6lowpan-stack). The application has a command-line interface that is used to send commands to Nanostack's MAC layer, for example starting PANs, scanning or sending data. The provided tests do not test performance or stability and only indirectly test RF functionalities. + +## Table of contents + +* [Prerequisites](#prerequisites) +* [Setting up the application](#setting-up-the-application) +* [Application usage](#application-usage) + * [Interactive mode](#interactive-mode) + * [Automated mode](#automated-mode) + * [Testcases](#testcases) + * [Considerations](#considerations) + +## Prerequisites + +* [Mbed CLI](https://github.com/ARMmbed/mbed-cli). +* Three supported boards with radio shields. For example, FRDM-K64F with FRDM-CR20A radio shield. + * https://os.mbed.com/platforms/FRDM-K64F/ + * https://os.mbed.com/components/FRDM-CR20A/ + +## Setting up the application + +### Import the application + +To import the application: + +``` +mbed import https://github.com/ARMmbed/nanostack-mac-tester.git +cd nanostack-mac-tester +``` +### Add your RF driver + +To add your RF driver: + +``` +mbed add https://www.github.com/me/my-rf-driver.git +``` + +Set `radio-type` in `mbed_app.json` to `OTHER`: + +``` +"config":{ + "radio-type":{ + "value": "ATMEL" --> "value": "OTHER" + } +} +``` + +Replace the placeholders in `main.cpp` with your RF driver header file and interface definition: + +``` +#elif MBED_CONF_APP_RADIO_TYPE == OTHER +#include "YOUR_DRIVER_HEADER.h" +YourDriverInterface rf_phy(...); +#endif +``` + +### Compile your binary + +Check that your choice of platform is supported with your choice of toolchain: + +``` +mbed compile --supported +``` + +**Note:** Targets are often abbreviated from the full model names. In the case of `FRDM-K64F` the target is `K64F`. + +#### Set your target + +To set your target HW: + +``` +mbed target TARGET +``` + +#### Set your toolchain + +To set the toolchain: + +``` +mbed toolchain TOOLCHAIN +``` + +Currently, the supported toolchains are `GCC_ARM`, `ARM` and `IAR`. + +#### Compile + +To compile the application: + +``` +mbed compile +``` + +Alternately, you can put everything in a single line: + +``` +mbed compile -m TARGET -t TOOLCHAIN +``` + +## Application usage + +You can use this application in interactive or automated mode. + +### Interactive mode + +To set the application to interactive mode: + +1. Connect your board and copy the compiled application binary from the `BUILD/TARGET/TOOLCHAIN` folder to the board. +2. Open a serial connection with a program such as PuTTY, screen or minicom. The default baudrate is 115200. +3. Press the reset button on the board. The Arm Mbed logo and trace appears in the terminal window. + +If the driver registration and SW MAC creation was successful, the application is ready to receive commands. + +To start off, type `help` to list all commands available and furthermore `help ` to print detailed information about a specific command. + +### Automated mode + +To use the automated testcases, you first need to install the [Icetea test framework](https://github.com/ARMmbed/icetea). + +After the framework is installed, you can run the test set in the root directory of this repository using the command `icetea`. +The following arguments can be used: +* `--tcdir testcases/` sets the target directory to search for testcases +* `--list` lists all testcases found in path, then exits +* `--tc all` specifies that all testcases should be run + `--tc test_name` or `--tc test_name1,test_name2` specify which testcases should be run +* `--repeat N` number of iterations on given set of testcases +* `--bin ` the path to the binary to flash to the devices. Optional once the devices have been flashed once. +* `--reset` reset the devices before the next testcase(highly recommended) + +Many of the provided testcases have a `self.channel` variable in the `setUp()` function for setting the RF channel. The default channel is 11. If you wish to run a test on another channel, you will need to change it manually. + +Some testcases also use a secondary channel for transmitting and will use the next higher channel for that purpose. If the given channel is 26, the secondary channel will default to 25. + +### Test cases + +The automated test set runs the following testcases included in the repository under `testcases/`. +* [Create and join PAN](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/create_and_join_PAN.py) +* [Direct data transmission](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/send_data.py) +* [Indirect data transmission](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/send_data_indirect.py) +* [ED scan](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/ED_scan.py) +* [Address read and write](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/address_write.py) +* [Large data transmission](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/send_large_payloads.py) + +### Considerations + +* Devices need to be power cycled if they are unresponsive to test framework commands even after resetting. +* Devices can be enclosed in an RF isolation box to improve the consistency of test results. +* Some boards and radio modules come with a PCB trace antenna, instead of an SMD antenna, and need to be spaced further apart to decrease interference. \ No newline at end of file diff --git a/TEST_APPS/device/nanostack_mac_tester/main.cpp b/TEST_APPS/device/nanostack_mac_tester/main.cpp new file mode 100644 index 00000000000..2fd3be6a82c --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/main.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "mbed.h" +#include "rtos.h" +#include "sw_mac.h" +#include "ns_hal_init.h" +#define MBED_CMDLINE_MAX_LINE_LENGTH 250 +#include "ns_cmdline.h" + +#include "mac_commands.h" + +#define HEAP_FOR_MAC_TESTER_SIZE 10000 +#define RX_BUFFER_SIZE 512 + +#define ATMEL 1 +#define MCR20 2 +#define OTHER 3 + +#define TRACE_GROUP "Main" +#include "mbed-trace/mbed_trace.h" + +#if MBED_CONF_APP_RADIO_TYPE == ATMEL +#include "NanostackRfPhyAtmel.h" +NanostackRfPhyAtmel rf_phy(ATMEL_SPI_MOSI, ATMEL_SPI_MISO, ATMEL_SPI_SCLK, ATMEL_SPI_CS, + ATMEL_SPI_RST, ATMEL_SPI_SLP, ATMEL_SPI_IRQ, ATMEL_I2C_SDA, ATMEL_I2C_SCL); +#elif MBED_CONF_APP_RADIO_TYPE == MCR20 +#include "NanostackRfPhyMcr20a.h" +NanostackRfPhyMcr20a rf_phy(MCR20A_SPI_MOSI, MCR20A_SPI_MISO, MCR20A_SPI_SCLK, MCR20A_SPI_CS, MCR20A_SPI_RST, MCR20A_SPI_IRQ); +#elif MBED_CONF_APP_RADIO_TYPE == OTHER +#include "YOUR_DRIVER_HEADER.h" +YourDriverInterface rf_phy(...); +#endif //MBED_CONF_APP_RADIO_TYPE + +extern mac_api_s *mac_interface; +RawSerial pc(USBTX, USBRX); +osThreadId main_thread; +static CircularBuffer rx_buffer; +static uint8_t ns_heap[HEAP_FOR_MAC_TESTER_SIZE]; + +static void app_heap_error_handler(heap_fail_t event) +{ + tr_error("Heap error (%d), app_heap_error_handler()", event); + switch (event) { + case NS_DYN_MEM_NULL_FREE: + case NS_DYN_MEM_DOUBLE_FREE: + case NS_DYN_MEM_ALLOCATE_SIZE_NOT_VALID: + case NS_DYN_MEM_POINTER_NOT_VALID: + case NS_DYN_MEM_HEAP_SECTOR_CORRUPTED: + case NS_DYN_MEM_HEAP_SECTOR_UNITIALIZED: + break; + default: + break; + } + while (1); +} + +static void rx_interrupt(void) +{ + uint8_t c = pc.getc(); + rx_buffer.push(c); + if (main_thread != NULL) { + osSignalSet(main_thread, 1); + } +} + +static void handle_rx_data(void) +{ + bool exit = false; + uint8_t data; + + while (1) { + exit = !rx_buffer.pop(data); + if (exit) { + break; + } + cmd_char_input(data); + } +} + + +static int mac_prepare(void) +{ + int8_t rf_driver_id = rf_phy.rf_register(); + uint8_t rf_eui64[8]; + + if (rf_driver_id < 0) { + tr_error("Failed to register RF driver."); + return -1; + } + rf_phy.get_mac_address(rf_eui64); + mac_description_storage_size_t mac_description; + mac_description.device_decription_table_size = DEVICE_DESCRIPTOR_TABLE_SIZE; /** MAC Device description list size */ + mac_description.key_description_table_size = KEY_DESCRIPTOR_TABLE_SIZE; /** MAC Key description list size */ + mac_description.key_lookup_size = LOOKUP_DESCRIPTOR_TABLE_SIZE; /** Key description key lookup list size */ + mac_description.key_usage_size = USAGE_DESCRIPTOR_TABLE_SIZE; /** Key description key usage list size */ + tr_info("Registered RF driver with id: %hhu, EUI64: %s", rf_driver_id, mbed_trace_array(rf_eui64, 8)); + mac_interface = ns_sw_mac_create(rf_driver_id, &mac_description); + if (!mac_interface) { + tr_error("Failed to create SW MAC."); + return -2; + } + + return mac_interface->mac_initialize(mac_interface, mac_data_confirm_handler, + mac_data_indication_handler, mac_purge_confirm_handler, mac_mlme_confirm_handler, + mac_mlme_indication_handler, rf_driver_id); +} + +static void cmd_ready_cb(int retcode) +{ + cmd_next(retcode); +} + +static void trace_printer(const char *str) +{ + printf("%s\n", str); + cmd_output(); + fflush(stdout); +} + +int main(void) +{ + main_thread = osThreadGetId(); + pc.baud(MBED_CONF_PLATFORM_STDIO_BAUD_RATE); + pc.attach(rx_interrupt); + ns_hal_init(ns_heap, HEAP_FOR_MAC_TESTER_SIZE, app_heap_error_handler, NULL); + mbed_trace_init(); + mbed_trace_print_function_set(trace_printer); + cmd_init(&default_cmd_response_out); + cmd_set_ready_cb(cmd_ready_cb); + mac_commands_init(); + + if (mac_prepare() != 0) { + return -1; + } + tr_info("Created driver & SW MAC"); + + while (true) { + osEvent os_event = Thread::signal_wait(1); + if (os_event.status != osEventSignal) { + osThreadYield(); + } else { + handle_rx_data(); + } + } + return 0; +} diff --git a/TEST_APPS/device/nanostack_mac_tester/mbed_app.json b/TEST_APPS/device/nanostack_mac_tester/mbed_app.json new file mode 100644 index 00000000000..9633f4b04f7 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/mbed_app.json @@ -0,0 +1,19 @@ +{ + "config": { + "radio-type":{ + "help": "options are ATMEL, MCR20, OTHER", + "value": "ATMEL" + } + }, + "macros": ["YOTTA_CFG_MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], + "target_overrides": { + "*": { + "target.features_add": ["NANOSTACK", "LOWPAN_HOST", "COMMON_PAL"], + "platform.stdio-convert-newlines": true, + "platform.stdio-baud-rate": 115200, + "mbed-mesh-api.heap-size": 6000, + "nanostack-hal.event_loop_thread_stack_size": 2000, + "mbed-trace.enable": true + } + } +} diff --git a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp new file mode 100644 index 00000000000..40066d74f51 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp @@ -0,0 +1,1642 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "mac_commands.h" + +//Need to disable filtering until mac_filter_api.h in mbed-os is updated +#define DISABLE_FILTERING + +#define TRACE_GROUP "MAC" + +#define MAN_MAC_START "start --- Starts a PAN\n"\ + " --pan_id PAN id in hex, default: 0x1234\n"\ + " --logical_channel Operated logical channel, default: 11 (0-26)\n"\ + " --channel_page Operated channel page, default: 0 (0-2)\n"\ + " --start_time Time at which to start sending beacons\n"\ + " default: 0\n"\ + " --beacon_order How often are beacons transmitted\n"\ + " default: 15 (0-15, 15 = never)\n"\ + " --super_frame_order Length of the superframe's active portion\n"\ + " if beacon_order is 15, this option is ignored\n"\ + " default: 15 (0-15)\n"\ + " --pan_coordinator Is this device the PAN coordinator?\n"\ + " default: true\n"\ + " --battery_life_extension Disable beaconing device periodically\n"\ + " to save power? default: false\n"\ + " --coord_realignment If true, coordinator realignment command\n"\ + " is sent prior to changing\n"\ + " the superframe configuration default: false\n" + +#define MAN_MAC_SCAN "scan --- Perform a scan\n"\ + " --scan_type What type of scan to perform, 0=ED, 1=active\n"\ + " 2=passive, 3=orphan, default: 1\n"\ + " --channel_page_enum default: 0 (0-10)\n"\ + " --channel_mask Bitmap of the channels on which to perform\n"\ + " the scan, lower 27-bits are used\n"\ + " bit 0 corresponds to channel 0 and so forth\n"\ + " default: 0x07FFF800 (channels 11-26)\n"\ + " --scan_duration How long to spend in each channel,\n"\ + " aBaseSuperFrameDuration * (2^n + 1) symbols\n"\ + " default: 5 (0-14)\n"\ + " --channel_page The channel page on which to perform the scan\n"\ + " default: 0 (0-31)\n"\ + MAN_MAC_SECURITY + +#define MAN_MAC_DATA "data --- Send arbitrary data\n"\ + " --src_addr_mode Source addressing mode, 0=no address, 1=reserved\n"\ + " 2=16-bit address, 3=64-bit address\n"\ + " default: 3 (0-3)\n"\ + " --dst_addr_mode Destination addressing mode, same as above\n"\ + " default: 3 (0-3)\n"\ + " --dst_pan_id Destination PAN id in hex\n"\ + " default: 0x1234 (0x0-0xFFFF)\n"\ + " --dst_addr Destination address, default: 00:00:00:...\n"\ + " --msdu_length Length of the data to send, default: 0 (0-255)\n"\ + " --msdu Data to transmit, default: \n"\ + " --msdu_handle Handle of this MSDU, default: 0 (0-255)\n"\ + " --tx_ack_req Is ack required for this transmission?\n"\ + " default: true\n"\ + " --indirect_tx Transmit indirectly? default: false\n"\ + " --pending_bit Specifies whether more fragments (higher layer)\n"\ + " are to be sent, default: false\n"\ + " --wait_for_confirm Should we block further commands until we have\n"\ + " received a data confirmation, default: true\n"\ + MAN_MAC_SECURITY + +#define MAN_MAC_POLL "poll --- Poll the coordinator for data\n"\ + " --coord_addr_mode Coordinator addressing mode, 2=16-bit address\n"\ + " 3=64-bit address, default: 3 (2-3)\n"\ + " --coord_pan_id Coordinator PAN id in hex\n"\ + " default: 0x1234 (0x0-0xFFFF)\n"\ + " --coord_address Coordinator address, default 00:00:00:...\n"\ + MAN_MAC_SECURITY + +#define MAN_MAC_PURGE "purge --- Remove a transmission from the queue\n"\ + " --msdu_handle Handle of the MSDU to be removed\n"\ + " default: 0 (0-255)\n"\ + +#define MAN_MAC_SET "mlme-set --- Set a specified PIB attribute\n"\ + " --attr ID of the attribute to set in hex (0x0-0xFF)\n"\ + " --attr_index <0-255> Index of the attribute, default: 0 (0-255)\n"\ + " --value_ascii Specify the set value as an ASCII string\n"\ + " --value_bytes Value as a string of bytes\n"\ + " --value_uint8/16/32 Value as a uint8, uint16, or uint32\n"\ + " Max value for uint32 is the max value for int32\n"\ + " --value_size Size of the value in bytes (0-255)\n" + +#define MAN_MAC_GET "mlme-get --- Get a specified PIB attribute\n"\ + " --attr ID of the attribute we want to get in hex (0x0-0xFF)\n"\ + " --attr_index Index of the attribute, default: 0 (0-255)\n" + +#define MAN_MAC_RESET "mlme-reset --- Reset the MAC sublayer\n"\ + " --set_default_pib When set to true, PIB attributes are set to\n"\ + " their default values\n"\ + " If set to false, PIB attributes retain\n"\ + " their values, default: true\n" + +#define MAN_MAC_ADDR "addr --- Configure 16/64-bit MAC addresses\n"\ + " having no options will display the addresses\n"\ + " --16-bit 16-bit MAC address in hex (0x0-0xFFFF)\n"\ + " --64-bit 64-bit MAC address\n" + +#define MAN_MAC_SECURITY " --security_level 0=no security, 1=MIC32, 2=MIC64, 3=MIC128,\n"\ + " 4=ENC, 5=ENC+MIC32, 6=ENC+MIC64, 7=ENC+MIC128\n"\ + " default: 0\n"\ + " --key_id_mode 0=implicit, 1=default key source\n"\ + " 2=2-byte key source\n"\ + " 3=8-byte key source, default: 0 (0-3)\n"\ + " --key_index Which key to use, default: 0 (0-255)\n"\ + " --key_source The originator of the key to be used\n" + +#define MAN_MAC_KEY "key --- Configure or add key descriptors\n"\ + "config --- Configure parameters for key descriptor\n"\ + " --key Actual security key, 16 bytes\n"\ + " default: C0:C1:C2:...:CF\n"\ + " --key_id_lookup_list_entries Amount of entries in the key's lookup\n"\ + " --key_device_list_entries ...device...\n"\ + " --key_usage_list_entries and usage list, default: 2 (0-255)\n"\ + " --lookup_index Which entry of the lookup list\n"\ + " are we accessing? default: 0 (0-255)\n"\ + " --lookup_data The lookup data for this key\n"\ + " length is 9 bytes regardless of\n"\ + " the next option\n"\ + " --lookup_data_size How large is the lookup data? (0-1)\n"\ + " 0=5 bytes, 1=9 bytes\n"\ + " --device_list_index Which entry of the device list\n"\ + " are we accessing, default: 0 (0-255)\n"\ + " --device_descriptor_handle Which entry in our neighbour table\n"\ + " do we want to use this key with\n"\ + " default: 0 (0-255)\n"\ + " --unique_device Is the device unique to the key?\n"\ + " --blacklisted Is the device blacklisted?\n"\ + " --usage_list_index Which entry of the usage list\n"\ + " are we accessing, default: 0 (0-255)\n"\ + " --frame_type What type of frame do we want to\n"\ + " use this key with? (0-3)\n"\ + " 0=beacon, 1=data, 2=ACK, 3=command\n"\ + " --command_frame_identifier Type of the command frame (1-9)\n"\ + " 1=association request\n"\ + " 2=association response\n"\ + " 3=disassociation notification\n"\ + " 4=data request\n"\ + " 5=PAN id conflict notification\n"\ + " 6=orphan notification\n"\ + " 7=beacon request\n"\ + " 8=coordinator realigment\n"\ + " 9=GTS request\n"\ + "add --- Add the key into the key descriptor table\n"\ + " --index Index in the key table (0-255)\n" + +#define MAN_MAC_ADD_NEIGHBOUR "add-neigh --- Add an entry to the neighbour table\n"\ + " --frame_ctr Frame counter for this neighbour\n"\ + " --mac16 16-bit MAC address in hex (0x0-0xFFFF)\n"\ + " --mac64 64-bit MAC address\n"\ + " --pan_id PAN id (0x0-0xFFFF)\n"\ + " --index Index in the neighbour table (0-255)\n" + +#define MAN_MAC_FILTER "filter --- Configure MAC layer filtering\n"\ + "start --- Start a generic filter in blacklist, whitelist or fixed mode\n"\ + " --mode Set the filtering mode, values: allow|block|fixed\n"\ + " --lqi_m LQI multiplier (fixed mode only)\n"\ + " --dbm_m dBm multiplier (fixed mode only)\n"\ + "add --- Add a filter by manually supplying values, or using a preset mode\n"\ + " --lqi_m LQI multiplier\n"\ + " --lqi_add Value added to the LQI\n"\ + " --dbm_m dBm multiplier\n"\ + " --dbm_add Value added to the dBm\n"\ + " --mode Filtering mode, values: allow|block|fixed\n"\ + " --short 16-bit address in hex to filter (0x0-0xFFFF)\n"\ + " --long 64-bit address as bytes to filter\n"\ + "remove --- Remove a filter\n"\ + " --short 16-bit address to remove from filter (0x0-0xFFFF)\n"\ + " --long 64-bit address to remove from filter\n"\ + "clear --- Clear all filters excluding the default one\n"\ + "stop --- Stop filtering completely\n"\ + +#define MAN_MAC_CONFIG_STATUS "config-status --- Configure expected status codes\n"\ + " having no options will display configured statuses\n"\ + " default: 0 (MLME_SUCCESS) for all\n"\ + " --data_conf MCPS-DATA.confirm (0-255)\n"\ + " --data_ind MCPS-DATA.indication (0-255)\n"\ + " --get MLME-GET.confirm (0-255)\n"\ + " --scan MLME-SCAN.confirm (0-255)\n"\ + " --poll MLME-POLL.confirm (0-255)\n"\ + " --purge MCPS-PURGE.confirm (0-255)\n"\ + " --comm_status MLME-COMM-STATUS.indication (0-255)\n"\ + " --list List all statuses\n"\ + " --reset Reset all statuses to default values\n" + +#define MAN_MAC_FIND_BEACON "find-beacon --- Search for a PAN in the\n"\ + " results of the last scan\n"\ + " --data Beacon data transmitted in the beacon\n" + +#define MAN_MAC_WAIT "wait --- Wait for data sent directly for a\n"\ + " specified amount of milliseconds\n"\ + " --timeout Milliseconds to wait, default: 1000\n" + +#define MAN_MAC_ED_ANALYZE "analyze-ed Channel to analyze (11-26)\n"\ + " --above Check if the ED value is above a given value\n"\ + " --below Check if the ED value is below a given value\n" + +#define MAN_RESET "reset --- Reset MCPS and MLME structures to default values\n" + +#define MAN_SILENT "silent-mode --- When enabled, doesn't print extended information\n"\ + " of MCPS/MLME primitives\n" + +mac_api_s *mac_interface = NULL; +static bool wait_for_confirm = true; +static bool silent_mode = false; +static volatile unsigned int data_count = 0; + +static mlme_start_t start_req = { + 0x1234, /*PANId*/ + 11, /*LogicalChannel*/ + 0, /*ChannelPage*/ + 0, /*StartTime*/ + 15, /*BeaconOrder*/ + 15, /*SuperframeOrder*/ + true, /*PANCoordinator*/ + false, /*BatteryLifeExtension*/ + false, /*CoordRealignment*/ +}; + +static mlme_scan_t scan_req = { + MAC_ACTIVE_SCAN, /*ScanType*/ + { + CHANNEL_PAGE_0, /*channel_page (enum)*/ + 0x07FFF800 /*channel_mask*/ + }, + 5, /*ScanDuration*/ + 0, /*ChannelPage*/ + { + 0, /*SecurityLevel*/ + 0, /*KeyIdMode*/ + 0, /*KeyIndex*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /*Keysource*/ + } +}; + +static mcps_data_req_t data_req = { + 3, /*SrcAddrMode*/ + 3, /*DstAddrMode*/ + 0x1234, /*DstPANId*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*DstAddr*/ + 0, /*msduLength*/ + NULL, /*msdu*/ + 0, /*msduHandle*/ + true, /*TxAckReq*/ + false, /*IndirectTx*/ + false, /*PendingBit*/ + { + 0, /*SecurityLevel*/ + 0, /*KeyIdMode*/ + 0, /*KeyIndex*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /*Keysource*/ + } +}; + +static mlme_poll_t poll_req = { + 3, /*CoordAddrMode*/ + 0x1234, /*CoordPANId*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*CoordAddress*/ + { + 0, /*SecurityLevel*/ + 0, /*KeyIdMode*/ + 0, /*KeyIndex*/ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /*Keysource*/ + } +}; + +static mcps_purge_t purge_req = { + 0 /*msduHandle*/ +}; + +static mlme_set_t set_req = { + (mlme_attr_t)0x39, /*attr*/ + 0, /*attr_index*/ + NULL, /*value_pointer*/ + 0 /*value_size*/ +}; + +static mlme_get_t get_req = { + (mlme_attr_t)0x39, /*attr*/ + 0 /*attr_index*/ +}; + +static mlme_reset_t reset_req = { + true /*SetDefaultPIB*/ +}; + +static mlme_key_id_lookup_descriptor_t lookup_descriptors[LOOKUP_DESCRIPTOR_TABLE_SIZE]; +static mlme_key_device_descriptor_t device_descriptors[DEVICE_DESCRIPTOR_TABLE_SIZE]; +static mlme_key_usage_descriptor_t usage_descriptors[USAGE_DESCRIPTOR_TABLE_SIZE]; +static mlme_key_descriptor_entry_t key_descriptor = { + lookup_descriptors, /*KeyIdLookupList*/ + LOOKUP_DESCRIPTOR_TABLE_SIZE, /*KeyIdLookupListEntries*/ + device_descriptors, /*KeyDeviceList*/ + DEVICE_DESCRIPTOR_TABLE_SIZE, /*KeyDeviceListEntries*/ + usage_descriptors, /*KeyUsageList*/ + USAGE_DESCRIPTOR_TABLE_SIZE, /*KeyUsageListEntries*/ + {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF} /*Key*/ +}; + +struct status_list_t { + uint8_t data_conf; + uint8_t get_conf; + uint8_t scan_conf; + uint8_t poll_conf; + uint8_t purge_conf; + uint8_t comm_status_ind; + uint8_t data_ind_len; + uint8_t *data_ind; +}; + +static struct status_list_t expected_statuses = { + MLME_SUCCESS, + MLME_SUCCESS, + MLME_SUCCESS, + MLME_SUCCESS, + MLME_SUCCESS, + MLME_SUCCESS, + 0, + NULL +}; + +struct beacon_list_t { + size_t count; + char *beacons[MLME_MAC_RES_SIZE_MAX]; + uint8_t beacon_lengths[MLME_MAC_RES_SIZE_MAX]; +}; + +static struct beacon_list_t received_beacons = {}; + +struct ed_scan_result_list_t { + uint8_t count; + uint8_t channel[MLME_MAC_RES_SIZE_MAX]; + uint8_t ED_values[MLME_MAC_RES_SIZE_MAX]; +}; + +static struct ed_scan_result_list_t last_ed_results; + +static void print_security(const mlme_security_t *key) +{ + if (key->SecurityLevel > 0) { + cmd_printf("Key.SecurityLevel: %u\n", key->SecurityLevel); + cmd_printf("Key.KeyIdMode: %u\n", key->KeyIdMode); + cmd_printf("Key.KeyIndex: %hhu\n", key->KeyIndex); + cmd_printf("Key.Keysource %s\n", mbed_trace_array(key->Keysource, 8)); + } +} + +static void print_PAN_descriptor(const mlme_pan_descriptor_t *desc) +{ + cmd_printf("PANDescriptor.CoordAddrMode: %u\n", desc->CoordAddrMode); + cmd_printf("PANDescriptor.CoordPANId: 0x%04X\n", desc->CoordPANId); + cmd_printf("PANDescriptor.CoordAddress: %s\n", mbed_trace_array(desc->CoordAddress, 8)); + cmd_printf("PANDescriptor.LogicalChannel: %hhu\n", desc->LogicalChannel); + cmd_printf("PANDescriptor.ChannelPage: %hhu\n", desc->ChannelPage); + cmd_printf("PANDescriptor.SuperframeSpec: %02x:%02x\n", desc->SuperframeSpec[0], desc->SuperframeSpec[1]); + cmd_printf("PANDescriptor.GTSPermit: %s\n", desc->GTSPermit ? "true" : "false"); + cmd_printf("PANDescriptor.LinkQuality: %hhu\n", desc->LinkQuality); + cmd_printf("PANDescriptor.Timestamp: %lu\n", desc->Timestamp); + cmd_printf("PANDescriptor.SecurityFailure: %hhu\n", desc->SecurityFailure); + print_security(&desc->Key); +} + +static int handle_security_args(int argc, char *argv[], mlme_security_t *key) +{ + char *str; + int32_t val; + + if (cmd_parameter_int(argc, argv, "--security_level", &val)) { + if (val >= 0 && val <= 7) { + key->SecurityLevel = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--key_id_mode", &val)) { + if (val >= 0 && val <= 3) { + key->KeyIdMode = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--key_index", &val)) { + if (val >= 0 && val <= 255) { + key->KeyIndex = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--key_source", &str)) { + if (strlen(str) == 2*8+7) { + if (string_to_bytes(str, key->Keysource, 8) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + return CMDLINE_RETCODE_SUCCESS; +} + +static void add_beacon(const uint8_t *beacon, uint8_t len) +{ + if (received_beacons.count >= MLME_MAC_RES_SIZE_MAX) { + tr_warn("List of received beacons is full. Discarding %s <%.*s>", mbed_trace_array(beacon, len), len, beacon); + return; + } + unsigned int cur_beacon = received_beacons.count; + received_beacons.beacon_lengths[cur_beacon] = len; + received_beacons.beacons[cur_beacon] = (char*)ns_dyn_mem_temporary_alloc(len <= 75 ? 75:len); + if (len != 0) { + std::memcpy(received_beacons.beacons[cur_beacon], beacon, len); + } + ++received_beacons.count; +} + +static void clear_beacons(void) +{ + for (unsigned int i = 0; i < received_beacons.count; ++i) { + ns_dyn_mem_free(received_beacons.beacons[i]); + received_beacons.beacons[i] = NULL; + received_beacons.beacon_lengths[i] = 0; + } + received_beacons.count = 0; +} + +void mac_commands_init(void) +{ + cmd_add("start", mac_start_command, "Start a PAN", MAN_MAC_START); + cmd_add("scan", mac_scan_command, "Perform a scan", MAN_MAC_SCAN); + cmd_add("data", mac_data_command, "Send data", MAN_MAC_DATA); + cmd_add("poll", mac_poll_command, "Poll for data", MAN_MAC_POLL); + cmd_add("purge", mac_purge_command, "Purge data from the transmission queue", MAN_MAC_PURGE); + cmd_add("mlme-set", mac_set_command, "Writes a given value to the PIB attribute", MAN_MAC_SET); + cmd_add("mlme-get", mac_get_command, "Gets the value of a given PIB attribute", MAN_MAC_GET); + cmd_add("mlme-reset", mac_reset_command, "Resets the MAC sublayer to default values", MAN_MAC_RESET); + cmd_add("addr", mac_address_command, "Configure MAC addresses", MAN_MAC_ADDR); + cmd_add("key", mac_key_command, "Configure or add security keys", MAN_MAC_KEY); + cmd_add("add-neigh", mac_add_neighbour_command, "Add a device to the neighbour table", MAN_MAC_ADD_NEIGHBOUR); + cmd_add("filter", mac_filter_command, "Add or remove filters based on MAC addresses", MAN_MAC_FILTER); + cmd_add("config-status", mac_config_status_command, "Set expected return statuses for confirmations and indications", MAN_MAC_CONFIG_STATUS); + cmd_add("find-beacon", mac_find_beacon_command, "Find a PAN by beacon data", MAN_MAC_FIND_BEACON); + cmd_add("wait", mac_wait_command, "Wait for data", MAN_MAC_WAIT); + cmd_add("analyze-ed", mac_analyze_ed_command, "Analyze the results of the last ED scan", MAN_MAC_ED_ANALYZE); + cmd_add("reset", reset_command, "Reset MCPS/MLME structures to default values", MAN_RESET); + cmd_add("silent-mode", silent_mode_command, "Stop printing fields of MCPS/MLME structures", MAN_SILENT); +} + +void mac_data_confirm_handler(const mac_api_t *api, const mcps_data_conf_t *data) +{ + cmd_printf("MCPS-DATA.confirm\n"); + if (!silent_mode) { + cmd_printf("msduHandle: %hhu\n", data->msduHandle); + cmd_printf("status: %hhu (%s)\n", data->status, mlme_status_string(data->status)); + cmd_printf("timestamp: %lu\n", data->timestamp); + cmd_printf("cca_retries:%hhu\n", data->cca_retries); + cmd_printf("tx_retries: %hhu\n", data->tx_retries); + } + if (data->status == expected_statuses.data_conf) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + cmd_ready(CMDLINE_RETCODE_FAIL); + } +} + +void mac_data_indication_handler(const mac_api_t *api, const mcps_data_ind_t *data) +{ + cmd_printf("MCPS-DATA.indication\n"); + if (!silent_mode) { + cmd_printf("SrcAddrMode: %u\n", data->SrcAddrMode); + cmd_printf("SrcPANId: 0x%04X\n", data->SrcPANId); + cmd_printf("SrcAddr: %s\n", mbed_trace_array(data->SrcAddr, 8)); + cmd_printf("DstAddrMode: %u\n", data->DstAddrMode); + cmd_printf("DstPANId: 0x%04X\n", data->DstPANId); + cmd_printf("DstAddr: %s\n", mbed_trace_array(data->DstAddr, 8)); + cmd_printf("mpduLinkQuality:%hhu\n", data->mpduLinkQuality); + cmd_printf("signal_dbm: %hhi\n", data->signal_dbm); + cmd_printf("timestamp: %lu\n", data->timestamp); + cmd_printf("DSN: %hhi\n", data->DSN); + print_security(&data->Key); + cmd_printf("msduLength %hu\n", data->msduLength); + cmd_printf("msdu_ptr: %s <%.*s>\n", mbed_trace_array(data->msdu_ptr, data->msduLength), data->msduLength, data->msdu_ptr); + } + if (data->msdu_ptr && expected_statuses.data_ind) { + if (data->msduLength != expected_statuses.data_ind_len) { + return; + } + if (strncmp((const char*)data->msdu_ptr, (const char*)expected_statuses.data_ind, expected_statuses.data_ind_len) == 0) { + ++data_count; + } else { + tr_warn("Received unexpected data!"); + } + } +} + +void mac_purge_confirm_handler(const mac_api_t *api, mcps_purge_conf_t *data) +{ + cmd_printf("MCPS-PURGE.confirm\n"); + if (!silent_mode) { + cmd_printf("msduHandle: %hhu\n", data->msduHandle); + cmd_printf("status: %hhu (%s)\n", data->status, mlme_status_string(data->status)); + } + if (data->status == expected_statuses.purge_conf) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + cmd_ready(CMDLINE_RETCODE_FAIL); + } +} + +void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const void *data) +{ + switch (id) { + case MLME_ASSOCIATE: { + break; + } + case MLME_DISASSOCIATE: { + break; + } + case MLME_GET: { + mlme_get_conf_t *get_data = (mlme_get_conf_t*)data; + cmd_printf("MLME-GET.confirm\n"); + if (!silent_mode) { + cmd_printf("status: %hhu (%s)\n", get_data->status, mlme_status_string(get_data->status)); + cmd_printf("attr: %hhu\n", get_data->attr); + cmd_printf("attr_index: %hhu\n", get_data->attr_index); + cmd_printf("value_pointer: %s\n", mbed_trace_array((uint8_t*)get_data->value_pointer, get_data->value_size)); + cmd_printf("value_size: %hhu\n", get_data->value_size); + } + if (get_data->status == expected_statuses.get_conf) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + cmd_ready(CMDLINE_RETCODE_FAIL); + } + break; + } + case MLME_GTS: { + break; + } + case MLME_RESET: { + break; + } + case MLME_RX_ENABLE: { + break; + } + case MLME_SCAN: { + mlme_scan_conf_t *scan_data = (mlme_scan_conf_t*)data; + cmd_printf("MLME-SCAN.confirm\n"); + if (!silent_mode) { + cmd_printf("status: %hhu (%s)\n", scan_data->status, mlme_status_string(scan_data->status)); + cmd_printf("ScanType: %u\n", scan_data->ScanType); + cmd_printf("ChannelPage: %hhu\n", scan_data->ChannelPage); + cmd_printf("UnscannedChannels: 0x%08lX\n", scan_data->UnscannedChannels.channel_mask[0]); + cmd_printf("ResultListSize: %hhu\n", scan_data->ResultListSize); + } + for (unsigned int i = 0; i < scan_data->ResultListSize; ++i) { + if (scan_data->ScanType == MAC_ED_SCAN_TYPE) { + cmd_printf("Channel %d ED value: %hhu\n", channel_from_mask(scan_req.ScanChannels.channel_mask[0], i), scan_data->ED_values[i]); + memcpy(last_ed_results.ED_values, scan_data->ED_values, scan_data->ResultListSize); + last_ed_results.count = scan_data->ResultListSize; + for (int i = 0; i < scan_data->ResultListSize; ++i) { + last_ed_results.channel[i] = channel_from_mask(scan_req.ScanChannels.channel_mask[0], i); + } + } else if (scan_data->ScanType == MAC_PASSIVE_SCAN) { + print_PAN_descriptor(scan_data->PAN_values[i]); + } + } + if (scan_data->status == expected_statuses.scan_conf || scan_data->status == MLME_LIMIT_REACHED) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + cmd_ready(CMDLINE_RETCODE_FAIL); + } + break; + } + case MLME_SET: { + break; + } + case MLME_START: { + break; + } + case MLME_POLL: { + mlme_poll_conf_t *poll_data = (mlme_poll_conf_t*)data; + cmd_printf("MLME-POLL.confirm\n"); + if (!silent_mode) { + cmd_printf("status: %hhu (%s)\n", poll_data->status, mlme_status_string(poll_data->status)); + cmd_printf("data_count %u\n", data_count); + } + if (expected_statuses.poll_conf == MLME_SUCCESS) { + if (data_count == 1 && poll_data->status == MLME_SUCCESS) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + cmd_ready(CMDLINE_RETCODE_FAIL); + } + } else if (expected_statuses.poll_conf == poll_data->status) { + cmd_ready(CMDLINE_RETCODE_SUCCESS); + } else { + cmd_ready(CMDLINE_RETCODE_FAIL); + } + break; + } + default: { + cmd_ready(CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); + break; + } + } +} + +void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const void *data) +{ + switch (id) { + case MLME_ASSOCIATE: { + break; + } + case MLME_DISASSOCIATE: { + break; + } + case MLME_BEACON_NOTIFY: { + const mlme_beacon_ind_t *beacon_ind = (mlme_beacon_ind_t*)data; + cmd_printf("MLME-BEACON-NOTIFY.indication\n"); + if (!silent_mode) { + cmd_printf("BSN: %hhu\n", beacon_ind->BSN); + print_PAN_descriptor(&beacon_ind->PANDescriptor); + cmd_printf("PendAddrSpec.short_address_count %u\n", beacon_ind->PendAddrSpec.short_address_count); + cmd_printf("PendAddrSpec.extended_address_count %u\n", beacon_ind->PendAddrSpec.extended_address_count); + cmd_printf("AddrList %s\n", mbed_trace_array(beacon_ind->AddrList, beacon_ind->PendAddrSpec.short_address_count * 2 + + beacon_ind->PendAddrSpec.extended_address_count * 8)); + cmd_printf("beacon_data_length %hu\n", beacon_ind->beacon_data_length); + cmd_printf("beacon_data %s\n", mbed_trace_array(beacon_ind->beacon_data, beacon_ind->beacon_data_length)); + } + add_beacon(beacon_ind->beacon_data, beacon_ind->beacon_data_length); + break; + } + case MLME_GTS: { + break; + } + case MLME_ORPHAN: { + break; + } + case MLME_COMM_STATUS: { + cmd_printf("MLME-COMM-STATUS.indication\n"); + const mlme_comm_status_t *comm_status_ind_data = (mlme_comm_status_t*)data; + if (!silent_mode) { + cmd_printf("PANId: 0x%04X\n", comm_status_ind_data->PANId); + cmd_printf("SrcAddrMode: %u\n", comm_status_ind_data->SrcAddrMode); + cmd_printf("SrcAddr: %s\n", mbed_trace_array(comm_status_ind_data->SrcAddr, 8)); + cmd_printf("DstAddrMode: %u\n", comm_status_ind_data->DstAddrMode); + cmd_printf("DstAddr: %s\n", mbed_trace_array(comm_status_ind_data->DstAddr, 8)); + cmd_printf("status: %hhu (%s)\n", comm_status_ind_data->status, mlme_status_string(comm_status_ind_data->status)); + print_security(&comm_status_ind_data->Key); + } + break; + } + case MLME_SYNC_LOSS: { + break; + } + default: + break; + } +} + +int mac_start_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + bool boolean; + + cmd_printf("MLME-START.request\n"); + if (cmd_parameter_val(argc, argv, "--pan_id", &str)) { + uint32_t pan_id = strtoul(str, NULL, 16); + if (pan_id <= 0xFFFF) { + start_req.PANId = pan_id; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--logical_channel", &val)) { + if (val >= 0 && val <= 26) { + start_req.LogicalChannel = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--channel_page", &val)) { + if (val >= 0 && val <= 2) { + start_req.ChannelPage = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--start_time", &val)) { + start_req.StartTime = val; + } + if (cmd_parameter_int(argc, argv, "--beacon_order", &val)) { + if (val >= 0 && val <= 15) { + start_req.BeaconOrder = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--super_frame_order", &val)) { + if (val >= 0 && val <= 15) { + start_req.SuperframeOrder = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_bool(argc, argv, "--pan_coordinator", &boolean)) { + start_req.PANCoordinator = boolean; + } + if (cmd_parameter_bool(argc, argv, "--battery_life_extension", &boolean)) { + start_req.BatteryLifeExtension = boolean; + } + if (cmd_parameter_bool(argc, argv, "--coord_realignment", &boolean)) { + start_req.CoordRealignment = boolean; + } + mac_interface->mlme_req(mac_interface, MLME_START, &start_req); + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_scan_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + + cmd_printf("MLME-SCAN.request\n"); + if (cmd_parameter_int(argc, argv, "--scan_type", &val)) { + if (val >= 0 && val <= 3) { + scan_req.ScanType = (mac_scan_type_t)val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--channel_page_enum", &val)) { + if (val >= 0 && val <= 10) { + scan_req.ScanChannels.channel_page = (channel_page_e)val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--channel_mask", &str)) { + scan_req.ScanChannels.channel_mask[0] = strtoul(str, NULL, 16); + } + if (cmd_parameter_int(argc, argv, "--scan_duration", &val)) { + if (val >= 0 && val <= 14) { + scan_req.ScanDuration = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--channel_page", &val)) { + if (val >= 0 && val <= 2) { + scan_req.ChannelPage = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (handle_security_args(argc, argv, &scan_req.Key) != CMDLINE_RETCODE_SUCCESS) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + clear_beacons(); + mac_interface->mlme_req(mac_interface, MLME_SCAN, &scan_req); + return CMDLINE_RETCODE_EXCUTING_CONTINUE; +} + +int mac_data_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + bool boolean; + + cmd_printf("MCPS-DATA.request\n"); + if (cmd_parameter_int(argc, argv, "--src_addr_mode", &val)) { + if (val == 0 || val == 2 || val == 3) { + data_req.SrcAddrMode = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--dst_addr_mode", &val)) { + if (val == 0 || val == 2 || val == 3) { + data_req.DstAddrMode = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--dst_pan_id", &str)) { + uint32_t pan_id = strtoul(str, NULL, 16); + if (pan_id <= 0xFFFF) { + data_req.DstPANId = pan_id; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--dst_addr", &str)) { + int len = (data_req.DstAddrMode == 2 ? 2 : 8); + if (string_to_bytes(str, data_req.DstAddr, len) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--msdu_length", &val)) { + data_req.msduLength = val; + } + if (cmd_parameter_val(argc, argv, "--msdu", &str)) { + ns_dyn_mem_free(data_req.msdu); + if (strlen(str) != data_req.msduLength) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + data_req.msdu = (uint8_t*)ns_dyn_mem_temporary_alloc(data_req.msduLength); + if (data_req.msdu == NULL) { + tr_error("Failed to allocate memory for the msdu"); + return CMDLINE_RETCODE_FAIL; + } + std::memcpy(data_req.msdu, str, data_req.msduLength); + } + if (cmd_parameter_int(argc, argv, "--msdu_handle", &val)) { + if (val >= 0 && val <= 255) { + data_req.msduHandle = val; + } + } + if (cmd_parameter_bool(argc, argv, "--tx_ack_req", &boolean)) { + data_req.TxAckReq = boolean; + } + if (cmd_parameter_bool(argc, argv, "--indirect_tx", &boolean)) { + data_req.InDirectTx = boolean; + } + if (cmd_parameter_bool(argc, argv, "--pending_bit", &boolean)) { + data_req.PendingBit = boolean; + } + if (cmd_parameter_bool(argc, argv, "--wait_for_confirm", &boolean)) { + wait_for_confirm = boolean; + } + if (handle_security_args(argc, argv, &data_req.Key) != CMDLINE_RETCODE_SUCCESS) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + mac_interface->mcps_data_req(mac_interface, &data_req); + if (wait_for_confirm) { + return CMDLINE_RETCODE_EXCUTING_CONTINUE; + } + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_poll_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + + cmd_printf("MLME-POLL.request\n"); + data_count = 0; + if (cmd_parameter_int(argc, argv, "--coord_addr_mode", &val)) { + if (val == 2 || val == 3) { + poll_req.CoordAddrMode = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--coord_pan_id", &str)) { + unsigned long pan_id = strtoul(str, NULL, 16); + if (pan_id <= 0xFFFF) { + poll_req.CoordPANId = pan_id; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--coord_address", &str)) { + int len = (poll_req.CoordAddrMode == 2 ? 2: 8); + if (string_to_bytes(str, poll_req.CoordAddress, len) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (handle_security_args(argc, argv, &poll_req.Key) != CMDLINE_RETCODE_SUCCESS) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + mac_interface->mlme_req(mac_interface, MLME_POLL, &poll_req); + return CMDLINE_RETCODE_EXCUTING_CONTINUE; +} + +int mac_purge_command(int argc, char *argv[]) +{ + int32_t val; + + cmd_printf("MCPS-PURGE.request\n"); + if (cmd_parameter_int(argc, argv, "--msdu_handle", &val)) { + if (val >= 0 && val <= 255) { + purge_req.msduHandle = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + mac_interface->mcps_purge_req(mac_interface, &purge_req); + return CMDLINE_RETCODE_EXCUTING_CONTINUE; +} + +int mac_set_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + uint8_t val_uint8 = 0; + uint16_t val_uint16 = 0; + uint32_t val_uint32 = 0; + uint8_t *val_ptr_array = NULL; + + cmd_printf("MLME-SET.request\n"); + if (cmd_parameter_val(argc, argv, "--attr", &str)) { + uint32_t attribute = strtoul(str, NULL, 16); + if (attribute <= 255) { + set_req.attr = (mlme_attr_t)attribute; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--attr_index", &val)) { + if (val >= 0 && val <= 255) { + set_req.attr_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--value_ascii", &str)) { + val_ptr_array = (uint8_t*)ns_dyn_mem_temporary_alloc(strlen(str)); + if (val_ptr_array == NULL) { + tr_error("Failed to allocate memory for MLME-SET.request"); + return CMDLINE_RETCODE_FAIL; + } + std::memcpy(val_ptr_array, str, strlen(str)); + set_req.value_pointer = val_ptr_array; + } else if (cmd_parameter_val(argc, argv, "--value_bytes", &str)) { + size_t bytes = (strlen(str) + 1) / 3; + val_ptr_array = (uint8_t*)ns_dyn_mem_temporary_alloc(bytes); + if (val_ptr_array == NULL) { + tr_error("Failed to allocate memory for MLME-SET.request"); + return CMDLINE_RETCODE_FAIL; + } + if (string_to_bytes(str, val_ptr_array, bytes) != 0) { + ns_dyn_mem_free(val_ptr_array); + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + set_req.value_pointer = val_ptr_array; + } else if (cmd_parameter_int(argc, argv, "--value_uint8", &val)) { + if (val >= 0 && val <= 0xFF) { + val_uint8 = val; + set_req.value_pointer = &val_uint8; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_int(argc, argv, "--value_uint16", &val)) { + if (val >= 0 && val <= 0xFFFF) { + val_uint16 = val; + set_req.value_pointer = &val_uint16; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_int(argc, argv, "--value_uint32", &val)) { + if (val >= 0 && val <= 0x7FFFFFFF) { + val_uint32 = val; + set_req.value_pointer = &val_uint32; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--value_size", &val)) { + if (val >= 0 && val <= 255) { + set_req.value_size = val; + } + } + + mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); + ns_dyn_mem_free(val_ptr_array); + set_req.value_pointer = NULL; + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_get_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + + cmd_printf("MLME-GET.request\n"); + if (cmd_parameter_val(argc, argv, "--attr", &str)) { + uint32_t attribute = strtoul(str, NULL, 16); + if (attribute <= 255) { + get_req.attr = (mlme_attr_t)attribute; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--attr_index", &val)) { + if (val >= 0 && val <= 255) { + get_req.attr_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + mac_interface->mlme_req(mac_interface, MLME_GET, &get_req); + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_reset_command(int argc, char *argv[]) +{ + bool boolean; + + cmd_printf("MLME-RESET.request\n"); + if (cmd_parameter_bool(argc, argv, "--set_default_pib", &boolean)) { + reset_req.SetDefaultPIB = boolean; + } + mac_interface->mlme_req(mac_interface, MLME_RESET, &reset_req); + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_address_command(int argc, char *argv[]) +{ + char *str; + uint8_t ext_addr[8]; + + if (cmd_parameter_val(argc, argv, "--64-bit", &str)) { + if (string_to_bytes(str, ext_addr, 8) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + mac_interface->mac64_set(mac_interface, ext_addr); + cmd_printf("64-bit MAC address set to: %s\n", mbed_trace_array(ext_addr, 8)); + } else if (cmd_parameter_val(argc, argv, "--16-bit", &str)) { + uint32_t short_addr_32 = strtoul(str, NULL, 16); + if (short_addr_32 <= 0xFFFF) { + uint16_t short_addr = short_addr_32; + mlme_set_t set_req; + set_req.attr = macShortAddress; + set_req.value_pointer = &short_addr; + set_req.value_size = 2; + mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); + cmd_printf("16-bit MAC address set to: 0x%04X\n", short_addr); + } else { + tr_warn("Invalid 16-bit MAC address given: %lu", short_addr_32); + } + } else if (argc == 1) { + if (mac_interface->mac64_get(mac_interface, MAC_EXTENDED_READ_ONLY, ext_addr) == 0) { + cmd_printf("EUI64: %s\n", mbed_trace_array(ext_addr, 8)); + } else { + tr_warn("Failed to read EUI64"); + return CMDLINE_RETCODE_FAIL; + } + if (mac_interface->mac64_get(mac_interface, MAC_EXTENDED_DYNAMIC, ext_addr) == 0) { + cmd_printf("MAC64: %s\n", mbed_trace_array(ext_addr, 8)); + } else { + tr_warn("Failed to read MAC64"); + return CMDLINE_RETCODE_FAIL; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + return CMDLINE_RETCODE_SUCCESS; +} + +static int key_config_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + bool boolean; + int lookup_index = 0, device_index = 0, usage_index = 0; + + if (cmd_parameter_val(argc, argv, "--key", &str)) { + if (strlen(str) == 2*16+15) { + if (string_to_bytes(str, key_descriptor.Key, 16) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--key_id_lookup_list_entries", &val)) { + if (val >= 0 && val < LOOKUP_DESCRIPTOR_TABLE_SIZE) { + key_descriptor.KeyIdLookupListEntries = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--key_device_list_entries", &val)) { + if (val >= 0 && val < DEVICE_DESCRIPTOR_TABLE_SIZE) { + key_descriptor.KeyDeviceListEntries = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--key_usage_list_entries", &val)) { + if (val >= 0 && val < USAGE_DESCRIPTOR_TABLE_SIZE) { + key_descriptor.KeyUsageListEntries = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--lookup_index", &val)) { + if (val >= 0 && val < LOOKUP_DESCRIPTOR_TABLE_SIZE) { + lookup_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--lookup_data", &str)) { + if (strlen(str) == 2*9+8) { + if (string_to_bytes(str, key_descriptor.KeyIdLookupList[lookup_index].LookupData, 9) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--lookup_data_size", &val)) { + if (val == 0 || val == 1) { + key_descriptor.KeyIdLookupList[lookup_index].LookupDataSize = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--device_list_index", &val)) { + if (val >= 0 && val < DEVICE_DESCRIPTOR_TABLE_SIZE) { + device_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--device_descriptor_handle", &val)) { + if (val >= 0 && val <= 255) { + key_descriptor.KeyDeviceList[device_index].DeviceDescriptorHandle = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_bool(argc, argv, "--unique_device", &boolean)) { + key_descriptor.KeyDeviceList[device_index].UniqueDevice = boolean; + } + if (cmd_parameter_bool(argc, argv, "--blacklisted", &boolean)) { + key_descriptor.KeyDeviceList[device_index].Blacklisted = boolean; + } + if (cmd_parameter_int(argc, argv, "--usage_index", &val)) { + if (val >= 0 && val <= USAGE_DESCRIPTOR_TABLE_SIZE) { + usage_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--frame_type", &val)) { + if (val >= 0 && val <= 3) { + key_descriptor.KeyUsageList[usage_index].FrameType = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (key_descriptor.KeyUsageList[usage_index].FrameType == 3) { + if (cmd_parameter_int(argc, argv, "--command_frame_identifier", &val)) { + if (val >= 1 && val <= 9) { + key_descriptor.KeyUsageList[usage_index].CommandFrameIdentifier = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + } + return CMDLINE_RETCODE_SUCCESS; +} + +static int key_add_command(int argc, char *argv[]) +{ + mlme_set_t set_req; + int32_t val; + int key_index = 0; + + if (cmd_parameter_int(argc, argv, "--index", &val)) { + if (val >= 0 && val <= 255) { + key_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + + set_req.attr = macKeyTable; + set_req.attr_index = key_index; + set_req.value_pointer = &key_descriptor; + set_req.value_size = sizeof(mlme_key_descriptor_entry_t); + mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_key_command(int argc, char *argv[]) +{ + char *cmd = argv[1]; + + if (strcmp(cmd, "config") == 0) { + return key_config_command(argc, argv); + } else if (strcmp(cmd, "add") == 0) { + return key_add_command(argc, argv); + } + return CMDLINE_RETCODE_INVALID_PARAMETERS; +} + +int mac_add_neighbour_command(int argc, char *argv[]) +{ + char *str; + int32_t val; + mlme_device_descriptor_t neighbour; + mlme_set_t set_req; + + neighbour.Exempt = false; + if (cmd_parameter_int(argc, argv, "--frame_ctr", &val)) { + neighbour.FrameCounter = val; + } + if (cmd_parameter_val(argc, argv, "--mac16", &str)) { + uint32_t short_addr = strtoul(str, NULL, 16); + if (short_addr <= 0xFFFF) { + neighbour.ShortAddress = short_addr; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--mac64", &str)) { + if (strlen(str) == 2*8+7) { + if (string_to_bytes(str, neighbour.ExtAddress, 8) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_val(argc, argv, "--pan_id", &str)) { + uint32_t pan_id = strtoul(str, NULL, 16); + if (pan_id <= 0xFFFF) { + neighbour.PANId = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + if (cmd_parameter_int(argc, argv, "--index", &val)) { + if (val >= 0 && val <= 255) { + set_req.attr_index = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + + set_req.attr = macDeviceTable; + set_req.value_pointer = &neighbour; + set_req.value_size = sizeof(mlme_device_descriptor_t); + mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); + return CMDLINE_RETCODE_SUCCESS; +} + +#ifndef DISABLE_FILTERING +static int filter_start(int argc, char *argv[]) +{ + char *str; + + if (cmd_parameter_val(argc, argv, "--mode", &str)) { + if (strcmp(str, "allow") == 0) { + return mac_filter_start(mac_interface->parent_id, MAC_FILTER_ALLOWED) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; + } else if (strcmp(str, "block") == 0) { + return mac_filter_start(mac_interface->parent_id, MAC_FILTER_BLOCKED) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS;; + } else if (strcmp(str, "fixed") == 0) { + int32_t lqi, dbm; + if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi) && + cmd_parameter_int(argc, argv, "--dbm_m", &dbm)) { + return mac_filter_start(mac_interface->parent_id, MAC_FILTER_FIXED(lqi, dbm)) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; + } + } + } + return CMDLINE_RETCODE_INVALID_PARAMETERS; +} + +static int filter_add(int argc, char *argv[]) +{ + char *str; + uint32_t short_addr; + uint8_t long_addr[8]; + int32_t lqi_m, lqi_add, dbm_m, dbm_add; + + if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi_m) && + cmd_parameter_int(argc, argv, "--lqi_add", &lqi_add) && + cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m) && + cmd_parameter_int(argc, argv, "--dbm_add", &dbm_add)) { + } else if (cmd_parameter_val(argc, argv, "--mode", &str)) { + if (strcmp(str, "allow")) { + lqi_m = dbm_m = 256; + lqi_add = dbm_add = 0; + } else if (strcmp(str, "block")) { + lqi_m = lqi_add = dbm_m = dbm_add = 0; + } else if (strcmp(str, "fixed")) { + lqi_add = dbm_add = 0; + if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi_m) && + cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m)) { + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + + if (cmd_parameter_val(argc, argv, "--short", &str)) { + short_addr = strtoul(str, NULL, 16); + if (short_addr <= 0xFFFF) { + return mac_filter_add_short(mac_interface->parent_id, short_addr, lqi_m, lqi_add, dbm_m, dbm_add) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_val(argc, argv, "--long", &str)) { + if (string_to_bytes(str, long_addr, 8) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + return mac_filter_add_long(mac_interface->parent_id, long_addr, lqi_m, lqi_add, dbm_m, dbm_add) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } +} + +static int filter_remove(int argc, char *argv[]) +{ + char *str; + uint32_t short_addr; + uint8_t long_addr[8]; + + if (cmd_parameter_val(argc, argv, "--short", &str)) { + short_addr = strtoul(str, NULL, 16); + if (short_addr <= 0xFFFF) { + return mac_filter_delete_short(mac_interface->parent_id, short_addr) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_val(argc, argv, "--long", &str)) { + if (string_to_bytes(str, long_addr, 8) != 0) { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + return mac_filter_delete_long(mac_interface->parent_id, long_addr) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } +} + +static int filter_clear(int argc, char *argv[]) +{ + return mac_filter_clear(mac_interface->parent_id) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; +} + +static int filter_stop(int argc, char *argv[]) +{ + mac_filter_stop(mac_interface->parent_id); + return CMDLINE_RETCODE_SUCCESS; +} +#else +static int filter_start(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return 0; +} + +static int filter_add(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return 0; +} +static int filter_remove(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return 0; +} +static int filter_clear(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return 0; +} +static int filter_stop(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + return 0; +} +#endif +int mac_filter_command(int argc, char *argv[]) +{ + char *cmd = argv[1]; + + if (strcmp(cmd, "start") == 0) { + return filter_start(argc, argv); + } else if (strcmp(cmd, "add") == 0) { + return filter_add(argc, argv); + } else if (strcmp(cmd, "remove") == 0) { + return filter_remove(argc, argv); + } else if (strcmp(cmd, "clear") == 0) { + return filter_clear(argc, argv); + } else if (strcmp(cmd, "stop") == 0) { + return filter_stop(argc, argv); + } + return CMDLINE_RETCODE_INVALID_PARAMETERS; +} + +int mac_config_status_command(int argc, char *argv[]) +{ + int32_t val; + char *str; + + if (cmd_parameter_int(argc, argv, "--data_conf", &val)) { + if (val >= 0 && val <= 255) { + expected_statuses.data_conf = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_val(argc, argv, "--data_ind", &str)) { + size_t len = strlen(str); + ns_dyn_mem_free(expected_statuses.data_ind); + expected_statuses.data_ind = (uint8_t*)ns_dyn_mem_temporary_alloc(len); + expected_statuses.data_ind_len = len; + std::memcpy(expected_statuses.data_ind, str, len); + } else if (cmd_parameter_int(argc, argv, "--get", &val)) { + if (val >= 0 && val <= 255) { + expected_statuses.get_conf = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_int(argc, argv, "--scan", &val)) { + if (val >= 0 && val <= 255) { + expected_statuses.scan_conf = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_int(argc, argv, "--poll", &val)) { + if (val >= 0 && val <= 255) { + expected_statuses.poll_conf = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_int(argc, argv, "--purge", &val)) { + if (val >= 0 && val <= 255) { + expected_statuses.purge_conf = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_int(argc, argv, "--comm_status", &val)) { + if (val >= 0 && val <= 255) { + expected_statuses.comm_status_ind = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else if (cmd_parameter_index(argc, argv, "--list") != -1) { + for (unsigned int i = 0; i < 256; ++i) { + const char *status = mlme_status_string(i); + if (status) { + cmd_printf("%hhu\t%s\n", i, status); + } + } + } else if (cmd_parameter_index(argc, argv, "--reset") != -1) { + expected_statuses.data_conf = MLME_SUCCESS; + expected_statuses.get_conf = MLME_SUCCESS; + expected_statuses.scan_conf = MLME_SUCCESS; + expected_statuses.poll_conf = MLME_SUCCESS; + expected_statuses.purge_conf = MLME_SUCCESS; + expected_statuses.comm_status_ind = MLME_SUCCESS; + expected_statuses.data_ind_len = 0; + expected_statuses.data_ind = NULL; + } else if (argc == 1) { + cmd_printf("MCPS-DATA.confirm: %d (%s)\n", expected_statuses.data_conf, mlme_status_string(expected_statuses.data_conf)); + cmd_printf("MLME-GET.confirm: %d (%s)\n", expected_statuses.get_conf, mlme_status_string(expected_statuses.get_conf)); + cmd_printf("MLME-SCAN.confirm: %d (%s)\n", expected_statuses.scan_conf, mlme_status_string(expected_statuses.scan_conf)); + cmd_printf("MLME-POLL.confirm: %d (%s)\n", expected_statuses.poll_conf, mlme_status_string(expected_statuses.poll_conf)); + cmd_printf("MCPS.PURGE.confirm. %d (%s)\n", expected_statuses.purge_conf, mlme_status_string(expected_statuses.purge_conf)); + cmd_printf("MLME-COMM-STATUS.indication: %d (%s)\n", expected_statuses.comm_status_ind, mlme_status_string(expected_statuses.comm_status_ind)); + cmd_printf("MCPS-DATA.indication: %s <%.*s>\n", mbed_trace_array(expected_statuses.data_ind, expected_statuses.data_ind_len), expected_statuses.data_ind_len, expected_statuses.data_ind); + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_find_beacon_command(int argc, char *argv[]) +{ + char *str; + + if (cmd_parameter_val(argc, argv, "--data", &str)) { + for (int i = 0; i < MLME_MAC_RES_SIZE_MAX; ++i) { + if (received_beacons.beacons[i] == NULL) { + continue; + } + if (strncmp(received_beacons.beacons[i], str, received_beacons.beacon_lengths[i]) == 0) { + return CMDLINE_RETCODE_SUCCESS; + } + } + return CMDLINE_RETCODE_FAIL; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } +} + +int mac_wait_command(int argc, char *argv[]) +{ + int32_t val; + static uint32_t timeout_ms = 1000; + int remaining_ms = timeout_ms; + if (cmd_parameter_int(argc, argv, "--timeout", &val)) { + if (val >= 0) { + timeout_ms = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } + while (data_count < 1) { + Thread::wait(10); + remaining_ms -= 10; + if (remaining_ms <= 0) { + return CMDLINE_RETCODE_FAIL; + } + } + data_count = 0; + return CMDLINE_RETCODE_SUCCESS; +} + +int mac_analyze_ed_command(int argc, char *argv[]) +{ + int32_t val; + int channel; + + if (cmd_parameter_int(argc, argv, "--channel", &val)) { + if (val >= 0 && val <= 26) { + channel = val; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + + if (cmd_parameter_int(argc, argv, "--above", &val)) { + for (int i = 0; i < last_ed_results.count; ++i) { + if (last_ed_results.channel[i] == channel) { + return last_ed_results.ED_values[i] >= val ? CMDLINE_RETCODE_SUCCESS : CMDLINE_RETCODE_FAIL; + } + } + } else if (cmd_parameter_int(argc, argv, "--below", &val)) { + for (int i = 0; i < last_ed_results.count; ++i) { + if (last_ed_results.channel[i] == channel) { + return last_ed_results.ED_values[i] <= val ? CMDLINE_RETCODE_SUCCESS : CMDLINE_RETCODE_FAIL; + } + } + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } + return CMDLINE_RETCODE_FAIL; +} + +static void reset_security(mlme_security_t *sec) +{ + sec->SecurityLevel = 0; + sec->KeyIdMode = 0; + sec->KeyIndex = 0; + memset(sec->Keysource, 0, 8); +} + +int reset_command(int argc, char *argv[]) +{ + wait_for_confirm = true; + silent_mode = false; + data_count = 0; + + start_req.PANId = 0x1234; + start_req.LogicalChannel = 11; + start_req.ChannelPage = 0; + start_req.StartTime = 0; + start_req.BeaconOrder = 15; + start_req.SuperframeOrder = 15; + start_req.PANCoordinator = true; + start_req.BatteryLifeExtension = false; + start_req.CoordRealignment = false; + reset_security(&start_req.CoordRealignKey); + reset_security(&start_req.BeaconRealignKey); + + scan_req.ScanType = MAC_ACTIVE_SCAN; + scan_req.ScanChannels.channel_page = CHANNEL_PAGE_0; + scan_req.ScanChannels.channel_mask[0] = 0x07FFF800; + reset_security(&scan_req.Key); + + data_req.SrcAddrMode = 3; + data_req.DstAddrMode = 3; + data_req.DstPANId = 0x1234; + memset(data_req.DstAddr, 0, 8); + data_req.msduLength = 0; + data_req.msdu = NULL; + data_req.msduHandle = 0; + data_req.TxAckReq = true; + data_req.InDirectTx = false; + data_req.PendingBit = false; + reset_security(&data_req.Key); + + poll_req.CoordAddrMode = 3; + poll_req.CoordPANId = 0x1234; + memset(poll_req.CoordAddress, 0, 8); + reset_security(&poll_req.Key); + + purge_req.msduHandle = 0; + + set_req.attr = (mlme_attr_t)0x39; + set_req.attr_index = 0; + set_req.value_pointer = NULL; + set_req.value_size = 0; + + get_req.attr = (mlme_attr_t)0x39; + get_req.attr_index = 0; + + reset_req.SetDefaultPIB = true; + + return CMDLINE_RETCODE_SUCCESS; +} + +int silent_mode_command(int argc, char *argv[]) +{ + char *cmd; + if (argc < 2) { + return CMDLINE_RETCODE_FAIL; + } + cmd = argv[1]; + if (strcmp(cmd, "on") == 0) { + silent_mode = true; + return CMDLINE_RETCODE_SUCCESS; + } else if (strcmp(cmd, "off") == 0) { + silent_mode = false; + return CMDLINE_RETCODE_SUCCESS; + } else { + return CMDLINE_RETCODE_INVALID_PARAMETERS; + } +} diff --git a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h new file mode 100644 index 00000000000..a57e97fc313 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 MAC_COMMANDS_H_ +#define MAC_COMMANDS_H_ + +#include +#include + +#include "ns_cmdline.h" +#include "nsdynmemLIB.h" +#include "mbed_trace.h" +#include "mac_api.h" +#include "mlme.h" +#include "mac_mcps.h" +#include "mac_common_defines.h" +#include "mac_filter_api.h" +#include "util.h" + +#define LOOKUP_DESCRIPTOR_TABLE_SIZE 2 +#define DEVICE_DESCRIPTOR_TABLE_SIZE 2 +#define USAGE_DESCRIPTOR_TABLE_SIZE 2 +#define KEY_DESCRIPTOR_TABLE_SIZE 2 + +void mac_commands_init(void); + +void mac_data_confirm_handler(const mac_api_t *api, const mcps_data_conf_t *data); +void mac_data_indication_handler(const mac_api_t *api, const mcps_data_ind_t *data); +void mac_purge_confirm_handler(const mac_api_t *api, mcps_purge_conf_t *data); +void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const void *data); +void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const void *data); + +int mac_start_command(int argc, char *argv[]); +int mac_scan_command(int argc, char *argv[]); +int mac_data_command(int argc, char *argv[]); +int mac_poll_command(int argc, char *argv[]); +int mac_purge_command(int argc, char *argv[]); +int mac_set_command(int argc, char *argv[]); +int mac_get_command(int argc, char *argv[]); +int mac_reset_command(int argc, char *argv[]); +int mac_address_command(int argc, char *argv[]); +int mac_key_command(int argc, char *argv[]); +int mac_add_neighbour_command(int argc, char *argv[]); +int mac_filter_command(int argc, char *argv[]); +int mac_config_status_command(int argc, char *argv[]); +int mac_find_beacon_command(int argc, char *argv[]); +int mac_wait_command(int argc, char *argv[]); +int mac_analyze_ed_command(int argc, char *argv[]); +int reset_command(int argc, char *argv[]); +int silent_mode_command(int argc, char *argv[]); + +#endif diff --git a/TEST_APPS/device/nanostack_mac_tester/source/util.cpp b/TEST_APPS/device/nanostack_mac_tester/source/util.cpp new file mode 100644 index 00000000000..286c6d68cad --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/source/util.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "util.h" + +int string_to_bytes(const char *str, uint8_t *buf, int bytes) +{ + int len = strlen(str); + + if (len <= (3 * bytes - 1)) { + int i; + + for (i = 0; i < bytes; ++i) { + if (i * 3 < len) { + buf[i] = (uint8_t)strtoul(str + i * 3, NULL, 16); + } else { + buf[i] = 0; + } + } + return 0; + } + return -1; +} + +const char *mlme_status_string(uint8_t status) +{ + switch (status) { + case MLME_SUCCESS: return "MLME_SUCCESS"; + case MLME_BUSY_CHAN: return "MLME_BUSY_CHAN"; + case MLME_BUSY_RX: return "MLME_BUSY_RX"; + case MLME_BUSY_TX: return "MLME_BUSY_TX"; + case MLME_FORCE_TRX_OFF: return "MLME_FORCE_TRX_OFF"; + case MLME_IDLE: return "MLME_IDLE"; + case MLME_RX_ON: return "MLME_RX_ON"; + case MLME_TRX_OFF: return "MLME_TRX_OFF"; + case MLME_TX_ON: return "MLME_TX_ON"; + case MLME_COUNTER_ERROR: return "MLME_COUNTER_ERROR"; + case MLME_IMPROPER_KEY_TYPE: return "MLME_IMPROPER_KEY_TYPE"; + case MLME_IMPROPER_SECURITY_LEVEL: return "MLME_IMPROPER_SECURITY_LEVEL"; + case MLME_UNSUPPORTED_LEGACY: return "MLME_UNSUPPORTED_LEGACY"; + case MLME_UNSUPPORTED_SECURITY: return "MLME_UNSUPPORTED_SECURITY"; + case MLME_SECURITY_FAIL: return "MLME_SECURITY_FAIL"; + case MLME_FRAME_TOO_LONG: return "MLME_FRAME_TOO_LONG"; + case MLME_INVALID_HANDLE: return "MLME_INVALID_HANDLE"; + case MLME_INVALID_PARAMETER: return "MLME_INVALID_PARAMETER"; + case MLME_TX_NO_ACK: return "MLME_TX_NO_ACK"; + case MLME_NO_BEACON: return "MLME_NO_BEACON"; + case MLME_NO_DATA: return "MLME_NO_DATA"; + case MLME_NO_SHORT_ADDRESS: return "MLME_NO_SHORT_ADDRESS"; + case MLME_PAN_ID_CONFLICT: return "MLME_PAN_ID_CONFLICT"; + case MLME_TRANSACTION_EXPIRED: return "MLME_TRANSACTION_EXPIRED"; + case MLME_TRANSACTION_OVERFLOW: return "MLME_TRANSACTION_OVERFLOW"; + case MLME_UNAVAILABLE_KEY: return "MLME_UNAVAILABLE_KEY"; + case MLME_UNSUPPORTED_ATTRIBUTE: return "MLME_UNSUPPORTED_ATTRIBUTE"; + case MLME_INVALID_ADDRESS: return "MLME_INVALID_ADDRESS"; + case MLME_INVALID_INDEX: return "MLME_INVALID_INDEX"; + case MLME_LIMIT_REACHED: return "MLME_LIMIT_REACHED"; + case MLME_READ_ONLY: return "MLME_READ_ONLY"; + case MLME_SCAN_IN_PROGRESS: return "MLME_SCAN_IN_PROGRESS"; + case MLME_DATA_POLL_NOTIFICATION: return "MLME_DATA_POLL_NOTIFICATION"; + default: return NULL; + } +} + +int channel_from_mask(uint32_t channel_mask, int index) +{ + int expected_index = 0; + for (int i = 0; i < 27; ++i) { + if ((channel_mask >> i) & 1) { + if (expected_index == index) { + return i; + } + ++expected_index; + } + } + return -1; +} diff --git a/TEST_APPS/device/nanostack_mac_tester/source/util.h b/TEST_APPS/device/nanostack_mac_tester/source/util.h new file mode 100644 index 00000000000..a7927314272 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/source/util.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 UTIL_H_ +#define UTIL_H_ + +#include +#include + +#include "mbed.h" +#include "mlme.h" + +int string_to_bytes(const char *str, uint8_t *buf, int bytes); +const char *mlme_status_string(uint8_t status); +int channel_from_mask(uint32_t channel_mask, int index); + +#endif diff --git a/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py b/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py new file mode 100644 index 00000000000..3fedf1e6c96 --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py @@ -0,0 +1,110 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" + +import threading +import os,sys +from icetea_lib.bench import Bench + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "ED_scan", + title = "ED scan test", + status = "development", + type = "smoke", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "Tests reading the ED values from channels 11-16", + feature = ["MLME-SCAN (ED)"], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":3, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { + "name": "TEST_APPS-device-nanostack_mac_tester" + } + }, + "1":{"nick": "First"}, + "2":{"nick": "Second"}, + "3":{"nick": "Third"} + }} + ) + + def setUp(self): + self.channel = 11 + self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") + self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") + self.command("Third", "addr --64-bit 01:02:03:00:00:00:00:03") + + def spam_channel(self, event): + while not event.wait(0.1): + self.lock_th.acquire() + self.command("First", "data --dst_addr 01:02:03:00:00:00:00:03 --msdu {} --msdu_length {} --wait_for_confirm false".format(self.payload, len(self.payload))) + self.command("Third", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu {} --msdu_length {} --wait_for_confirm false".format(self.payload, len(self.payload))) + self.lock_th.release() + + def mask_from_channel_list(self, channels): + res = 0 + for ch in channels: + res = res | ( 1 << ch) + return hex(res) + + def case(self): + self.lock_th = threading.Lock() + self.payload = "01234567890123456789012345678901234567890123456789" + + self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) + self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + + #No reason to print their spamming + self.command("First", "silent-mode on") + self.command("Third", "silent-mode on") + + self.stop_event = threading.Event() + self.th = threading.Thread(target=self.spam_channel, args=(self.stop_event,)) + self.th.start() + self.stopped = True + channels = range(11,27) + for i in range(0, 3): + self.lock_th.acquire() + self.command("First", "mlme-reset") + self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) + self.command("Third", "mlme-reset") + self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + self.lock_th.release() + self.command("Second", "scan --scan_type 0 --scan_duration 7 --channel_mask {}".format(self.mask_from_channel_list(channels))) + self.command("Second", "analyze-ed --channel {} --above 100".format(self.channel)) + + def tearDown(self): + self.command("First", "silent-mode off") + self.command("Third", "silent-mode off") + self.stop_event.set() + self.th.join() + del self.th + self.reset_dut() + +if __name__=='__main__': + sys.exit( Testcase().run() ) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/__init__.py b/TEST_APPS/testcases/nanostack_mac_tester/__init__.py new file mode 100644 index 00000000000..4265cc3e6c1 --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/__init__.py @@ -0,0 +1 @@ +#!/usr/bin/env python diff --git a/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py b/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py new file mode 100644 index 00000000000..7b1d34fd781 --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py @@ -0,0 +1,68 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" + +import os,sys +from icetea_lib.bench import Bench + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "address_read_and_write", + title = "MAC address and PAN id read/write test", + status = "development", + type = "smoke", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "Tests reading a MAC address from the driver, and writing to the modifiable MAC address", + feature = ["MLME-SET"], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":1, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { + "name": "TEST_APPS-device-nanostack_mac_tester" + } + }, + "1":{"nick": "First"} + }} + ) + + def setUp(self): + pass + + def case(self): + self.command("First", "addr") + self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") + self.command("First", "addr --16-bit 0xABCD") + #macPANId + self.command("First", "mlme-set --attr 0x50 --value_bytes CD:CD --value_size 2") + self.command("First", "addr") + self.verify_trace(1, "MAC64: 01:02:03:00:00:00:00:01") + + def tearDown(self): + self.reset_dut() + +if __name__=='__main__': + sys.exit( Testcase().run() ) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py b/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py new file mode 100644 index 00000000000..13819ad0e5f --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py @@ -0,0 +1,89 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" + +import os,sys +from icetea_lib.bench import Bench + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "create_and_join_PAN", + title = "Create a PAN and have a device join it", + status = "development", + type = "smoke", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "", + feature = ["MLME-START", "MLME-SCAN (active)"], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":3, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { + "name": "TEST_APPS-device-nanostack_mac_tester" + } + }, + "1":{"nick": "First"}, + "2":{"nick": "Second"}, + "3":{"nick": "Third"} + }} + ) + + def mask_from_channel_list(self, channels): + res = 0 + for ch in channels: + res = res | ( 1 << ch) + return hex(res) + + def setUp(self): + self.channel = 11 + + def case(self): + #Beacon payload & length + self.command("First", "mlme-set --attr 0x45 --value_ascii mac-tester --value_size 10") + self.command("First", "mlme-set --attr 0x46 --value_uint8 10 --value_size 1") + + self.command("Second", "mlme-set --attr 0x45 --value_ascii second-mac-tester --value_size 17") + self.command("Second", "mlme-set --attr 0x46 --value_uint8 17 --value_size 1") + + self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) + self.command("Second", "start --pan_coordinator true --logical_channel {}".format(int(self.channel)+1)) + self.delay(3) + if self.channel == 11: + channels = [11,12] + elif self.channel == 26: + channels = [25,26] + else: + channels = [self.channel, self.channel+1] + self.command("Third", "scan --channel_mask {}".format(self.mask_from_channel_list(channels))) + self.delay(0.2) + self.command("Third", "find-beacon --data mac-tester") + self.command("Third", "find-beacon --data second-mac-tester") + + def tearDown(self): + self.reset_dut() + +if __name__=='__main__': + sys.exit( Testcase().run() ) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_data.py b/TEST_APPS/testcases/nanostack_mac_tester/send_data.py new file mode 100644 index 00000000000..e647169e4b8 --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/send_data.py @@ -0,0 +1,69 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" + +import os,sys +from icetea_lib.bench import Bench + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "send_data", + title = "Simple data transmission test", + status = "development", + type = "smoke", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "Tests that sending data works", + feature = ["MCPS-DATA"], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":2, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { + "name": "TEST_APPS-device-nanostack_mac_tester" + } + }, + "1":{"nick": "First"}, + "2":{"nick": "Second"} + }} + ) + + def setUp(self): + self.channel = 11 + self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") + self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") + + def case(self): + self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) + self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + + self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde") + self.command("Second", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length 5 --msdu 12345") + + def tearDown(self): + self.reset_dut() + +if __name__=='__main__': + sys.exit( Testcase().run() ) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py b/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py new file mode 100644 index 00000000000..be73162d645 --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py @@ -0,0 +1,99 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" + +import os,sys +from icetea_lib.bench import Bench + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "send_data_indirect", + title = "Indirect data transmission test", + status = "development", + type = "smoke", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "Tests sending data indirectly, i.e polling the coordinator for data", + feature = ["MCPS-DATA", "MLME-POLL"], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":3, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { + "name": "TEST_APPS-device-nanostack_mac_tester" + } + }, + "1":{"nick": "First"}, + "2":{"nick": "Second"}, + "3":{"nick": "Third"} + }} + ) + + def setUp(self): + self.channel = 11 + self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") + self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") + self.command("Third", "addr --64-bit 01:02:03:00:00:00:00:03") + + def case(self): + self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) + self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + + #macRxOnWhenIdle + self.command("Second", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1") + self.command("Third", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1") + + self.command("First", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:02 --pan_id 0x1234 --index 0") + self.command("First", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:03 --pan_id 0x1234 --index 1") + self.command("Second", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") + self.command("Third", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") + + self.command("Second", "config-status --data_ind abcde") + self.command("Third", "config-status --data_ind 12345") + + #Runs into timing issues if extensive printing is enabled + self.command("*", "silent-mode on") + self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde --indirect_tx true --wait_for_confirm false") + self.command("Second", "poll --coord_address 01:02:03:00:00:00:00:01") + + self.command("First", "data") + self.command("First", "data") + self.command("Second", "poll") + self.command("Second", "poll") + self.command("Second", "config-status --poll 235") + self.command("Second", "poll") + + self.command("Second", "config-status --poll 235") + self.command("First", "data --dst_addr 01:02:03:00:00:00:00:03 --msdu 12345") + self.command("Second", "poll") + self.command("Third", "poll --coord_address 01:02:03:00:00:00:00:01") + self.command("*", "silent-mode off") + + def tearDown(self): + self.reset_dut() + +if __name__=='__main__': + sys.exit( Testcase().run() ) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py b/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py new file mode 100644 index 00000000000..478a2ea423d --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py @@ -0,0 +1,82 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" + +import os,sys +from icetea_lib.bench import Bench + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "send_large_payloads", + title = "Data transmission test with large packets", + status = "development", + type = "reliability", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "Repeatedly sends long packets, checking that the payload is correct in each one", + feature = ["MCPS-DATA"], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":2, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { + "name": "TEST_APPS-device-nanostack_mac_tester" + } + }, + "1":{"nick": "First"}, + "2":{"nick": "Second"} + }} + ) + + def setUp(self): + self.channel = 11 + self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") + self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") + + def case(self): + #104 characters, headers are 2+1+2+8+8+2=23 bytes, resulting in a packet size of 127 (max) + large_payload = "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZZZZZZZZ0123456789012345678901234567891234" + self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) + self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) + + self.command("First", "config-status --data_ind {}".format(large_payload)) + self.command("Second", "config-status --data_ind {}".format(large_payload)) + + self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length {} --msdu {}".format(len(large_payload), large_payload)) + self.command("Second", "wait --timeout 500") + + self.command("Second", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length {} --msdu {}".format(len(large_payload), large_payload)) + self.command("First", "wait --timeout 500") + for i in range(0, 25): + self.command("First", "data") + self.command("Second", "wait") + self.command("Second", "data") + self.command("First", "wait") + + def tearDown(self): + self.reset_dut() + +if __name__=='__main__': + sys.exit( Testcase().run() ) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/template b/TEST_APPS/testcases/nanostack_mac_tester/template new file mode 100644 index 00000000000..4335851efd0 --- /dev/null +++ b/TEST_APPS/testcases/nanostack_mac_tester/template @@ -0,0 +1,65 @@ +""" +Copyright (c) 2017, Arm Limited and affiliates. +SPDX-License-Identifier: Apache-2.0 + +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. +""" +import os,sys +# ensure that test/ directory is first choice for imports +test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if not test_dir in sys.path: + sys.path.insert(0, test_dir) +from GenericTestcase import Bench +from Error import TestStepFail + +class Testcase(Bench): + def __init__(self): + Bench.__init__(self, name = "template", # Name should be the same as the filename + title = "TITLE", + status = "development", + type = "TYPE", + subtype = "", + execution = { + "skip": { + "value": False, + "reason": "" + } + }, + author = "Valtteri Erkkila", + purpose = "", + feature = [""], + component = ["MAC"], + requirements = { + "duts": { + '*': { + "count":2, # Count must reflect the amount of specified devices below + "type": "hardware", + "application":{ "name":"generalTestApplication", "version": "1.0"}, + "rf_channel": 11 + }, + "1":{"nick": "First"}, + "2":{"nick": "Second"} + }} + ) + + def setUp(self): + pass + + def case(self): + pass + + def tearDown(self): + pass + +if __name__=='__main__': + sys.exit( Testcase().run() ) From 5eb3456b6ea094bd62d08840184f072a79ec17a9 Mon Sep 17 00:00:00 2001 From: Juhani Puurula Date: Fri, 10 Aug 2018 12:45:52 +0300 Subject: [PATCH 21/40] Added test config for mac tester --- astyle-branch.out | 0 tools/test_configs/MACTester.json | 48 ++++++++++++++++++++++++++ tools/test_configs/config_paths.json | 4 +++ tools/test_configs/target_configs.json | 4 +-- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 astyle-branch.out create mode 100644 tools/test_configs/MACTester.json diff --git a/astyle-branch.out b/astyle-branch.out new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/test_configs/MACTester.json b/tools/test_configs/MACTester.json new file mode 100644 index 00000000000..a47277bbe0d --- /dev/null +++ b/tools/test_configs/MACTester.json @@ -0,0 +1,48 @@ +{ + "config": { + "radio-type":{ + "help": "options are ATMEL, MCR20, OTHER", + "value": "ATMEL" + }, + "header-file": { + "help" : "String for including your driver header file", + "value" : "\"EthernetInterface.h\"" + }, + "object-construction" : { + "value" : "new EthernetInterface()" + }, + "connect-statement" : { + "help" : "Must use 'net' variable name", + "value" : "net->connect()" + }, + "echo-server-addr" : { + "help" : "IP address of echo server", + "value" : "\"echo.mbedcloudtesting.com\"" + }, + "echo-server-port" : { + "help" : "Port of echo server", + "value" : "7" + }, + "tcp-echo-prefix" : { + "help" : "Some servers send a prefix before echoed message", + "value" : null + }, + "sim-blockdevice": { + "help": "Simulated block device, requires sufficient heap", + "macro_name": "MBED_TEST_SIM_BLOCKDEVICE", + "value": "HeapBlockDevice" + } + }, + "macros": ["YOTTA_CFG_MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], + "target_overrides": { + "*": { + "target.features_add": ["NANOSTACK", "LOWPAN_HOST", "COMMON_PAL", "LWIP"], + "platform.stdio-convert-newlines": true, + "platform.stdio-baud-rate": 115200, + "mbed-mesh-api.heap-size": 6000, + "nanostack-hal.event_loop_thread_stack_size": 2000, + "mbed-trace.enable": true, + "nsapi.default-stack": "LWIP" + } + } +} diff --git a/tools/test_configs/config_paths.json b/tools/test_configs/config_paths.json index 34244ca98f5..27f2a8dd4c7 100644 --- a/tools/test_configs/config_paths.json +++ b/tools/test_configs/config_paths.json @@ -3,6 +3,9 @@ "HEAPBLOCKDEVICE": "HeapBlockDevice.json", "HEAPBLOCKDEVICE_AND_ETHERNET": "HeapBlockDeviceAndEthernetInterface.json", "HEAPBLOCKDEVICE_AND_WIFI": "HeapBlockDeviceAndWifiInterface.json", + "ODIN_WIFI" : "OdinInterface.json", + "ODIN_ETHERNET" : "Odin_EthernetInterface.json", + "REALTEK_WIFI" : "RealtekInterface.json", "ESP8266_WIFI" : "ESP8266Interface.json", "ISM43362_WIFI" : "ISM43362Interface.json", "IDW0XX1_WIFI" : "SpwfSAInterface.json", @@ -10,4 +13,5 @@ "6LOWPAN_ROUTER" : "6lowpanInterface_router.json", "THREAD_END_DEVICE" : "ThreadInterface_end_device.json", "THREAD_ROUTER" : "ThreadInterface_router.json" + "MAC_TESTER" : "MACTester.json" } diff --git a/tools/test_configs/target_configs.json b/tools/test_configs/target_configs.json index 85af0aebac4..e74e6dbe0d7 100644 --- a/tools/test_configs/target_configs.json +++ b/tools/test_configs/target_configs.json @@ -9,11 +9,11 @@ }, "K64F": { "default_test_configuration": "HEAPBLOCKDEVICE_AND_ETHERNET", - "test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET", "ESP8266_WIFI", "ETHERNET"] + "test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET", "MAC_TESTER", "ESP8266_WIFI", "ETHERNET"] }, "NUCLEO_F429ZI": { "default_test_configuration": "HEAPBLOCKDEVICE_AND_ETHERNET", - "test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET"] + "test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET", "MAC_TESTER"] }, "DISCO_L475VG_IOT01A": { "default_test_configuration": "NONE", From a5088962e4bb1994952621e06f80f5b29c474a58 Mon Sep 17 00:00:00 2001 From: Seppo Takalo Date: Thu, 16 Aug 2018 15:04:42 +0300 Subject: [PATCH 22/40] Use NanostackRfPhy::get_default_instance() for finding the driver (#2) --- .../device/nanostack_mac_tester/README.md | 23 +++++-------------- .../device/nanostack_mac_tester/main.cpp | 18 ++++++--------- .../device/nanostack_mac_tester/mbed_app.json | 15 +++++------- tools/test_configs/MACTester.json | 8 ++++--- 4 files changed, 24 insertions(+), 40 deletions(-) diff --git a/TEST_APPS/device/nanostack_mac_tester/README.md b/TEST_APPS/device/nanostack_mac_tester/README.md index 92b21c2d85f..a346c8e3370 100644 --- a/TEST_APPS/device/nanostack_mac_tester/README.md +++ b/TEST_APPS/device/nanostack_mac_tester/README.md @@ -15,9 +15,7 @@ You can use this application to test the Nanostack RF driver implementations tha ## Prerequisites * [Mbed CLI](https://github.com/ARMmbed/mbed-cli). -* Three supported boards with radio shields. For example, FRDM-K64F with FRDM-CR20A radio shield. - * https://os.mbed.com/platforms/FRDM-K64F/ - * https://os.mbed.com/components/FRDM-CR20A/ +* Mbed OS target board with build in radio, OR RF shield with Mbed OS driver ## Setting up the application @@ -37,24 +35,15 @@ To add your RF driver: mbed add https://www.github.com/me/my-rf-driver.git ``` -Set `radio-type` in `mbed_app.json` to `OTHER`: +When using RF shield, you need to configure it to be a default RF driver and instruct Mbed OS that RF driver is present. For example, configuring Atmel RF driver to provide default driver: ``` -"config":{ - "radio-type":{ - "value": "ATMEL" --> "value": "OTHER" - } -} + "target_overrides": { + "*": { + "target.device_has_add": ["802_15_4_RF_PHY"], + "atmel-rf.provide-default": true ``` -Replace the placeholders in `main.cpp` with your RF driver header file and interface definition: - -``` -#elif MBED_CONF_APP_RADIO_TYPE == OTHER -#include "YOUR_DRIVER_HEADER.h" -YourDriverInterface rf_phy(...); -#endif -``` ### Compile your binary diff --git a/TEST_APPS/device/nanostack_mac_tester/main.cpp b/TEST_APPS/device/nanostack_mac_tester/main.cpp index 2fd3be6a82c..4e95b4010b3 100644 --- a/TEST_APPS/device/nanostack_mac_tester/main.cpp +++ b/TEST_APPS/device/nanostack_mac_tester/main.cpp @@ -33,17 +33,12 @@ #define TRACE_GROUP "Main" #include "mbed-trace/mbed_trace.h" -#if MBED_CONF_APP_RADIO_TYPE == ATMEL -#include "NanostackRfPhyAtmel.h" -NanostackRfPhyAtmel rf_phy(ATMEL_SPI_MOSI, ATMEL_SPI_MISO, ATMEL_SPI_SCLK, ATMEL_SPI_CS, - ATMEL_SPI_RST, ATMEL_SPI_SLP, ATMEL_SPI_IRQ, ATMEL_I2C_SDA, ATMEL_I2C_SCL); -#elif MBED_CONF_APP_RADIO_TYPE == MCR20 -#include "NanostackRfPhyMcr20a.h" -NanostackRfPhyMcr20a rf_phy(MCR20A_SPI_MOSI, MCR20A_SPI_MISO, MCR20A_SPI_SCLK, MCR20A_SPI_CS, MCR20A_SPI_RST, MCR20A_SPI_IRQ); -#elif MBED_CONF_APP_RADIO_TYPE == OTHER -#include "YOUR_DRIVER_HEADER.h" -YourDriverInterface rf_phy(...); -#endif //MBED_CONF_APP_RADIO_TYPE +#include "NanostackRfPhy.h" + +#if !DEVICE_802_15_4_PHY +#error [NOT_SUPPORTED] No 802.15.4 RF driver found for this target +#endif + extern mac_api_s *mac_interface; RawSerial pc(USBTX, USBRX); @@ -94,6 +89,7 @@ static void handle_rx_data(void) static int mac_prepare(void) { + NanostackRfPhy &rf_phy = NanostackRfPhy::get_default_instance(); int8_t rf_driver_id = rf_phy.rf_register(); uint8_t rf_eui64[8]; diff --git a/TEST_APPS/device/nanostack_mac_tester/mbed_app.json b/TEST_APPS/device/nanostack_mac_tester/mbed_app.json index 9633f4b04f7..d86bfd2dc72 100644 --- a/TEST_APPS/device/nanostack_mac_tester/mbed_app.json +++ b/TEST_APPS/device/nanostack_mac_tester/mbed_app.json @@ -1,19 +1,16 @@ { - "config": { - "radio-type":{ - "help": "options are ATMEL, MCR20, OTHER", - "value": "ATMEL" - } - }, - "macros": ["YOTTA_CFG_MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], + "macros": ["MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], "target_overrides": { "*": { - "target.features_add": ["NANOSTACK", "LOWPAN_HOST", "COMMON_PAL"], + "nanostack.configuration": "lowpan_host", "platform.stdio-convert-newlines": true, "platform.stdio-baud-rate": 115200, "mbed-mesh-api.heap-size": 6000, "nanostack-hal.event_loop_thread_stack_size": 2000, - "mbed-trace.enable": true + "mbed-trace.enable": true, + "nsapi.default-stack": "LWIP", + "target.device_has_add": ["802_15_4_PHY"], + "atmel-rf.provide-default": true } } } diff --git a/tools/test_configs/MACTester.json b/tools/test_configs/MACTester.json index a47277bbe0d..f412d0f1623 100644 --- a/tools/test_configs/MACTester.json +++ b/tools/test_configs/MACTester.json @@ -33,16 +33,18 @@ "value": "HeapBlockDevice" } }, - "macros": ["YOTTA_CFG_MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], + "macros": ["MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], "target_overrides": { "*": { - "target.features_add": ["NANOSTACK", "LOWPAN_HOST", "COMMON_PAL", "LWIP"], + "nanostack.configuration": "lowpan_host", "platform.stdio-convert-newlines": true, "platform.stdio-baud-rate": 115200, "mbed-mesh-api.heap-size": 6000, "nanostack-hal.event_loop_thread_stack_size": 2000, "mbed-trace.enable": true, - "nsapi.default-stack": "LWIP" + "nsapi.default-stack": "LWIP", + "target.device_has_add": ["802_15_4_PHY"], + "atmel-rf.provide-default": true } } } From e4f4e64c3b54b65391ac6b81f1e559a1fed620f1 Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Mon, 20 Aug 2018 12:07:53 +0300 Subject: [PATCH 23/40] Use nanostack-libservice stoip4 to check if address is ipv4 --- TEST_APPS/device/socket_app/cmd_ifconfig.cpp | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp index 955756bad14..eb95fc5fe1e 100644 --- a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp @@ -19,6 +19,8 @@ #include "mbed-client-cli/ns_cmdline.h" #include "mbed-trace/mbed_trace.h" +#include "ip4string.h" + #ifndef MBED_CONF_APP_CONNECT_STATEMENT #error [NOT_SUPPORTED] No network configuration found for this target. #endif @@ -42,26 +44,16 @@ int cmd_ifconfig(int argc, char *argv[]); const char* MAN_IFCONFIG = " ifup interface up\r\n"\ " ifdown interface down\r\n"; -static bool is_ipv4(const char *str) -{ - int dot_count = 0; - for (int i = 0; str[i]; i++) { - if (str[i] == '.' && ++dot_count == 3) { - return true; - } - } - return false; -} - static void ifconfig_print() { - if(!net) + if(!net) { cmd_printf("No interface configured\r\n"); return; + } const char *str = net->get_ip_address(); - if (str) { - if (is_ipv4(str)) { + uint8_t buf[4]; + if (stoip4(str, strlen(str), buf)) { cmd_printf("IPv4 if addr: %s\r\n", str); } else { cmd_printf("IPv6 if addr:\r\n [0]: %s\r\n", str); From bf7dc1b98ee01903336676b7d91ec407e53309bb Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Mon, 20 Aug 2018 12:18:25 +0300 Subject: [PATCH 24/40] Fix copyrights in python scripts --- TEST_APPS/icetea_plugins/ip_test_parsers.py | 17 ++++++++++++++--- TEST_APPS/icetea_plugins/plugins_to_load.py | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/TEST_APPS/icetea_plugins/ip_test_parsers.py b/TEST_APPS/icetea_plugins/ip_test_parsers.py index f208dcbb6ca..79794be0a84 100644 --- a/TEST_APPS/icetea_plugins/ip_test_parsers.py +++ b/TEST_APPS/icetea_plugins/ip_test_parsers.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2018, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" import re import time from collections import OrderedDict diff --git a/TEST_APPS/icetea_plugins/plugins_to_load.py b/TEST_APPS/icetea_plugins/plugins_to_load.py index 356547618ae..1322fbcc466 100644 --- a/TEST_APPS/icetea_plugins/plugins_to_load.py +++ b/TEST_APPS/icetea_plugins/plugins_to_load.py @@ -1,6 +1,17 @@ -# -# Copyright (c) 2018, Arm Limited and affiliates. -# +""" +Copyright 2018 ARM Limited +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. +""" from ip_test_parsers import IpTestParsers plugins_to_load = { From 8a107acaed66c2e724c101cc1eca1d4a83f8184f Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Mon, 20 Aug 2018 12:48:20 +0300 Subject: [PATCH 25/40] Remove unused aliases --- TEST_APPS/device/socket_app/cmd_socket.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/TEST_APPS/device/socket_app/cmd_socket.cpp b/TEST_APPS/device/socket_app/cmd_socket.cpp index 739ca898562..cd9013e56bd 100644 --- a/TEST_APPS/device/socket_app/cmd_socket.cpp +++ b/TEST_APPS/device/socket_app/cmd_socket.cpp @@ -314,9 +314,6 @@ static void sigio_handler(SInfo *info) { void cmd_socket_init(void) { cmd_add("socket", cmd_socket, "socket", MAN_SOCKET); - cmd_alias_add("socket help", "socket -h"); - cmd_alias_add("socket --help", "socket -h"); - cmd_alias_add("ping server start", "socket echo-server new start"); } int handle_nsapi_error(const char *function, nsapi_error_t ret) From 449ef92b4db484163c9941c4d581bb4a5e4e18d2 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 12:48:59 +0300 Subject: [PATCH 26/40] Update licenses --- tools/run_icetea.py | 5 +---- tools/test/run_icetea/TEST_DIR/test_pass.py | 2 +- tools/test/run_icetea/TEST_DIR/test_print.py | 2 +- tools/test/run_icetea/TEST_DIR_HW/__init__.py | 14 -------------- tools/test/run_icetea/TEST_DIR_HW/test_K64F.py | 2 +- .../TEST_DIR_HW/test_predefined_platforms.py | 2 +- tools/test/run_icetea/run_icetea_test.py | 6 ++---- tools/test/run_icetea/run_icetea_unittest.py | 6 ++---- 8 files changed, 9 insertions(+), 30 deletions(-) diff --git a/tools/run_icetea.py b/tools/run_icetea.py index 94283ce7307..689660d9041 100644 --- a/tools/run_icetea.py +++ b/tools/run_icetea.py @@ -1,8 +1,6 @@ #! /usr/bin/env python2 """ -mbed SDK -Copyright (c) 2011-2013 ARM Limited - +Copyright 2018 ARM Limited 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 @@ -14,7 +12,6 @@ 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. - """ from __future__ import print_function, division, absolute_import diff --git a/tools/test/run_icetea/TEST_DIR/test_pass.py b/tools/test/run_icetea/TEST_DIR/test_pass.py index 4e1f646a9f7..f834ea99c46 100644 --- a/tools/test/run_icetea/TEST_DIR/test_pass.py +++ b/tools/test/run_icetea/TEST_DIR/test_pass.py @@ -1,5 +1,5 @@ """ -Copyright 2017 ARM Limited +Copyright 2018 ARM Limited 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 diff --git a/tools/test/run_icetea/TEST_DIR/test_print.py b/tools/test/run_icetea/TEST_DIR/test_print.py index 04654acbeff..b5158b4e7b7 100644 --- a/tools/test/run_icetea/TEST_DIR/test_print.py +++ b/tools/test/run_icetea/TEST_DIR/test_print.py @@ -1,5 +1,5 @@ """ -Copyright 2017 ARM Limited +Copyright 2018 ARM Limited 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 diff --git a/tools/test/run_icetea/TEST_DIR_HW/__init__.py b/tools/test/run_icetea/TEST_DIR_HW/__init__.py index ecb93799a76..e69de29bb2d 100644 --- a/tools/test/run_icetea/TEST_DIR_HW/__init__.py +++ b/tools/test/run_icetea/TEST_DIR_HW/__init__.py @@ -1,14 +0,0 @@ -""" -Copyright 2017 ARM Limited -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. -""" \ No newline at end of file diff --git a/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py b/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py index 778af71a849..8e9fc196e5f 100644 --- a/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py +++ b/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py @@ -1,5 +1,5 @@ """ -Copyright 2017 ARM Limited +Copyright 2018 ARM Limited 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 diff --git a/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py b/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py index 15735913d4c..8e5145b25cb 100644 --- a/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py +++ b/tools/test/run_icetea/TEST_DIR_HW/test_predefined_platforms.py @@ -1,5 +1,5 @@ """ -Copyright 2017 ARM Limited +Copyright 2018 ARM Limited 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 diff --git a/tools/test/run_icetea/run_icetea_test.py b/tools/test/run_icetea/run_icetea_test.py index ad1e29def4b..393a3d0262a 100644 --- a/tools/test/run_icetea/run_icetea_test.py +++ b/tools/test/run_icetea/run_icetea_test.py @@ -1,12 +1,10 @@ """ -mbed SDK -Copyright (c) 2016 ARM Limited - +Copyright 2018 ARM Limited 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 + 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, diff --git a/tools/test/run_icetea/run_icetea_unittest.py b/tools/test/run_icetea/run_icetea_unittest.py index bb94c3efeab..052f5a40132 100644 --- a/tools/test/run_icetea/run_icetea_unittest.py +++ b/tools/test/run_icetea/run_icetea_unittest.py @@ -1,12 +1,10 @@ """ -mbed SDK -Copyright (c) 2016 ARM Limited - +Copyright 2018 ARM Limited 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 + 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, From b4822c8631d09d683de7364cf2f9d4f627b9d1f1 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 12:50:35 +0300 Subject: [PATCH 27/40] Fix typo --- tools/test/run_icetea/TEST_DIR_HW/test_K64F.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py b/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py index 8e9fc196e5f..f25ddf8d569 100644 --- a/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py +++ b/tools/test/run_icetea/TEST_DIR_HW/test_K64F.py @@ -20,7 +20,7 @@ class Testcase(Bench): def __init__(self): Bench.__init__(self, name="test_K64F_only", - title="Test a test case which have onely K64F support", + title="Test a test case which have only K64F support", status="released", purpose="Just for testing scripts", component=[], From cae3d1d34e67754999e42ed257460ccc840aa71f Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Mon, 20 Aug 2018 18:59:31 +0300 Subject: [PATCH 28/40] update example --- TEST_APPS/testcases/example/test_cmdline.py | 10 +++++----- .../netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/TEST_APPS/testcases/example/test_cmdline.py b/TEST_APPS/testcases/example/test_cmdline.py index d1dbcd7ea0f..52a22e92027 100644 --- a/TEST_APPS/testcases/example/test_cmdline.py +++ b/TEST_APPS/testcases/example/test_cmdline.py @@ -18,7 +18,6 @@ class Testcase(Bench): def __init__(self): - self.device = None Bench.__init__(self, name="test_cmdline", title="Smoke test for command line interface", @@ -34,17 +33,18 @@ def __init__(self): "application": { "name": "TEST_APPS-device-exampleapp" } - } + }, + "1": {"nick": "dut1"}, } } ) def setup(self): - self.device = self.get_node_endpoint(1) + pass def case(self): - self.command(1, "echo hello world", timeout=5) - self.device.command("help") + self.command("dut1", "echo hello world") + self.command("dut1", "help") def teardown(self): pass diff --git a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py index 0d7eb932639..3ac98da062e 100644 --- a/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py +++ b/TEST_APPS/testcases/netsocket/TCPSOCKET_ECHOTEST_BURST_SHORT.py @@ -34,7 +34,9 @@ def __init__(self): '*': { # requirements for all nodes "count": 1, "type": "hardware", - "application": {"name": "TEST_APPS-device-socket_app"} + "application": { + "name": "TEST_APPS-device-socket_app" + } }, "1": {"nick": "dut1"}, } From 2d787c594be5c74e1f0c4b12b379935b3bf3fb93 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 21 Aug 2018 09:22:45 +0300 Subject: [PATCH 29/40] Autoformatted mac tests --- .../testcases/nanostack_mac_tester/ED_scan.py | 83 ++++++++++-------- .../address_read_and_write.py | 66 +++++++------- .../create_and_join_PAN.py | 80 ++++++++--------- .../nanostack_mac_tester/send_data.py | 66 +++++++------- .../send_data_indirect.py | 87 ++++++++++--------- .../send_large_payloads.py | 76 ++++++++-------- .../testcases/nanostack_mac_tester/template | 2 +- 7 files changed, 243 insertions(+), 217 deletions(-) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py b/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py index 3fedf1e6c96..746fb271383 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py +++ b/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py @@ -16,41 +16,42 @@ """ import threading -import os,sys +import sys from icetea_lib.bench import Bench + class Testcase(Bench): def __init__(self): - Bench.__init__(self, name = "ED_scan", - title = "ED scan test", - status = "development", - type = "smoke", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "Tests reading the ED values from channels 11-16", - feature = ["MLME-SCAN (ED)"], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":3, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { + Bench.__init__(self, name="ED_scan", + title="ED scan test", + status="development", + type="smoke", + subtype="", + execution={ + "skip": { + "value": False, + "reason": "" + } + }, + author="Valtteri Erkkila", + purpose="Tests reading the ED values from channels 11-16", + feature=["MLME-SCAN (ED)"], + component=["MAC"], + requirements={ + "duts": { + '*': { + "count": 3, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1":{"nick": "First"}, - "2":{"nick": "Second"}, - "3":{"nick": "Third"} - }} - ) + } + }, + "1": {"nick": "First"}, + "2": {"nick": "Second"}, + "3": {"nick": "Third"} + }} + ) def setUp(self): self.channel = 11 @@ -61,14 +62,18 @@ def setUp(self): def spam_channel(self, event): while not event.wait(0.1): self.lock_th.acquire() - self.command("First", "data --dst_addr 01:02:03:00:00:00:00:03 --msdu {} --msdu_length {} --wait_for_confirm false".format(self.payload, len(self.payload))) - self.command("Third", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu {} --msdu_length {} --wait_for_confirm false".format(self.payload, len(self.payload))) + self.command("First", + "data --dst_addr 01:02:03:00:00:00:00:03 --msdu {} --msdu_length {} --wait_for_confirm false".format( + self.payload, len(self.payload))) + self.command("Third", + "data --dst_addr 01:02:03:00:00:00:00:01 --msdu {} --msdu_length {} --wait_for_confirm false".format( + self.payload, len(self.payload))) self.lock_th.release() def mask_from_channel_list(self, channels): res = 0 for ch in channels: - res = res | ( 1 << ch) + res = res | (1 << ch) return hex(res) def case(self): @@ -79,7 +84,7 @@ def case(self): self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - #No reason to print their spamming + # No reason to print their spamming self.command("First", "silent-mode on") self.command("Third", "silent-mode on") @@ -87,7 +92,7 @@ def case(self): self.th = threading.Thread(target=self.spam_channel, args=(self.stop_event,)) self.th.start() self.stopped = True - channels = range(11,27) + channels = range(11, 27) for i in range(0, 3): self.lock_th.acquire() self.command("First", "mlme-reset") @@ -95,7 +100,8 @@ def case(self): self.command("Third", "mlme-reset") self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) self.lock_th.release() - self.command("Second", "scan --scan_type 0 --scan_duration 7 --channel_mask {}".format(self.mask_from_channel_list(channels))) + self.command("Second", "scan --scan_type 0 --scan_duration 7 --channel_mask {}".format( + self.mask_from_channel_list(channels))) self.command("Second", "analyze-ed --channel {} --above 100".format(self.channel)) def tearDown(self): @@ -106,5 +112,6 @@ def tearDown(self): del self.th self.reset_dut() -if __name__=='__main__': - sys.exit( Testcase().run() ) + +if __name__ == '__main__': + sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py b/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py index 7b1d34fd781..0ef66aadffd 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py +++ b/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2017, Arm Limited and affiliates. +Copyright (c) 2018, Arm Limited and affiliates. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,39 +15,40 @@ limitations under the License. """ -import os,sys +import sys from icetea_lib.bench import Bench + class Testcase(Bench): def __init__(self): - Bench.__init__(self, name = "address_read_and_write", - title = "MAC address and PAN id read/write test", - status = "development", - type = "smoke", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "Tests reading a MAC address from the driver, and writing to the modifiable MAC address", - feature = ["MLME-SET"], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":1, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { + Bench.__init__(self, name="address_read_and_write", + title="MAC address and PAN id read/write test", + status="development", + type="smoke", + subtype="", + execution={ + "skip": { + "value": False, + "reason": "" + } + }, + author="Valtteri Erkkila", + purpose="Tests reading a MAC address from the driver, and writing to the modifiable MAC address", + feature=["MLME-SET"], + component=["MAC"], + requirements={ + "duts": { + '*': { + "count": 1, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1":{"nick": "First"} - }} - ) + } + }, + "1": {"nick": "First"} + }} + ) def setUp(self): pass @@ -56,7 +57,7 @@ def case(self): self.command("First", "addr") self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") self.command("First", "addr --16-bit 0xABCD") - #macPANId + # macPANId self.command("First", "mlme-set --attr 0x50 --value_bytes CD:CD --value_size 2") self.command("First", "addr") self.verify_trace(1, "MAC64: 01:02:03:00:00:00:00:01") @@ -64,5 +65,6 @@ def case(self): def tearDown(self): self.reset_dut() -if __name__=='__main__': - sys.exit( Testcase().run() ) + +if __name__ == '__main__': + sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py b/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py index 13819ad0e5f..0ac0088d3ca 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py +++ b/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2017, Arm Limited and affiliates. +Copyright (c) 2018, Arm Limited and affiliates. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,53 +15,54 @@ limitations under the License. """ -import os,sys +import sys from icetea_lib.bench import Bench + class Testcase(Bench): def __init__(self): - Bench.__init__(self, name = "create_and_join_PAN", - title = "Create a PAN and have a device join it", - status = "development", - type = "smoke", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "", - feature = ["MLME-START", "MLME-SCAN (active)"], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":3, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { + Bench.__init__(self, name="create_and_join_PAN", + title="Create a PAN and have a device join it", + status="development", + type="smoke", + subtype="", + execution={ + "skip": { + "value": False, + "reason": "" + } + }, + author="Valtteri Erkkila", + purpose="", + feature=["MLME-START", "MLME-SCAN (active)"], + component=["MAC"], + requirements={ + "duts": { + '*': { + "count": 3, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1":{"nick": "First"}, - "2":{"nick": "Second"}, - "3":{"nick": "Third"} - }} - ) + } + }, + "1": {"nick": "First"}, + "2": {"nick": "Second"}, + "3": {"nick": "Third"} + }} + ) def mask_from_channel_list(self, channels): res = 0 for ch in channels: - res = res | ( 1 << ch) + res = res | (1 << ch) return hex(res) def setUp(self): self.channel = 11 def case(self): - #Beacon payload & length + # Beacon payload & length self.command("First", "mlme-set --attr 0x45 --value_ascii mac-tester --value_size 10") self.command("First", "mlme-set --attr 0x46 --value_uint8 10 --value_size 1") @@ -69,14 +70,14 @@ def case(self): self.command("Second", "mlme-set --attr 0x46 --value_uint8 17 --value_size 1") self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Second", "start --pan_coordinator true --logical_channel {}".format(int(self.channel)+1)) + self.command("Second", "start --pan_coordinator true --logical_channel {}".format(int(self.channel) + 1)) self.delay(3) if self.channel == 11: - channels = [11,12] + channels = [11, 12] elif self.channel == 26: - channels = [25,26] + channels = [25, 26] else: - channels = [self.channel, self.channel+1] + channels = [self.channel, self.channel + 1] self.command("Third", "scan --channel_mask {}".format(self.mask_from_channel_list(channels))) self.delay(0.2) self.command("Third", "find-beacon --data mac-tester") @@ -85,5 +86,6 @@ def case(self): def tearDown(self): self.reset_dut() -if __name__=='__main__': - sys.exit( Testcase().run() ) + +if __name__ == '__main__': + sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_data.py b/TEST_APPS/testcases/nanostack_mac_tester/send_data.py index e647169e4b8..5d00222f064 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/send_data.py +++ b/TEST_APPS/testcases/nanostack_mac_tester/send_data.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2017, Arm Limited and affiliates. +Copyright (c) 2018, Arm Limited and affiliates. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,40 +15,41 @@ limitations under the License. """ -import os,sys +import sys from icetea_lib.bench import Bench + class Testcase(Bench): def __init__(self): - Bench.__init__(self, name = "send_data", - title = "Simple data transmission test", - status = "development", - type = "smoke", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "Tests that sending data works", - feature = ["MCPS-DATA"], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":2, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { + Bench.__init__(self, name="send_data", + title="Simple data transmission test", + status="development", + type="smoke", + subtype="", + execution={ + "skip": { + "value": False, + "reason": "" + } + }, + author="Valtteri Erkkila", + purpose="Tests that sending data works", + feature=["MCPS-DATA"], + component=["MAC"], + requirements={ + "duts": { + '*': { + "count": 2, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1":{"nick": "First"}, - "2":{"nick": "Second"} - }} - ) + } + }, + "1": {"nick": "First"}, + "2": {"nick": "Second"} + }} + ) def setUp(self): self.channel = 11 @@ -65,5 +66,6 @@ def case(self): def tearDown(self): self.reset_dut() -if __name__=='__main__': - sys.exit( Testcase().run() ) + +if __name__ == '__main__': + sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py b/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py index be73162d645..30fc4530834 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py +++ b/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2017, Arm Limited and affiliates. +Copyright (c) 2018, Arm Limited and affiliates. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,41 +15,42 @@ limitations under the License. """ -import os,sys +import sys from icetea_lib.bench import Bench + class Testcase(Bench): def __init__(self): - Bench.__init__(self, name = "send_data_indirect", - title = "Indirect data transmission test", - status = "development", - type = "smoke", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "Tests sending data indirectly, i.e polling the coordinator for data", - feature = ["MCPS-DATA", "MLME-POLL"], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":3, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { + Bench.__init__(self, name="send_data_indirect", + title="Indirect data transmission test", + status="development", + type="smoke", + subtype="", + execution={ + "skip": { + "value": False, + "reason": "" + } + }, + author="Valtteri Erkkila", + purpose="Tests sending data indirectly, i.e polling the coordinator for data", + feature=["MCPS-DATA", "MLME-POLL"], + component=["MAC"], + requirements={ + "duts": { + '*': { + "count": 3, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1":{"nick": "First"}, - "2":{"nick": "Second"}, - "3":{"nick": "Third"} - }} - ) + } + }, + "1": {"nick": "First"}, + "2": {"nick": "Second"}, + "3": {"nick": "Third"} + }} + ) def setUp(self): self.channel = 11 @@ -62,21 +63,26 @@ def case(self): self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - #macRxOnWhenIdle + # macRxOnWhenIdle self.command("Second", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1") self.command("Third", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1") - self.command("First", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:02 --pan_id 0x1234 --index 0") - self.command("First", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:03 --pan_id 0x1234 --index 1") - self.command("Second", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") - self.command("Third", "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") + self.command("First", + "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:02 --pan_id 0x1234 --index 0") + self.command("First", + "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:03 --pan_id 0x1234 --index 1") + self.command("Second", + "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") + self.command("Third", + "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") self.command("Second", "config-status --data_ind abcde") self.command("Third", "config-status --data_ind 12345") - #Runs into timing issues if extensive printing is enabled + # Runs into timing issues if extensive printing is enabled self.command("*", "silent-mode on") - self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde --indirect_tx true --wait_for_confirm false") + self.command("First", + "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde --indirect_tx true --wait_for_confirm false") self.command("Second", "poll --coord_address 01:02:03:00:00:00:00:01") self.command("First", "data") @@ -95,5 +101,6 @@ def case(self): def tearDown(self): self.reset_dut() -if __name__=='__main__': - sys.exit( Testcase().run() ) + +if __name__ == '__main__': + sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py b/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py index 478a2ea423d..df2dd371b17 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py +++ b/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py @@ -1,5 +1,5 @@ """ -Copyright (c) 2017, Arm Limited and affiliates. +Copyright (c) 2018, Arm Limited and affiliates. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,40 +15,41 @@ limitations under the License. """ -import os,sys +import sys from icetea_lib.bench import Bench + class Testcase(Bench): def __init__(self): - Bench.__init__(self, name = "send_large_payloads", - title = "Data transmission test with large packets", - status = "development", - type = "reliability", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "Repeatedly sends long packets, checking that the payload is correct in each one", - feature = ["MCPS-DATA"], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":2, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { + Bench.__init__(self, name="send_large_payloads", + title="Data transmission test with large packets", + status="development", + type="reliability", + subtype="", + execution={ + "skip": { + "value": False, + "reason": "" + } + }, + author="Valtteri Erkkila", + purpose="Repeatedly sends long packets, checking that the payload is correct in each one", + feature=["MCPS-DATA"], + component=["MAC"], + requirements={ + "duts": { + '*': { + "count": 2, + "type": "hardware", + "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], + "application": { "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1":{"nick": "First"}, - "2":{"nick": "Second"} - }} - ) + } + }, + "1": {"nick": "First"}, + "2": {"nick": "Second"} + }} + ) def setUp(self): self.channel = 11 @@ -56,7 +57,7 @@ def setUp(self): self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") def case(self): - #104 characters, headers are 2+1+2+8+8+2=23 bytes, resulting in a packet size of 127 (max) + # 104 characters, headers are 2+1+2+8+8+2=23 bytes, resulting in a packet size of 127 (max) large_payload = "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZZZZZZZZ0123456789012345678901234567891234" self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) @@ -64,10 +65,14 @@ def case(self): self.command("First", "config-status --data_ind {}".format(large_payload)) self.command("Second", "config-status --data_ind {}".format(large_payload)) - self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length {} --msdu {}".format(len(large_payload), large_payload)) + self.command("First", + "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length {} --msdu {}".format(len(large_payload), + large_payload)) self.command("Second", "wait --timeout 500") - self.command("Second", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length {} --msdu {}".format(len(large_payload), large_payload)) + self.command("Second", + "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length {} --msdu {}".format(len(large_payload), + large_payload)) self.command("First", "wait --timeout 500") for i in range(0, 25): self.command("First", "data") @@ -78,5 +83,6 @@ def case(self): def tearDown(self): self.reset_dut() -if __name__=='__main__': - sys.exit( Testcase().run() ) + +if __name__ == '__main__': + sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/template b/TEST_APPS/testcases/nanostack_mac_tester/template index 4335851efd0..ffc79b672b4 100644 --- a/TEST_APPS/testcases/nanostack_mac_tester/template +++ b/TEST_APPS/testcases/nanostack_mac_tester/template @@ -1,5 +1,5 @@ """ -Copyright (c) 2017, Arm Limited and affiliates. +Copyright (c) 2018, Arm Limited and affiliates. SPDX-License-Identifier: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); From 564fdd5a21ee6b5271084de0eabab90f3dca7750 Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Tue, 21 Aug 2018 10:49:03 +0300 Subject: [PATCH 30/40] Astyle for test applications --- TEST_APPS/device/exampleapp/main.cpp | 8 +- .../source/mac_commands.cpp | 52 +-- .../nanostack_mac_tester/source/util.cpp | 104 +++-- TEST_APPS/device/socket_app/cmd_ifconfig.cpp | 91 ++-- TEST_APPS/device/socket_app/cmd_ifconfig.h | 4 +- TEST_APPS/device/socket_app/cmd_socket.cpp | 436 +++++++++++------- TEST_APPS/device/socket_app/main.cpp | 8 +- TEST_APPS/device/socket_app/strconv.c | 4 +- 8 files changed, 448 insertions(+), 259 deletions(-) diff --git a/TEST_APPS/device/exampleapp/main.cpp b/TEST_APPS/device/exampleapp/main.cpp index 36d1aeb6850..0eca93a0f08 100644 --- a/TEST_APPS/device/exampleapp/main.cpp +++ b/TEST_APPS/device/exampleapp/main.cpp @@ -35,7 +35,8 @@ void cmd_ready_cb(int retcode) cmd_next(retcode); } -void wrap_printf(const char *f, va_list a) { +void wrap_printf(const char *f, va_list a) +{ vprintf(f, a); } @@ -44,13 +45,14 @@ int main() cmd_init(&wrap_printf); int c; - while((c = getchar()) != EOF) { + while ((c = getchar()) != EOF) { cmd_char_input(c); } return 0; } -FileHandle* mbed::mbed_override_console(int) { +FileHandle *mbed::mbed_override_console(int) +{ static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); diff --git a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp index 40066d74f51..0274ca9ca3a 100644 --- a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp +++ b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp @@ -404,7 +404,7 @@ static int handle_security_args(int argc, char *argv[], mlme_security_t *key) } } if (cmd_parameter_val(argc, argv, "--key_source", &str)) { - if (strlen(str) == 2*8+7) { + if (strlen(str) == 2 * 8 + 7) { if (string_to_bytes(str, key->Keysource, 8) != 0) { return CMDLINE_RETCODE_INVALID_PARAMETERS; } @@ -423,9 +423,9 @@ static void add_beacon(const uint8_t *beacon, uint8_t len) } unsigned int cur_beacon = received_beacons.count; received_beacons.beacon_lengths[cur_beacon] = len; - received_beacons.beacons[cur_beacon] = (char*)ns_dyn_mem_temporary_alloc(len <= 75 ? 75:len); + received_beacons.beacons[cur_beacon] = (char *)ns_dyn_mem_temporary_alloc(len <= 75 ? 75 : len); if (len != 0) { - std::memcpy(received_beacons.beacons[cur_beacon], beacon, len); + std::memcpy(received_beacons.beacons[cur_beacon], beacon, len); } ++received_beacons.count; } @@ -501,7 +501,7 @@ void mac_data_indication_handler(const mac_api_t *api, const mcps_data_ind_t *da if (data->msduLength != expected_statuses.data_ind_len) { return; } - if (strncmp((const char*)data->msdu_ptr, (const char*)expected_statuses.data_ind, expected_statuses.data_ind_len) == 0) { + if (strncmp((const char *)data->msdu_ptr, (const char *)expected_statuses.data_ind, expected_statuses.data_ind_len) == 0) { ++data_count; } else { tr_warn("Received unexpected data!"); @@ -533,13 +533,13 @@ void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const voi break; } case MLME_GET: { - mlme_get_conf_t *get_data = (mlme_get_conf_t*)data; + mlme_get_conf_t *get_data = (mlme_get_conf_t *)data; cmd_printf("MLME-GET.confirm\n"); if (!silent_mode) { cmd_printf("status: %hhu (%s)\n", get_data->status, mlme_status_string(get_data->status)); cmd_printf("attr: %hhu\n", get_data->attr); cmd_printf("attr_index: %hhu\n", get_data->attr_index); - cmd_printf("value_pointer: %s\n", mbed_trace_array((uint8_t*)get_data->value_pointer, get_data->value_size)); + cmd_printf("value_pointer: %s\n", mbed_trace_array((uint8_t *)get_data->value_pointer, get_data->value_size)); cmd_printf("value_size: %hhu\n", get_data->value_size); } if (get_data->status == expected_statuses.get_conf) { @@ -559,7 +559,7 @@ void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const voi break; } case MLME_SCAN: { - mlme_scan_conf_t *scan_data = (mlme_scan_conf_t*)data; + mlme_scan_conf_t *scan_data = (mlme_scan_conf_t *)data; cmd_printf("MLME-SCAN.confirm\n"); if (!silent_mode) { cmd_printf("status: %hhu (%s)\n", scan_data->status, mlme_status_string(scan_data->status)); @@ -594,7 +594,7 @@ void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const voi break; } case MLME_POLL: { - mlme_poll_conf_t *poll_data = (mlme_poll_conf_t*)data; + mlme_poll_conf_t *poll_data = (mlme_poll_conf_t *)data; cmd_printf("MLME-POLL.confirm\n"); if (!silent_mode) { cmd_printf("status: %hhu (%s)\n", poll_data->status, mlme_status_string(poll_data->status)); @@ -630,7 +630,7 @@ void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const break; } case MLME_BEACON_NOTIFY: { - const mlme_beacon_ind_t *beacon_ind = (mlme_beacon_ind_t*)data; + const mlme_beacon_ind_t *beacon_ind = (mlme_beacon_ind_t *)data; cmd_printf("MLME-BEACON-NOTIFY.indication\n"); if (!silent_mode) { cmd_printf("BSN: %hhu\n", beacon_ind->BSN); @@ -638,7 +638,7 @@ void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const cmd_printf("PendAddrSpec.short_address_count %u\n", beacon_ind->PendAddrSpec.short_address_count); cmd_printf("PendAddrSpec.extended_address_count %u\n", beacon_ind->PendAddrSpec.extended_address_count); cmd_printf("AddrList %s\n", mbed_trace_array(beacon_ind->AddrList, beacon_ind->PendAddrSpec.short_address_count * 2 + - beacon_ind->PendAddrSpec.extended_address_count * 8)); + beacon_ind->PendAddrSpec.extended_address_count * 8)); cmd_printf("beacon_data_length %hu\n", beacon_ind->beacon_data_length); cmd_printf("beacon_data %s\n", mbed_trace_array(beacon_ind->beacon_data, beacon_ind->beacon_data_length)); } @@ -653,7 +653,7 @@ void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const } case MLME_COMM_STATUS: { cmd_printf("MLME-COMM-STATUS.indication\n"); - const mlme_comm_status_t *comm_status_ind_data = (mlme_comm_status_t*)data; + const mlme_comm_status_t *comm_status_ind_data = (mlme_comm_status_t *)data; if (!silent_mode) { cmd_printf("PANId: 0x%04X\n", comm_status_ind_data->PANId); cmd_printf("SrcAddrMode: %u\n", comm_status_ind_data->SrcAddrMode); @@ -820,7 +820,7 @@ int mac_data_command(int argc, char *argv[]) if (strlen(str) != data_req.msduLength) { return CMDLINE_RETCODE_INVALID_PARAMETERS; } - data_req.msdu = (uint8_t*)ns_dyn_mem_temporary_alloc(data_req.msduLength); + data_req.msdu = (uint8_t *)ns_dyn_mem_temporary_alloc(data_req.msduLength); if (data_req.msdu == NULL) { tr_error("Failed to allocate memory for the msdu"); return CMDLINE_RETCODE_FAIL; @@ -877,7 +877,7 @@ int mac_poll_command(int argc, char *argv[]) } } if (cmd_parameter_val(argc, argv, "--coord_address", &str)) { - int len = (poll_req.CoordAddrMode == 2 ? 2: 8); + int len = (poll_req.CoordAddrMode == 2 ? 2 : 8); if (string_to_bytes(str, poll_req.CoordAddress, len) != 0) { return CMDLINE_RETCODE_INVALID_PARAMETERS; } @@ -913,7 +913,7 @@ int mac_set_command(int argc, char *argv[]) uint16_t val_uint16 = 0; uint32_t val_uint32 = 0; uint8_t *val_ptr_array = NULL; - + cmd_printf("MLME-SET.request\n"); if (cmd_parameter_val(argc, argv, "--attr", &str)) { uint32_t attribute = strtoul(str, NULL, 16); @@ -931,7 +931,7 @@ int mac_set_command(int argc, char *argv[]) } } if (cmd_parameter_val(argc, argv, "--value_ascii", &str)) { - val_ptr_array = (uint8_t*)ns_dyn_mem_temporary_alloc(strlen(str)); + val_ptr_array = (uint8_t *)ns_dyn_mem_temporary_alloc(strlen(str)); if (val_ptr_array == NULL) { tr_error("Failed to allocate memory for MLME-SET.request"); return CMDLINE_RETCODE_FAIL; @@ -940,7 +940,7 @@ int mac_set_command(int argc, char *argv[]) set_req.value_pointer = val_ptr_array; } else if (cmd_parameter_val(argc, argv, "--value_bytes", &str)) { size_t bytes = (strlen(str) + 1) / 3; - val_ptr_array = (uint8_t*)ns_dyn_mem_temporary_alloc(bytes); + val_ptr_array = (uint8_t *)ns_dyn_mem_temporary_alloc(bytes); if (val_ptr_array == NULL) { tr_error("Failed to allocate memory for MLME-SET.request"); return CMDLINE_RETCODE_FAIL; @@ -1072,7 +1072,7 @@ static int key_config_command(int argc, char *argv[]) int lookup_index = 0, device_index = 0, usage_index = 0; if (cmd_parameter_val(argc, argv, "--key", &str)) { - if (strlen(str) == 2*16+15) { + if (strlen(str) == 2 * 16 + 15) { if (string_to_bytes(str, key_descriptor.Key, 16) != 0) { return CMDLINE_RETCODE_INVALID_PARAMETERS; } @@ -1109,7 +1109,7 @@ static int key_config_command(int argc, char *argv[]) } } if (cmd_parameter_val(argc, argv, "--lookup_data", &str)) { - if (strlen(str) == 2*9+8) { + if (strlen(str) == 2 * 9 + 8) { if (string_to_bytes(str, key_descriptor.KeyIdLookupList[lookup_index].LookupData, 9) != 0) { return CMDLINE_RETCODE_INVALID_PARAMETERS; } @@ -1224,7 +1224,7 @@ int mac_add_neighbour_command(int argc, char *argv[]) } } if (cmd_parameter_val(argc, argv, "--mac64", &str)) { - if (strlen(str) == 2*8+7) { + if (strlen(str) == 2 * 8 + 7) { if (string_to_bytes(str, neighbour.ExtAddress, 8) != 0) { return CMDLINE_RETCODE_INVALID_PARAMETERS; } @@ -1268,7 +1268,7 @@ static int filter_start(int argc, char *argv[]) } else if (strcmp(str, "fixed") == 0) { int32_t lqi, dbm; if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi) && - cmd_parameter_int(argc, argv, "--dbm_m", &dbm)) { + cmd_parameter_int(argc, argv, "--dbm_m", &dbm)) { return mac_filter_start(mac_interface->parent_id, MAC_FILTER_FIXED(lqi, dbm)) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; } } @@ -1284,9 +1284,9 @@ static int filter_add(int argc, char *argv[]) int32_t lqi_m, lqi_add, dbm_m, dbm_add; if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi_m) && - cmd_parameter_int(argc, argv, "--lqi_add", &lqi_add) && - cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m) && - cmd_parameter_int(argc, argv, "--dbm_add", &dbm_add)) { + cmd_parameter_int(argc, argv, "--lqi_add", &lqi_add) && + cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m) && + cmd_parameter_int(argc, argv, "--dbm_add", &dbm_add)) { } else if (cmd_parameter_val(argc, argv, "--mode", &str)) { if (strcmp(str, "allow")) { lqi_m = dbm_m = 256; @@ -1296,7 +1296,7 @@ static int filter_add(int argc, char *argv[]) } else if (strcmp(str, "fixed")) { lqi_add = dbm_add = 0; if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi_m) && - cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m)) { + cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m)) { } else { return CMDLINE_RETCODE_INVALID_PARAMETERS; } @@ -1422,7 +1422,7 @@ int mac_config_status_command(int argc, char *argv[]) } else if (cmd_parameter_val(argc, argv, "--data_ind", &str)) { size_t len = strlen(str); ns_dyn_mem_free(expected_statuses.data_ind); - expected_statuses.data_ind = (uint8_t*)ns_dyn_mem_temporary_alloc(len); + expected_statuses.data_ind = (uint8_t *)ns_dyn_mem_temporary_alloc(len); expected_statuses.data_ind_len = len; std::memcpy(expected_statuses.data_ind, str, len); } else if (cmd_parameter_int(argc, argv, "--get", &val)) { @@ -1539,7 +1539,7 @@ int mac_analyze_ed_command(int argc, char *argv[]) return CMDLINE_RETCODE_INVALID_PARAMETERS; } } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; + return CMDLINE_RETCODE_INVALID_PARAMETERS; } if (cmd_parameter_int(argc, argv, "--above", &val)) { diff --git a/TEST_APPS/device/nanostack_mac_tester/source/util.cpp b/TEST_APPS/device/nanostack_mac_tester/source/util.cpp index 286c6d68cad..7a914a7edbb 100644 --- a/TEST_APPS/device/nanostack_mac_tester/source/util.cpp +++ b/TEST_APPS/device/nanostack_mac_tester/source/util.cpp @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - #include "util.h" +#include "util.h" int string_to_bytes(const char *str, uint8_t *buf, int bytes) { @@ -38,40 +38,74 @@ int string_to_bytes(const char *str, uint8_t *buf, int bytes) const char *mlme_status_string(uint8_t status) { switch (status) { - case MLME_SUCCESS: return "MLME_SUCCESS"; - case MLME_BUSY_CHAN: return "MLME_BUSY_CHAN"; - case MLME_BUSY_RX: return "MLME_BUSY_RX"; - case MLME_BUSY_TX: return "MLME_BUSY_TX"; - case MLME_FORCE_TRX_OFF: return "MLME_FORCE_TRX_OFF"; - case MLME_IDLE: return "MLME_IDLE"; - case MLME_RX_ON: return "MLME_RX_ON"; - case MLME_TRX_OFF: return "MLME_TRX_OFF"; - case MLME_TX_ON: return "MLME_TX_ON"; - case MLME_COUNTER_ERROR: return "MLME_COUNTER_ERROR"; - case MLME_IMPROPER_KEY_TYPE: return "MLME_IMPROPER_KEY_TYPE"; - case MLME_IMPROPER_SECURITY_LEVEL: return "MLME_IMPROPER_SECURITY_LEVEL"; - case MLME_UNSUPPORTED_LEGACY: return "MLME_UNSUPPORTED_LEGACY"; - case MLME_UNSUPPORTED_SECURITY: return "MLME_UNSUPPORTED_SECURITY"; - case MLME_SECURITY_FAIL: return "MLME_SECURITY_FAIL"; - case MLME_FRAME_TOO_LONG: return "MLME_FRAME_TOO_LONG"; - case MLME_INVALID_HANDLE: return "MLME_INVALID_HANDLE"; - case MLME_INVALID_PARAMETER: return "MLME_INVALID_PARAMETER"; - case MLME_TX_NO_ACK: return "MLME_TX_NO_ACK"; - case MLME_NO_BEACON: return "MLME_NO_BEACON"; - case MLME_NO_DATA: return "MLME_NO_DATA"; - case MLME_NO_SHORT_ADDRESS: return "MLME_NO_SHORT_ADDRESS"; - case MLME_PAN_ID_CONFLICT: return "MLME_PAN_ID_CONFLICT"; - case MLME_TRANSACTION_EXPIRED: return "MLME_TRANSACTION_EXPIRED"; - case MLME_TRANSACTION_OVERFLOW: return "MLME_TRANSACTION_OVERFLOW"; - case MLME_UNAVAILABLE_KEY: return "MLME_UNAVAILABLE_KEY"; - case MLME_UNSUPPORTED_ATTRIBUTE: return "MLME_UNSUPPORTED_ATTRIBUTE"; - case MLME_INVALID_ADDRESS: return "MLME_INVALID_ADDRESS"; - case MLME_INVALID_INDEX: return "MLME_INVALID_INDEX"; - case MLME_LIMIT_REACHED: return "MLME_LIMIT_REACHED"; - case MLME_READ_ONLY: return "MLME_READ_ONLY"; - case MLME_SCAN_IN_PROGRESS: return "MLME_SCAN_IN_PROGRESS"; - case MLME_DATA_POLL_NOTIFICATION: return "MLME_DATA_POLL_NOTIFICATION"; - default: return NULL; + case MLME_SUCCESS: + return "MLME_SUCCESS"; + case MLME_BUSY_CHAN: + return "MLME_BUSY_CHAN"; + case MLME_BUSY_RX: + return "MLME_BUSY_RX"; + case MLME_BUSY_TX: + return "MLME_BUSY_TX"; + case MLME_FORCE_TRX_OFF: + return "MLME_FORCE_TRX_OFF"; + case MLME_IDLE: + return "MLME_IDLE"; + case MLME_RX_ON: + return "MLME_RX_ON"; + case MLME_TRX_OFF: + return "MLME_TRX_OFF"; + case MLME_TX_ON: + return "MLME_TX_ON"; + case MLME_COUNTER_ERROR: + return "MLME_COUNTER_ERROR"; + case MLME_IMPROPER_KEY_TYPE: + return "MLME_IMPROPER_KEY_TYPE"; + case MLME_IMPROPER_SECURITY_LEVEL: + return "MLME_IMPROPER_SECURITY_LEVEL"; + case MLME_UNSUPPORTED_LEGACY: + return "MLME_UNSUPPORTED_LEGACY"; + case MLME_UNSUPPORTED_SECURITY: + return "MLME_UNSUPPORTED_SECURITY"; + case MLME_SECURITY_FAIL: + return "MLME_SECURITY_FAIL"; + case MLME_FRAME_TOO_LONG: + return "MLME_FRAME_TOO_LONG"; + case MLME_INVALID_HANDLE: + return "MLME_INVALID_HANDLE"; + case MLME_INVALID_PARAMETER: + return "MLME_INVALID_PARAMETER"; + case MLME_TX_NO_ACK: + return "MLME_TX_NO_ACK"; + case MLME_NO_BEACON: + return "MLME_NO_BEACON"; + case MLME_NO_DATA: + return "MLME_NO_DATA"; + case MLME_NO_SHORT_ADDRESS: + return "MLME_NO_SHORT_ADDRESS"; + case MLME_PAN_ID_CONFLICT: + return "MLME_PAN_ID_CONFLICT"; + case MLME_TRANSACTION_EXPIRED: + return "MLME_TRANSACTION_EXPIRED"; + case MLME_TRANSACTION_OVERFLOW: + return "MLME_TRANSACTION_OVERFLOW"; + case MLME_UNAVAILABLE_KEY: + return "MLME_UNAVAILABLE_KEY"; + case MLME_UNSUPPORTED_ATTRIBUTE: + return "MLME_UNSUPPORTED_ATTRIBUTE"; + case MLME_INVALID_ADDRESS: + return "MLME_INVALID_ADDRESS"; + case MLME_INVALID_INDEX: + return "MLME_INVALID_INDEX"; + case MLME_LIMIT_REACHED: + return "MLME_LIMIT_REACHED"; + case MLME_READ_ONLY: + return "MLME_READ_ONLY"; + case MLME_SCAN_IN_PROGRESS: + return "MLME_SCAN_IN_PROGRESS"; + case MLME_DATA_POLL_NOTIFICATION: + return "MLME_DATA_POLL_NOTIFICATION"; + default: + return NULL; } } diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp index eb95fc5fe1e..4973b3cbbb2 100644 --- a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp @@ -21,18 +21,18 @@ #include "ip4string.h" - #ifndef MBED_CONF_APP_CONNECT_STATEMENT - #error [NOT_SUPPORTED] No network configuration found for this target. - #endif +#ifndef MBED_CONF_APP_CONNECT_STATEMENT +#error [NOT_SUPPORTED] No network configuration found for this target. +#endif #include #include MBED_CONF_APP_HEADER_FILE #define TRACE_GROUP "Aifc" -NetworkInterface* net; +NetworkInterface *net; -NetworkInterface* get_interface(void) +NetworkInterface *get_interface(void) { return net; } @@ -41,12 +41,12 @@ int cmd_ifup(int argc, char *argv[]); int cmd_ifdown(int argc, char *argv[]); int cmd_ifconfig(int argc, char *argv[]); -const char* MAN_IFCONFIG = " ifup interface up\r\n"\ - " ifdown interface down\r\n"; +const char *MAN_IFCONFIG = " ifup interface up\r\n"\ + " ifdown interface down\r\n"; static void ifconfig_print() { - if(!net) { + if (!net) { cmd_printf("No interface configured\r\n"); return; } @@ -62,7 +62,7 @@ static void ifconfig_print() cmd_printf("No IP address\r\n"); } str = net->get_mac_address(); - if(str) { + if (str) { cmd_printf("MAC-48: %s\r\n", str); } else { cmd_printf("MAC-48: unknown\r\n"); @@ -85,11 +85,13 @@ int cmd_ifconfig(int argc, char *argv[]) int cmd_ifup(int argc, char *argv[]) { - if(!net) + if (!net) { net = MBED_CONF_APP_OBJECT_CONSTRUCTION; + } int err = MBED_CONF_APP_CONNECT_STATEMENT; - if(err != NSAPI_ERROR_OK) + if (err != NSAPI_ERROR_OK) { return CMDLINE_RETCODE_FAIL; + } ifconfig_print(); return CMDLINE_RETCODE_SUCCESS; @@ -97,38 +99,59 @@ int cmd_ifup(int argc, char *argv[]) int cmd_ifdown(int argc, char *argv[]) { - if(!net) + if (!net) { return CMDLINE_RETCODE_FAIL; + } int err = net->disconnect(); - if(err != NSAPI_ERROR_OK) + if (err != NSAPI_ERROR_OK) { return CMDLINE_RETCODE_FAIL; + } return CMDLINE_RETCODE_SUCCESS; } -const char* networkstack_error_to_str(int errorcode) +const char *networkstack_error_to_str(int errorcode) { - switch(errorcode) { - case NSAPI_ERROR_OK: return "NSAPI_ERROR_OK"; - case NSAPI_ERROR_WOULD_BLOCK: return "NSAPI_ERROR_WOULD_BLOCK"; - case NSAPI_ERROR_UNSUPPORTED: return "NSAPI_ERROR_UNSUPPORTED"; - case NSAPI_ERROR_PARAMETER: return "NSAPI_ERROR_PARAMETER"; - case NSAPI_ERROR_NO_CONNECTION: return "NSAPI_ERROR_NO_CONNECTION"; - case NSAPI_ERROR_NO_SOCKET: return "NSAPI_ERROR_NO_SOCKET"; - case NSAPI_ERROR_NO_ADDRESS: return "NSAPI_ERROR_NO_ADDRESS"; - case NSAPI_ERROR_NO_MEMORY: return "NSAPI_ERROR_NO_MEMORY"; - case NSAPI_ERROR_NO_SSID: return "NSAPI_ERROR_NO_SSID"; - case NSAPI_ERROR_DNS_FAILURE: return "NSAPI_ERROR_DNS_FAILURE"; - case NSAPI_ERROR_DHCP_FAILURE: return "NSAPI_ERROR_DHCP_FAILURE"; - case NSAPI_ERROR_AUTH_FAILURE: return "NSAPI_ERROR_AUTH_FAILURE"; - case NSAPI_ERROR_DEVICE_ERROR: return "NSAPI_ERROR_DEVICE_ERROR"; - case NSAPI_ERROR_IN_PROGRESS: return "NSAPI_ERROR_IN_PROGRESS"; - case NSAPI_ERROR_ALREADY: return "NSAPI_ERROR_ALREADY"; - case NSAPI_ERROR_IS_CONNECTED: return "NSAPI_ERROR_IS_CONNECTED"; - case NSAPI_ERROR_CONNECTION_LOST: return "NSAPI_ERROR_CONNECTION_LOST"; - case NSAPI_ERROR_CONNECTION_TIMEOUT: return "NSAPI_ERROR_CONNECTION_TIMEOUT"; - default: return "unknown error code"; + switch (errorcode) { + case NSAPI_ERROR_OK: + return "NSAPI_ERROR_OK"; + case NSAPI_ERROR_WOULD_BLOCK: + return "NSAPI_ERROR_WOULD_BLOCK"; + case NSAPI_ERROR_UNSUPPORTED: + return "NSAPI_ERROR_UNSUPPORTED"; + case NSAPI_ERROR_PARAMETER: + return "NSAPI_ERROR_PARAMETER"; + case NSAPI_ERROR_NO_CONNECTION: + return "NSAPI_ERROR_NO_CONNECTION"; + case NSAPI_ERROR_NO_SOCKET: + return "NSAPI_ERROR_NO_SOCKET"; + case NSAPI_ERROR_NO_ADDRESS: + return "NSAPI_ERROR_NO_ADDRESS"; + case NSAPI_ERROR_NO_MEMORY: + return "NSAPI_ERROR_NO_MEMORY"; + case NSAPI_ERROR_NO_SSID: + return "NSAPI_ERROR_NO_SSID"; + case NSAPI_ERROR_DNS_FAILURE: + return "NSAPI_ERROR_DNS_FAILURE"; + case NSAPI_ERROR_DHCP_FAILURE: + return "NSAPI_ERROR_DHCP_FAILURE"; + case NSAPI_ERROR_AUTH_FAILURE: + return "NSAPI_ERROR_AUTH_FAILURE"; + case NSAPI_ERROR_DEVICE_ERROR: + return "NSAPI_ERROR_DEVICE_ERROR"; + case NSAPI_ERROR_IN_PROGRESS: + return "NSAPI_ERROR_IN_PROGRESS"; + case NSAPI_ERROR_ALREADY: + return "NSAPI_ERROR_ALREADY"; + case NSAPI_ERROR_IS_CONNECTED: + return "NSAPI_ERROR_IS_CONNECTED"; + case NSAPI_ERROR_CONNECTION_LOST: + return "NSAPI_ERROR_CONNECTION_LOST"; + case NSAPI_ERROR_CONNECTION_TIMEOUT: + return "NSAPI_ERROR_CONNECTION_TIMEOUT"; + default: + return "unknown error code"; } } diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.h b/TEST_APPS/device/socket_app/cmd_ifconfig.h index 54c6124f806..37d50b942b7 100644 --- a/TEST_APPS/device/socket_app/cmd_ifconfig.h +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.h @@ -26,9 +26,9 @@ * * @return pointer to the network interface, or NULL if unrecognized or ambiguous */ -NetworkInterface* get_interface(void); +NetworkInterface *get_interface(void); void cmd_ifconfig_init(void); -const char* networkstack_error_to_str(int errorcode); +const char *networkstack_error_to_str(int errorcode); #endif diff --git a/TEST_APPS/device/socket_app/cmd_socket.cpp b/TEST_APPS/device/socket_app/cmd_socket.cpp index cd9013e56bd..4aefe700dc7 100644 --- a/TEST_APPS/device/socket_app/cmd_socket.cpp +++ b/TEST_APPS/device/socket_app/cmd_socket.cpp @@ -88,71 +88,71 @@ class SInfo; static Queue event_queue; -static int id_count=0; +static int id_count = 0; -class SInfo -{ - public: +class SInfo { +public: enum SocketType { TCP_CLIENT, TCP_SERVER, UDP }; - SInfo(TCPSocket* sock): - _id(id_count++), - _sock(sock), - _type(SInfo::TCP_CLIENT), - _blocking(true), - _dataLen(0), - _maxRecvLen(0), - _repeatBufferFill(1), - _receivedTotal(0), - _receiverThread(NULL), - _receiveBuffer(NULL), - _senderThreadId(NULL), - _receiverThreadId(NULL), - _packetSizes(NULL), - _check_pattern(false) + SInfo(TCPSocket *sock): + _id(id_count++), + _sock(sock), + _type(SInfo::TCP_CLIENT), + _blocking(true), + _dataLen(0), + _maxRecvLen(0), + _repeatBufferFill(1), + _receivedTotal(0), + _receiverThread(NULL), + _receiveBuffer(NULL), + _senderThreadId(NULL), + _receiverThreadId(NULL), + _packetSizes(NULL), + _check_pattern(false) { assert(sock); } - SInfo(TCPServer* sock): - _id(id_count++), - _sock(sock), - _type(SInfo::TCP_SERVER), - _blocking(true), - _dataLen(0), - _maxRecvLen(0), - _repeatBufferFill(1), - _receivedTotal(0), - _receiverThread(NULL), - _receiveBuffer(NULL), - _senderThreadId(NULL), - _receiverThreadId(NULL), - _packetSizes(NULL), - _check_pattern(false) + SInfo(TCPServer *sock): + _id(id_count++), + _sock(sock), + _type(SInfo::TCP_SERVER), + _blocking(true), + _dataLen(0), + _maxRecvLen(0), + _repeatBufferFill(1), + _receivedTotal(0), + _receiverThread(NULL), + _receiveBuffer(NULL), + _senderThreadId(NULL), + _receiverThreadId(NULL), + _packetSizes(NULL), + _check_pattern(false) { assert(sock); } - SInfo(UDPSocket* sock): - _id(id_count++), - _sock(sock), - _type(SInfo::UDP), - _blocking(true), - _dataLen(0), - _maxRecvLen(0), - _repeatBufferFill(1), - _receivedTotal(0), - _receiverThread(NULL), - _receiveBuffer(NULL), - _senderThreadId(NULL), - _receiverThreadId(NULL), - _packetSizes(NULL), - _check_pattern(false) + SInfo(UDPSocket *sock): + _id(id_count++), + _sock(sock), + _type(SInfo::UDP), + _blocking(true), + _dataLen(0), + _maxRecvLen(0), + _repeatBufferFill(1), + _receivedTotal(0), + _receiverThread(NULL), + _receiveBuffer(NULL), + _senderThreadId(NULL), + _receiverThreadId(NULL), + _packetSizes(NULL), + _check_pattern(false) { assert(sock); } - ~SInfo() { + ~SInfo() + { this->_sock->sigio(Callback()); if (this->_receiverThread) { this->_receiverThread->terminate(); @@ -163,35 +163,122 @@ class SInfo } delete this->_sock; } - int id() const { return this->_id; } - Socket& socket() { return *(this->_sock); } - Socket& socket() const { return *(this->_sock); } - TCPSocket *tcp_socket() { return this->_type == SInfo::TCP_CLIENT ? static_cast(this->_sock) : NULL; } - TCPServer *tcp_server() { return this->_type == SInfo::TCP_SERVER ? static_cast(this->_sock) : NULL; } - UDPSocket *udp_socket() { return this->_type == SInfo::UDP ? static_cast(this->_sock) : NULL; } - SInfo::SocketType type() const { return this->_type; } - void setDataCount(int dataCount) { this->_dataLen = dataCount; } - int getDataCount() { return this->_dataLen; } - void setReceiverThread(Thread *receiverThread) { this->_receiverThread = receiverThread; } - Thread *getReceiverThread() { return this->_receiverThread; } - void setReceiveBuffer(uint8_t *receiveBuffer) { this->_receiveBuffer = receiveBuffer; } - uint8_t *getReceiveBuffer() { return this->_receiveBuffer; } - void setMaxRecvLen(int recvLen) { this->_maxRecvLen = recvLen; } - int getMaxRecvLen() { return this->_maxRecvLen; } - void setRepeatBufferFill(int n) { this->_repeatBufferFill = n; } - int getRepeatBufferFill() { return this->_repeatBufferFill; } - void setRecvTotal(int n) { this->_receivedTotal = n; } - int getRecvTotal() { return this->_receivedTotal; } - void setSenderThreadId(osThreadId threadID) { this->_senderThreadId = threadID; } - void setReceiverThreadId(osThreadId threadID) { this->_receiverThreadId = threadID; } - osThreadId getSenderThreadId() { return this->_senderThreadId; } - osThreadId getReceiverThreadId() { return this->_receiverThreadId; } - void setPacketSizeArray(int *ptr) { this->_packetSizes = ptr; } - int *getPacketSizeArray() { return this->_packetSizes; } - void setUnavailable() { this->_available = false;} - void setAvailable() {this->_available = true;} - bool available() { return this->_available; } - void set_pattern_check(bool enabled) { _check_pattern = enabled; }; + int id() const + { + return this->_id; + } + Socket &socket() + { + return *(this->_sock); + } + Socket &socket() const + { + return *(this->_sock); + } + TCPSocket *tcp_socket() + { + return this->_type == SInfo::TCP_CLIENT ? static_cast(this->_sock) : NULL; + } + TCPServer *tcp_server() + { + return this->_type == SInfo::TCP_SERVER ? static_cast(this->_sock) : NULL; + } + UDPSocket *udp_socket() + { + return this->_type == SInfo::UDP ? static_cast(this->_sock) : NULL; + } + SInfo::SocketType type() const + { + return this->_type; + } + void setDataCount(int dataCount) + { + this->_dataLen = dataCount; + } + int getDataCount() + { + return this->_dataLen; + } + void setReceiverThread(Thread *receiverThread) + { + this->_receiverThread = receiverThread; + } + Thread *getReceiverThread() + { + return this->_receiverThread; + } + void setReceiveBuffer(uint8_t *receiveBuffer) + { + this->_receiveBuffer = receiveBuffer; + } + uint8_t *getReceiveBuffer() + { + return this->_receiveBuffer; + } + void setMaxRecvLen(int recvLen) + { + this->_maxRecvLen = recvLen; + } + int getMaxRecvLen() + { + return this->_maxRecvLen; + } + void setRepeatBufferFill(int n) + { + this->_repeatBufferFill = n; + } + int getRepeatBufferFill() + { + return this->_repeatBufferFill; + } + void setRecvTotal(int n) + { + this->_receivedTotal = n; + } + int getRecvTotal() + { + return this->_receivedTotal; + } + void setSenderThreadId(osThreadId threadID) + { + this->_senderThreadId = threadID; + } + void setReceiverThreadId(osThreadId threadID) + { + this->_receiverThreadId = threadID; + } + osThreadId getSenderThreadId() + { + return this->_senderThreadId; + } + osThreadId getReceiverThreadId() + { + return this->_receiverThreadId; + } + void setPacketSizeArray(int *ptr) + { + this->_packetSizes = ptr; + } + int *getPacketSizeArray() + { + return this->_packetSizes; + } + void setUnavailable() + { + this->_available = false; + } + void setAvailable() + { + this->_available = true; + } + bool available() + { + return this->_available; + } + void set_pattern_check(bool enabled) + { + _check_pattern = enabled; + }; bool check_pattern(void *buffer, size_t len); const char *type_str() const @@ -213,19 +300,50 @@ class SInfo } return str; } - bool blocking() const { return this->_blocking; } - void set_blocking(bool blocking) { socket().set_blocking(blocking); this->_blocking = blocking; } - bool can_connect() { return (this->type() == SInfo::TCP_CLIENT); } - bool can_bind() { return (this->type() == SInfo::UDP || this->type() == SInfo::TCP_SERVER); } - bool can_send() { return (this->type() == SInfo::TCP_CLIENT); } - bool can_recv() { return (this->type() == SInfo::TCP_CLIENT); } - bool can_sendto() { return (this->type() == SInfo::UDP); } - bool can_recvfrom() { return (this->type() == SInfo::UDP); } - bool can_listen() { return (this->type() == SInfo::TCP_SERVER); } - bool can_accept() { return (this->type() == SInfo::TCP_SERVER); } - private: + bool blocking() const + { + return this->_blocking; + } + void set_blocking(bool blocking) + { + socket().set_blocking(blocking); + this->_blocking = blocking; + } + bool can_connect() + { + return (this->type() == SInfo::TCP_CLIENT); + } + bool can_bind() + { + return (this->type() == SInfo::UDP || this->type() == SInfo::TCP_SERVER); + } + bool can_send() + { + return (this->type() == SInfo::TCP_CLIENT); + } + bool can_recv() + { + return (this->type() == SInfo::TCP_CLIENT); + } + bool can_sendto() + { + return (this->type() == SInfo::UDP); + } + bool can_recvfrom() + { + return (this->type() == SInfo::UDP); + } + bool can_listen() + { + return (this->type() == SInfo::TCP_SERVER); + } + bool can_accept() + { + return (this->type() == SInfo::TCP_SERVER); + } +private: const int _id; - Socket* _sock; + Socket *_sock; const SInfo::SocketType _type; bool _blocking; int _dataLen; @@ -243,7 +361,7 @@ class SInfo SInfo(); }; -static std::vector m_sockets; +static std::vector m_sockets; static enum { PRINT_DISABLED, @@ -274,12 +392,13 @@ static void print_data_as_hex(const uint8_t *buf, int len, int col_width); static void generate_RFC_864_pattern(size_t offset, uint8_t *buf, size_t len) { while (len--) { - if (offset % 74 == 72) + if (offset % 74 == 72) { *buf++ = '\r'; - else if (offset % 74 == 73) + } else if (offset % 74 == 73) { *buf++ = '\n'; - else - *buf++ = ' ' + (offset%74 + offset/74) % 95 ; + } else { + *buf++ = ' ' + (offset % 74 + offset / 74) % 95 ; + } offset++; } } @@ -290,19 +409,21 @@ bool SInfo::check_pattern(void *buffer, size_t len) return true; } void *buf = malloc(len); - if (!buf) + if (!buf) { return false; + } size_t offset = _receivedTotal; - generate_RFC_864_pattern(offset, (uint8_t*)buf, len); + generate_RFC_864_pattern(offset, (uint8_t *)buf, len); bool match = memcmp(buf, buffer, len) == 0; if (!match) { - cmd_printf("Pattern check failed\r\nWAS:%.*s\r\nREF:%.*s\r\n", len, (char*)buffer, len, (char*)buf); + cmd_printf("Pattern check failed\r\nWAS:%.*s\r\nREF:%.*s\r\n", len, (char *)buffer, len, (char *)buf); } free(buf); return match; } -static void sigio_handler(SInfo *info) { +static void sigio_handler(SInfo *info) +{ if (info->getReceiverThreadId()) { osSignalSet(info->getReceiverThreadId(), SIGNAL_SIGIO); } @@ -335,19 +456,21 @@ int handle_nsapi_size_or_error(const char *function, nsapi_size_or_error_t ret) return CMDLINE_RETCODE_SUCCESS; } -static SInfo *get_sinfo(int id) { +static SInfo *get_sinfo(int id) +{ - for (std::vector::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) { - if( (*it)->id() == id) { + for (std::vector::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) { + if ((*it)->id() == id) { return *it; } } return NULL; } -static int del_sinfo(SInfo *info) { - for (std::vector::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) { - if( (*it) == info) { +static int del_sinfo(SInfo *info) +{ + for (std::vector::iterator it = m_sockets.begin(); it != m_sockets.end(); it++) { + if ((*it) == info) { delete info; m_sockets.erase(it); return CMDLINE_RETCODE_SUCCESS; @@ -397,14 +520,14 @@ static void udp_receiver_thread(SInfo *info) info->setReceiverThreadId(Thread::gettid()); - while (i(info->socket()).recvfrom(&addr, info->getReceiveBuffer() + received, info->getDataCount() - received); + while (i < n) { + ret = static_cast(info->socket()).recvfrom(&addr, info->getReceiveBuffer() + received, info->getDataCount() - received); if (ret > 0) { if (!info->check_pattern(info->getReceiveBuffer() + received, ret)) { return; } received += ret; - packetSizes[i%PACKET_SIZE_ARRAY_LEN] = ret; + packetSizes[i % PACKET_SIZE_ARRAY_LEN] = ret; i++; info->setRecvTotal(info->getRecvTotal() + ret); } else if (ret == NSAPI_ERROR_WOULD_BLOCK) { @@ -431,7 +554,7 @@ static nsapi_size_or_error_t start_udp_receiver_thread(SInfo *info, int argc, ch return CMDLINE_RETCODE_INVALID_PARAMETERS; } } - uint8_t *dataIn = (uint8_t *)malloc(max_size+1); + uint8_t *dataIn = (uint8_t *)malloc(max_size + 1); if (!dataIn) { cmd_printf("malloc() failed\r\n"); return CMDLINE_RETCODE_FAIL; @@ -441,10 +564,10 @@ static nsapi_size_or_error_t start_udp_receiver_thread(SInfo *info, int argc, ch cmd_printf("Allocation failed\r\n"); return CMDLINE_RETCODE_FAIL; } - for (int i=0; isetReceiveBuffer(dataIn); info->setDataCount(max_size); info->setRepeatBufferFill(n); @@ -483,14 +606,13 @@ static nsapi_size_or_error_t udp_sendto_command_handler(SInfo *info, int argc, c if (strcmp(argv[5], "NULL") == 0) { data = NULL; len = 0; - } - else { + } else { data = argv[5]; len = strlen(argv[5]); } } - nsapi_size_or_error_t ret = static_cast(info->socket()).sendto(host, port, data, len); + nsapi_size_or_error_t ret = static_cast(info->socket()).sendto(host, port, data, len); if (ret > 0) { cmd_printf("sent: %d bytes\r\n", ret); } @@ -516,13 +638,14 @@ static nsapi_size_or_error_t udp_recvfrom_command_handler(SInfo *info, int argc, cmd_printf("malloc() failed\r\n"); return CMDLINE_RETCODE_FAIL; } - nsapi_size_or_error_t ret = static_cast(info->socket()).recvfrom(&addr, data, len); + nsapi_size_or_error_t ret = static_cast(info->socket()).recvfrom(&addr, data, len); if (ret > 0) { cmd_printf("UDPSocket::recvfrom, addr=%s port=%d\r\n", addr.get_ip_address(), addr.get_port()); cmd_printf("received: %d bytes\r\n", ret); print_data((const uint8_t *)data, len); - if(!info->check_pattern(data, len)) - ret=-1; + if (!info->check_pattern(data, len)) { + ret = -1; + } info->setRecvTotal(info->getRecvTotal() + ret); } free(data); @@ -539,16 +662,17 @@ static void tcp_receiver_thread(SInfo *info) info->setReceiverThreadId(Thread::gettid()); - for (i=0; i(info->socket()).recv(info->getReceiveBuffer() + received, recv_len - received); + ret = static_cast(info->socket()).recv(info->getReceiveBuffer() + received, recv_len - received); if (ret > 0) { - if(!info->check_pattern(info->getReceiveBuffer() + received, ret)) + if (!info->check_pattern(info->getReceiveBuffer() + received, ret)) { return; + } received += ret; info->setRecvTotal(info->getRecvTotal() + ret); - } else if (ret == NSAPI_ERROR_WOULD_BLOCK){ + } else if (ret == NSAPI_ERROR_WOULD_BLOCK) { Thread::signal_wait(SIGNAL_SIGIO); } else { handle_nsapi_size_or_error("Thread: TCPSocket::recv()", ret); @@ -584,7 +708,7 @@ static nsapi_size_or_error_t start_tcp_receiver_thread(SInfo *info, int argc, ch } } - uint8_t *dataIn = (uint8_t *)malloc(len+1); + uint8_t *dataIn = (uint8_t *)malloc(len + 1); if (!dataIn) { cmd_printf("malloc() failed\r\n"); return CMDLINE_RETCODE_FAIL; @@ -618,7 +742,7 @@ static nsapi_size_or_error_t tcp_send_command_handler(SInfo *info, int argc, cha len = strlen(argv[3]); } - ret = static_cast(info->socket()).send(data, len); + ret = static_cast(info->socket()).send(data, len); if (ret > 0) { cmd_printf("sent: %d bytes\r\n", ret); @@ -644,12 +768,13 @@ static nsapi_size_or_error_t tcp_recv_command_handler(SInfo *info, int argc, cha return CMDLINE_RETCODE_FAIL; } - nsapi_size_or_error_t ret = static_cast(info->socket()).recv(data, len); + nsapi_size_or_error_t ret = static_cast(info->socket()).recv(data, len); if (ret > 0) { cmd_printf("received: %d bytes\r\n", ret); print_data((const uint8_t *)data, ret); - if(!info->check_pattern(data, ret)) - ret=-1; + if (!info->check_pattern(data, ret)) { + ret = -1; + } info->setRecvTotal(info->getRecvTotal() + ret); } free(data); @@ -681,21 +806,21 @@ static nsapi_size_or_error_t recv_all(char *const rbuffer, const int expt_len, S static void bg_traffic_thread(SInfo *info) { static const int data_len = 10; - char sbuffer[data_len+1] = "dummydata_"; - char rbuffer[data_len+1]; + char sbuffer[data_len + 1] = "dummydata_"; + char rbuffer[data_len + 1]; int scount, rtotal = 0; info->setSenderThreadId(Thread::gettid()); - for(;;) { + for (;;) { if (!info->available()) { (void)handle_nsapi_size_or_error(__func__, rtotal); break; } - sbuffer[data_len-1] = 'A' + (rand() % 26); + sbuffer[data_len - 1] = 'A' + (rand() % 26); scount = info->tcp_socket()->send(sbuffer, data_len); rtotal = recv_all(rbuffer, data_len, info); - if(scount != rtotal || (strcmp(sbuffer, rbuffer) != 0)) { + if (scount != rtotal || (strcmp(sbuffer, rbuffer) != 0)) { info->setUnavailable(); tr_err("Background received data does not match to sent data"); @@ -732,7 +857,7 @@ static void thread_clean_up(SInfo *info) static int cmd_socket(int argc, char *argv[]) { - if(cmd_parameter_index(argc, argv, "new") == 1) { + if (cmd_parameter_index(argc, argv, "new") == 1) { return cmd_socket_new(argc, argv); } else if (cmd_parameter_index(argc, argv, "print-mode") > 0) { if (cmd_parameter_index(argc, argv, "--string") > 0) { @@ -755,8 +880,9 @@ static int cmd_socket(int argc, char *argv[]) printing_col_width = (int)parsed_col_width; } // Allow print-mode to be used as a parameter to other commands - if (cmd_parameter_index(argc, argv, "print-mode") == 1) + if (cmd_parameter_index(argc, argv, "print-mode") == 1) { return CMDLINE_RETCODE_SUCCESS; + } } // Rest of the commands require Socket @@ -767,13 +893,13 @@ static int cmd_socket(int argc, char *argv[]) } bool enable_pattern_check; - if(cmd_parameter_bool(argc, argv, "set_RFC_864_pattern_check", &enable_pattern_check)) { + if (cmd_parameter_bool(argc, argv, "set_RFC_864_pattern_check", &enable_pattern_check)) { info->set_pattern_check(enable_pattern_check); return CMDLINE_RETCODE_SUCCESS; } // Helper macro for checking the which command was given - #define COMMAND_IS(cmd) (cmd_parameter_index(argc, argv, cmd) == 2) +#define COMMAND_IS(cmd) (cmd_parameter_index(argc, argv, cmd) == 2) /* * Generic Socket commands: @@ -793,7 +919,7 @@ static int cmd_socket(int argc, char *argv[]) return CMDLINE_RETCODE_FAIL; } - switch(info->type()) { + switch (info->type()) { case SInfo::TCP_CLIENT: return handle_nsapi_error("Socket::open()", info->tcp_socket()->open(interface)); case SInfo::UDP: @@ -841,10 +967,11 @@ static int cmd_socket(int argc, char *argv[]) cmd_printf("Need timeout value"); return CMDLINE_RETCODE_INVALID_PARAMETERS; } - if (ms == -1) + if (ms == -1) { info->set_blocking(true); - else + } else { info->set_blocking(false); + } info->socket().set_timeout(ms); return CMDLINE_RETCODE_SUCCESS; @@ -860,7 +987,7 @@ static int cmd_socket(int argc, char *argv[]) * sendto, recvfrom */ if ((COMMAND_IS("sendto") || COMMAND_IS("recvfrom") || COMMAND_IS("start_udp_receiver_thread") - || COMMAND_IS("last_data_received")) && info->type() != SInfo::UDP) { + || COMMAND_IS("last_data_received")) && info->type() != SInfo::UDP) { cmd_printf("Not UDPSocket\r\n"); return CMDLINE_RETCODE_FAIL; } @@ -876,7 +1003,7 @@ static int cmd_socket(int argc, char *argv[]) if (info->getPacketSizeArray()) { int *packetSizes = info->getPacketSizeArray(); cmd_printf("packet_sizes: "); - for (int i=0; itype() != SInfo::TCP_CLIENT) { + || COMMAND_IS("start_tcp_receiver_thread") || COMMAND_IS("join_tcp_receiver_thread") + || COMMAND_IS("start_bg_traffic_thread") || COMMAND_IS("join_bg_traffic_thread") + || COMMAND_IS("setsockopt_keepalive") || COMMAND_IS("getsockopt_keepalive")) + && info->type() != SInfo::TCP_CLIENT) { cmd_printf("Not TCPSocket\r\n"); return CMDLINE_RETCODE_FAIL; } @@ -919,7 +1046,7 @@ static int cmd_socket(int argc, char *argv[]) } cmd_printf("Host name: %s port: %" PRId32 "\r\n", host, port); - return handle_nsapi_error("TCPSocket::connect()", static_cast(info->socket()).connect(host, port)); + return handle_nsapi_error("TCPSocket::connect()", static_cast(info->socket()).connect(host, port)); } else if (COMMAND_IS("send")) { return tcp_send_command_handler(info, argc, argv); @@ -945,7 +1072,7 @@ static int cmd_socket(int argc, char *argv[]) } else if (COMMAND_IS("join_bg_traffic_thread")) { int bg_thread_success = CMDLINE_RETCODE_SUCCESS; - if( !info->available()) { // Tells that background thread stumbled to an issue and stopped prematurely + if (!info->available()) { // Tells that background thread stumbled to an issue and stopped prematurely bg_thread_success = CMDLINE_RETCODE_FAIL; } @@ -991,10 +1118,11 @@ static int cmd_socket(int argc, char *argv[]) } if (COMMAND_IS("listen")) { int32_t backlog; - if (cmd_parameter_int(argc, argv, "listen", &backlog)) - return handle_nsapi_error("TCPServer::listen()", static_cast(info->socket()).listen(backlog)); - else - return handle_nsapi_error("TCPServer::listen()", static_cast(info->socket()).listen()); + if (cmd_parameter_int(argc, argv, "listen", &backlog)) { + return handle_nsapi_error("TCPServer::listen()", static_cast(info->socket()).listen(backlog)); + } else { + return handle_nsapi_error("TCPServer::listen()", static_cast(info->socket()).listen()); + } } else if (COMMAND_IS("accept")) { SocketAddress addr; @@ -1008,11 +1136,11 @@ static int cmd_socket(int argc, char *argv[]) cmd_printf("Invalid socket id\r\n"); return CMDLINE_RETCODE_FAIL; } - TCPSocket *new_sock = static_cast(&new_info->socket()); - nsapi_error_t ret = static_cast(info->socket()).accept(new_sock, &addr); + TCPSocket *new_sock = static_cast(&new_info->socket()); + nsapi_error_t ret = static_cast(info->socket()).accept(new_sock, &addr); if (ret == NSAPI_ERROR_OK) { cmd_printf("TCPServer::accept() new socket sid: %d connection from %s port %d\r\n", - new_info->id(), addr.get_ip_address(), addr.get_port()); + new_info->id(), addr.get_ip_address(), addr.get_port()); } return handle_nsapi_error("TCPServer::accept()", ret); } diff --git a/TEST_APPS/device/socket_app/main.cpp b/TEST_APPS/device/socket_app/main.cpp index e64eb295c28..3bc1365613f 100644 --- a/TEST_APPS/device/socket_app/main.cpp +++ b/TEST_APPS/device/socket_app/main.cpp @@ -37,7 +37,8 @@ void cmd_ready_cb(int retcode) cmd_next(retcode); } -void wrap_printf(const char *f, va_list a) { +void wrap_printf(const char *f, va_list a) +{ vprintf(f, a); } @@ -48,13 +49,14 @@ int main() cmd_socket_init(); int c; - while((c = getchar()) != EOF) { + while ((c = getchar()) != EOF) { cmd_char_input(c); } return 0; } -FileHandle* mbed::mbed_override_console(int) { +FileHandle *mbed::mbed_override_console(int) +{ static UARTSerial console(STDIO_UART_TX, STDIO_UART_RX, SERIAL_CONSOLE_BAUD_RATE); #if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS console.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC); diff --git a/TEST_APPS/device/socket_app/strconv.c b/TEST_APPS/device/socket_app/strconv.c index 38dc207880b..720cf446831 100644 --- a/TEST_APPS/device/socket_app/strconv.c +++ b/TEST_APPS/device/socket_app/strconv.c @@ -47,11 +47,11 @@ int hexstr_to_bytes_inplace(char *str) return -1; } len = strlen(str); - if (len < 2 || (len+1)%3 != 0) { + if (len < 2 || (len + 1) % 3 != 0) { return -1; } for (i = 0, j = 0; i < len; i += 3, ++j) { - str[j] = (char)strtol(str+i, 0, 16); + str[j] = (char)strtol(str + i, 0, 16); } return j; } From dddac58bf4f63319a72464b87251e5ca64257585 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 28 Aug 2018 09:57:56 +0300 Subject: [PATCH 31/40] Fix readme based on comments in pull request --- TEST_APPS/readme.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TEST_APPS/readme.md b/TEST_APPS/readme.md index b4fa79359bd..a5747e4d598 100644 --- a/TEST_APPS/readme.md +++ b/TEST_APPS/readme.md @@ -1,21 +1,21 @@ -## Running IceTea tests located under mbed-os +## Running Icetea tests located under mbed-os ### Structure -mbed-os has a folder called TEST_APPS that contains everything related to IceTea -testing. -There are currently 3 folders +mbed-os has a folder called TEST_APPS that contains everything related to Icetea testing. +There are currently 3 folders: - device - contains all the different test applications you can flash to your board - icetea-plugins - contains plugins that are being used by some of the testcases, needed for the test execution -- testcases - contains IceTea testcases written in Python +- testcases - contains Icetea testcases written in Python -The testcases and test applications have a dependency +The testcases dependends on test applications ### Preparing your work environment #### Prerequisities -You should have IceTea and forked mbed-cli that supports IceTea, installed. +You need Icetea and mbed-cli that supports Icetea, installed. #### Selecting the network interface to use @@ -88,7 +88,7 @@ Modified: ### Test results -IceTea prints the results from the test run to the command line, and the final result looks similar to this. +Icetea prints the results from the test run to the command line, and the final result looks similar to this. ``` +--------------------------------+---------+-------------+-------------+-----------+----------+ @@ -111,5 +111,5 @@ IceTea prints the results from the test run to the command line, and the final r +---------------+----------------+ ``` -The results from the tests can also be found from mbed-os/log -folder. -You probably want to add the log -folder to your .mbedignore -file to prevent issues with build commands becoming too long over the time. +The results from the tests can also be found from mbed-os/log folder. +You probably want to add the log folder to your .mbedignore file to prevent issues with build commands becoming too long over the time. From 2f01555f81b92c6b6d253a429d7cb43ad9238fed Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 28 Aug 2018 10:01:00 +0300 Subject: [PATCH 32/40] Fix typo --- TEST_APPS/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TEST_APPS/readme.md b/TEST_APPS/readme.md index a5747e4d598..db68e4f8acf 100644 --- a/TEST_APPS/readme.md +++ b/TEST_APPS/readme.md @@ -9,7 +9,7 @@ There are currently 3 folders: - icetea-plugins - contains plugins that are being used by some of the testcases, needed for the test execution - testcases - contains Icetea testcases written in Python -The testcases dependends on test applications +The testcases depends on test applications ### Preparing your work environment From b41d21559717eb3432e6406191235ee52e2e0a9f Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Tue, 28 Aug 2018 15:28:59 +0300 Subject: [PATCH 33/40] Update to icetea 1.* version --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0d4130656a2..0a37fe1589c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,4 @@ future>=0.16.0 six>=1.11.0 git+https://github.com/armmbed/manifest-tool.git@v1.4.5 mbed-cloud-sdk==2.0.0 -icetea==1.0.* +icetea==1.* From 1209e73e0c4d25766e6b87e00ec16368dda53a8d Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Wed, 29 Aug 2018 10:49:37 +0300 Subject: [PATCH 34/40] Fix issues coming from network default interface --- TEST_APPS/device/socket_app/cmd_ifconfig.cpp | 9 ++-- TEST_APPS/readme.md | 48 ++++++-------------- 2 files changed, 18 insertions(+), 39 deletions(-) diff --git a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp index 4973b3cbbb2..cb55e3bedff 100644 --- a/TEST_APPS/device/socket_app/cmd_ifconfig.cpp +++ b/TEST_APPS/device/socket_app/cmd_ifconfig.cpp @@ -21,12 +21,13 @@ #include "ip4string.h" -#ifndef MBED_CONF_APP_CONNECT_STATEMENT +#define WIFI 2 +#if !defined(MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE) || \ + (MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE == WIFI && !defined(MBED_CONF_NSAPI_DEFAULT_WIFI_SSID)) #error [NOT_SUPPORTED] No network configuration found for this target. #endif #include -#include MBED_CONF_APP_HEADER_FILE #define TRACE_GROUP "Aifc" @@ -86,9 +87,9 @@ int cmd_ifconfig(int argc, char *argv[]) int cmd_ifup(int argc, char *argv[]) { if (!net) { - net = MBED_CONF_APP_OBJECT_CONSTRUCTION; + net = NetworkInterface::get_default_instance(); } - int err = MBED_CONF_APP_CONNECT_STATEMENT; + int err = net->connect(); if (err != NSAPI_ERROR_OK) { return CMDLINE_RETCODE_FAIL; } diff --git a/TEST_APPS/readme.md b/TEST_APPS/readme.md index db68e4f8acf..9f37ef05c2e 100644 --- a/TEST_APPS/readme.md +++ b/TEST_APPS/readme.md @@ -22,6 +22,7 @@ You need Icetea and mbed-cli that supports Icetea, installed. Depending on a device, there might be a default network interface type defined in the mbed-os/targets/targets.json, which is used to locate a test-config file by default. If there is not, or you want to use a different interface than the default, you need to provide a relevant test-config -file to the mbed test with --test-config option. The test-config file contains the necessary information for the test application, there are some test-config files located under mbed-os/tools/test-configs. +Devices which have their network drivers residing inside mbed-os can use generic test-configs like HeapBlockDeviceAndEthernetInterface.json and HeapBlockDeviceAndWifiInterface.json. Otherwise you need to use a device specific test-config. ### Running the tests @@ -41,49 +42,26 @@ Some devices may offer multiple network interfaces to operate with. For example, The tests can be run for either one of those using already existing test-config -files. To run the tests with Wi-Fi interface: -`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/OdinInterface.json` +`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/HeapBlockDeviceAndWifiInterface.json.json` To run the tests with ethernet interface: -`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/Odin_EthernetInterface.json` +`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/HeapBlockDeviceAndEthernetInterface.json` #### Providing Wi-Fi access point information If you are using Wi-Fi interface for running the tests, you need to provide also information about the used access point. -The information can be provided in the used test-config -file. Depending on the used interface you might need to provide ssid, password and security. -You can either provide separate WIFI_SSID, WIFI_PASSWORD and WIFI_SECURITY macros, or provide the SSID, password and security in the connect statement provided in the test-config -file. +The information can be provided in the used test-config -file. -Example of separate macros: +Example of access point information: ``` - "wifi-ssid": { - "help": "WiFi SSID", - "value": "\"ssid\"", - "macro_name": "WIFI_SSID" - }, - "wifi-password": { - "help": "WiFi Password", - "value": "\"password\"", - "macro_name": "WIFI_PASSWORD" - }, - "wifi-security": { - "help": "WiFi Security, values from nsapi_security from features/netsocket/nsapi_types.h" - "value": "NSAPI_SECURITY_WPA_WPA2" - "macro_name": "WIFI_SECURITY" -``` - -Example of modifying the connect statement -Original: -``` - "connect-statement" : { - "help" : "Must use 'net' variable name", - "value" : "net->wifiInterface()->connect(WIFI_SSID, WIFI_PASSWORD, WIFI_SECURITY)" - }, -``` -Modified: -``` - "connect-statement" : { - "help" : "Must use 'net' variable name", - "value" : "net->wifiInterface()->connect(\"ssid\", \"password\", NSAPI_SECURITY_WPA_WPA2)" - }, + "target_overrides": { + "*": { + "target.network-default-interface-type": "WIFI", + "nsapi.default-wifi-ssid": "\"ssid\"", + "nsapi.default-wifi-password": "\"password\"", + "nsapi.default-wifi-security": "WPA_WPA2" + } + } ``` ### Test results From 4e38f7306726f061e75f80ed839a279a770d699f Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Wed, 29 Aug 2018 15:00:42 +0300 Subject: [PATCH 35/40] Fix after rebase --- tools/test_api.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/tools/test_api.py b/tools/test_api.py index 2d5561d873e..848b5f84058 100644 --- a/tools/test_api.py +++ b/tools/test_api.py @@ -2110,11 +2110,22 @@ def find_tests(base_dir, target_name, toolchain_name, icetea, greentea, app_conf d = join(directory, test_group_directory, test_case_directory) if not isdir(d) or ignoreset.is_ignored(d): continue - if 'device' == subdir: - for test_dir in os.listdir(d): - test_dir_path = join(d, test_dir) - test_name = test_path_to_name(test_dir_path, base_dir) - tests[(test_name, directory, subdir, test_dir)] = [test_dir_path] + special_dirs = ['host_tests', 'COMMON'] + if test_group_directory not in special_dirs and test_case_directory not in special_dirs: + test_name = test_path_to_name(d, base_dir) + tests[(test_name, directory, test_group_directory, test_case_directory)] = [d] + if test_case_directory == 'COMMON': + def predicate(base_pred, group_pred, name_base_group_case): + (name, base, group, case) = name_base_group_case + return base == base_pred and group == group_pred + + commons.append((functools.partial(predicate, directory, test_group_directory), d)) + if test_group_directory == 'COMMON': + def predicate(base_pred, name_base_group_case): + (name, base, group, case) = name_base_group_case + return base == base_pred + + commons.append((functools.partial(predicate, directory), grp_dir)) if icetea: dirs = [d for d in base_resources.ignored_dirs if basename(d) == 'TEST_APPS'] @@ -2124,6 +2135,12 @@ def find_tests(base_dir, target_name, toolchain_name, icetea, greentea, app_conf for subdir in os.listdir(directory): d = join(directory, subdir) if not isdir(d): + continue + if 'device' == subdir: + for test_dir in os.listdir(d): + test_dir_path = join(d, test_dir) + test_name = test_path_to_name(test_dir_path, base_dir) + tests[(test_name, directory, subdir, test_dir)] = [test_dir_path] # Apply common directories for pred, path in commons: From 701490530cb3c57d81e74bfc7b6ae3cc1556a0a6 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Thu, 30 Aug 2018 16:13:13 +0300 Subject: [PATCH 36/40] Remove MAC tests because not completely ready yet --- .../device/nanostack_mac_tester/README.md | 138 -- .../device/nanostack_mac_tester/main.cpp | 156 -- .../device/nanostack_mac_tester/mbed_app.json | 16 - .../source/mac_commands.cpp | 1642 ----------------- .../source/mac_commands.h | 65 - .../nanostack_mac_tester/source/util.cpp | 124 -- .../device/nanostack_mac_tester/source/util.h | 30 - .../testcases/nanostack_mac_tester/ED_scan.py | 117 -- .../nanostack_mac_tester/__init__.py | 1 - .../address_read_and_write.py | 70 - .../create_and_join_PAN.py | 91 - .../nanostack_mac_tester/send_data.py | 71 - .../send_data_indirect.py | 106 -- .../send_large_payloads.py | 88 - .../testcases/nanostack_mac_tester/template | 65 - 15 files changed, 2780 deletions(-) delete mode 100644 TEST_APPS/device/nanostack_mac_tester/README.md delete mode 100644 TEST_APPS/device/nanostack_mac_tester/main.cpp delete mode 100644 TEST_APPS/device/nanostack_mac_tester/mbed_app.json delete mode 100644 TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp delete mode 100644 TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h delete mode 100644 TEST_APPS/device/nanostack_mac_tester/source/util.cpp delete mode 100644 TEST_APPS/device/nanostack_mac_tester/source/util.h delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/__init__.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/send_data.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py delete mode 100644 TEST_APPS/testcases/nanostack_mac_tester/template diff --git a/TEST_APPS/device/nanostack_mac_tester/README.md b/TEST_APPS/device/nanostack_mac_tester/README.md deleted file mode 100644 index a346c8e3370..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# Nanostack MAC test application - -You can use this application to test the Nanostack RF driver implementations that follow the [Nanostack RF driver porting instructions](https://os.mbed.com/docs/v5.6/reference/contributing-connectivity.html#porting-new-rf-driver-for-6lowpan-stack). The application has a command-line interface that is used to send commands to Nanostack's MAC layer, for example starting PANs, scanning or sending data. The provided tests do not test performance or stability and only indirectly test RF functionalities. - -## Table of contents - -* [Prerequisites](#prerequisites) -* [Setting up the application](#setting-up-the-application) -* [Application usage](#application-usage) - * [Interactive mode](#interactive-mode) - * [Automated mode](#automated-mode) - * [Testcases](#testcases) - * [Considerations](#considerations) - -## Prerequisites - -* [Mbed CLI](https://github.com/ARMmbed/mbed-cli). -* Mbed OS target board with build in radio, OR RF shield with Mbed OS driver - -## Setting up the application - -### Import the application - -To import the application: - -``` -mbed import https://github.com/ARMmbed/nanostack-mac-tester.git -cd nanostack-mac-tester -``` -### Add your RF driver - -To add your RF driver: - -``` -mbed add https://www.github.com/me/my-rf-driver.git -``` - -When using RF shield, you need to configure it to be a default RF driver and instruct Mbed OS that RF driver is present. For example, configuring Atmel RF driver to provide default driver: - -``` - "target_overrides": { - "*": { - "target.device_has_add": ["802_15_4_RF_PHY"], - "atmel-rf.provide-default": true -``` - - -### Compile your binary - -Check that your choice of platform is supported with your choice of toolchain: - -``` -mbed compile --supported -``` - -**Note:** Targets are often abbreviated from the full model names. In the case of `FRDM-K64F` the target is `K64F`. - -#### Set your target - -To set your target HW: - -``` -mbed target TARGET -``` - -#### Set your toolchain - -To set the toolchain: - -``` -mbed toolchain TOOLCHAIN -``` - -Currently, the supported toolchains are `GCC_ARM`, `ARM` and `IAR`. - -#### Compile - -To compile the application: - -``` -mbed compile -``` - -Alternately, you can put everything in a single line: - -``` -mbed compile -m TARGET -t TOOLCHAIN -``` - -## Application usage - -You can use this application in interactive or automated mode. - -### Interactive mode - -To set the application to interactive mode: - -1. Connect your board and copy the compiled application binary from the `BUILD/TARGET/TOOLCHAIN` folder to the board. -2. Open a serial connection with a program such as PuTTY, screen or minicom. The default baudrate is 115200. -3. Press the reset button on the board. The Arm Mbed logo and trace appears in the terminal window. - -If the driver registration and SW MAC creation was successful, the application is ready to receive commands. - -To start off, type `help` to list all commands available and furthermore `help ` to print detailed information about a specific command. - -### Automated mode - -To use the automated testcases, you first need to install the [Icetea test framework](https://github.com/ARMmbed/icetea). - -After the framework is installed, you can run the test set in the root directory of this repository using the command `icetea`. -The following arguments can be used: -* `--tcdir testcases/` sets the target directory to search for testcases -* `--list` lists all testcases found in path, then exits -* `--tc all` specifies that all testcases should be run - `--tc test_name` or `--tc test_name1,test_name2` specify which testcases should be run -* `--repeat N` number of iterations on given set of testcases -* `--bin ` the path to the binary to flash to the devices. Optional once the devices have been flashed once. -* `--reset` reset the devices before the next testcase(highly recommended) - -Many of the provided testcases have a `self.channel` variable in the `setUp()` function for setting the RF channel. The default channel is 11. If you wish to run a test on another channel, you will need to change it manually. - -Some testcases also use a secondary channel for transmitting and will use the next higher channel for that purpose. If the given channel is 26, the secondary channel will default to 25. - -### Test cases - -The automated test set runs the following testcases included in the repository under `testcases/`. -* [Create and join PAN](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/create_and_join_PAN.py) -* [Direct data transmission](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/send_data.py) -* [Indirect data transmission](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/send_data_indirect.py) -* [ED scan](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/ED_scan.py) -* [Address read and write](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/address_write.py) -* [Large data transmission](https://github.com/ARMmbed/nanostack-mac-tester/blob/master/testcases/send_large_payloads.py) - -### Considerations - -* Devices need to be power cycled if they are unresponsive to test framework commands even after resetting. -* Devices can be enclosed in an RF isolation box to improve the consistency of test results. -* Some boards and radio modules come with a PCB trace antenna, instead of an SMD antenna, and need to be spaced further apart to decrease interference. \ No newline at end of file diff --git a/TEST_APPS/device/nanostack_mac_tester/main.cpp b/TEST_APPS/device/nanostack_mac_tester/main.cpp deleted file mode 100644 index 4e95b4010b3..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/main.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * 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 "mbed.h" -#include "rtos.h" -#include "sw_mac.h" -#include "ns_hal_init.h" -#define MBED_CMDLINE_MAX_LINE_LENGTH 250 -#include "ns_cmdline.h" - -#include "mac_commands.h" - -#define HEAP_FOR_MAC_TESTER_SIZE 10000 -#define RX_BUFFER_SIZE 512 - -#define ATMEL 1 -#define MCR20 2 -#define OTHER 3 - -#define TRACE_GROUP "Main" -#include "mbed-trace/mbed_trace.h" - -#include "NanostackRfPhy.h" - -#if !DEVICE_802_15_4_PHY -#error [NOT_SUPPORTED] No 802.15.4 RF driver found for this target -#endif - - -extern mac_api_s *mac_interface; -RawSerial pc(USBTX, USBRX); -osThreadId main_thread; -static CircularBuffer rx_buffer; -static uint8_t ns_heap[HEAP_FOR_MAC_TESTER_SIZE]; - -static void app_heap_error_handler(heap_fail_t event) -{ - tr_error("Heap error (%d), app_heap_error_handler()", event); - switch (event) { - case NS_DYN_MEM_NULL_FREE: - case NS_DYN_MEM_DOUBLE_FREE: - case NS_DYN_MEM_ALLOCATE_SIZE_NOT_VALID: - case NS_DYN_MEM_POINTER_NOT_VALID: - case NS_DYN_MEM_HEAP_SECTOR_CORRUPTED: - case NS_DYN_MEM_HEAP_SECTOR_UNITIALIZED: - break; - default: - break; - } - while (1); -} - -static void rx_interrupt(void) -{ - uint8_t c = pc.getc(); - rx_buffer.push(c); - if (main_thread != NULL) { - osSignalSet(main_thread, 1); - } -} - -static void handle_rx_data(void) -{ - bool exit = false; - uint8_t data; - - while (1) { - exit = !rx_buffer.pop(data); - if (exit) { - break; - } - cmd_char_input(data); - } -} - - -static int mac_prepare(void) -{ - NanostackRfPhy &rf_phy = NanostackRfPhy::get_default_instance(); - int8_t rf_driver_id = rf_phy.rf_register(); - uint8_t rf_eui64[8]; - - if (rf_driver_id < 0) { - tr_error("Failed to register RF driver."); - return -1; - } - rf_phy.get_mac_address(rf_eui64); - mac_description_storage_size_t mac_description; - mac_description.device_decription_table_size = DEVICE_DESCRIPTOR_TABLE_SIZE; /** MAC Device description list size */ - mac_description.key_description_table_size = KEY_DESCRIPTOR_TABLE_SIZE; /** MAC Key description list size */ - mac_description.key_lookup_size = LOOKUP_DESCRIPTOR_TABLE_SIZE; /** Key description key lookup list size */ - mac_description.key_usage_size = USAGE_DESCRIPTOR_TABLE_SIZE; /** Key description key usage list size */ - tr_info("Registered RF driver with id: %hhu, EUI64: %s", rf_driver_id, mbed_trace_array(rf_eui64, 8)); - mac_interface = ns_sw_mac_create(rf_driver_id, &mac_description); - if (!mac_interface) { - tr_error("Failed to create SW MAC."); - return -2; - } - - return mac_interface->mac_initialize(mac_interface, mac_data_confirm_handler, - mac_data_indication_handler, mac_purge_confirm_handler, mac_mlme_confirm_handler, - mac_mlme_indication_handler, rf_driver_id); -} - -static void cmd_ready_cb(int retcode) -{ - cmd_next(retcode); -} - -static void trace_printer(const char *str) -{ - printf("%s\n", str); - cmd_output(); - fflush(stdout); -} - -int main(void) -{ - main_thread = osThreadGetId(); - pc.baud(MBED_CONF_PLATFORM_STDIO_BAUD_RATE); - pc.attach(rx_interrupt); - ns_hal_init(ns_heap, HEAP_FOR_MAC_TESTER_SIZE, app_heap_error_handler, NULL); - mbed_trace_init(); - mbed_trace_print_function_set(trace_printer); - cmd_init(&default_cmd_response_out); - cmd_set_ready_cb(cmd_ready_cb); - mac_commands_init(); - - if (mac_prepare() != 0) { - return -1; - } - tr_info("Created driver & SW MAC"); - - while (true) { - osEvent os_event = Thread::signal_wait(1); - if (os_event.status != osEventSignal) { - osThreadYield(); - } else { - handle_rx_data(); - } - } - return 0; -} diff --git a/TEST_APPS/device/nanostack_mac_tester/mbed_app.json b/TEST_APPS/device/nanostack_mac_tester/mbed_app.json deleted file mode 100644 index d86bfd2dc72..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/mbed_app.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "macros": ["MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], - "target_overrides": { - "*": { - "nanostack.configuration": "lowpan_host", - "platform.stdio-convert-newlines": true, - "platform.stdio-baud-rate": 115200, - "mbed-mesh-api.heap-size": 6000, - "nanostack-hal.event_loop_thread_stack_size": 2000, - "mbed-trace.enable": true, - "nsapi.default-stack": "LWIP", - "target.device_has_add": ["802_15_4_PHY"], - "atmel-rf.provide-default": true - } - } -} diff --git a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp deleted file mode 100644 index 0274ca9ca3a..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp +++ /dev/null @@ -1,1642 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * 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 "mac_commands.h" - -//Need to disable filtering until mac_filter_api.h in mbed-os is updated -#define DISABLE_FILTERING - -#define TRACE_GROUP "MAC" - -#define MAN_MAC_START "start --- Starts a PAN\n"\ - " --pan_id PAN id in hex, default: 0x1234\n"\ - " --logical_channel Operated logical channel, default: 11 (0-26)\n"\ - " --channel_page Operated channel page, default: 0 (0-2)\n"\ - " --start_time Time at which to start sending beacons\n"\ - " default: 0\n"\ - " --beacon_order How often are beacons transmitted\n"\ - " default: 15 (0-15, 15 = never)\n"\ - " --super_frame_order Length of the superframe's active portion\n"\ - " if beacon_order is 15, this option is ignored\n"\ - " default: 15 (0-15)\n"\ - " --pan_coordinator Is this device the PAN coordinator?\n"\ - " default: true\n"\ - " --battery_life_extension Disable beaconing device periodically\n"\ - " to save power? default: false\n"\ - " --coord_realignment If true, coordinator realignment command\n"\ - " is sent prior to changing\n"\ - " the superframe configuration default: false\n" - -#define MAN_MAC_SCAN "scan --- Perform a scan\n"\ - " --scan_type What type of scan to perform, 0=ED, 1=active\n"\ - " 2=passive, 3=orphan, default: 1\n"\ - " --channel_page_enum default: 0 (0-10)\n"\ - " --channel_mask Bitmap of the channels on which to perform\n"\ - " the scan, lower 27-bits are used\n"\ - " bit 0 corresponds to channel 0 and so forth\n"\ - " default: 0x07FFF800 (channels 11-26)\n"\ - " --scan_duration How long to spend in each channel,\n"\ - " aBaseSuperFrameDuration * (2^n + 1) symbols\n"\ - " default: 5 (0-14)\n"\ - " --channel_page The channel page on which to perform the scan\n"\ - " default: 0 (0-31)\n"\ - MAN_MAC_SECURITY - -#define MAN_MAC_DATA "data --- Send arbitrary data\n"\ - " --src_addr_mode Source addressing mode, 0=no address, 1=reserved\n"\ - " 2=16-bit address, 3=64-bit address\n"\ - " default: 3 (0-3)\n"\ - " --dst_addr_mode Destination addressing mode, same as above\n"\ - " default: 3 (0-3)\n"\ - " --dst_pan_id Destination PAN id in hex\n"\ - " default: 0x1234 (0x0-0xFFFF)\n"\ - " --dst_addr Destination address, default: 00:00:00:...\n"\ - " --msdu_length Length of the data to send, default: 0 (0-255)\n"\ - " --msdu Data to transmit, default: \n"\ - " --msdu_handle Handle of this MSDU, default: 0 (0-255)\n"\ - " --tx_ack_req Is ack required for this transmission?\n"\ - " default: true\n"\ - " --indirect_tx Transmit indirectly? default: false\n"\ - " --pending_bit Specifies whether more fragments (higher layer)\n"\ - " are to be sent, default: false\n"\ - " --wait_for_confirm Should we block further commands until we have\n"\ - " received a data confirmation, default: true\n"\ - MAN_MAC_SECURITY - -#define MAN_MAC_POLL "poll --- Poll the coordinator for data\n"\ - " --coord_addr_mode Coordinator addressing mode, 2=16-bit address\n"\ - " 3=64-bit address, default: 3 (2-3)\n"\ - " --coord_pan_id Coordinator PAN id in hex\n"\ - " default: 0x1234 (0x0-0xFFFF)\n"\ - " --coord_address Coordinator address, default 00:00:00:...\n"\ - MAN_MAC_SECURITY - -#define MAN_MAC_PURGE "purge --- Remove a transmission from the queue\n"\ - " --msdu_handle Handle of the MSDU to be removed\n"\ - " default: 0 (0-255)\n"\ - -#define MAN_MAC_SET "mlme-set --- Set a specified PIB attribute\n"\ - " --attr ID of the attribute to set in hex (0x0-0xFF)\n"\ - " --attr_index <0-255> Index of the attribute, default: 0 (0-255)\n"\ - " --value_ascii Specify the set value as an ASCII string\n"\ - " --value_bytes Value as a string of bytes\n"\ - " --value_uint8/16/32 Value as a uint8, uint16, or uint32\n"\ - " Max value for uint32 is the max value for int32\n"\ - " --value_size Size of the value in bytes (0-255)\n" - -#define MAN_MAC_GET "mlme-get --- Get a specified PIB attribute\n"\ - " --attr ID of the attribute we want to get in hex (0x0-0xFF)\n"\ - " --attr_index Index of the attribute, default: 0 (0-255)\n" - -#define MAN_MAC_RESET "mlme-reset --- Reset the MAC sublayer\n"\ - " --set_default_pib When set to true, PIB attributes are set to\n"\ - " their default values\n"\ - " If set to false, PIB attributes retain\n"\ - " their values, default: true\n" - -#define MAN_MAC_ADDR "addr --- Configure 16/64-bit MAC addresses\n"\ - " having no options will display the addresses\n"\ - " --16-bit 16-bit MAC address in hex (0x0-0xFFFF)\n"\ - " --64-bit 64-bit MAC address\n" - -#define MAN_MAC_SECURITY " --security_level 0=no security, 1=MIC32, 2=MIC64, 3=MIC128,\n"\ - " 4=ENC, 5=ENC+MIC32, 6=ENC+MIC64, 7=ENC+MIC128\n"\ - " default: 0\n"\ - " --key_id_mode 0=implicit, 1=default key source\n"\ - " 2=2-byte key source\n"\ - " 3=8-byte key source, default: 0 (0-3)\n"\ - " --key_index Which key to use, default: 0 (0-255)\n"\ - " --key_source The originator of the key to be used\n" - -#define MAN_MAC_KEY "key --- Configure or add key descriptors\n"\ - "config --- Configure parameters for key descriptor\n"\ - " --key Actual security key, 16 bytes\n"\ - " default: C0:C1:C2:...:CF\n"\ - " --key_id_lookup_list_entries Amount of entries in the key's lookup\n"\ - " --key_device_list_entries ...device...\n"\ - " --key_usage_list_entries and usage list, default: 2 (0-255)\n"\ - " --lookup_index Which entry of the lookup list\n"\ - " are we accessing? default: 0 (0-255)\n"\ - " --lookup_data The lookup data for this key\n"\ - " length is 9 bytes regardless of\n"\ - " the next option\n"\ - " --lookup_data_size How large is the lookup data? (0-1)\n"\ - " 0=5 bytes, 1=9 bytes\n"\ - " --device_list_index Which entry of the device list\n"\ - " are we accessing, default: 0 (0-255)\n"\ - " --device_descriptor_handle Which entry in our neighbour table\n"\ - " do we want to use this key with\n"\ - " default: 0 (0-255)\n"\ - " --unique_device Is the device unique to the key?\n"\ - " --blacklisted Is the device blacklisted?\n"\ - " --usage_list_index Which entry of the usage list\n"\ - " are we accessing, default: 0 (0-255)\n"\ - " --frame_type What type of frame do we want to\n"\ - " use this key with? (0-3)\n"\ - " 0=beacon, 1=data, 2=ACK, 3=command\n"\ - " --command_frame_identifier Type of the command frame (1-9)\n"\ - " 1=association request\n"\ - " 2=association response\n"\ - " 3=disassociation notification\n"\ - " 4=data request\n"\ - " 5=PAN id conflict notification\n"\ - " 6=orphan notification\n"\ - " 7=beacon request\n"\ - " 8=coordinator realigment\n"\ - " 9=GTS request\n"\ - "add --- Add the key into the key descriptor table\n"\ - " --index Index in the key table (0-255)\n" - -#define MAN_MAC_ADD_NEIGHBOUR "add-neigh --- Add an entry to the neighbour table\n"\ - " --frame_ctr Frame counter for this neighbour\n"\ - " --mac16 16-bit MAC address in hex (0x0-0xFFFF)\n"\ - " --mac64 64-bit MAC address\n"\ - " --pan_id PAN id (0x0-0xFFFF)\n"\ - " --index Index in the neighbour table (0-255)\n" - -#define MAN_MAC_FILTER "filter --- Configure MAC layer filtering\n"\ - "start --- Start a generic filter in blacklist, whitelist or fixed mode\n"\ - " --mode Set the filtering mode, values: allow|block|fixed\n"\ - " --lqi_m LQI multiplier (fixed mode only)\n"\ - " --dbm_m dBm multiplier (fixed mode only)\n"\ - "add --- Add a filter by manually supplying values, or using a preset mode\n"\ - " --lqi_m LQI multiplier\n"\ - " --lqi_add Value added to the LQI\n"\ - " --dbm_m dBm multiplier\n"\ - " --dbm_add Value added to the dBm\n"\ - " --mode Filtering mode, values: allow|block|fixed\n"\ - " --short 16-bit address in hex to filter (0x0-0xFFFF)\n"\ - " --long 64-bit address as bytes to filter\n"\ - "remove --- Remove a filter\n"\ - " --short 16-bit address to remove from filter (0x0-0xFFFF)\n"\ - " --long 64-bit address to remove from filter\n"\ - "clear --- Clear all filters excluding the default one\n"\ - "stop --- Stop filtering completely\n"\ - -#define MAN_MAC_CONFIG_STATUS "config-status --- Configure expected status codes\n"\ - " having no options will display configured statuses\n"\ - " default: 0 (MLME_SUCCESS) for all\n"\ - " --data_conf MCPS-DATA.confirm (0-255)\n"\ - " --data_ind MCPS-DATA.indication (0-255)\n"\ - " --get MLME-GET.confirm (0-255)\n"\ - " --scan MLME-SCAN.confirm (0-255)\n"\ - " --poll MLME-POLL.confirm (0-255)\n"\ - " --purge MCPS-PURGE.confirm (0-255)\n"\ - " --comm_status MLME-COMM-STATUS.indication (0-255)\n"\ - " --list List all statuses\n"\ - " --reset Reset all statuses to default values\n" - -#define MAN_MAC_FIND_BEACON "find-beacon --- Search for a PAN in the\n"\ - " results of the last scan\n"\ - " --data Beacon data transmitted in the beacon\n" - -#define MAN_MAC_WAIT "wait --- Wait for data sent directly for a\n"\ - " specified amount of milliseconds\n"\ - " --timeout Milliseconds to wait, default: 1000\n" - -#define MAN_MAC_ED_ANALYZE "analyze-ed Channel to analyze (11-26)\n"\ - " --above Check if the ED value is above a given value\n"\ - " --below Check if the ED value is below a given value\n" - -#define MAN_RESET "reset --- Reset MCPS and MLME structures to default values\n" - -#define MAN_SILENT "silent-mode --- When enabled, doesn't print extended information\n"\ - " of MCPS/MLME primitives\n" - -mac_api_s *mac_interface = NULL; -static bool wait_for_confirm = true; -static bool silent_mode = false; -static volatile unsigned int data_count = 0; - -static mlme_start_t start_req = { - 0x1234, /*PANId*/ - 11, /*LogicalChannel*/ - 0, /*ChannelPage*/ - 0, /*StartTime*/ - 15, /*BeaconOrder*/ - 15, /*SuperframeOrder*/ - true, /*PANCoordinator*/ - false, /*BatteryLifeExtension*/ - false, /*CoordRealignment*/ -}; - -static mlme_scan_t scan_req = { - MAC_ACTIVE_SCAN, /*ScanType*/ - { - CHANNEL_PAGE_0, /*channel_page (enum)*/ - 0x07FFF800 /*channel_mask*/ - }, - 5, /*ScanDuration*/ - 0, /*ChannelPage*/ - { - 0, /*SecurityLevel*/ - 0, /*KeyIdMode*/ - 0, /*KeyIndex*/ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /*Keysource*/ - } -}; - -static mcps_data_req_t data_req = { - 3, /*SrcAddrMode*/ - 3, /*DstAddrMode*/ - 0x1234, /*DstPANId*/ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*DstAddr*/ - 0, /*msduLength*/ - NULL, /*msdu*/ - 0, /*msduHandle*/ - true, /*TxAckReq*/ - false, /*IndirectTx*/ - false, /*PendingBit*/ - { - 0, /*SecurityLevel*/ - 0, /*KeyIdMode*/ - 0, /*KeyIndex*/ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /*Keysource*/ - } -}; - -static mlme_poll_t poll_req = { - 3, /*CoordAddrMode*/ - 0x1234, /*CoordPANId*/ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*CoordAddress*/ - { - 0, /*SecurityLevel*/ - 0, /*KeyIdMode*/ - 0, /*KeyIndex*/ - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} /*Keysource*/ - } -}; - -static mcps_purge_t purge_req = { - 0 /*msduHandle*/ -}; - -static mlme_set_t set_req = { - (mlme_attr_t)0x39, /*attr*/ - 0, /*attr_index*/ - NULL, /*value_pointer*/ - 0 /*value_size*/ -}; - -static mlme_get_t get_req = { - (mlme_attr_t)0x39, /*attr*/ - 0 /*attr_index*/ -}; - -static mlme_reset_t reset_req = { - true /*SetDefaultPIB*/ -}; - -static mlme_key_id_lookup_descriptor_t lookup_descriptors[LOOKUP_DESCRIPTOR_TABLE_SIZE]; -static mlme_key_device_descriptor_t device_descriptors[DEVICE_DESCRIPTOR_TABLE_SIZE]; -static mlme_key_usage_descriptor_t usage_descriptors[USAGE_DESCRIPTOR_TABLE_SIZE]; -static mlme_key_descriptor_entry_t key_descriptor = { - lookup_descriptors, /*KeyIdLookupList*/ - LOOKUP_DESCRIPTOR_TABLE_SIZE, /*KeyIdLookupListEntries*/ - device_descriptors, /*KeyDeviceList*/ - DEVICE_DESCRIPTOR_TABLE_SIZE, /*KeyDeviceListEntries*/ - usage_descriptors, /*KeyUsageList*/ - USAGE_DESCRIPTOR_TABLE_SIZE, /*KeyUsageListEntries*/ - {0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF} /*Key*/ -}; - -struct status_list_t { - uint8_t data_conf; - uint8_t get_conf; - uint8_t scan_conf; - uint8_t poll_conf; - uint8_t purge_conf; - uint8_t comm_status_ind; - uint8_t data_ind_len; - uint8_t *data_ind; -}; - -static struct status_list_t expected_statuses = { - MLME_SUCCESS, - MLME_SUCCESS, - MLME_SUCCESS, - MLME_SUCCESS, - MLME_SUCCESS, - MLME_SUCCESS, - 0, - NULL -}; - -struct beacon_list_t { - size_t count; - char *beacons[MLME_MAC_RES_SIZE_MAX]; - uint8_t beacon_lengths[MLME_MAC_RES_SIZE_MAX]; -}; - -static struct beacon_list_t received_beacons = {}; - -struct ed_scan_result_list_t { - uint8_t count; - uint8_t channel[MLME_MAC_RES_SIZE_MAX]; - uint8_t ED_values[MLME_MAC_RES_SIZE_MAX]; -}; - -static struct ed_scan_result_list_t last_ed_results; - -static void print_security(const mlme_security_t *key) -{ - if (key->SecurityLevel > 0) { - cmd_printf("Key.SecurityLevel: %u\n", key->SecurityLevel); - cmd_printf("Key.KeyIdMode: %u\n", key->KeyIdMode); - cmd_printf("Key.KeyIndex: %hhu\n", key->KeyIndex); - cmd_printf("Key.Keysource %s\n", mbed_trace_array(key->Keysource, 8)); - } -} - -static void print_PAN_descriptor(const mlme_pan_descriptor_t *desc) -{ - cmd_printf("PANDescriptor.CoordAddrMode: %u\n", desc->CoordAddrMode); - cmd_printf("PANDescriptor.CoordPANId: 0x%04X\n", desc->CoordPANId); - cmd_printf("PANDescriptor.CoordAddress: %s\n", mbed_trace_array(desc->CoordAddress, 8)); - cmd_printf("PANDescriptor.LogicalChannel: %hhu\n", desc->LogicalChannel); - cmd_printf("PANDescriptor.ChannelPage: %hhu\n", desc->ChannelPage); - cmd_printf("PANDescriptor.SuperframeSpec: %02x:%02x\n", desc->SuperframeSpec[0], desc->SuperframeSpec[1]); - cmd_printf("PANDescriptor.GTSPermit: %s\n", desc->GTSPermit ? "true" : "false"); - cmd_printf("PANDescriptor.LinkQuality: %hhu\n", desc->LinkQuality); - cmd_printf("PANDescriptor.Timestamp: %lu\n", desc->Timestamp); - cmd_printf("PANDescriptor.SecurityFailure: %hhu\n", desc->SecurityFailure); - print_security(&desc->Key); -} - -static int handle_security_args(int argc, char *argv[], mlme_security_t *key) -{ - char *str; - int32_t val; - - if (cmd_parameter_int(argc, argv, "--security_level", &val)) { - if (val >= 0 && val <= 7) { - key->SecurityLevel = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--key_id_mode", &val)) { - if (val >= 0 && val <= 3) { - key->KeyIdMode = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--key_index", &val)) { - if (val >= 0 && val <= 255) { - key->KeyIndex = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--key_source", &str)) { - if (strlen(str) == 2 * 8 + 7) { - if (string_to_bytes(str, key->Keysource, 8) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - return CMDLINE_RETCODE_SUCCESS; -} - -static void add_beacon(const uint8_t *beacon, uint8_t len) -{ - if (received_beacons.count >= MLME_MAC_RES_SIZE_MAX) { - tr_warn("List of received beacons is full. Discarding %s <%.*s>", mbed_trace_array(beacon, len), len, beacon); - return; - } - unsigned int cur_beacon = received_beacons.count; - received_beacons.beacon_lengths[cur_beacon] = len; - received_beacons.beacons[cur_beacon] = (char *)ns_dyn_mem_temporary_alloc(len <= 75 ? 75 : len); - if (len != 0) { - std::memcpy(received_beacons.beacons[cur_beacon], beacon, len); - } - ++received_beacons.count; -} - -static void clear_beacons(void) -{ - for (unsigned int i = 0; i < received_beacons.count; ++i) { - ns_dyn_mem_free(received_beacons.beacons[i]); - received_beacons.beacons[i] = NULL; - received_beacons.beacon_lengths[i] = 0; - } - received_beacons.count = 0; -} - -void mac_commands_init(void) -{ - cmd_add("start", mac_start_command, "Start a PAN", MAN_MAC_START); - cmd_add("scan", mac_scan_command, "Perform a scan", MAN_MAC_SCAN); - cmd_add("data", mac_data_command, "Send data", MAN_MAC_DATA); - cmd_add("poll", mac_poll_command, "Poll for data", MAN_MAC_POLL); - cmd_add("purge", mac_purge_command, "Purge data from the transmission queue", MAN_MAC_PURGE); - cmd_add("mlme-set", mac_set_command, "Writes a given value to the PIB attribute", MAN_MAC_SET); - cmd_add("mlme-get", mac_get_command, "Gets the value of a given PIB attribute", MAN_MAC_GET); - cmd_add("mlme-reset", mac_reset_command, "Resets the MAC sublayer to default values", MAN_MAC_RESET); - cmd_add("addr", mac_address_command, "Configure MAC addresses", MAN_MAC_ADDR); - cmd_add("key", mac_key_command, "Configure or add security keys", MAN_MAC_KEY); - cmd_add("add-neigh", mac_add_neighbour_command, "Add a device to the neighbour table", MAN_MAC_ADD_NEIGHBOUR); - cmd_add("filter", mac_filter_command, "Add or remove filters based on MAC addresses", MAN_MAC_FILTER); - cmd_add("config-status", mac_config_status_command, "Set expected return statuses for confirmations and indications", MAN_MAC_CONFIG_STATUS); - cmd_add("find-beacon", mac_find_beacon_command, "Find a PAN by beacon data", MAN_MAC_FIND_BEACON); - cmd_add("wait", mac_wait_command, "Wait for data", MAN_MAC_WAIT); - cmd_add("analyze-ed", mac_analyze_ed_command, "Analyze the results of the last ED scan", MAN_MAC_ED_ANALYZE); - cmd_add("reset", reset_command, "Reset MCPS/MLME structures to default values", MAN_RESET); - cmd_add("silent-mode", silent_mode_command, "Stop printing fields of MCPS/MLME structures", MAN_SILENT); -} - -void mac_data_confirm_handler(const mac_api_t *api, const mcps_data_conf_t *data) -{ - cmd_printf("MCPS-DATA.confirm\n"); - if (!silent_mode) { - cmd_printf("msduHandle: %hhu\n", data->msduHandle); - cmd_printf("status: %hhu (%s)\n", data->status, mlme_status_string(data->status)); - cmd_printf("timestamp: %lu\n", data->timestamp); - cmd_printf("cca_retries:%hhu\n", data->cca_retries); - cmd_printf("tx_retries: %hhu\n", data->tx_retries); - } - if (data->status == expected_statuses.data_conf) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } else { - cmd_ready(CMDLINE_RETCODE_FAIL); - } -} - -void mac_data_indication_handler(const mac_api_t *api, const mcps_data_ind_t *data) -{ - cmd_printf("MCPS-DATA.indication\n"); - if (!silent_mode) { - cmd_printf("SrcAddrMode: %u\n", data->SrcAddrMode); - cmd_printf("SrcPANId: 0x%04X\n", data->SrcPANId); - cmd_printf("SrcAddr: %s\n", mbed_trace_array(data->SrcAddr, 8)); - cmd_printf("DstAddrMode: %u\n", data->DstAddrMode); - cmd_printf("DstPANId: 0x%04X\n", data->DstPANId); - cmd_printf("DstAddr: %s\n", mbed_trace_array(data->DstAddr, 8)); - cmd_printf("mpduLinkQuality:%hhu\n", data->mpduLinkQuality); - cmd_printf("signal_dbm: %hhi\n", data->signal_dbm); - cmd_printf("timestamp: %lu\n", data->timestamp); - cmd_printf("DSN: %hhi\n", data->DSN); - print_security(&data->Key); - cmd_printf("msduLength %hu\n", data->msduLength); - cmd_printf("msdu_ptr: %s <%.*s>\n", mbed_trace_array(data->msdu_ptr, data->msduLength), data->msduLength, data->msdu_ptr); - } - if (data->msdu_ptr && expected_statuses.data_ind) { - if (data->msduLength != expected_statuses.data_ind_len) { - return; - } - if (strncmp((const char *)data->msdu_ptr, (const char *)expected_statuses.data_ind, expected_statuses.data_ind_len) == 0) { - ++data_count; - } else { - tr_warn("Received unexpected data!"); - } - } -} - -void mac_purge_confirm_handler(const mac_api_t *api, mcps_purge_conf_t *data) -{ - cmd_printf("MCPS-PURGE.confirm\n"); - if (!silent_mode) { - cmd_printf("msduHandle: %hhu\n", data->msduHandle); - cmd_printf("status: %hhu (%s)\n", data->status, mlme_status_string(data->status)); - } - if (data->status == expected_statuses.purge_conf) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } else { - cmd_ready(CMDLINE_RETCODE_FAIL); - } -} - -void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const void *data) -{ - switch (id) { - case MLME_ASSOCIATE: { - break; - } - case MLME_DISASSOCIATE: { - break; - } - case MLME_GET: { - mlme_get_conf_t *get_data = (mlme_get_conf_t *)data; - cmd_printf("MLME-GET.confirm\n"); - if (!silent_mode) { - cmd_printf("status: %hhu (%s)\n", get_data->status, mlme_status_string(get_data->status)); - cmd_printf("attr: %hhu\n", get_data->attr); - cmd_printf("attr_index: %hhu\n", get_data->attr_index); - cmd_printf("value_pointer: %s\n", mbed_trace_array((uint8_t *)get_data->value_pointer, get_data->value_size)); - cmd_printf("value_size: %hhu\n", get_data->value_size); - } - if (get_data->status == expected_statuses.get_conf) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } else { - cmd_ready(CMDLINE_RETCODE_FAIL); - } - break; - } - case MLME_GTS: { - break; - } - case MLME_RESET: { - break; - } - case MLME_RX_ENABLE: { - break; - } - case MLME_SCAN: { - mlme_scan_conf_t *scan_data = (mlme_scan_conf_t *)data; - cmd_printf("MLME-SCAN.confirm\n"); - if (!silent_mode) { - cmd_printf("status: %hhu (%s)\n", scan_data->status, mlme_status_string(scan_data->status)); - cmd_printf("ScanType: %u\n", scan_data->ScanType); - cmd_printf("ChannelPage: %hhu\n", scan_data->ChannelPage); - cmd_printf("UnscannedChannels: 0x%08lX\n", scan_data->UnscannedChannels.channel_mask[0]); - cmd_printf("ResultListSize: %hhu\n", scan_data->ResultListSize); - } - for (unsigned int i = 0; i < scan_data->ResultListSize; ++i) { - if (scan_data->ScanType == MAC_ED_SCAN_TYPE) { - cmd_printf("Channel %d ED value: %hhu\n", channel_from_mask(scan_req.ScanChannels.channel_mask[0], i), scan_data->ED_values[i]); - memcpy(last_ed_results.ED_values, scan_data->ED_values, scan_data->ResultListSize); - last_ed_results.count = scan_data->ResultListSize; - for (int i = 0; i < scan_data->ResultListSize; ++i) { - last_ed_results.channel[i] = channel_from_mask(scan_req.ScanChannels.channel_mask[0], i); - } - } else if (scan_data->ScanType == MAC_PASSIVE_SCAN) { - print_PAN_descriptor(scan_data->PAN_values[i]); - } - } - if (scan_data->status == expected_statuses.scan_conf || scan_data->status == MLME_LIMIT_REACHED) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } else { - cmd_ready(CMDLINE_RETCODE_FAIL); - } - break; - } - case MLME_SET: { - break; - } - case MLME_START: { - break; - } - case MLME_POLL: { - mlme_poll_conf_t *poll_data = (mlme_poll_conf_t *)data; - cmd_printf("MLME-POLL.confirm\n"); - if (!silent_mode) { - cmd_printf("status: %hhu (%s)\n", poll_data->status, mlme_status_string(poll_data->status)); - cmd_printf("data_count %u\n", data_count); - } - if (expected_statuses.poll_conf == MLME_SUCCESS) { - if (data_count == 1 && poll_data->status == MLME_SUCCESS) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } else { - cmd_ready(CMDLINE_RETCODE_FAIL); - } - } else if (expected_statuses.poll_conf == poll_data->status) { - cmd_ready(CMDLINE_RETCODE_SUCCESS); - } else { - cmd_ready(CMDLINE_RETCODE_FAIL); - } - break; - } - default: { - cmd_ready(CMDLINE_RETCODE_COMMAND_NOT_IMPLEMENTED); - break; - } - } -} - -void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const void *data) -{ - switch (id) { - case MLME_ASSOCIATE: { - break; - } - case MLME_DISASSOCIATE: { - break; - } - case MLME_BEACON_NOTIFY: { - const mlme_beacon_ind_t *beacon_ind = (mlme_beacon_ind_t *)data; - cmd_printf("MLME-BEACON-NOTIFY.indication\n"); - if (!silent_mode) { - cmd_printf("BSN: %hhu\n", beacon_ind->BSN); - print_PAN_descriptor(&beacon_ind->PANDescriptor); - cmd_printf("PendAddrSpec.short_address_count %u\n", beacon_ind->PendAddrSpec.short_address_count); - cmd_printf("PendAddrSpec.extended_address_count %u\n", beacon_ind->PendAddrSpec.extended_address_count); - cmd_printf("AddrList %s\n", mbed_trace_array(beacon_ind->AddrList, beacon_ind->PendAddrSpec.short_address_count * 2 + - beacon_ind->PendAddrSpec.extended_address_count * 8)); - cmd_printf("beacon_data_length %hu\n", beacon_ind->beacon_data_length); - cmd_printf("beacon_data %s\n", mbed_trace_array(beacon_ind->beacon_data, beacon_ind->beacon_data_length)); - } - add_beacon(beacon_ind->beacon_data, beacon_ind->beacon_data_length); - break; - } - case MLME_GTS: { - break; - } - case MLME_ORPHAN: { - break; - } - case MLME_COMM_STATUS: { - cmd_printf("MLME-COMM-STATUS.indication\n"); - const mlme_comm_status_t *comm_status_ind_data = (mlme_comm_status_t *)data; - if (!silent_mode) { - cmd_printf("PANId: 0x%04X\n", comm_status_ind_data->PANId); - cmd_printf("SrcAddrMode: %u\n", comm_status_ind_data->SrcAddrMode); - cmd_printf("SrcAddr: %s\n", mbed_trace_array(comm_status_ind_data->SrcAddr, 8)); - cmd_printf("DstAddrMode: %u\n", comm_status_ind_data->DstAddrMode); - cmd_printf("DstAddr: %s\n", mbed_trace_array(comm_status_ind_data->DstAddr, 8)); - cmd_printf("status: %hhu (%s)\n", comm_status_ind_data->status, mlme_status_string(comm_status_ind_data->status)); - print_security(&comm_status_ind_data->Key); - } - break; - } - case MLME_SYNC_LOSS: { - break; - } - default: - break; - } -} - -int mac_start_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - bool boolean; - - cmd_printf("MLME-START.request\n"); - if (cmd_parameter_val(argc, argv, "--pan_id", &str)) { - uint32_t pan_id = strtoul(str, NULL, 16); - if (pan_id <= 0xFFFF) { - start_req.PANId = pan_id; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--logical_channel", &val)) { - if (val >= 0 && val <= 26) { - start_req.LogicalChannel = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--channel_page", &val)) { - if (val >= 0 && val <= 2) { - start_req.ChannelPage = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--start_time", &val)) { - start_req.StartTime = val; - } - if (cmd_parameter_int(argc, argv, "--beacon_order", &val)) { - if (val >= 0 && val <= 15) { - start_req.BeaconOrder = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--super_frame_order", &val)) { - if (val >= 0 && val <= 15) { - start_req.SuperframeOrder = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_bool(argc, argv, "--pan_coordinator", &boolean)) { - start_req.PANCoordinator = boolean; - } - if (cmd_parameter_bool(argc, argv, "--battery_life_extension", &boolean)) { - start_req.BatteryLifeExtension = boolean; - } - if (cmd_parameter_bool(argc, argv, "--coord_realignment", &boolean)) { - start_req.CoordRealignment = boolean; - } - mac_interface->mlme_req(mac_interface, MLME_START, &start_req); - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_scan_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - - cmd_printf("MLME-SCAN.request\n"); - if (cmd_parameter_int(argc, argv, "--scan_type", &val)) { - if (val >= 0 && val <= 3) { - scan_req.ScanType = (mac_scan_type_t)val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--channel_page_enum", &val)) { - if (val >= 0 && val <= 10) { - scan_req.ScanChannels.channel_page = (channel_page_e)val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--channel_mask", &str)) { - scan_req.ScanChannels.channel_mask[0] = strtoul(str, NULL, 16); - } - if (cmd_parameter_int(argc, argv, "--scan_duration", &val)) { - if (val >= 0 && val <= 14) { - scan_req.ScanDuration = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--channel_page", &val)) { - if (val >= 0 && val <= 2) { - scan_req.ChannelPage = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (handle_security_args(argc, argv, &scan_req.Key) != CMDLINE_RETCODE_SUCCESS) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - clear_beacons(); - mac_interface->mlme_req(mac_interface, MLME_SCAN, &scan_req); - return CMDLINE_RETCODE_EXCUTING_CONTINUE; -} - -int mac_data_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - bool boolean; - - cmd_printf("MCPS-DATA.request\n"); - if (cmd_parameter_int(argc, argv, "--src_addr_mode", &val)) { - if (val == 0 || val == 2 || val == 3) { - data_req.SrcAddrMode = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--dst_addr_mode", &val)) { - if (val == 0 || val == 2 || val == 3) { - data_req.DstAddrMode = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--dst_pan_id", &str)) { - uint32_t pan_id = strtoul(str, NULL, 16); - if (pan_id <= 0xFFFF) { - data_req.DstPANId = pan_id; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--dst_addr", &str)) { - int len = (data_req.DstAddrMode == 2 ? 2 : 8); - if (string_to_bytes(str, data_req.DstAddr, len) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--msdu_length", &val)) { - data_req.msduLength = val; - } - if (cmd_parameter_val(argc, argv, "--msdu", &str)) { - ns_dyn_mem_free(data_req.msdu); - if (strlen(str) != data_req.msduLength) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - data_req.msdu = (uint8_t *)ns_dyn_mem_temporary_alloc(data_req.msduLength); - if (data_req.msdu == NULL) { - tr_error("Failed to allocate memory for the msdu"); - return CMDLINE_RETCODE_FAIL; - } - std::memcpy(data_req.msdu, str, data_req.msduLength); - } - if (cmd_parameter_int(argc, argv, "--msdu_handle", &val)) { - if (val >= 0 && val <= 255) { - data_req.msduHandle = val; - } - } - if (cmd_parameter_bool(argc, argv, "--tx_ack_req", &boolean)) { - data_req.TxAckReq = boolean; - } - if (cmd_parameter_bool(argc, argv, "--indirect_tx", &boolean)) { - data_req.InDirectTx = boolean; - } - if (cmd_parameter_bool(argc, argv, "--pending_bit", &boolean)) { - data_req.PendingBit = boolean; - } - if (cmd_parameter_bool(argc, argv, "--wait_for_confirm", &boolean)) { - wait_for_confirm = boolean; - } - if (handle_security_args(argc, argv, &data_req.Key) != CMDLINE_RETCODE_SUCCESS) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - mac_interface->mcps_data_req(mac_interface, &data_req); - if (wait_for_confirm) { - return CMDLINE_RETCODE_EXCUTING_CONTINUE; - } - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_poll_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - - cmd_printf("MLME-POLL.request\n"); - data_count = 0; - if (cmd_parameter_int(argc, argv, "--coord_addr_mode", &val)) { - if (val == 2 || val == 3) { - poll_req.CoordAddrMode = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--coord_pan_id", &str)) { - unsigned long pan_id = strtoul(str, NULL, 16); - if (pan_id <= 0xFFFF) { - poll_req.CoordPANId = pan_id; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--coord_address", &str)) { - int len = (poll_req.CoordAddrMode == 2 ? 2 : 8); - if (string_to_bytes(str, poll_req.CoordAddress, len) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (handle_security_args(argc, argv, &poll_req.Key) != CMDLINE_RETCODE_SUCCESS) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - mac_interface->mlme_req(mac_interface, MLME_POLL, &poll_req); - return CMDLINE_RETCODE_EXCUTING_CONTINUE; -} - -int mac_purge_command(int argc, char *argv[]) -{ - int32_t val; - - cmd_printf("MCPS-PURGE.request\n"); - if (cmd_parameter_int(argc, argv, "--msdu_handle", &val)) { - if (val >= 0 && val <= 255) { - purge_req.msduHandle = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - mac_interface->mcps_purge_req(mac_interface, &purge_req); - return CMDLINE_RETCODE_EXCUTING_CONTINUE; -} - -int mac_set_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - uint8_t val_uint8 = 0; - uint16_t val_uint16 = 0; - uint32_t val_uint32 = 0; - uint8_t *val_ptr_array = NULL; - - cmd_printf("MLME-SET.request\n"); - if (cmd_parameter_val(argc, argv, "--attr", &str)) { - uint32_t attribute = strtoul(str, NULL, 16); - if (attribute <= 255) { - set_req.attr = (mlme_attr_t)attribute; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--attr_index", &val)) { - if (val >= 0 && val <= 255) { - set_req.attr_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--value_ascii", &str)) { - val_ptr_array = (uint8_t *)ns_dyn_mem_temporary_alloc(strlen(str)); - if (val_ptr_array == NULL) { - tr_error("Failed to allocate memory for MLME-SET.request"); - return CMDLINE_RETCODE_FAIL; - } - std::memcpy(val_ptr_array, str, strlen(str)); - set_req.value_pointer = val_ptr_array; - } else if (cmd_parameter_val(argc, argv, "--value_bytes", &str)) { - size_t bytes = (strlen(str) + 1) / 3; - val_ptr_array = (uint8_t *)ns_dyn_mem_temporary_alloc(bytes); - if (val_ptr_array == NULL) { - tr_error("Failed to allocate memory for MLME-SET.request"); - return CMDLINE_RETCODE_FAIL; - } - if (string_to_bytes(str, val_ptr_array, bytes) != 0) { - ns_dyn_mem_free(val_ptr_array); - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - set_req.value_pointer = val_ptr_array; - } else if (cmd_parameter_int(argc, argv, "--value_uint8", &val)) { - if (val >= 0 && val <= 0xFF) { - val_uint8 = val; - set_req.value_pointer = &val_uint8; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_int(argc, argv, "--value_uint16", &val)) { - if (val >= 0 && val <= 0xFFFF) { - val_uint16 = val; - set_req.value_pointer = &val_uint16; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_int(argc, argv, "--value_uint32", &val)) { - if (val >= 0 && val <= 0x7FFFFFFF) { - val_uint32 = val; - set_req.value_pointer = &val_uint32; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--value_size", &val)) { - if (val >= 0 && val <= 255) { - set_req.value_size = val; - } - } - - mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); - ns_dyn_mem_free(val_ptr_array); - set_req.value_pointer = NULL; - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_get_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - - cmd_printf("MLME-GET.request\n"); - if (cmd_parameter_val(argc, argv, "--attr", &str)) { - uint32_t attribute = strtoul(str, NULL, 16); - if (attribute <= 255) { - get_req.attr = (mlme_attr_t)attribute; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--attr_index", &val)) { - if (val >= 0 && val <= 255) { - get_req.attr_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - mac_interface->mlme_req(mac_interface, MLME_GET, &get_req); - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_reset_command(int argc, char *argv[]) -{ - bool boolean; - - cmd_printf("MLME-RESET.request\n"); - if (cmd_parameter_bool(argc, argv, "--set_default_pib", &boolean)) { - reset_req.SetDefaultPIB = boolean; - } - mac_interface->mlme_req(mac_interface, MLME_RESET, &reset_req); - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_address_command(int argc, char *argv[]) -{ - char *str; - uint8_t ext_addr[8]; - - if (cmd_parameter_val(argc, argv, "--64-bit", &str)) { - if (string_to_bytes(str, ext_addr, 8) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - mac_interface->mac64_set(mac_interface, ext_addr); - cmd_printf("64-bit MAC address set to: %s\n", mbed_trace_array(ext_addr, 8)); - } else if (cmd_parameter_val(argc, argv, "--16-bit", &str)) { - uint32_t short_addr_32 = strtoul(str, NULL, 16); - if (short_addr_32 <= 0xFFFF) { - uint16_t short_addr = short_addr_32; - mlme_set_t set_req; - set_req.attr = macShortAddress; - set_req.value_pointer = &short_addr; - set_req.value_size = 2; - mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); - cmd_printf("16-bit MAC address set to: 0x%04X\n", short_addr); - } else { - tr_warn("Invalid 16-bit MAC address given: %lu", short_addr_32); - } - } else if (argc == 1) { - if (mac_interface->mac64_get(mac_interface, MAC_EXTENDED_READ_ONLY, ext_addr) == 0) { - cmd_printf("EUI64: %s\n", mbed_trace_array(ext_addr, 8)); - } else { - tr_warn("Failed to read EUI64"); - return CMDLINE_RETCODE_FAIL; - } - if (mac_interface->mac64_get(mac_interface, MAC_EXTENDED_DYNAMIC, ext_addr) == 0) { - cmd_printf("MAC64: %s\n", mbed_trace_array(ext_addr, 8)); - } else { - tr_warn("Failed to read MAC64"); - return CMDLINE_RETCODE_FAIL; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - return CMDLINE_RETCODE_SUCCESS; -} - -static int key_config_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - bool boolean; - int lookup_index = 0, device_index = 0, usage_index = 0; - - if (cmd_parameter_val(argc, argv, "--key", &str)) { - if (strlen(str) == 2 * 16 + 15) { - if (string_to_bytes(str, key_descriptor.Key, 16) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--key_id_lookup_list_entries", &val)) { - if (val >= 0 && val < LOOKUP_DESCRIPTOR_TABLE_SIZE) { - key_descriptor.KeyIdLookupListEntries = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--key_device_list_entries", &val)) { - if (val >= 0 && val < DEVICE_DESCRIPTOR_TABLE_SIZE) { - key_descriptor.KeyDeviceListEntries = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--key_usage_list_entries", &val)) { - if (val >= 0 && val < USAGE_DESCRIPTOR_TABLE_SIZE) { - key_descriptor.KeyUsageListEntries = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--lookup_index", &val)) { - if (val >= 0 && val < LOOKUP_DESCRIPTOR_TABLE_SIZE) { - lookup_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--lookup_data", &str)) { - if (strlen(str) == 2 * 9 + 8) { - if (string_to_bytes(str, key_descriptor.KeyIdLookupList[lookup_index].LookupData, 9) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--lookup_data_size", &val)) { - if (val == 0 || val == 1) { - key_descriptor.KeyIdLookupList[lookup_index].LookupDataSize = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--device_list_index", &val)) { - if (val >= 0 && val < DEVICE_DESCRIPTOR_TABLE_SIZE) { - device_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--device_descriptor_handle", &val)) { - if (val >= 0 && val <= 255) { - key_descriptor.KeyDeviceList[device_index].DeviceDescriptorHandle = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_bool(argc, argv, "--unique_device", &boolean)) { - key_descriptor.KeyDeviceList[device_index].UniqueDevice = boolean; - } - if (cmd_parameter_bool(argc, argv, "--blacklisted", &boolean)) { - key_descriptor.KeyDeviceList[device_index].Blacklisted = boolean; - } - if (cmd_parameter_int(argc, argv, "--usage_index", &val)) { - if (val >= 0 && val <= USAGE_DESCRIPTOR_TABLE_SIZE) { - usage_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--frame_type", &val)) { - if (val >= 0 && val <= 3) { - key_descriptor.KeyUsageList[usage_index].FrameType = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (key_descriptor.KeyUsageList[usage_index].FrameType == 3) { - if (cmd_parameter_int(argc, argv, "--command_frame_identifier", &val)) { - if (val >= 1 && val <= 9) { - key_descriptor.KeyUsageList[usage_index].CommandFrameIdentifier = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - } - return CMDLINE_RETCODE_SUCCESS; -} - -static int key_add_command(int argc, char *argv[]) -{ - mlme_set_t set_req; - int32_t val; - int key_index = 0; - - if (cmd_parameter_int(argc, argv, "--index", &val)) { - if (val >= 0 && val <= 255) { - key_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - - set_req.attr = macKeyTable; - set_req.attr_index = key_index; - set_req.value_pointer = &key_descriptor; - set_req.value_size = sizeof(mlme_key_descriptor_entry_t); - mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_key_command(int argc, char *argv[]) -{ - char *cmd = argv[1]; - - if (strcmp(cmd, "config") == 0) { - return key_config_command(argc, argv); - } else if (strcmp(cmd, "add") == 0) { - return key_add_command(argc, argv); - } - return CMDLINE_RETCODE_INVALID_PARAMETERS; -} - -int mac_add_neighbour_command(int argc, char *argv[]) -{ - char *str; - int32_t val; - mlme_device_descriptor_t neighbour; - mlme_set_t set_req; - - neighbour.Exempt = false; - if (cmd_parameter_int(argc, argv, "--frame_ctr", &val)) { - neighbour.FrameCounter = val; - } - if (cmd_parameter_val(argc, argv, "--mac16", &str)) { - uint32_t short_addr = strtoul(str, NULL, 16); - if (short_addr <= 0xFFFF) { - neighbour.ShortAddress = short_addr; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--mac64", &str)) { - if (strlen(str) == 2 * 8 + 7) { - if (string_to_bytes(str, neighbour.ExtAddress, 8) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_val(argc, argv, "--pan_id", &str)) { - uint32_t pan_id = strtoul(str, NULL, 16); - if (pan_id <= 0xFFFF) { - neighbour.PANId = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - if (cmd_parameter_int(argc, argv, "--index", &val)) { - if (val >= 0 && val <= 255) { - set_req.attr_index = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - - set_req.attr = macDeviceTable; - set_req.value_pointer = &neighbour; - set_req.value_size = sizeof(mlme_device_descriptor_t); - mac_interface->mlme_req(mac_interface, MLME_SET, &set_req); - return CMDLINE_RETCODE_SUCCESS; -} - -#ifndef DISABLE_FILTERING -static int filter_start(int argc, char *argv[]) -{ - char *str; - - if (cmd_parameter_val(argc, argv, "--mode", &str)) { - if (strcmp(str, "allow") == 0) { - return mac_filter_start(mac_interface->parent_id, MAC_FILTER_ALLOWED) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; - } else if (strcmp(str, "block") == 0) { - return mac_filter_start(mac_interface->parent_id, MAC_FILTER_BLOCKED) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS;; - } else if (strcmp(str, "fixed") == 0) { - int32_t lqi, dbm; - if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi) && - cmd_parameter_int(argc, argv, "--dbm_m", &dbm)) { - return mac_filter_start(mac_interface->parent_id, MAC_FILTER_FIXED(lqi, dbm)) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; - } - } - } - return CMDLINE_RETCODE_INVALID_PARAMETERS; -} - -static int filter_add(int argc, char *argv[]) -{ - char *str; - uint32_t short_addr; - uint8_t long_addr[8]; - int32_t lqi_m, lqi_add, dbm_m, dbm_add; - - if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi_m) && - cmd_parameter_int(argc, argv, "--lqi_add", &lqi_add) && - cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m) && - cmd_parameter_int(argc, argv, "--dbm_add", &dbm_add)) { - } else if (cmd_parameter_val(argc, argv, "--mode", &str)) { - if (strcmp(str, "allow")) { - lqi_m = dbm_m = 256; - lqi_add = dbm_add = 0; - } else if (strcmp(str, "block")) { - lqi_m = lqi_add = dbm_m = dbm_add = 0; - } else if (strcmp(str, "fixed")) { - lqi_add = dbm_add = 0; - if (cmd_parameter_int(argc, argv, "--lqi_m", &lqi_m) && - cmd_parameter_int(argc, argv, "--dbm_m", &dbm_m)) { - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - - if (cmd_parameter_val(argc, argv, "--short", &str)) { - short_addr = strtoul(str, NULL, 16); - if (short_addr <= 0xFFFF) { - return mac_filter_add_short(mac_interface->parent_id, short_addr, lqi_m, lqi_add, dbm_m, dbm_add) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_val(argc, argv, "--long", &str)) { - if (string_to_bytes(str, long_addr, 8) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - return mac_filter_add_long(mac_interface->parent_id, long_addr, lqi_m, lqi_add, dbm_m, dbm_add) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } -} - -static int filter_remove(int argc, char *argv[]) -{ - char *str; - uint32_t short_addr; - uint8_t long_addr[8]; - - if (cmd_parameter_val(argc, argv, "--short", &str)) { - short_addr = strtoul(str, NULL, 16); - if (short_addr <= 0xFFFF) { - return mac_filter_delete_short(mac_interface->parent_id, short_addr) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_val(argc, argv, "--long", &str)) { - if (string_to_bytes(str, long_addr, 8) != 0) { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - return mac_filter_delete_long(mac_interface->parent_id, long_addr) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } -} - -static int filter_clear(int argc, char *argv[]) -{ - return mac_filter_clear(mac_interface->parent_id) < 0 ? CMDLINE_RETCODE_FAIL : CMDLINE_RETCODE_SUCCESS; -} - -static int filter_stop(int argc, char *argv[]) -{ - mac_filter_stop(mac_interface->parent_id); - return CMDLINE_RETCODE_SUCCESS; -} -#else -static int filter_start(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - return 0; -} - -static int filter_add(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - return 0; -} -static int filter_remove(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - return 0; -} -static int filter_clear(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - return 0; -} -static int filter_stop(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - return 0; -} -#endif -int mac_filter_command(int argc, char *argv[]) -{ - char *cmd = argv[1]; - - if (strcmp(cmd, "start") == 0) { - return filter_start(argc, argv); - } else if (strcmp(cmd, "add") == 0) { - return filter_add(argc, argv); - } else if (strcmp(cmd, "remove") == 0) { - return filter_remove(argc, argv); - } else if (strcmp(cmd, "clear") == 0) { - return filter_clear(argc, argv); - } else if (strcmp(cmd, "stop") == 0) { - return filter_stop(argc, argv); - } - return CMDLINE_RETCODE_INVALID_PARAMETERS; -} - -int mac_config_status_command(int argc, char *argv[]) -{ - int32_t val; - char *str; - - if (cmd_parameter_int(argc, argv, "--data_conf", &val)) { - if (val >= 0 && val <= 255) { - expected_statuses.data_conf = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_val(argc, argv, "--data_ind", &str)) { - size_t len = strlen(str); - ns_dyn_mem_free(expected_statuses.data_ind); - expected_statuses.data_ind = (uint8_t *)ns_dyn_mem_temporary_alloc(len); - expected_statuses.data_ind_len = len; - std::memcpy(expected_statuses.data_ind, str, len); - } else if (cmd_parameter_int(argc, argv, "--get", &val)) { - if (val >= 0 && val <= 255) { - expected_statuses.get_conf = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_int(argc, argv, "--scan", &val)) { - if (val >= 0 && val <= 255) { - expected_statuses.scan_conf = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_int(argc, argv, "--poll", &val)) { - if (val >= 0 && val <= 255) { - expected_statuses.poll_conf = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_int(argc, argv, "--purge", &val)) { - if (val >= 0 && val <= 255) { - expected_statuses.purge_conf = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_int(argc, argv, "--comm_status", &val)) { - if (val >= 0 && val <= 255) { - expected_statuses.comm_status_ind = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else if (cmd_parameter_index(argc, argv, "--list") != -1) { - for (unsigned int i = 0; i < 256; ++i) { - const char *status = mlme_status_string(i); - if (status) { - cmd_printf("%hhu\t%s\n", i, status); - } - } - } else if (cmd_parameter_index(argc, argv, "--reset") != -1) { - expected_statuses.data_conf = MLME_SUCCESS; - expected_statuses.get_conf = MLME_SUCCESS; - expected_statuses.scan_conf = MLME_SUCCESS; - expected_statuses.poll_conf = MLME_SUCCESS; - expected_statuses.purge_conf = MLME_SUCCESS; - expected_statuses.comm_status_ind = MLME_SUCCESS; - expected_statuses.data_ind_len = 0; - expected_statuses.data_ind = NULL; - } else if (argc == 1) { - cmd_printf("MCPS-DATA.confirm: %d (%s)\n", expected_statuses.data_conf, mlme_status_string(expected_statuses.data_conf)); - cmd_printf("MLME-GET.confirm: %d (%s)\n", expected_statuses.get_conf, mlme_status_string(expected_statuses.get_conf)); - cmd_printf("MLME-SCAN.confirm: %d (%s)\n", expected_statuses.scan_conf, mlme_status_string(expected_statuses.scan_conf)); - cmd_printf("MLME-POLL.confirm: %d (%s)\n", expected_statuses.poll_conf, mlme_status_string(expected_statuses.poll_conf)); - cmd_printf("MCPS.PURGE.confirm. %d (%s)\n", expected_statuses.purge_conf, mlme_status_string(expected_statuses.purge_conf)); - cmd_printf("MLME-COMM-STATUS.indication: %d (%s)\n", expected_statuses.comm_status_ind, mlme_status_string(expected_statuses.comm_status_ind)); - cmd_printf("MCPS-DATA.indication: %s <%.*s>\n", mbed_trace_array(expected_statuses.data_ind, expected_statuses.data_ind_len), expected_statuses.data_ind_len, expected_statuses.data_ind); - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_find_beacon_command(int argc, char *argv[]) -{ - char *str; - - if (cmd_parameter_val(argc, argv, "--data", &str)) { - for (int i = 0; i < MLME_MAC_RES_SIZE_MAX; ++i) { - if (received_beacons.beacons[i] == NULL) { - continue; - } - if (strncmp(received_beacons.beacons[i], str, received_beacons.beacon_lengths[i]) == 0) { - return CMDLINE_RETCODE_SUCCESS; - } - } - return CMDLINE_RETCODE_FAIL; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } -} - -int mac_wait_command(int argc, char *argv[]) -{ - int32_t val; - static uint32_t timeout_ms = 1000; - int remaining_ms = timeout_ms; - if (cmd_parameter_int(argc, argv, "--timeout", &val)) { - if (val >= 0) { - timeout_ms = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } - while (data_count < 1) { - Thread::wait(10); - remaining_ms -= 10; - if (remaining_ms <= 0) { - return CMDLINE_RETCODE_FAIL; - } - } - data_count = 0; - return CMDLINE_RETCODE_SUCCESS; -} - -int mac_analyze_ed_command(int argc, char *argv[]) -{ - int32_t val; - int channel; - - if (cmd_parameter_int(argc, argv, "--channel", &val)) { - if (val >= 0 && val <= 26) { - channel = val; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - - if (cmd_parameter_int(argc, argv, "--above", &val)) { - for (int i = 0; i < last_ed_results.count; ++i) { - if (last_ed_results.channel[i] == channel) { - return last_ed_results.ED_values[i] >= val ? CMDLINE_RETCODE_SUCCESS : CMDLINE_RETCODE_FAIL; - } - } - } else if (cmd_parameter_int(argc, argv, "--below", &val)) { - for (int i = 0; i < last_ed_results.count; ++i) { - if (last_ed_results.channel[i] == channel) { - return last_ed_results.ED_values[i] <= val ? CMDLINE_RETCODE_SUCCESS : CMDLINE_RETCODE_FAIL; - } - } - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } - return CMDLINE_RETCODE_FAIL; -} - -static void reset_security(mlme_security_t *sec) -{ - sec->SecurityLevel = 0; - sec->KeyIdMode = 0; - sec->KeyIndex = 0; - memset(sec->Keysource, 0, 8); -} - -int reset_command(int argc, char *argv[]) -{ - wait_for_confirm = true; - silent_mode = false; - data_count = 0; - - start_req.PANId = 0x1234; - start_req.LogicalChannel = 11; - start_req.ChannelPage = 0; - start_req.StartTime = 0; - start_req.BeaconOrder = 15; - start_req.SuperframeOrder = 15; - start_req.PANCoordinator = true; - start_req.BatteryLifeExtension = false; - start_req.CoordRealignment = false; - reset_security(&start_req.CoordRealignKey); - reset_security(&start_req.BeaconRealignKey); - - scan_req.ScanType = MAC_ACTIVE_SCAN; - scan_req.ScanChannels.channel_page = CHANNEL_PAGE_0; - scan_req.ScanChannels.channel_mask[0] = 0x07FFF800; - reset_security(&scan_req.Key); - - data_req.SrcAddrMode = 3; - data_req.DstAddrMode = 3; - data_req.DstPANId = 0x1234; - memset(data_req.DstAddr, 0, 8); - data_req.msduLength = 0; - data_req.msdu = NULL; - data_req.msduHandle = 0; - data_req.TxAckReq = true; - data_req.InDirectTx = false; - data_req.PendingBit = false; - reset_security(&data_req.Key); - - poll_req.CoordAddrMode = 3; - poll_req.CoordPANId = 0x1234; - memset(poll_req.CoordAddress, 0, 8); - reset_security(&poll_req.Key); - - purge_req.msduHandle = 0; - - set_req.attr = (mlme_attr_t)0x39; - set_req.attr_index = 0; - set_req.value_pointer = NULL; - set_req.value_size = 0; - - get_req.attr = (mlme_attr_t)0x39; - get_req.attr_index = 0; - - reset_req.SetDefaultPIB = true; - - return CMDLINE_RETCODE_SUCCESS; -} - -int silent_mode_command(int argc, char *argv[]) -{ - char *cmd; - if (argc < 2) { - return CMDLINE_RETCODE_FAIL; - } - cmd = argv[1]; - if (strcmp(cmd, "on") == 0) { - silent_mode = true; - return CMDLINE_RETCODE_SUCCESS; - } else if (strcmp(cmd, "off") == 0) { - silent_mode = false; - return CMDLINE_RETCODE_SUCCESS; - } else { - return CMDLINE_RETCODE_INVALID_PARAMETERS; - } -} diff --git a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h deleted file mode 100644 index a57e97fc313..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * 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 MAC_COMMANDS_H_ -#define MAC_COMMANDS_H_ - -#include -#include - -#include "ns_cmdline.h" -#include "nsdynmemLIB.h" -#include "mbed_trace.h" -#include "mac_api.h" -#include "mlme.h" -#include "mac_mcps.h" -#include "mac_common_defines.h" -#include "mac_filter_api.h" -#include "util.h" - -#define LOOKUP_DESCRIPTOR_TABLE_SIZE 2 -#define DEVICE_DESCRIPTOR_TABLE_SIZE 2 -#define USAGE_DESCRIPTOR_TABLE_SIZE 2 -#define KEY_DESCRIPTOR_TABLE_SIZE 2 - -void mac_commands_init(void); - -void mac_data_confirm_handler(const mac_api_t *api, const mcps_data_conf_t *data); -void mac_data_indication_handler(const mac_api_t *api, const mcps_data_ind_t *data); -void mac_purge_confirm_handler(const mac_api_t *api, mcps_purge_conf_t *data); -void mac_mlme_confirm_handler(const mac_api_t *api, mlme_primitive id, const void *data); -void mac_mlme_indication_handler(const mac_api_t *api, mlme_primitive id, const void *data); - -int mac_start_command(int argc, char *argv[]); -int mac_scan_command(int argc, char *argv[]); -int mac_data_command(int argc, char *argv[]); -int mac_poll_command(int argc, char *argv[]); -int mac_purge_command(int argc, char *argv[]); -int mac_set_command(int argc, char *argv[]); -int mac_get_command(int argc, char *argv[]); -int mac_reset_command(int argc, char *argv[]); -int mac_address_command(int argc, char *argv[]); -int mac_key_command(int argc, char *argv[]); -int mac_add_neighbour_command(int argc, char *argv[]); -int mac_filter_command(int argc, char *argv[]); -int mac_config_status_command(int argc, char *argv[]); -int mac_find_beacon_command(int argc, char *argv[]); -int mac_wait_command(int argc, char *argv[]); -int mac_analyze_ed_command(int argc, char *argv[]); -int reset_command(int argc, char *argv[]); -int silent_mode_command(int argc, char *argv[]); - -#endif diff --git a/TEST_APPS/device/nanostack_mac_tester/source/util.cpp b/TEST_APPS/device/nanostack_mac_tester/source/util.cpp deleted file mode 100644 index 7a914a7edbb..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/source/util.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * 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 "util.h" - -int string_to_bytes(const char *str, uint8_t *buf, int bytes) -{ - int len = strlen(str); - - if (len <= (3 * bytes - 1)) { - int i; - - for (i = 0; i < bytes; ++i) { - if (i * 3 < len) { - buf[i] = (uint8_t)strtoul(str + i * 3, NULL, 16); - } else { - buf[i] = 0; - } - } - return 0; - } - return -1; -} - -const char *mlme_status_string(uint8_t status) -{ - switch (status) { - case MLME_SUCCESS: - return "MLME_SUCCESS"; - case MLME_BUSY_CHAN: - return "MLME_BUSY_CHAN"; - case MLME_BUSY_RX: - return "MLME_BUSY_RX"; - case MLME_BUSY_TX: - return "MLME_BUSY_TX"; - case MLME_FORCE_TRX_OFF: - return "MLME_FORCE_TRX_OFF"; - case MLME_IDLE: - return "MLME_IDLE"; - case MLME_RX_ON: - return "MLME_RX_ON"; - case MLME_TRX_OFF: - return "MLME_TRX_OFF"; - case MLME_TX_ON: - return "MLME_TX_ON"; - case MLME_COUNTER_ERROR: - return "MLME_COUNTER_ERROR"; - case MLME_IMPROPER_KEY_TYPE: - return "MLME_IMPROPER_KEY_TYPE"; - case MLME_IMPROPER_SECURITY_LEVEL: - return "MLME_IMPROPER_SECURITY_LEVEL"; - case MLME_UNSUPPORTED_LEGACY: - return "MLME_UNSUPPORTED_LEGACY"; - case MLME_UNSUPPORTED_SECURITY: - return "MLME_UNSUPPORTED_SECURITY"; - case MLME_SECURITY_FAIL: - return "MLME_SECURITY_FAIL"; - case MLME_FRAME_TOO_LONG: - return "MLME_FRAME_TOO_LONG"; - case MLME_INVALID_HANDLE: - return "MLME_INVALID_HANDLE"; - case MLME_INVALID_PARAMETER: - return "MLME_INVALID_PARAMETER"; - case MLME_TX_NO_ACK: - return "MLME_TX_NO_ACK"; - case MLME_NO_BEACON: - return "MLME_NO_BEACON"; - case MLME_NO_DATA: - return "MLME_NO_DATA"; - case MLME_NO_SHORT_ADDRESS: - return "MLME_NO_SHORT_ADDRESS"; - case MLME_PAN_ID_CONFLICT: - return "MLME_PAN_ID_CONFLICT"; - case MLME_TRANSACTION_EXPIRED: - return "MLME_TRANSACTION_EXPIRED"; - case MLME_TRANSACTION_OVERFLOW: - return "MLME_TRANSACTION_OVERFLOW"; - case MLME_UNAVAILABLE_KEY: - return "MLME_UNAVAILABLE_KEY"; - case MLME_UNSUPPORTED_ATTRIBUTE: - return "MLME_UNSUPPORTED_ATTRIBUTE"; - case MLME_INVALID_ADDRESS: - return "MLME_INVALID_ADDRESS"; - case MLME_INVALID_INDEX: - return "MLME_INVALID_INDEX"; - case MLME_LIMIT_REACHED: - return "MLME_LIMIT_REACHED"; - case MLME_READ_ONLY: - return "MLME_READ_ONLY"; - case MLME_SCAN_IN_PROGRESS: - return "MLME_SCAN_IN_PROGRESS"; - case MLME_DATA_POLL_NOTIFICATION: - return "MLME_DATA_POLL_NOTIFICATION"; - default: - return NULL; - } -} - -int channel_from_mask(uint32_t channel_mask, int index) -{ - int expected_index = 0; - for (int i = 0; i < 27; ++i) { - if ((channel_mask >> i) & 1) { - if (expected_index == index) { - return i; - } - ++expected_index; - } - } - return -1; -} diff --git a/TEST_APPS/device/nanostack_mac_tester/source/util.h b/TEST_APPS/device/nanostack_mac_tester/source/util.h deleted file mode 100644 index a7927314272..00000000000 --- a/TEST_APPS/device/nanostack_mac_tester/source/util.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * 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 UTIL_H_ -#define UTIL_H_ - -#include -#include - -#include "mbed.h" -#include "mlme.h" - -int string_to_bytes(const char *str, uint8_t *buf, int bytes); -const char *mlme_status_string(uint8_t status); -int channel_from_mask(uint32_t channel_mask, int index); - -#endif diff --git a/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py b/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py deleted file mode 100644 index 746fb271383..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/ED_scan.py +++ /dev/null @@ -1,117 +0,0 @@ -""" -Copyright (c) 2017, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" - -import threading -import sys -from icetea_lib.bench import Bench - - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name="ED_scan", - title="ED scan test", - status="development", - type="smoke", - subtype="", - execution={ - "skip": { - "value": False, - "reason": "" - } - }, - author="Valtteri Erkkila", - purpose="Tests reading the ED values from channels 11-16", - feature=["MLME-SCAN (ED)"], - component=["MAC"], - requirements={ - "duts": { - '*': { - "count": 3, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { - "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1": {"nick": "First"}, - "2": {"nick": "Second"}, - "3": {"nick": "Third"} - }} - ) - - def setUp(self): - self.channel = 11 - self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") - self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") - self.command("Third", "addr --64-bit 01:02:03:00:00:00:00:03") - - def spam_channel(self, event): - while not event.wait(0.1): - self.lock_th.acquire() - self.command("First", - "data --dst_addr 01:02:03:00:00:00:00:03 --msdu {} --msdu_length {} --wait_for_confirm false".format( - self.payload, len(self.payload))) - self.command("Third", - "data --dst_addr 01:02:03:00:00:00:00:01 --msdu {} --msdu_length {} --wait_for_confirm false".format( - self.payload, len(self.payload))) - self.lock_th.release() - - def mask_from_channel_list(self, channels): - res = 0 - for ch in channels: - res = res | (1 << ch) - return hex(res) - - def case(self): - self.lock_th = threading.Lock() - self.payload = "01234567890123456789012345678901234567890123456789" - - self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - - # No reason to print their spamming - self.command("First", "silent-mode on") - self.command("Third", "silent-mode on") - - self.stop_event = threading.Event() - self.th = threading.Thread(target=self.spam_channel, args=(self.stop_event,)) - self.th.start() - self.stopped = True - channels = range(11, 27) - for i in range(0, 3): - self.lock_th.acquire() - self.command("First", "mlme-reset") - self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Third", "mlme-reset") - self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - self.lock_th.release() - self.command("Second", "scan --scan_type 0 --scan_duration 7 --channel_mask {}".format( - self.mask_from_channel_list(channels))) - self.command("Second", "analyze-ed --channel {} --above 100".format(self.channel)) - - def tearDown(self): - self.command("First", "silent-mode off") - self.command("Third", "silent-mode off") - self.stop_event.set() - self.th.join() - del self.th - self.reset_dut() - - -if __name__ == '__main__': - sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/__init__.py b/TEST_APPS/testcases/nanostack_mac_tester/__init__.py deleted file mode 100644 index 4265cc3e6c1..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#!/usr/bin/env python diff --git a/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py b/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py deleted file mode 100644 index 0ef66aadffd..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/address_read_and_write.py +++ /dev/null @@ -1,70 +0,0 @@ -""" -Copyright (c) 2018, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" - -import sys -from icetea_lib.bench import Bench - - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name="address_read_and_write", - title="MAC address and PAN id read/write test", - status="development", - type="smoke", - subtype="", - execution={ - "skip": { - "value": False, - "reason": "" - } - }, - author="Valtteri Erkkila", - purpose="Tests reading a MAC address from the driver, and writing to the modifiable MAC address", - feature=["MLME-SET"], - component=["MAC"], - requirements={ - "duts": { - '*': { - "count": 1, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { - "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1": {"nick": "First"} - }} - ) - - def setUp(self): - pass - - def case(self): - self.command("First", "addr") - self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") - self.command("First", "addr --16-bit 0xABCD") - # macPANId - self.command("First", "mlme-set --attr 0x50 --value_bytes CD:CD --value_size 2") - self.command("First", "addr") - self.verify_trace(1, "MAC64: 01:02:03:00:00:00:00:01") - - def tearDown(self): - self.reset_dut() - - -if __name__ == '__main__': - sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py b/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py deleted file mode 100644 index 0ac0088d3ca..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/create_and_join_PAN.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -Copyright (c) 2018, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" - -import sys -from icetea_lib.bench import Bench - - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name="create_and_join_PAN", - title="Create a PAN and have a device join it", - status="development", - type="smoke", - subtype="", - execution={ - "skip": { - "value": False, - "reason": "" - } - }, - author="Valtteri Erkkila", - purpose="", - feature=["MLME-START", "MLME-SCAN (active)"], - component=["MAC"], - requirements={ - "duts": { - '*': { - "count": 3, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { - "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1": {"nick": "First"}, - "2": {"nick": "Second"}, - "3": {"nick": "Third"} - }} - ) - - def mask_from_channel_list(self, channels): - res = 0 - for ch in channels: - res = res | (1 << ch) - return hex(res) - - def setUp(self): - self.channel = 11 - - def case(self): - # Beacon payload & length - self.command("First", "mlme-set --attr 0x45 --value_ascii mac-tester --value_size 10") - self.command("First", "mlme-set --attr 0x46 --value_uint8 10 --value_size 1") - - self.command("Second", "mlme-set --attr 0x45 --value_ascii second-mac-tester --value_size 17") - self.command("Second", "mlme-set --attr 0x46 --value_uint8 17 --value_size 1") - - self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Second", "start --pan_coordinator true --logical_channel {}".format(int(self.channel) + 1)) - self.delay(3) - if self.channel == 11: - channels = [11, 12] - elif self.channel == 26: - channels = [25, 26] - else: - channels = [self.channel, self.channel + 1] - self.command("Third", "scan --channel_mask {}".format(self.mask_from_channel_list(channels))) - self.delay(0.2) - self.command("Third", "find-beacon --data mac-tester") - self.command("Third", "find-beacon --data second-mac-tester") - - def tearDown(self): - self.reset_dut() - - -if __name__ == '__main__': - sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_data.py b/TEST_APPS/testcases/nanostack_mac_tester/send_data.py deleted file mode 100644 index 5d00222f064..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/send_data.py +++ /dev/null @@ -1,71 +0,0 @@ -""" -Copyright (c) 2018, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" - -import sys -from icetea_lib.bench import Bench - - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name="send_data", - title="Simple data transmission test", - status="development", - type="smoke", - subtype="", - execution={ - "skip": { - "value": False, - "reason": "" - } - }, - author="Valtteri Erkkila", - purpose="Tests that sending data works", - feature=["MCPS-DATA"], - component=["MAC"], - requirements={ - "duts": { - '*': { - "count": 2, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { - "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1": {"nick": "First"}, - "2": {"nick": "Second"} - }} - ) - - def setUp(self): - self.channel = 11 - self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") - self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") - - def case(self): - self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - - self.command("First", "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde") - self.command("Second", "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length 5 --msdu 12345") - - def tearDown(self): - self.reset_dut() - - -if __name__ == '__main__': - sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py b/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py deleted file mode 100644 index 30fc4530834..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/send_data_indirect.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -Copyright (c) 2018, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" - -import sys -from icetea_lib.bench import Bench - - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name="send_data_indirect", - title="Indirect data transmission test", - status="development", - type="smoke", - subtype="", - execution={ - "skip": { - "value": False, - "reason": "" - } - }, - author="Valtteri Erkkila", - purpose="Tests sending data indirectly, i.e polling the coordinator for data", - feature=["MCPS-DATA", "MLME-POLL"], - component=["MAC"], - requirements={ - "duts": { - '*': { - "count": 3, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { - "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1": {"nick": "First"}, - "2": {"nick": "Second"}, - "3": {"nick": "Third"} - }} - ) - - def setUp(self): - self.channel = 11 - self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") - self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") - self.command("Third", "addr --64-bit 01:02:03:00:00:00:00:03") - - def case(self): - self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - self.command("Third", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - - # macRxOnWhenIdle - self.command("Second", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1") - self.command("Third", "mlme-set --attr 0x52 --value_uint8 0 --value_size 1") - - self.command("First", - "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:02 --pan_id 0x1234 --index 0") - self.command("First", - "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:03 --pan_id 0x1234 --index 1") - self.command("Second", - "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") - self.command("Third", - "add-neigh --frame_ctr 0 --mac16 0xFFFF --mac64 01:02:03:00:00:00:00:01 --pan_id 0x1234 --index 0") - - self.command("Second", "config-status --data_ind abcde") - self.command("Third", "config-status --data_ind 12345") - - # Runs into timing issues if extensive printing is enabled - self.command("*", "silent-mode on") - self.command("First", - "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length 5 --msdu abcde --indirect_tx true --wait_for_confirm false") - self.command("Second", "poll --coord_address 01:02:03:00:00:00:00:01") - - self.command("First", "data") - self.command("First", "data") - self.command("Second", "poll") - self.command("Second", "poll") - self.command("Second", "config-status --poll 235") - self.command("Second", "poll") - - self.command("Second", "config-status --poll 235") - self.command("First", "data --dst_addr 01:02:03:00:00:00:00:03 --msdu 12345") - self.command("Second", "poll") - self.command("Third", "poll --coord_address 01:02:03:00:00:00:00:01") - self.command("*", "silent-mode off") - - def tearDown(self): - self.reset_dut() - - -if __name__ == '__main__': - sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py b/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py deleted file mode 100644 index df2dd371b17..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/send_large_payloads.py +++ /dev/null @@ -1,88 +0,0 @@ -""" -Copyright (c) 2018, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" - -import sys -from icetea_lib.bench import Bench - - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name="send_large_payloads", - title="Data transmission test with large packets", - status="development", - type="reliability", - subtype="", - execution={ - "skip": { - "value": False, - "reason": "" - } - }, - author="Valtteri Erkkila", - purpose="Repeatedly sends long packets, checking that the payload is correct in each one", - feature=["MCPS-DATA"], - component=["MAC"], - requirements={ - "duts": { - '*': { - "count": 2, - "type": "hardware", - "allowed_platforms": ["K64F", "K66F", "NUCLEO_F429ZI", "KW24D", "UBLOX_EVK_ODIN_W2"], - "application": { - "name": "TEST_APPS-device-nanostack_mac_tester" - } - }, - "1": {"nick": "First"}, - "2": {"nick": "Second"} - }} - ) - - def setUp(self): - self.channel = 11 - self.command("First", "addr --64-bit 01:02:03:00:00:00:00:01") - self.command("Second", "addr --64-bit 01:02:03:00:00:00:00:02") - - def case(self): - # 104 characters, headers are 2+1+2+8+8+2=23 bytes, resulting in a packet size of 127 (max) - large_payload = "0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZZZZZZZZZ0123456789012345678901234567891234" - self.command("First", "start --pan_coordinator true --logical_channel {}".format(self.channel)) - self.command("Second", "start --pan_coordinator false --logical_channel {}".format(self.channel)) - - self.command("First", "config-status --data_ind {}".format(large_payload)) - self.command("Second", "config-status --data_ind {}".format(large_payload)) - - self.command("First", - "data --dst_addr 01:02:03:00:00:00:00:02 --msdu_length {} --msdu {}".format(len(large_payload), - large_payload)) - self.command("Second", "wait --timeout 500") - - self.command("Second", - "data --dst_addr 01:02:03:00:00:00:00:01 --msdu_length {} --msdu {}".format(len(large_payload), - large_payload)) - self.command("First", "wait --timeout 500") - for i in range(0, 25): - self.command("First", "data") - self.command("Second", "wait") - self.command("Second", "data") - self.command("First", "wait") - - def tearDown(self): - self.reset_dut() - - -if __name__ == '__main__': - sys.exit(Testcase().run()) diff --git a/TEST_APPS/testcases/nanostack_mac_tester/template b/TEST_APPS/testcases/nanostack_mac_tester/template deleted file mode 100644 index ffc79b672b4..00000000000 --- a/TEST_APPS/testcases/nanostack_mac_tester/template +++ /dev/null @@ -1,65 +0,0 @@ -""" -Copyright (c) 2018, Arm Limited and affiliates. -SPDX-License-Identifier: Apache-2.0 - -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. -""" -import os,sys -# ensure that test/ directory is first choice for imports -test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) -if not test_dir in sys.path: - sys.path.insert(0, test_dir) -from GenericTestcase import Bench -from Error import TestStepFail - -class Testcase(Bench): - def __init__(self): - Bench.__init__(self, name = "template", # Name should be the same as the filename - title = "TITLE", - status = "development", - type = "TYPE", - subtype = "", - execution = { - "skip": { - "value": False, - "reason": "" - } - }, - author = "Valtteri Erkkila", - purpose = "", - feature = [""], - component = ["MAC"], - requirements = { - "duts": { - '*': { - "count":2, # Count must reflect the amount of specified devices below - "type": "hardware", - "application":{ "name":"generalTestApplication", "version": "1.0"}, - "rf_channel": 11 - }, - "1":{"nick": "First"}, - "2":{"nick": "Second"} - }} - ) - - def setUp(self): - pass - - def case(self): - pass - - def tearDown(self): - pass - -if __name__=='__main__': - sys.exit( Testcase().run() ) From b433687e6244d41599bcca7e726f4dd3f56a95cd Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Fri, 31 Aug 2018 10:22:55 +0300 Subject: [PATCH 37/40] Skip the example app --- TEST_APPS/device/exampleapp/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TEST_APPS/device/exampleapp/main.cpp b/TEST_APPS/device/exampleapp/main.cpp index 0eca93a0f08..8e1654e07f7 100644 --- a/TEST_APPS/device/exampleapp/main.cpp +++ b/TEST_APPS/device/exampleapp/main.cpp @@ -18,6 +18,9 @@ #include "mbed.h" #include "mbed-client-cli/ns_cmdline.h" +#ifndef ICETEA_EXAMPLE_ENABLED +#error [NOT_SUPPORTED] Skipping example application. +#endif /** * Macros for setting console flow control. */ From 2edc5f0bfc94d505fa1f00dde91498573acfbd38 Mon Sep 17 00:00:00 2001 From: Jarno Lamsa Date: Fri, 31 Aug 2018 10:30:20 +0300 Subject: [PATCH 38/40] Readme and help fixes --- TEST_APPS/readme.md | 6 +++--- tools/run_icetea.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TEST_APPS/readme.md b/TEST_APPS/readme.md index 9f37ef05c2e..178be60d618 100644 --- a/TEST_APPS/readme.md +++ b/TEST_APPS/readme.md @@ -42,7 +42,7 @@ Some devices may offer multiple network interfaces to operate with. For example, The tests can be run for either one of those using already existing test-config -files. To run the tests with Wi-Fi interface: -`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/HeapBlockDeviceAndWifiInterface.json.json` +`>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/HeapBlockDeviceAndWifiInterface.json` To run the tests with ethernet interface: `>mbed test -m UBLOX_EVK_ODIN_W2 -t --icetea --test-config tools/test-configs/HeapBlockDeviceAndEthernetInterface.json` @@ -50,7 +50,7 @@ To run the tests with ethernet interface: #### Providing Wi-Fi access point information If you are using Wi-Fi interface for running the tests, you need to provide also information about the used access point. -The information can be provided in the used test-config -file. +The information can be provided in the used test-config file. Example of access point information: ``` @@ -89,5 +89,5 @@ Icetea prints the results from the test run to the command line, and the final r +---------------+----------------+ ``` -The results from the tests can also be found from mbed-os/log folder. +The results from the tests can also be found in the mbed-os/log folder. You probably want to add the log folder to your .mbedignore file to prevent issues with build commands becoming too long over the time. diff --git a/tools/run_icetea.py b/tools/run_icetea.py index 689660d9041..35a7f7066af 100644 --- a/tools/run_icetea.py +++ b/tools/run_icetea.py @@ -249,7 +249,7 @@ def load_build_data(build_data_path): action='store_true', dest='application_list', default=False, - help='List application that need to be build') + help='List applications that need to be build') parser.add_argument('--ignore-checks', action='store_true', From a58aa58730de5659f50b373898aac9620e74a3a1 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Fri, 31 Aug 2018 11:59:11 +0300 Subject: [PATCH 39/40] Remove MAC tester related code --- tools/test_configs/MACTester.json | 50 ---------------------------- tools/test_configs/config_paths.json | 1 - 2 files changed, 51 deletions(-) delete mode 100644 tools/test_configs/MACTester.json diff --git a/tools/test_configs/MACTester.json b/tools/test_configs/MACTester.json deleted file mode 100644 index f412d0f1623..00000000000 --- a/tools/test_configs/MACTester.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "config": { - "radio-type":{ - "help": "options are ATMEL, MCR20, OTHER", - "value": "ATMEL" - }, - "header-file": { - "help" : "String for including your driver header file", - "value" : "\"EthernetInterface.h\"" - }, - "object-construction" : { - "value" : "new EthernetInterface()" - }, - "connect-statement" : { - "help" : "Must use 'net' variable name", - "value" : "net->connect()" - }, - "echo-server-addr" : { - "help" : "IP address of echo server", - "value" : "\"echo.mbedcloudtesting.com\"" - }, - "echo-server-port" : { - "help" : "Port of echo server", - "value" : "7" - }, - "tcp-echo-prefix" : { - "help" : "Some servers send a prefix before echoed message", - "value" : null - }, - "sim-blockdevice": { - "help": "Simulated block device, requires sufficient heap", - "macro_name": "MBED_TEST_SIM_BLOCKDEVICE", - "value": "HeapBlockDevice" - } - }, - "macros": ["MBED_TRACE_LINE_LENGTH=200", "OS_TASKCNT=4", "OS_IDLESTKSIZE=32"], - "target_overrides": { - "*": { - "nanostack.configuration": "lowpan_host", - "platform.stdio-convert-newlines": true, - "platform.stdio-baud-rate": 115200, - "mbed-mesh-api.heap-size": 6000, - "nanostack-hal.event_loop_thread_stack_size": 2000, - "mbed-trace.enable": true, - "nsapi.default-stack": "LWIP", - "target.device_has_add": ["802_15_4_PHY"], - "atmel-rf.provide-default": true - } - } -} diff --git a/tools/test_configs/config_paths.json b/tools/test_configs/config_paths.json index 27f2a8dd4c7..0921610a6a9 100644 --- a/tools/test_configs/config_paths.json +++ b/tools/test_configs/config_paths.json @@ -13,5 +13,4 @@ "6LOWPAN_ROUTER" : "6lowpanInterface_router.json", "THREAD_END_DEVICE" : "ThreadInterface_end_device.json", "THREAD_ROUTER" : "ThreadInterface_router.json" - "MAC_TESTER" : "MACTester.json" } From 6609faa5b20bf71779a83b5f60594fd91ea95e33 Mon Sep 17 00:00:00 2001 From: Olli-Pekka Puolitaival Date: Fri, 31 Aug 2018 19:58:52 +0300 Subject: [PATCH 40/40] Update icetea --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0a37fe1589c..3a14d41d20f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,4 +17,4 @@ future>=0.16.0 six>=1.11.0 git+https://github.com/armmbed/manifest-tool.git@v1.4.5 mbed-cloud-sdk==2.0.0 -icetea==1.* +icetea>=1.0.1,<2