mirror of
https://github.com/espressif/openthread.git
synced 2026-08-23 10:49:51 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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--)
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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} {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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"
|
||||
Reference in New Issue
Block a user