From ee58635945944766376cb329f0d8cfb132fb1090 Mon Sep 17 00:00:00 2001 From: jinran-google Date: Sat, 18 Sep 2021 09:36:26 +0800 Subject: [PATCH] [radio] use stack reset in RCP (#6645) This commit makes RCP use a stack reset instead of doing a hard reset when a reset command is received. --- .github/workflows/posix.yml | 24 +++++ include/openthread/instance.h | 20 +++- src/core/api/instance_api.cpp | 9 ++ src/core/common/instance.cpp | 8 ++ src/core/common/instance.hpp | 8 ++ src/core/mac/link_raw.cpp | 20 +++- src/core/mac/link_raw.hpp | 6 ++ src/core/mac/sub_mac.cpp | 44 ++++++--- src/core/mac/sub_mac.hpp | 11 +++ src/core/radio/radio.cpp | 37 ++++++++ src/core/radio/radio.hpp | 8 ++ src/lib/spinel/spinel.h | 12 ++- src/ncp/ncp_base.cpp | 47 +++++++--- .../scripts/expect/posix-rcp-stack-reset.exp | 80 ++++++++++++++++ tests/scripts/expect/v1_2-rcp-stack-reset.exp | 30 ++++++ tests/scripts/misc/test_rcp_reset.py | 91 +++++++++++++++++++ tests/unit/test_platform.cpp | 7 ++ 17 files changed, 427 insertions(+), 35 deletions(-) create mode 100755 tests/scripts/expect/posix-rcp-stack-reset.exp create mode 100755 tests/scripts/expect/v1_2-rcp-stack-reset.exp create mode 100644 tests/scripts/misc/test_rcp_reset.py diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index eeed9ba60..a0a8a7243 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -240,6 +240,30 @@ jobs: run: | script/check-posix-pty check + rcp-stack-reset: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Bootstrap + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat + sudo python3 -m pip install git+https://github.com/openthread/pyspinel + - name: Build + run: | + script/cmake-build simulation -DOT_CSL_RECEIVER=ON -DOT_FULL_LOGS=ON -DOT_LOG_OUTPUT=PLATFORM_DEFINED + - name: Run + run: | + python3 tests/scripts/misc/test_rcp_reset.py build/simulation/examples/apps/ncp/ot-rcp + - name: Generate Coverage + run: | + ./script/test generate_coverage gcc + - uses: actions/upload-artifact@v2 + with: + name: cov-rcp-stack-reset + path: tmp/coverage.info + upload-coverage: needs: - expects-linux diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 9605ec7a3..0b47d267d 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (166) +#define OPENTHREAD_API_VERSION (167) /** * @addtogroup api-instance @@ -244,17 +244,31 @@ void otRemoveStateChangeCallback(otInstance *aInstance, otStateChangedCallback a * The reset process ensures that all the OpenThread state/info (stored in volatile memory) is erased. Note that the * `otPlatformReset` does not erase any persistent state/info saved in non-volatile memory. * - * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aInstance A pointer to an OpenThread instance. + * */ void otInstanceReset(otInstance *aInstance); /** * This method deletes all the settings stored on non-volatile memory, and then triggers platform reset. * - * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aInstance A pointer to an OpenThread instance. + * */ void otInstanceFactoryReset(otInstance *aInstance); +/** + * This method resets the internal states of the OpenThread radio stack. + * + * Callbacks and configurations are preserved. + * + * This API is only available under radio builds (`OPENTHREAD_RADIO = 1`). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otInstanceResetRadioStack(otInstance *aInstance); + /** * This function erases all the OpenThread persistent info (network settings) stored on non-volatile memory. * Erase is successful only if the device is in `disabled` state/role. diff --git a/src/core/api/instance_api.cpp b/src/core/api/instance_api.cpp index c70786a26..b5b0a79c5 100644 --- a/src/core/api/instance_api.cpp +++ b/src/core/api/instance_api.cpp @@ -129,6 +129,15 @@ otError otInstanceErasePersistentInfo(otInstance *aInstance) } #endif // OPENTHREAD_MTD || OPENTHREAD_FTD +#if OPENTHREAD_RADIO +void otInstanceResetRadioStack(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + instance.ResetRadioStack(); +} +#endif + const char *otGetVersionString(void) { /** diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index c0a7e1b1c..dc1142172 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -173,6 +173,14 @@ void Instance::Reset(void) otPlatReset(this); } +#if OPENTHREAD_RADIO +void Instance::ResetRadioStack(void) +{ + mRadio.Init(); + mLinkRaw.Init(); +} +#endif + void Instance::AfterInit(void) { mIsInitialized = true; diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 7b6456a62..15a2a9bd5 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -194,6 +194,14 @@ public: */ void Reset(void); +#if OPENTHREAD_RADIO + /** + * This method resets the internal states of the radio. + * + */ + void ResetRadioStack(void); +#endif + /** * This method returns the active log level. * diff --git a/src/core/mac/link_raw.cpp b/src/core/mac/link_raw.cpp index 550c44f57..cc01e005b 100644 --- a/src/core/mac/link_raw.cpp +++ b/src/core/mac/link_raw.cpp @@ -51,17 +51,27 @@ namespace Mac { LinkRaw::LinkRaw(Instance &aInstance) : InstanceLocator(aInstance) - , mReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL) - , mPanId(kPanIdBroadcast) - , mReceiveDoneCallback(nullptr) - , mTransmitDoneCallback(nullptr) - , mEnergyScanDoneCallback(nullptr) #if OPENTHREAD_RADIO , mSubMac(aInstance) #elif OPENTHREAD_CONFIG_LINK_RAW_ENABLE , mSubMac(aInstance.Get()) #endif { + Init(); +} + +void LinkRaw::Init(void) +{ + mEnergyScanDoneCallback = nullptr; + mTransmitDoneCallback = nullptr; + mReceiveDoneCallback = nullptr; + + mReceiveChannel = OPENTHREAD_CONFIG_DEFAULT_CHANNEL; + mPanId = kPanIdBroadcast; + mReceiveDoneCallback = nullptr; +#if OPENTHREAD_RADIO + mSubMac.Init(); +#endif } Error LinkRaw::SetReceiveDone(otLinkRawReceiveDone aCallback) diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index 2f1bbf02c..2018bb9d2 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -65,6 +65,12 @@ public: */ explicit LinkRaw(Instance &aInstance); + /** + * This method initializes the states of the raw link-layer. + * + */ + void Init(void); + /** * This method returns true if the raw link-layer is enabled. * diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index b05324961..5334f62e6 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -52,32 +52,48 @@ namespace Mac { SubMac::SubMac(Instance &aInstance) : InstanceLocator(aInstance) , mRadioCaps(Get().GetCaps()) - , mState(kStateDisabled) - , mCsmaBackoffs(0) - , mTransmitRetries(0) - , mShortAddress(kShortAddrInvalid) - , mRxOnWhenBackoff(true) - , mEnergyScanMaxRssi(kInvalidRssiValue) - , mEnergyScanEndTime(0) , mTransmitFrame(Get().GetTransmitBuffer()) , mCallbacks(aInstance) , mPcapCallback(nullptr) , mPcapCallbackContext(nullptr) - , mFrameCounter(0) - , mKeyId(0) , mTimer(aInstance, SubMac::HandleTimer) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - , mCslPeriod(0) - , mCslChannel(0) - , mIsCslChannelSpecified(false) - , mCslLastSync(0) , mCslParentAccuracy(kCslWorstCrystalPpm) , mCslParentUncert(kCslWorstUncertainty) - , mCslState(kCslIdle) , mCslTimer(aInstance, SubMac::HandleCslTimer) #endif { + Init(); +} + +void SubMac::Init(void) +{ + mState = kStateDisabled; + mCsmaBackoffs = 0; + mTransmitRetries = 0; + mShortAddress = kShortAddrInvalid; mExtAddress.Clear(); + mRxOnWhenBackoff = true; + mEnergyScanMaxRssi = kInvalidRssiValue; + mEnergyScanEndTime = Time{0}; + + mPrevKey.Clear(); + mCurrKey.Clear(); + mNextKey.Clear(); + + mFrameCounter = 0; + mKeyId = 0; + mTimer.Stop(); + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + mCslPeriod = 0; + mCslChannel = 0; + mIsCslChannelSpecified = false; + mCslSampleTime = TimeMicro{0}; + mCslLastSync = TimeMicro{0}; + mCslState = kCslIdle; + mCslTimer.Stop(); +#endif } otRadioCaps SubMac::GetCaps(void) const diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index e51a10248..9281e6fdb 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -81,6 +81,10 @@ namespace Mac { #error "OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is required for OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE." #endif +#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE +class LinkRaw; +#endif + /** * This class implements the IEEE 802.15.4 MAC (sub-MAC). * @@ -101,6 +105,7 @@ namespace Mac { class SubMac : public InstanceLocator, private NonCopyable { friend class Radio::Callbacks; + friend class LinkRaw; public: static constexpr int8_t kInvalidRssiValue = 127; ///< Invalid Received Signal Strength Indicator (RSSI) value. @@ -591,6 +596,12 @@ private: }; #endif + /** + * This method initializes the states of the sub-MAC layer. + * + */ + void Init(void); + bool RadioSupportsCsmaBackoff(void) const { return ((mRadioCaps & (OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES)) != 0); diff --git a/src/core/radio/radio.cpp b/src/core/radio/radio.cpp index c62d91712..964fe6e10 100644 --- a/src/core/radio/radio.cpp +++ b/src/core/radio/radio.cpp @@ -33,6 +33,43 @@ namespace ot { +#if OPENTHREAD_RADIO +void Radio::Init(void) +{ +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + Error error = OT_ERROR_NONE; + + OT_UNUSED_VARIABLE(error); + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + error = EnableCsl(0, Mac::kShortAddrInvalid, nullptr); + OT_ASSERT(error == OT_ERROR_NONE); +#endif + + EnableSrcMatch(false); + ClearSrcMatchShortEntries(); + ClearSrcMatchExtEntries(); + + if (IsEnabled()) + { + error = Sleep(); + OT_ASSERT(error == OT_ERROR_NONE); + + error = Disable(); + OT_ASSERT(error == OT_ERROR_NONE); + } + + SetPanId(Mac::kPanIdBroadcast); + SetExtendedAddress(Mac::ExtAddress{}); + SetShortAddress(Mac::kShortAddrInvalid); + SetMacKey(0, 0, Mac::KeyMaterial{}, Mac::KeyMaterial{}, Mac::KeyMaterial{}); + SetMacFrameCounter(0); + + SetPromiscuous(false); +#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +} +#endif // OPENTHREAD_RADIO + #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE void Radio::SetExtendedAddress(const Mac::ExtAddress &aExtAddress) diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 25c7170ea..63e6911cd 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -244,6 +244,14 @@ public: */ int8_t GetReceiveSensitivity(void); +#if OPENTHREAD_RADIO + /** + * This method initializes the states of the Thread radio. + * + */ + void Init(void); +#endif + /** * This method sets the PAN ID for address filtering. * diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 19809976f..973ee52bc 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -851,6 +851,12 @@ enum SPINEL_MD_FLAG_RESERVED = 0xFFC2, //!< Flags reserved for future use. }; +enum +{ + SPINEL_RESET_PLATFORM = 1, + SPINEL_RESET_STACK = 2, +}; + enum { /** @@ -870,14 +876,16 @@ enum /** * Reset NCP command (Host -> NCP) * - * Encoding: Empty + * Encoding: Empty or `C` * * Causes the NCP to perform a software reset. Due to the nature of * this command, the TID is ignored. The host should instead wait * for a `CMD_PROP_VALUE_IS` command from the NCP indicating * `PROP_LAST_STATUS` has been set to `STATUS_RESET_SOFTWARE`. * - * The command payload for this command SHOULD be empty. + * The optional command payload specifies the reset type, can be + * `SPINEL_RESET_PLATFORM` or `SPINEL_RESET_STACK`. Defaults to stack + * reset if unspecified. * * If an error occurs, the value of `PROP_LAST_STATUS` will be emitted * instead with the value set to the generated status code for the error. diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 69cfb6bed..fe35ffec7 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -1214,22 +1214,47 @@ otError NcpBase::CommandHandler_RESET(uint8_t aHeader) { OT_UNUSED_VARIABLE(aHeader); - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + uint8_t reset_type = SPINEL_RESET_STACK; - // Signal a platform reset. If implemented, this function - // shouldn't return. - otInstanceReset(mInstance); + if (mDecoder.GetRemainingLengthInStruct() > 0) + { + error = mDecoder.ReadUint8(reset_type); + OT_ASSERT(error == OT_ERROR_NONE); + } + +#if OPENTHREAD_RADIO + if (reset_type == SPINEL_RESET_STACK) + { + otInstanceResetRadioStack(mInstance); + + mIsRawStreamEnabled = false; + mCurTransmitTID = 0; + mCurScanChannel = kInvalidScanChannel; + mSrcMatchEnabled = false; + + ResetCounters(); + + error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_POWER_ON); + OT_ASSERT(error == OT_ERROR_NONE); + } + else +#endif + { + // Signal a platform reset. If implemented, this function + // shouldn't return. + otInstanceReset(mInstance); #if OPENTHREAD_MTD || OPENTHREAD_FTD - // We only get to this point if the - // platform doesn't support resetting. - // In such a case we fake it. - - IgnoreError(otThreadSetEnabled(mInstance, false)); - IgnoreError(otIp6SetEnabled(mInstance, false)); + // We only get to this point if the + // platform doesn't support resetting. + // In such a case we fake it. + IgnoreError(otThreadSetEnabled(mInstance, false)); + IgnoreError(otIp6SetEnabled(mInstance, false)); #endif - sNcpInstance = nullptr; + sNcpInstance = nullptr; + } return error; } diff --git a/tests/scripts/expect/posix-rcp-stack-reset.exp b/tests/scripts/expect/posix-rcp-stack-reset.exp new file mode 100755 index 000000000..2f5acea76 --- /dev/null +++ b/tests/scripts/expect/posix-rcp-stack-reset.exp @@ -0,0 +1,80 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2021, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +source "tests/scripts/expect/_common.exp" +source "tests/scripts/expect/_multinode.exp" + + +# The expect on macOS doesn't support `try` or `file tempfile`. +skip_on_macos + +file tempfile socat_out +set socat_pid [exec socat -d -d pty,raw,echo=0 pty,raw,echo=0 >/dev/null 2>$socat_out &] +while {true} { + if {[exec head -n2 $socat_out | wc -l] == 2} { + set radio_pty [exec head -n1 $socat_out | grep -o {/dev/.\+}] + set host_pty [exec head -n2 $socat_out | tail -n1 | grep -o {/dev/.\+}] + break + } + sleep 1 +} + +puts "Radio PTY: $radio_pty" +puts "Host PTY: $host_pty" + +set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &] +puts "RCP PID: $rcp_pid" + +try { + spawn_node 1 "rcp" "spinel+hdlc_uart://$host_pty" + spawn_node 2 + + setup_two_nodes "rn" false + + switch_node 2 + set addr [get_ipaddr mleid] + + switch_node 1 + send "ping $addr\n" + expect "16 bytes from $addr: icmp_seq=1" + expect_line "Done" + send "reset\n" + wait_for "state" "disabled" + expect_line "Done" + send "ifconfig up\n" + expect_line "Done" + send "thread start\n" + expect_line "Done" + wait_for "state" "leader" + wait_for "ping $addr" "16 bytes from $addr: icmp_seq=\\d+" + expect_line "Done" +} finally { + exec kill $rcp_pid + exec kill $socat_pid +} diff --git a/tests/scripts/expect/v1_2-rcp-stack-reset.exp b/tests/scripts/expect/v1_2-rcp-stack-reset.exp new file mode 100755 index 000000000..bdcb9212c --- /dev/null +++ b/tests/scripts/expect/v1_2-rcp-stack-reset.exp @@ -0,0 +1,30 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2021, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +source "tests/scripts/expect/posix-rcp-stack-reset.exp" diff --git a/tests/scripts/misc/test_rcp_reset.py b/tests/scripts/misc/test_rcp_reset.py new file mode 100644 index 000000000..3b9a37b44 --- /dev/null +++ b/tests/scripts/misc/test_rcp_reset.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import argparse + +from spinel.const import SPINEL +from spinel.codec import WpanApi +from spinel.stream import StreamOpen + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('rcp', type=str, help='path to rcp executable') + + args = parser.parse_args() + + stream = StreamOpen('p', f'{args.rcp} 1', False, 115200, False) + wpan_api = WpanApi(stream, 1) + + assert wpan_api.prop_set_value(SPINEL.PROP_PHY_ENABLED, 1) == 1 + + ext_addr = b'\x00\x11\x22\x33\x44\x55\x66\x77' + + assert wpan_api.prop_set_value(SPINEL.PROP_PHY_CHAN, 17) == 17 + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_15_4_SADDR, 0x1234, 'H') == 0x1234 + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_15_4_LADDR, ext_addr, '8s') == ext_addr + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SRC_MATCH_ENABLED, 1) == 1 + + while wpan_api.prop_insert_value(SPINEL.PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, ext_addr, '8s') == ext_addr: + ext_addr = (int.from_bytes(ext_addr, 'little') + 1).to_bytes(8, 'little') + + short_addr = 0x2345 + while wpan_api.prop_insert_value(SPINEL.PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, short_addr, 'H') == short_addr: + short_addr += 1 + + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_FILTER_MODE, SPINEL.MAC_FILTER_MODE_PROMISCUOUS) in { + SPINEL.MAC_FILTER_MODE_PROMISCUOUS, + SPINEL.MAC_FILTER_MODE_MONITOR, + } + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_RAW_STREAM_ENABLED, 1) == 1 + + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SCAN_MASK, 11) == [11] + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SCAN_STATE, SPINEL.SCAN_STATE_ENERGY) == SPINEL.SCAN_STATE_ENERGY + + wpan_api.cmd_send(SPINEL.CMD_RESET) + + assert wpan_api.prop_get_value(SPINEL.PROP_PHY_ENABLED) == 0 + assert wpan_api.prop_set_value(SPINEL.PROP_PHY_ENABLED, 1) == 1 + + assert wpan_api.prop_get_value(SPINEL.PROP_PHY_CHAN) == 11 + assert wpan_api.prop_get_value(SPINEL.PROP_MAC_15_4_SADDR) == 0xfffe + assert wpan_api.prop_get_value(SPINEL.PROP_MAC_15_4_LADDR) == b'\x00' * 8 + + assert wpan_api.prop_get_value(SPINEL.PROP_MAC_SRC_MATCH_ENABLED) == 0 + assert wpan_api.prop_set_value(SPINEL.PROP_MAC_SRC_MATCH_ENABLED, 1) == 1 + assert wpan_api.prop_insert_value(SPINEL.PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, ext_addr, '8s') == ext_addr + assert wpan_api.prop_insert_value(SPINEL.PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, short_addr, 'H') == short_addr + + assert wpan_api.prop_get_value(SPINEL.PROP_MAC_FILTER_MODE) == SPINEL.MAC_FILTER_MDOE_NORMAL + assert wpan_api.prop_get_value(SPINEL.PROP_MAC_RAW_STREAM_ENABLED) == 0 + assert wpan_api.prop_get_value(SPINEL.PROP_MAC_SCAN_STATE) == SPINEL.SCAN_STATE_IDLE + + +if __name__ == '__main__': + main() diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 465ae588d..3189f26a0 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -550,4 +550,11 @@ bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef) #endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE +otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aThreshold); + + return OT_ERROR_NONE; +} } // extern "C"