From ad0cc2f3520ff0c0f5f7e3258400be8afa130498 Mon Sep 17 00:00:00 2001 From: Juhani Puurula Date: Mon, 13 Aug 2018 11:24:39 +0300 Subject: [PATCH] Added nanostack mac tester and testcases --- .../device/nanostack_mac_tester/README.md | 88 + .../device/nanostack_mac_tester/main.cpp | 156 ++ .../device/nanostack_mac_tester/mbed_app.json | 16 + .../source/mac_commands.cpp | 1644 +++++++++++++++++ .../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 + tools/test_configs/MACTester.json | 16 + tools/test_configs/config_paths.json | 3 +- 17 files changed, 2690 insertions(+), 1 deletion(-) 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 create mode 100644 tools/test_configs/MACTester.json 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 0000000000..0929c7d258 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/README.md @@ -0,0 +1,88 @@ +# 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 + +### Add your RF driver + +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 +``` + +### Checking platform support + +Check that your choice of platform is supported by 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`. + +## Run tests + +You can use this application in interactive or automated mode. + +### Interactive mode + +To set the application to interactive mode: + +1. Build the application. +``` +mbed test --compile --icetea -t TOOLCHAIN -m TARGET_PLATFORM --test-config MAC_TESTER -n address_read_and_write,send_data,send_data_indirect,send_large_payloads,create_and_join_PAN,ED_scan +``` +2. Connect your board and copy the compiled application binary from the `BUILD/tests/TARGET_PLATFORM/TOOLCHAIN/TEST_APPS/device/nanostack_mac_tester/` folder to the board. +3. Wait for the device to flash the binary. +4. Open a serial connection with a program such as PuTTY, screen or minicom. The default baudrate is 115200. +5. 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 + +``` +mbed test --clean --compile --run --icetea -t TOOLCHAIN -m TARGET_PLATFORM --test-config MAC_TESTER -n address_read_and_write,send_data,send_data_indirect,send_large_payloads,create_and_join_PAN,ED_scan +``` + +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 in TEST_APPS/testcases/nanostack_mac_tester/. + +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 `TEST_APPS/testcases/nanostack_mac_tester/`. +* Create and join PAN +* Direct data transmission(Transmission between two devices) +* Indirect data transmission(Transmission between two devices with one device acting as intermediary) +* ED scan(Energy Density scanning) +* Address read and write +* Large data transmission + +### 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 0000000000..4e95b4010b --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/main.cpp @@ -0,0 +1,156 @@ +/* + * 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 new file mode 100644 index 0000000000..d86bfd2dc7 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/mbed_app.json @@ -0,0 +1,16 @@ +{ + "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 new file mode 100644 index 0000000000..068ccafe20 --- /dev/null +++ b/TEST_APPS/device/nanostack_mac_tester/source/mac_commands.cpp @@ -0,0 +1,1644 @@ +/* + * 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*/ + false, /*SeqNumSuppressed*/ + false, /*PanIdSuppressed*/ + { + 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 0000000000..a57e97fc31 --- /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 0000000000..286c6d68ca --- /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 0000000000..a792731427 --- /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 0000000000..3fedf1e6c9 --- /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 0000000000..4265cc3e6c --- /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 0000000000..7b1d34fd78 --- /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 0000000000..13819ad0e5 --- /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 0000000000..e647169e4b --- /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 0000000000..be73162d64 --- /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 0000000000..478a2ea423 --- /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 0000000000..4335851efd --- /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() ) diff --git a/tools/test_configs/MACTester.json b/tools/test_configs/MACTester.json new file mode 100644 index 0000000000..b332b4f909 --- /dev/null +++ b/tools/test_configs/MACTester.json @@ -0,0 +1,16 @@ +{ + "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 74d1819332..c1328baf2d 100644 --- a/tools/test_configs/config_paths.json +++ b/tools/test_configs/config_paths.json @@ -13,5 +13,6 @@ "6LOWPAN_ROUTER" : "6lowpanInterface_router.json", "THREAD_END_DEVICE" : "ThreadInterface_end_device.json", "THREAD_ROUTER" : "ThreadInterface_router.json", - "NO_NETWORK": "no_network.json" + "NO_NETWORK": "no_network.json", + "MAC_TESTER": "MACTester.json" }