[spinel] RCP restoration on failures (#5694)

This commit implements the RCP restoration on failure feature. It
keeps the host process running while resetting and initializing the
RCP.
This commit is contained in:
Moandor
2020-11-26 08:57:13 -08:00
committed by GitHub
parent f4554e8ff7
commit 9d9646afaa
25 changed files with 1022 additions and 132 deletions
+1 -1
View File
@@ -220,7 +220,7 @@ jobs:
- uses: actions/checkout@v2
- name: Bootstrap
run: |
sudo apt-get --no-install-recommends install -y expect ninja-build lcov
sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
- name: Run RCP Mode
run: |
OT_OPTIONS=-DOT_READLINE=OFF OT_NODE_TYPE=rcp ./script/test build expect
+1 -1
View File
@@ -279,7 +279,7 @@ jobs:
- uses: actions/checkout@v2
- name: Bootstrap
run: |
OT_OPTIONS=-DOT_READLINE=OFF sudo apt-get --no-install-recommends install -y expect ninja-build lcov
OT_OPTIONS=-DOT_READLINE=OFF sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat
- name: Run
run: |
OT_OPTIONS=-DOT_TIME_SYNC=ON VIRTUAL_TIME=0 ./script/test build expect
+7
View File
@@ -297,6 +297,13 @@ if(OT_OTNS)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_OTNS_ENABLE=1")
endif()
set(OT_RCP_RESTORATION_MAX_COUNT "0" CACHE STRING "set max RCP restoration count")
if(OT_RCP_RESTORATION_MAX_COUNT MATCHES "^[0-9]+$")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT=${OT_RCP_RESTORATION_MAX_COUNT}")
else()
message(FATAL_ERROR "Invalid max RCP restoration count: ${OT_RCP_RESTORATION_MAX_COUNT}")
endif()
# Checks
if(OT_PLATFORM_UDP AND OT_UDP_FORWARD)
message(FATAL_ERROR "OT_PLATFORM_UDP and OT_UDP_FORWARD are exclusive")
+50 -47
View File
@@ -28,56 +28,57 @@
# OpenThread Features (Makefile default configuration).
BACKBONE_ROUTER ?= 0
BIG_ENDIAN ?= 0
BORDER_AGENT ?= 0
BORDER_ROUTER ?= 0
COAP ?= 0
COAP_OBSERVE ?= 0
COAPS ?= 0
COMMISSIONER ?= 0
COVERAGE ?= 0
CHANNEL_MANAGER ?= 0
CHANNEL_MONITOR ?= 0
CHILD_SUPERVISION ?= 0
CLI_TRANSPORT ?= UART
DATASET_UPDATER ?= 0
DEBUG ?= 0
DHCP6_CLIENT ?= 0
DHCP6_SERVER ?= 0
DIAGNOSTIC ?= 0
DISABLE_DOC ?= 0
DISABLE_TOOLS ?= 0
DNS_CLIENT ?= 0
DUA ?= 0
DYNAMIC_LOG_LEVEL ?= 0
ECDSA ?= 0
EXTERNAL_HEAP ?= 0
IP6_FRAGM ?= 0
JAM_DETECTION ?= 0
JOINER ?= 0
LEGACY ?= 0
BACKBONE_ROUTER ?= 0
BIG_ENDIAN ?= 0
BORDER_AGENT ?= 0
BORDER_ROUTER ?= 0
COAP ?= 0
COAP_OBSERVE ?= 0
COAPS ?= 0
COMMISSIONER ?= 0
COVERAGE ?= 0
CHANNEL_MANAGER ?= 0
CHANNEL_MONITOR ?= 0
CHILD_SUPERVISION ?= 0
CLI_TRANSPORT ?= UART
DATASET_UPDATER ?= 0
DEBUG ?= 0
DHCP6_CLIENT ?= 0
DHCP6_SERVER ?= 0
DIAGNOSTIC ?= 0
DISABLE_DOC ?= 0
DISABLE_TOOLS ?= 0
DNS_CLIENT ?= 0
DUA ?= 0
DYNAMIC_LOG_LEVEL ?= 0
ECDSA ?= 0
EXTERNAL_HEAP ?= 0
IP6_FRAGM ?= 0
JAM_DETECTION ?= 0
JOINER ?= 0
LEGACY ?= 0
ifeq ($(REFERENCE_DEVICE),1)
LOG_OUTPUT ?= APP
LOG_OUTPUT ?= APP
endif
LINK_RAW ?= 0
MAC_FILTER ?= 0
MESSAGE_USE_HEAP ?= 0
MLE_LONG_ROUTES ?= 0
MLR ?= 0
MTD_NETDIAG ?= 0
MULTIPLE_INSTANCE ?= 0
OTNS ?= 0
PLATFORM_UDP ?= 0
REFERENCE_DEVICE ?= 0
SERVICE ?= 0
SETTINGS_RAM ?= 0
LINK_RAW ?= 0
MAC_FILTER ?= 0
MESSAGE_USE_HEAP ?= 0
MLE_LONG_ROUTES ?= 0
MLR ?= 0
MTD_NETDIAG ?= 0
MULTIPLE_INSTANCE ?= 0
OTNS ?= 0
PLATFORM_UDP ?= 0
REFERENCE_DEVICE ?= 0
SERVICE ?= 0
SETTINGS_RAM ?= 0
# SLAAC is enabled by default
SLAAC ?= 1
SNTP_CLIENT ?= 0
THREAD_VERSION ?= 1.1
TIME_SYNC ?= 0
UDP_FORWARD ?= 0
SLAAC ?= 1
SNTP_CLIENT ?= 0
THREAD_VERSION ?= 1.1
TIME_SYNC ?= 0
UDP_FORWARD ?= 0
RCP_RESTORATION_MAX_COUNT ?= 0
ifeq ($(BACKBONE_ROUTER),1)
@@ -310,6 +311,8 @@ ifneq ($(SPINEL_ENCRYPTER_LIBS),)
configure_OPTIONS += --with-ncp-spinel-encrypter-libs=$(SPINEL_ENCRYPTER_LIBS)
endif
COMMONCFLAGS += -DOPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT=${RCP_RESTORATION_MAX_COUNT}
ifeq ($(FULL_LOGS),1)
# HINT: Add more here, or comment out ones you do not need/want
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG
+2 -2
View File
@@ -37,11 +37,11 @@ reset_source()
build()
{
reset_source
make -f src/posix/Makefile-posix
make -f src/posix/Makefile-posix RCP_RESTORATION_MAX_COUNT=2
if [[ $OSTYPE != "darwin"* ]]; then
reset_source
make -f src/posix/Makefile-posix RCP_BUS=spi
make -f src/posix/Makefile-posix RCP_RESTORATION_MAX_COUNT=2 RCP_BUS=spi
fi
}
+1
View File
@@ -93,6 +93,7 @@ readonly OT_POSIX_SIM_COMMON_OPTIONS=(
"-DOT_COVERAGE=ON"
"-DOT_LOG_LEVEL_DYNAMIC=ON"
"-DOT_COMPILE_WARNING_AS_ERROR=ON"
"-DOT_RCP_RESTORATION_MAX_COUNT=2"
)
die()
+2
View File
@@ -449,6 +449,8 @@ do_upload_codecov()
envsetup()
{
export THREAD_VERSION
if [[ ${OT_NODE_TYPE} == rcp* ]]; then
export RADIO_DEVICE="${OT_BUILDDIR}/cmake/openthread-simulation-${THREAD_VERSION}/examples/apps/ncp/ot-rcp"
export OT_CLI_PATH="${OT_BUILDDIR}/cmake/openthread-posix-${THREAD_VERSION}/src/posix/ot-cli"
+7
View File
@@ -209,6 +209,13 @@ Decoder::Decoder(FrameWritePointer &aFrameWritePointer, FrameHandler aFrameHandl
{
}
void Decoder::Reset(void)
{
mState = kStateNoSync;
mFcs = 0;
mDecodedLength = 0;
}
void Decoder::Decode(const uint8_t *aData, uint16_t aLength)
{
while (aLength--)
+6
View File
@@ -566,6 +566,12 @@ public:
*/
void Decode(const uint8_t *aData, uint16_t aLength);
/**
* This method resets internal states of the decoder.
*
*/
void Reset(void);
private:
enum State
{
+11
View File
@@ -44,4 +44,15 @@
#define OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE 0
#endif
/**
* @def OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT
*
* Defines the max count of RCP failures allowed to be recovered.
* 0 means to disable RCP failure recovering.
*
*/
#ifndef OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT
#define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 0
#endif
#endif // OPENTHREAD_SPINEL_CONFIG_H_
+56
View File
@@ -36,6 +36,7 @@
#include <openthread/platform/radio.h>
#include "openthread-spinel-config.h"
#include "spinel.h"
#include "spinel_interface.hpp"
#include "ncp/ncp_config.h"
@@ -819,6 +820,21 @@ private:
otError RequestV(bool aWait, uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, va_list aArgs);
otError Request(bool aWait, uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, ...);
otError RequestWithPropertyFormat(const char * aPropertyFormat,
uint32_t aCommand,
spinel_prop_key_t aKey,
const char * aFormat,
...);
otError RequestWithPropertyFormatV(const char * aPropertyFormat,
uint32_t aCommand,
spinel_prop_key_t aKey,
const char * aFormat,
va_list aArgs);
otError RequestWithExpectedCommandV(uint32_t aExpectedCommand,
uint32_t aCommand,
spinel_prop_key_t aKey,
const char * aFormat,
va_list aArgs);
otError WaitResponse(void);
otError SendReset(void);
otError SendCommand(uint32_t command,
@@ -859,6 +875,14 @@ private:
void CalcRcpTimeOffset(void);
void HandleRcpUnexpectedReset(spinel_status_t aStatus);
void HandleRcpTimeout(void);
void RecoverFromRcpFailure(void);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
void RestoreProperties(void);
#endif
otInstance *mInstance;
SpinelInterface::RxFrameBuffer mRxFrameBuffer;
@@ -899,6 +923,38 @@ private:
bool mSupportsLogStream : 1; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format.
bool mIsTimeSynced : 1; ///< Host has calculated the time difference between host and RCP.
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
bool mResetRadioOnStartup : 1; ///< Whether should send reset command when init.
int16_t mRcpFailureCount; ///< Count of consecutive RCP failures.
// Properties set by core.
uint8_t mKeyIdMode;
uint8_t mKeyId;
otMacKey mPrevKey;
otMacKey mCurrKey;
otMacKey mNextKey;
uint16_t mSrcMatchShortEntries[OPENTHREAD_CONFIG_MLE_MAX_CHILDREN];
int16_t mSrcMatchShortEntryCount;
otExtAddress mSrcMatchExtEntries[OPENTHREAD_CONFIG_MLE_MAX_CHILDREN];
int16_t mSrcMatchExtEntryCount;
uint8_t mScanChannel;
uint16_t mScanDuration;
int8_t mCcaEnergyDetectThreshold;
int8_t mTransmitPower;
int8_t mFemLnaGain;
bool mCoexEnabled : 1;
bool mMacKeySet : 1; ///< Whether MAC key has been set.
bool mCcaEnergyDetectThresholdSet : 1; ///< Whether CCA energy detect threshold has been set.
bool mTransmitPowerSet : 1; ///< Whether transmit power has been set.
bool mCoexEnabledSet : 1; ///< Whether coex enabled has been set.
bool mFemLnaGainSet : 1; ///< Whether FEM LNA gain has benn set.
bool mRcpFailed : 1; ///< RCP failure happened, should recover and retry operation.
bool mEnergyScanning : 1; ///< If fails while scanning, restarts scanning.
#endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
#if OPENTHREAD_CONFIG_DIAG_ENABLE
bool mDiagMode;
char * mDiagOutput;
+462 -41
View File
@@ -43,10 +43,12 @@
#include "common/code_utils.hpp"
#include "common/encoding.hpp"
#include "common/instance.hpp"
#include "common/logging.hpp"
#include "common/new.hpp"
#include "common/settings.hpp"
#include "lib/platform/exit_code.h"
#include "lib/spinel/radio_spinel.hpp"
#include "lib/spinel/spinel_decoder.hpp"
#include "meshcop/dataset.hpp"
#include "meshcop/meshcop_tlvs.hpp"
@@ -192,6 +194,18 @@ RadioSpinel<InterfaceType, ProcessContextType>::RadioSpinel(void)
, mIsReady(false)
, mSupportsLogStream(false)
, mIsTimeSynced(false)
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
, mRcpFailureCount(0)
, mSrcMatchShortEntryCount(0)
, mSrcMatchExtEntryCount(0)
, mMacKeySet(false)
, mCcaEnergyDetectThresholdSet(false)
, mTransmitPowerSet(false)
, mCoexEnabledSet(false)
, mFemLnaGainSet(false)
, mRcpFailed(false)
, mEnergyScanning(false)
#endif
#if OPENTHREAD_CONFIG_DIAG_ENABLE
, mDiagMode(false)
, mDiagOutput(nullptr)
@@ -209,6 +223,10 @@ void RadioSpinel<InterfaceType, ProcessContextType>::Init(bool aResetRadio, bool
{
otError error = OT_ERROR_NONE;
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mResetRadioOnStartup = aResetRadio;
#endif
if (aResetRadio)
{
SuccessOrExit(error = SendReset());
@@ -764,9 +782,8 @@ void RadioSpinel<InterfaceType, ProcessContextType>::HandleValueIs(spinel_prop_k
{
if (IsEnabled())
{
// If RCP crashes/resets while radio was enabled, posix app exits.
otLogCritPlat("Unexpected RCP reset: %s", spinel_status_to_cstr(status));
DieNow(OT_EXIT_RADIO_SPINEL_RESET);
HandleRcpUnexpectedReset(status);
ExitNow();
}
otLogInfoPlat("RCP reset: %s", spinel_status_to_cstr(status));
@@ -785,6 +802,11 @@ void RadioSpinel<InterfaceType, ProcessContextType>::HandleValueIs(spinel_prop_k
unpacked = spinel_datatype_unpack(aBuffer, aLength, "Cc", &scanChannel, &maxRssi);
VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mEnergyScanning = false;
#endif
otPlatRadioEnergyScanDone(mInstance, maxRssi);
}
else if (aKey == SPINEL_PROP_STREAM_DEBUG)
@@ -992,7 +1014,8 @@ void RadioSpinel<InterfaceType, ProcessContextType>::ProcessRadioStateMachine(vo
else if (mState == kStateTransmitting && otPlatTimeGet() >= mTxRadioEndUs)
{
// Frame has been successfully passed to radio, but no `TransmitDone` event received within TX_WAIT_US.
DieNowWithMessage("radio tx timeout", OT_EXIT_FAILURE);
otLogWarnPlat("radio tx timeout");
HandleRcpTimeout();
}
}
@@ -1002,16 +1025,20 @@ void RadioSpinel<InterfaceType, ProcessContextType>::Process(const ProcessContex
if (mRxFrameBuffer.HasSavedFrame())
{
ProcessFrameQueue();
RecoverFromRcpFailure();
}
GetSpinelInterface().Process(aContext);
RecoverFromRcpFailure();
if (mRxFrameBuffer.HasSavedFrame())
{
ProcessFrameQueue();
RecoverFromRcpFailure();
}
ProcessRadioStateMachine();
RecoverFromRcpFailure();
CalcRcpTimeOffset();
}
@@ -1056,6 +1083,15 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::SetMacKey(uint8_t
aKeyIdMode, aKeyId, aPrevKey.m8, sizeof(otMacKey), aCurrKey.m8, sizeof(otMacKey),
aNextKey.m8, sizeof(otMacKey)));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mKeyIdMode = aKeyIdMode;
mKeyId = aKeyId;
memcpy(mPrevKey.m8, aPrevKey.m8, OT_MAC_KEY_SIZE);
memcpy(mCurrKey.m8, aCurrKey.m8, OT_MAC_KEY_SIZE);
memcpy(mNextKey.m8, aNextKey.m8, OT_MAC_KEY_SIZE);
mMacKeySet = true;
#endif
exit:
return error;
}
@@ -1113,37 +1149,129 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::EnableSrcMatch(bool aEna
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::AddSrcMatchShortEntry(uint16_t aShortAddress)
{
return Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress);
otError error;
SuccessOrExit(error = Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
assert(mSrcMatchShortEntryCount < OPENTHREAD_CONFIG_MLE_MAX_CHILDREN);
for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
{
if (mSrcMatchShortEntries[i] == aShortAddress)
{
ExitNow();
}
}
mSrcMatchShortEntries[mSrcMatchShortEntryCount] = aShortAddress;
++mSrcMatchShortEntryCount;
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::AddSrcMatchExtEntry(const otExtAddress &aExtAddress)
{
return Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8);
otError error;
SuccessOrExit(error =
Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
assert(mSrcMatchExtEntryCount < OPENTHREAD_CONFIG_MLE_MAX_CHILDREN);
for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
{
if (memcmp(aExtAddress.m8, mSrcMatchExtEntries[i].m8, OT_EXT_ADDRESS_SIZE) == 0)
{
ExitNow();
}
}
mSrcMatchExtEntries[mSrcMatchExtEntryCount] = aExtAddress;
++mSrcMatchExtEntryCount;
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchShortEntry(uint16_t aShortAddress)
{
return Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress);
otError error;
SuccessOrExit(error = Remove(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, aShortAddress));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
{
if (mSrcMatchShortEntries[i] == aShortAddress)
{
mSrcMatchShortEntries[i] = mSrcMatchShortEntries[mSrcMatchShortEntryCount - 1];
--mSrcMatchShortEntryCount;
break;
}
}
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchExtEntry(const otExtAddress &aExtAddress)
{
return Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8);
otError error;
SuccessOrExit(error =
Remove(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, aExtAddress.m8));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
{
if (memcmp(mSrcMatchExtEntries[i].m8, aExtAddress.m8, OT_EXT_ADDRESS_SIZE) == 0)
{
mSrcMatchExtEntries[i] = mSrcMatchExtEntries[mSrcMatchExtEntryCount - 1];
--mSrcMatchExtEntryCount;
break;
}
}
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchShortEntries(void)
{
return Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr);
otError error;
SuccessOrExit(error = Set(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, nullptr));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mSrcMatchShortEntryCount = 0;
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::ClearSrcMatchExtEntries(void)
{
return Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr);
otError error;
SuccessOrExit(error = Set(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, nullptr));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mSrcMatchExtEntryCount = 0;
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
@@ -1187,7 +1315,17 @@ int8_t RadioSpinel<InterfaceType, ProcessContextType>::GetRssi(void)
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::SetCoexEnabled(bool aEnabled)
{
return Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, aEnabled);
otError error;
SuccessOrExit(error = Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, aEnabled));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mCoexEnabled = aEnabled;
mCoexEnabledSet = true;
#endif
exit:
return error;
}
template <typename InterfaceType, typename ProcessContextType>
@@ -1244,7 +1382,16 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::GetCoexMetrics(otRadioCo
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::SetTransmitPower(int8_t aPower)
{
otError error = Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, aPower);
otError error;
SuccessOrExit(error = Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, aPower));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mTransmitPower = aPower;
mTransmitPowerSet = true;
#endif
exit:
LogIfFail("Set transmit power failed", error);
return error;
}
@@ -1252,7 +1399,16 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::SetTransmitPower(int8_t
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::SetCcaEnergyDetectThreshold(int8_t aThreshold)
{
otError error = Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, aThreshold);
otError error;
SuccessOrExit(error = Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, aThreshold));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mCcaEnergyDetectThreshold = aThreshold;
mCcaEnergyDetectThresholdSet = true;
#endif
exit:
LogIfFail("Set CCA ED threshold failed", error);
return error;
}
@@ -1260,7 +1416,16 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::SetCcaEnergyDetectThresh
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::SetFemLnaGain(int8_t aGain)
{
otError error = Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, aGain);
otError error;
SuccessOrExit(error = Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, aGain));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mFemLnaGain = aGain;
mFemLnaGainSet = true;
#endif
exit:
LogIfFail("Set FEM LNA gain failed", error);
return error;
}
@@ -1272,6 +1437,12 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::EnergyScan(uint8_t aScan
VerifyOrExit(mRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN, error = OT_ERROR_NOT_CAPABLE);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mScanChannel = aScanChannel;
mScanDuration = aScanDuration;
mEnergyScanning = true;
#endif
SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_MASK, SPINEL_DATATYPE_DATA_S, &aScanChannel, sizeof(uint8_t)));
SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_PERIOD, SPINEL_DATATYPE_UINT16_S, aScanDuration));
SuccessOrExit(error = Set(SPINEL_PROP_MAC_SCAN_STATE, SPINEL_DATATYPE_UINT8_S, SPINEL_SCAN_STATE_ENERGY));
@@ -1287,11 +1458,17 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::Get(spinel_prop_key_t aK
assert(mWaitingTid == 0);
mPropertyFormat = aFormat;
va_start(mPropertyArgs, aFormat);
error = RequestV(true, SPINEL_CMD_PROP_VALUE_GET, aKey, nullptr, mPropertyArgs);
va_end(mPropertyArgs);
mPropertyFormat = nullptr;
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
do
{
RecoverFromRcpFailure();
#endif
va_start(mPropertyArgs, aFormat);
error = RequestWithPropertyFormatV(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, nullptr, mPropertyArgs);
va_end(mPropertyArgs);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
} while (mRcpFailed);
#endif
return error;
}
@@ -1308,11 +1485,18 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::GetWithParam(spinel_prop
assert(mWaitingTid == 0);
mPropertyFormat = aFormat;
va_start(mPropertyArgs, aFormat);
error = Request(true, SPINEL_CMD_PROP_VALUE_GET, aKey, SPINEL_DATATYPE_DATA_S, aParam, aParamSize);
va_end(mPropertyArgs);
mPropertyFormat = nullptr;
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
do
{
RecoverFromRcpFailure();
#endif
va_start(mPropertyArgs, aFormat);
error = RequestWithPropertyFormat(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, SPINEL_DATATYPE_DATA_S, aParam,
aParamSize);
va_end(mPropertyArgs);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
} while (mRcpFailed);
#endif
return error;
}
@@ -1324,11 +1508,18 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::Set(spinel_prop_key_t aK
assert(mWaitingTid == 0);
mExpectedCommand = SPINEL_CMD_PROP_VALUE_IS;
va_start(mPropertyArgs, aFormat);
error = RequestV(true, SPINEL_CMD_PROP_VALUE_SET, aKey, aFormat, mPropertyArgs);
va_end(mPropertyArgs);
mExpectedCommand = SPINEL_CMD_NOOP;
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
do
{
RecoverFromRcpFailure();
#endif
va_start(mPropertyArgs, aFormat);
error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_IS, SPINEL_CMD_PROP_VALUE_SET, aKey, aFormat,
mPropertyArgs);
va_end(mPropertyArgs);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
} while (mRcpFailed);
#endif
return error;
}
@@ -1340,11 +1531,18 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::Insert(spinel_prop_key_t
assert(mWaitingTid == 0);
mExpectedCommand = SPINEL_CMD_PROP_VALUE_INSERTED;
va_start(mPropertyArgs, aFormat);
error = RequestV(true, SPINEL_CMD_PROP_VALUE_INSERT, aKey, aFormat, mPropertyArgs);
va_end(mPropertyArgs);
mExpectedCommand = SPINEL_CMD_NOOP;
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
do
{
RecoverFromRcpFailure();
#endif
va_start(mPropertyArgs, aFormat);
error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_CMD_PROP_VALUE_INSERT, aKey, aFormat,
mPropertyArgs);
va_end(mPropertyArgs);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
} while (mRcpFailed);
#endif
return error;
}
@@ -1356,11 +1554,18 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::Remove(spinel_prop_key_t
assert(mWaitingTid == 0);
mExpectedCommand = SPINEL_CMD_PROP_VALUE_REMOVED;
va_start(mPropertyArgs, aFormat);
error = RequestV(true, SPINEL_CMD_PROP_VALUE_REMOVE, aKey, aFormat, mPropertyArgs);
va_end(mPropertyArgs);
mExpectedCommand = SPINEL_CMD_NOOP;
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
do
{
RecoverFromRcpFailure();
#endif
va_start(mPropertyArgs, aFormat);
error = RequestWithExpectedCommandV(SPINEL_CMD_PROP_VALUE_REMOVED, SPINEL_CMD_PROP_VALUE_REMOVE, aKey, aFormat,
mPropertyArgs);
va_end(mPropertyArgs);
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
} while (mRcpFailed);
#endif
return error;
}
@@ -1378,15 +1583,25 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::WaitResponse(void)
uint64_t remain;
now = otPlatTimeGet();
VerifyOrDie(end > now, OT_EXIT_RADIO_SPINEL_NO_RESPONSE);
if (end <= now)
{
HandleRcpTimeout();
ExitNow(mError = OT_ERROR_NONE);
}
remain = end - now;
VerifyOrDie(mSpinelInterface.WaitForFrame(remain) == OT_ERROR_NONE, OT_EXIT_RADIO_SPINEL_NO_RESPONSE);
if (mSpinelInterface.WaitForFrame(remain) != OT_ERROR_NONE)
{
HandleRcpTimeout();
ExitNow(mError = OT_ERROR_NONE);
}
} while (mWaitingTid || !mIsReady);
LogIfFail("Error waiting response", mError);
// This indicates end of waiting response.
mWaitingKey = SPINEL_PROP_LAST_STATUS;
exit:
return mError;
}
@@ -1506,6 +1721,55 @@ otError RadioSpinel<InterfaceType, ProcessContextType>::Request(bool
return status;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::RequestWithPropertyFormat(const char * aPropertyFormat,
uint32_t aCommand,
spinel_prop_key_t aKey,
const char * aFormat,
...)
{
otError error;
va_list args;
va_start(args, aFormat);
error = RequestWithPropertyFormatV(aPropertyFormat, aCommand, aKey, aFormat, args);
va_end(args);
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::RequestWithPropertyFormatV(const char * aPropertyFormat,
uint32_t aCommand,
spinel_prop_key_t aKey,
const char * aFormat,
va_list aArgs)
{
otError error;
mPropertyFormat = aPropertyFormat;
error = RequestV(true, aCommand, aKey, aFormat, aArgs);
mPropertyFormat = nullptr;
return error;
}
template <typename InterfaceType, typename ProcessContextType>
otError RadioSpinel<InterfaceType, ProcessContextType>::RequestWithExpectedCommandV(uint32_t aExpectedCommand,
uint32_t aCommand,
spinel_prop_key_t aKey,
const char * aFormat,
va_list aArgs)
{
otError error;
mExpectedCommand = aExpectedCommand;
error = RequestV(true, aCommand, aKey, aFormat, aArgs);
mExpectedCommand = SPINEL_CMD_NOOP;
return error;
}
template <typename InterfaceType, typename ProcessContextType>
void RadioSpinel<InterfaceType, ProcessContextType>::HandleTransmitDone(uint32_t aCommand,
spinel_prop_key_t aKey,
@@ -1835,5 +2099,162 @@ uint32_t RadioSpinel<InterfaceType, ProcessContextType>::GetBusSpeed(void) const
return mSpinelInterface.GetBusSpeed();
}
template <typename InterfaceType, typename ProcessContextType>
void RadioSpinel<InterfaceType, ProcessContextType>::HandleRcpUnexpectedReset(spinel_status_t aStatus)
{
otLogCritPlat("Unexpected RCP reset: %s", spinel_status_to_cstr(aStatus));
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mRcpFailed = true;
#else
DieNow(OT_EXIT_RADIO_SPINEL_RESET);
#endif
}
template <typename InterfaceType, typename ProcessContextType>
void RadioSpinel<InterfaceType, ProcessContextType>::HandleRcpTimeout(void)
{
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
mRcpFailed = true;
#else
DieNow(OT_EXIT_RADIO_SPINEL_NO_RESPONSE);
#endif
}
template <typename InterfaceType, typename ProcessContextType>
void RadioSpinel<InterfaceType, ProcessContextType>::RecoverFromRcpFailure(void)
{
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
constexpr int16_t kMaxFailureCount = OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT;
State recoveringState = mState;
if (!mRcpFailed)
{
ExitNow();
}
mRcpFailed = false;
otLogWarnPlat("RCP failure detected");
++mRcpFailureCount;
if (mRcpFailureCount > kMaxFailureCount)
{
otLogCritPlat("Too many rcp failures, exiting");
DieNow(OT_EXIT_FAILURE);
}
otLogWarnPlat("Trying to recover (%d/%d)", mRcpFailureCount, kMaxFailureCount);
mState = kStateDisabled;
mRxFrameBuffer.Clear();
mSpinelInterface.OnRcpReset();
mCmdTidsInUse = 0;
mCmdNextTid = 1;
mTxRadioTid = 0;
mWaitingTid = 0;
mWaitingKey = SPINEL_PROP_LAST_STATUS;
mIsReady = false;
mIsTimeSynced = false;
if (mResetRadioOnStartup)
{
SuccessOrDie(SendReset());
}
SuccessOrDie(WaitResponse());
SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
mState = kStateSleep;
RestoreProperties();
switch (recoveringState)
{
case kStateDisabled:
case kStateSleep:
break;
case kStateReceive:
SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
mState = kStateReceive;
break;
case kStateTransmitting:
case kStateTransmitDone:
SuccessOrDie(Set(SPINEL_PROP_MAC_RAW_STREAM_ENABLED, SPINEL_DATATYPE_BOOL_S, true));
mTxError = OT_ERROR_ABORT;
mState = kStateTransmitDone;
break;
}
if (mEnergyScanning)
{
SuccessOrDie(EnergyScan(mScanChannel, mScanDuration));
}
--mRcpFailureCount;
exit:
return;
#endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
}
#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
template <typename InterfaceType, typename ProcessContextType>
void RadioSpinel<InterfaceType, ProcessContextType>::RestoreProperties(void)
{
Settings::NetworkInfo networkInfo;
SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_PANID, SPINEL_DATATYPE_UINT16_S, mPanId));
SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_SADDR, SPINEL_DATATYPE_UINT16_S, mShortAddress));
SuccessOrDie(Set(SPINEL_PROP_MAC_15_4_LADDR, SPINEL_DATATYPE_EUI64_S, mExtendedAddress.m8));
SuccessOrDie(Set(SPINEL_PROP_PHY_CHAN, SPINEL_DATATYPE_UINT8_S, mChannel));
if (mMacKeySet)
{
SuccessOrDie(Set(SPINEL_PROP_RCP_MAC_KEY,
SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S
SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S,
mKeyIdMode, mKeyId, mPrevKey.m8, sizeof(otMacKey), mCurrKey.m8, sizeof(otMacKey), mNextKey.m8,
sizeof(otMacKey)));
}
SuccessOrDie(Instance::Get().template Get<Settings>().ReadNetworkInfo(networkInfo));
SuccessOrDie(Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, networkInfo.GetMacFrameCounter()));
for (int i = 0; i < mSrcMatchShortEntryCount; ++i)
{
SuccessOrDie(
Insert(SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES, SPINEL_DATATYPE_UINT16_S, mSrcMatchShortEntries[i]));
}
for (int i = 0; i < mSrcMatchExtEntryCount; ++i)
{
SuccessOrDie(
Insert(SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES, SPINEL_DATATYPE_EUI64_S, mSrcMatchExtEntries[i].m8));
}
if (mCcaEnergyDetectThresholdSet)
{
SuccessOrDie(Set(SPINEL_PROP_PHY_CCA_THRESHOLD, SPINEL_DATATYPE_INT8_S, mCcaEnergyDetectThreshold));
}
if (mTransmitPowerSet)
{
SuccessOrDie(Set(SPINEL_PROP_PHY_TX_POWER, SPINEL_DATATYPE_INT8_S, mTransmitPower));
}
if (mCoexEnabledSet)
{
SuccessOrDie(Set(SPINEL_PROP_RADIO_COEX_ENABLE, SPINEL_DATATYPE_BOOL_S, mCoexEnabled));
}
if (mFemLnaGainSet)
{
SuccessOrDie(Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, mFemLnaGain));
}
CalcRcpTimeOffset();
}
#endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
} // namespace Spinel
} // namespace ot
+8
View File
@@ -2251,6 +2251,14 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
ret = "DEBUG_TEST_WATCHDOG";
break;
case SPINEL_PROP_RCP_MAC_FRAME_COUNTER:
ret = "RCP_MAC_FRAME_COUNTER";
break;
case SPINEL_PROP_RCP_MAC_KEY:
ret = "RCP_MAC_KEY";
break;
default:
break;
}
+2
View File
@@ -100,6 +100,8 @@ target_compile_definitions(openthread-posix
${OT_PUBLIC_DEFINES}
PRIVATE
${OT_PLATFORM_DEFINES}
OPENTHREAD_FTD=1
OPENTHREAD_MTD=0
)
target_compile_options(openthread-posix PRIVATE
+5
View File
@@ -135,6 +135,11 @@ HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback,
{
}
void HdlcInterface::OnRcpReset(void)
{
mHdlcDecoder.Reset();
}
otError HdlcInterface::Init(const RadioUrl &aRadioUrl)
{
otError error = OT_ERROR_NONE;
+6
View File
@@ -156,6 +156,12 @@ public:
*/
uint32_t GetBusSpeed(void) const { return mBaudRate; }
/**
* This method is called when RCP failure detected and resets internal states of the interface.
*
*/
void OnRcpReset(void);
private:
/**
* This method instructs `HdlcInterface` to read and decode data from radio over the socket.
+17 -2
View File
@@ -91,6 +91,20 @@ SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback,
{
}
void SpiInterface::OnRcpReset(void)
{
mSpiValidFrameCount = 0;
mSpiTxIsReady = false;
mSpiTxRefusedCount = 0;
mSpiTxPayloadSize = 0;
mDidPrintRateLimitLog = false;
mSpiSlaveDataLen = 0;
memset(mSpiTxFrameBuffer, 0, sizeof(mSpiTxFrameBuffer));
TriggerReset();
usleep(static_cast<useconds_t>(mSpiResetDelay) * kUsecPerMsec);
}
otError SpiInterface::Init(const RadioUrl &aRadioUrl)
{
const char *spiGpioIntDevice;
@@ -155,6 +169,7 @@ otError SpiInterface::Init(const RadioUrl &aRadioUrl)
VerifyOrDie(spiAlignAllowance <= kSpiAlignAllowanceMax, OT_EXIT_FAILURE);
mSpiResetDelay = spiResetDelay;
mSpiCsDelayUs = spiCsDelay;
mSpiSmallPacketSize = spiSmallPacketSize;
mSpiAlignAllowance = spiAlignAllowance;
@@ -173,7 +188,7 @@ otError SpiInterface::Init(const RadioUrl &aRadioUrl)
InitSpiDev(aRadioUrl.GetPath(), spiMode, spiSpeed);
// Reset RCP chip.
TrigerReset();
TriggerReset();
// Waiting for the RCP chip starts up.
usleep(static_cast<useconds_t>(spiResetDelay) * kUsecPerMsec);
@@ -318,7 +333,7 @@ exit:
}
}
void SpiInterface::TrigerReset(void)
void SpiInterface::TriggerReset(void)
{
// Set Reset pin to low level.
SetGpioValue(mResetGpioValueFd, 0);
+8 -1
View File
@@ -146,6 +146,12 @@ public:
*/
uint32_t GetBusSpeed(void) const { return ((mSpiDevFd >= 0) ? mSpiSpeedHz : 0); }
/**
* This method is called when RCP failure detected and resets internal states of the interface.
*
*/
void OnRcpReset(void);
private:
int SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel);
int SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel);
@@ -155,7 +161,7 @@ private:
void InitResetPin(const char *aCharDev, uint8_t aLine);
void InitIntPin(const char *aCharDev, uint8_t aLine);
void InitSpiDev(const char *aPath, uint8_t aMode, uint32_t aSpeed);
void TrigerReset(void);
void TriggerReset(void);
uint8_t *GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength);
otError DoSpiTransfer(uint8_t *aSpiRxFrameBuffer, uint32_t aTransferLength);
@@ -208,6 +214,7 @@ private:
uint8_t mSpiMode;
uint8_t mSpiAlignAllowance;
uint32_t mSpiResetDelay;
uint16_t mSpiCsDelayUs;
uint16_t mSpiSmallPacketSize;
uint32_t mSpiSpeedHz;
+19 -5
View File
@@ -40,7 +40,7 @@ proc wait_for {command success {failure {[\r\n]FAILURE_NOT_EXPECTED[\r\n]}}} {
set result 1
}
-re $failure {
exit 1
error "Got failure in wait_for"
}
timeout {
# Do nothing
@@ -51,11 +51,11 @@ proc wait_for {command success {failure {[\r\n]FAILURE_NOT_EXPECTED[\r\n]}}} {
}
}
if {$result == 0} {
exit 1
error "Timed out in wait_for"
}
}
proc spawn_node {id {type ""}} {
proc spawn_node {id {type ""} {radio_url ""}} {
global spawn_id
global spawn_ids
@@ -63,26 +63,39 @@ proc spawn_node {id {type ""}} {
set type $::env(OT_NODE_TYPE)
}
if {${radio_url} == ""} {
set radio_url "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=$id"
}
send_user "\n# ${id} ${type}\n"
switch -regexp ${type} {
{rcp|rcp-cli} {
spawn $::env(OT_POSIX_APPS)/ot-cli "spinel+hdlc+uart://$::env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=$id"
spawn $::env(OT_POSIX_APPS)/ot-cli $radio_url
send "factoryreset\n"
wait_for "state" "disabled"
expect "Done"
send "routerselectionjitter 1\n"
expect "Done"
}
cli {
spawn $::env(OT_SIMULATION_APPS)/cli/ot-cli-ftd $id
send "factoryreset\n"
wait_for "state" "disabled"
expect "Done"
send "routerselectionjitter 1\n"
expect "Done"
}
mtd {
spawn $::env(OT_SIMULATION_APPS)/cli/ot-cli-mtd $id
send "factoryreset\n"
wait_for "state" "disabled"
expect "Done"
}
}
expect_after {
timeout { exit 1 }
timeout { error "Timed out" }
}
set spawn_ids($id) $spawn_id
@@ -123,6 +136,7 @@ proc dispose_all {} {
for {set i 1} {$i <= $max_node} {incr i} {
dispose_node $i
}
array unset spawn_ids
}
proc get_ipaddr {type} {
+11 -3
View File
@@ -29,19 +29,27 @@
source "tests/scripts/expect/_common.exp"
proc setup_two_nodes {{childmode {r}}} {
proc setup_two_nodes {{childmode {r}} {should_spawn_node true}} {
# Sets up a Thread network with 2 nodes, spawn_1 as the leader and spawn_2
# as a child.
set psk "J01NME"
spawn_node 2
if {$should_spawn_node} {
spawn_node 2
} else {
switch_node 2
}
send "eui64\n"
expect -re {([0-9a-f]{16})}
set eui64 $expect_out(1,string)
expect "Done"
# Sets up a Thread network with node 1 as the leader.
spawn_node 1
if {$should_spawn_node} {
spawn_node 1
} else {
switch_node 1
}
setup_leader
send "commissioner start\n"
-9
View File
@@ -31,9 +31,6 @@ source "tests/scripts/expect/_common.exp"
set max_node 15
spawn_node 1
expect_after {
timeout { exit 1 }
}
send "panid 0xface\n"
expect "Done"
send "ifconfig up\n"
@@ -45,9 +42,6 @@ expect "Done"
for {set i 2} {$i <= $max_node} {incr i} {
spawn_node $i
expect_after {
timeout { exit 1 }
}
send "mode r\n"
expect "Done"
send "panid 0xface\n"
@@ -61,9 +55,6 @@ for {set i 2} {$i <= $max_node} {incr i} {
}
set spawn_id $spawn_ids(1)
expect_after {
timeout { exit 1 }
}
send "child table\n"
expect "Done"
@@ -30,10 +30,7 @@
source "tests/scripts/expect/_common.exp"
# allows 11-25 and forbidden 26
spawn $env(OT_POSIX_APPS)/ot-cli "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?max-power-table=11,12,13,14,15,16,17,18,19,20,21,22,23,24,-1,0x7f&forkpty-arg=1"
expect_after {
timeout { exit 1 }
}
spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?max-power-table=11,12,13,14,15,16,17,18,19,20,21,22,23,24,-1,0x7f&forkpty-arg=1"
send "channel supported\n"
expect "0x3fff800"
expect "Done"
@@ -43,10 +40,7 @@ expect "Done"
send "\x04"
expect eof
# allows all channels by default
spawn $env(OT_POSIX_APPS)/ot-cli "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=1"
expect_after {
timeout { exit 1 }
}
spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=1"
send "channel supported\n"
expect "0x7fff800"
expect "Done"
+306
View File
@@ -0,0 +1,306 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2020, 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"
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 {
puts "Before enabling"
spawn_node 1 "rcp" "spinel+hdlc_uart://$host_pty"
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
expect eof
puts "Queued parent-to-child packets, as parent"
spawn_node 1 "rcp" "spinel+hdlc_uart://$host_pty"
spawn_node 2
setup_two_nodes "-" false
switch_node 2
send "ipaddr mleid\n"
expect "ipaddr mleid"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set addr_2 $expect_out(1,string)
send "udp open\n"
expect "Done"
send "udp bind :: 11003\n"
expect "Done"
send "pollperiod 100000\n"
expect "Done"
sleep 1
switch_node 1
send "ipaddr mleid\n"
expect "ipaddr mleid"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set addr_1 $expect_out(1,string)
send "udp open\n"
expect "Done"
send "udp bind :: 11004\n"
expect "Done"
send "udp connect $addr_2 11003\n"
expect "Done"
send "udp send hello\n"
expect "Done"
send "udp send there\n"
expect "Done"
sleep 1
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
switch_node 2
send "pollperiod 1000\n"
expect "Done"
expect "5 bytes from $addr_1 11004 hello"
expect "5 bytes from $addr_1 11004 there"
dispose_all
puts "Queued parent-to-child packets, as child"
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 2 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
spawn_node 1
spawn_node 2 "rcp" "spinel+hdlc_uart://$host_pty"
setup_two_nodes "-" false
switch_node 2
send "ipaddr mleid\n"
expect "ipaddr mleid"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set addr_2 $expect_out(1,string)
send "udp open\n"
expect "Done"
send "udp bind :: 11003\n"
expect "Done"
send "pollperiod 100000\n"
expect "Done"
sleep 1
switch_node 1
send "ipaddr mleid\n"
expect "ipaddr mleid"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set addr_1 $expect_out(1,string)
send "udp open\n"
expect "Done"
send "udp bind :: 11004\n"
expect "Done"
send "udp connect $addr_2 11003\n"
expect "Done"
send "udp send hello\n"
expect "Done"
sleep 1
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 2 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
sleep 1
switch_node 2
send "pollperiod 1000\n"
expect "Done"
expect "5 bytes from $addr_1 11004 hello"
dispose_all
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
puts "Many children, queued child-to-child packets"
spawn_node 1 "rcp" "spinel+hdlc_uart://$host_pty"
send "panid 0xface\n"
expect "Done"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "leader"
expect "Done"
send "ipaddr mleid\n"
expect "ipaddr mleid"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set addr(1) $expect_out(1,string)
send "udp open\n"
expect "Done"
send "udp bind :: 11004\n"
expect "Done"
set max_children 15
for {set i 2} {$i <= $max_children + 1} {incr i} {
spawn_node $i
send "mode -\n"
expect "Done"
send "panid 0xface\n"
expect "Done"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "child"
send "ipaddr mleid\n"
expect "ipaddr mleid"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set addr($i) $expect_out(1,string)
expect "Done"
send "udp open\n"
expect "Done"
send "udp bind :: 11003\n"
expect "Done"
send "pollperiod 100000\n"
expect "Done"
}
sleep 1
switch_node 1
for {set i 2} {$i <= $max_children + 1} {incr i} {
send "udp send $addr($i) 11003 hello\n"
expect "Done"
}
sleep 1
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
for {set i 7} {$i <= 9} {incr i} {
switch_node $i
send "pollperiod 1000\n"
expect "Done"
expect "5 bytes from $addr(1) 11004 hello"
}
switch_node 4
send "udp send $addr(5) 11003 hello_from_node_4\n"
expect "Done"
if {$::env(THREAD_VERSION) != "1.1"} {
expect "5 bytes from $addr(1) 11004 hello"
}
sleep 1
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
switch_node 5
send "pollperiod 1000\n"
expect "Done"
expect "17 bytes from $addr(4) 11003 hello_from_node_4"
dispose_all
puts "While energy scanning"
spawn_node 1 "rcp" "spinel+hdlc_uart://$host_pty"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "leader"
expect "Done"
send "scan energy 100\n"
expect "| Ch | RSSI |"
expect "+----+------+"
sleep 1
exec kill $rcp_pid
puts "Killed $rcp_pid"
sleep 1
set rcp_pid [exec $::env(OT_SIMULATION_APPS)/ncp/ot-rcp 1 < $radio_pty > $radio_pty &]
puts "RCP PID: $rcp_pid"
for {set i 11} {$i <= 26} {incr i} {
expect -re "\\| +$i \\| +-?\\d+ \\|"
}
expect "Done"
dispose_all
} finally {
exec kill $rcp_pid
exec kill $socat_pid
}
@@ -29,12 +29,7 @@
source "tests/scripts/expect/_common.exp"
spawn $env(OT_POSIX_APPS)/ot-cli "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--sleep-to-tx&forkpty-arg=1"
set node_1 $spawn_id
expect_after {
timeout { exit 1 }
}
spawn_node 1 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--sleep-to-tx&forkpty-arg=1"
send "dataset init new\n"
expect "Done"
send "dataset panid 0xface\n"
@@ -75,12 +70,7 @@ expect -re {(\d+)}
set channel $expect_out(1,string)
expect "Done"
spawn $env(OT_POSIX_APPS)/ot-cli "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--sleep-to-tx&forkpty-arg=2"
set node_2 $spawn_id
expect_after {
timeout { exit 1 }
}
spawn_node 2 "rcp" "spinel+hdlc+uart://$env(OT_SIMULATION_APPS)/ncp/ot-rcp?forkpty-arg=--sleep-to-tx&forkpty-arg=2"
send "scan\n"
expect "| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |"
expect "+---+------------------+------------------+------+------------------+----+-----+-----+"
@@ -0,0 +1,30 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2020, 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-restoration.exp"