diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 732690701..d14f7c0aa 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -962,6 +962,7 @@ private: static void HandleReceivedFrame(void *aContext); + void ResetRcp(bool aResetRadio); otError CheckSpinelVersion(void); otError CheckRadioCapabilities(void); otError CheckRcpApiVersion(bool aSupportsRcpApiVersion, bool aSupportsMinHostRcpApiVersion); @@ -998,7 +999,7 @@ private: spinel_prop_key_t aKey, const char *aFormat, va_list aArgs); - otError WaitResponse(void); + otError WaitResponse(bool aHandleRcpTimeout = true); otError SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t aTid, diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index 4bdc033ac..c9c676576 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -230,23 +230,7 @@ void RadioSpinel::Init(bool aResetRadio, mResetRadioOnStartup = aResetRadio; #endif - if (aResetRadio) - { - SuccessOrExit(error = SendReset(SPINEL_RESET_STACK)); - SuccessOrDie(mSpinelInterface.ResetConnection()); - } - - SuccessOrExit(error = WaitResponse()); - -#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - while (mRcpFailed) - { - RecoverFromRcpFailure(); - } -#endif - - VerifyOrExit(mIsReady, error = OT_ERROR_FAILED); - + ResetRcp(aResetRadio); SuccessOrExit(error = CheckSpinelVersion()); SuccessOrExit(error = Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, mVersion, sizeof(mVersion))); SuccessOrExit(error = Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, mIeeeEui64.m8)); @@ -279,6 +263,43 @@ exit: SuccessOrDie(error); } +template +void RadioSpinel::ResetRcp(bool aResetRadio) +{ + bool hardwareReset; + bool resetDone = false; + + mIsReady = false; + mWaitingKey = SPINEL_PROP_LAST_STATUS; + + if (aResetRadio && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (WaitResponse(false) == OT_ERROR_NONE)) + { + otLogInfoPlat("Software reset RCP successfully"); + ExitNow(resetDone = true); + } + + hardwareReset = (mSpinelInterface.HardwareReset() == OT_ERROR_NONE); + SuccessOrExit(WaitResponse(false)); + + resetDone = true; + + if (hardwareReset) + { + otLogInfoPlat("Hardware reset RCP successfully"); + } + else + { + otLogInfoPlat("RCP self reset successfully"); + } + +exit: + if (!resetDone) + { + otLogCritPlat("Failed to reset RCP!"); + DieNow(OT_EXIT_FAILURE); + } +} + template otError RadioSpinel::CheckSpinelVersion(void) { @@ -1699,7 +1720,7 @@ otError RadioSpinel::Remove(spinel_prop_key_t } template -otError RadioSpinel::WaitResponse(void) +otError RadioSpinel::WaitResponse(bool aHandleRcpTimeout) { uint64_t end = otPlatTimeGet() + kMaxWaitTime * US_PER_MS; @@ -1713,7 +1734,10 @@ otError RadioSpinel::WaitResponse(void) if ((end <= now) || (mSpinelInterface.WaitForFrame(end - now) != OT_ERROR_NONE)) { otLogWarnPlat("Wait for response timeout"); - HandleRcpTimeout(); + if (aHandleRcpTimeout) + { + HandleRcpTimeout(); + } ExitNow(mError = OT_ERROR_NONE); } } while (mWaitingTid || !mIsReady); @@ -2305,24 +2329,14 @@ void RadioSpinel::RecoverFromRcpFailure(void) mState = kStateDisabled; mRxFrameBuffer.Clear(); - mSpinelInterface.OnRcpReset(); mCmdTidsInUse = 0; mCmdNextTid = 1; mTxRadioTid = 0; mWaitingTid = 0; - mWaitingKey = SPINEL_PROP_LAST_STATUS; mError = OT_ERROR_NONE; - mIsReady = false; mIsTimeSynced = false; - if (mResetRadioOnStartup) - { - SuccessOrDie(SendReset(SPINEL_RESET_STACK)); - SuccessOrDie(mSpinelInterface.ResetConnection()); - } - - SuccessOrDie(WaitResponse()); - + ResetRcp(mResetRadioOnStartup); SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true)); mState = kStateSleep; diff --git a/src/lib/spinel/spinel_interface.hpp b/src/lib/spinel/spinel_interface.hpp index 585f257aa..1feafddd4 100644 --- a/src/lib/spinel/spinel_interface.hpp +++ b/src/lib/spinel/spinel_interface.hpp @@ -36,6 +36,7 @@ #define POSIX_APP_SPINEL_INTERFACE_HPP_ #include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/spinel.h" namespace ot { namespace Spinel { @@ -58,6 +59,23 @@ public: typedef Hdlc::MultiFrameBuffer RxFrameBuffer; typedef void (*ReceiveFrameCallback)(void *aContext); + + /** + * This method indicates whether or not the frame is the Spinel SPINEL_CMD_RESET frame. + * + * @param[in] aFrame A pointer to buffer containing the spinel frame. + * @param[in] aLength The length (number of bytes) in the frame. + * + * @retval true If the frame is a Spinel SPINEL_CMD_RESET frame. + * @retval false If the frame is not a Spinel SPINEL_CMD_RESET frame. + * + */ + static bool IsSpinelResetCommand(const uint8_t *aFrame, uint16_t aLength) + { + static constexpr uint8_t kSpinelResetCommand[] = {SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET}; + return (aLength >= sizeof(kSpinelResetCommand)) && + (memcmp(aFrame, kSpinelResetCommand, sizeof(kSpinelResetCommand)) == 0); + } }; } // namespace Spinel } // namespace ot diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index 9315bab4c..a37427e57 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -60,6 +60,7 @@ #include #include "common/code_utils.hpp" +#include "lib/spinel/spinel.h" #ifdef __APPLE__ @@ -139,8 +140,6 @@ HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback, mInterfaceMetrics.mRcpInterfaceType = OT_POSIX_RCP_BUS_UART; } -void HdlcInterface::OnRcpReset(void) { mHdlcDecoder.Reset(); } - otError HdlcInterface::Init(const Url::Url &aRadioUrl) { otError error = OT_ERROR_NONE; @@ -210,6 +209,12 @@ otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) error = Write(encoderBuffer.GetFrame(), encoderBuffer.GetLength()); exit: + if ((error == OT_ERROR_NONE) && ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength)) + { + mHdlcDecoder.Reset(); + error = ResetConnection(); + } + return error; } diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp index f2d580127..dc9fbf89a 100644 --- a/src/posix/platform/hdlc_interface.hpp +++ b/src/posix/platform/hdlc_interface.hpp @@ -158,16 +158,13 @@ public: uint32_t GetBusSpeed(void) const { return mBaudRate; } /** - * This method is called when RCP failure detected and resets internal states of the interface. + * This method hardware resets the RCP. + * + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. * */ - void OnRcpReset(void); - - /** - * This method is called when RCP is reset to recreate the connection with it. - * - */ - otError ResetConnection(void); + otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; } /** * This method returns the RCP interface metrics. @@ -178,6 +175,12 @@ public: const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } private: + /** + * This method is called when RCP is reset to recreate the connection with it. + * + */ + otError ResetConnection(void); + /** * This method instructs `HdlcInterface` to read and decode data from radio over the socket. * diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index 73ed45c1a..d3bf52e36 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -85,7 +85,7 @@ SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback, { } -void SpiInterface::OnRcpReset(void) +void SpiInterface::ResetStates(void) { mSpiTxIsReady = false; mSpiTxRefusedCount = 0; @@ -95,9 +95,20 @@ void SpiInterface::OnRcpReset(void) memset(mSpiTxFrameBuffer, 0, sizeof(mSpiTxFrameBuffer)); memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics)); mInterfaceMetrics.mRcpInterfaceType = OT_POSIX_RCP_BUS_SPI; +} +otError SpiInterface::HardwareReset(void) +{ + ResetStates(); TriggerReset(); + + // If the `INT` pin is set to low during the restart of the RCP chip, which triggers continuous invalid SPI + // transactions by the host, it will cause the function `PushPullSpi()` to output lots of invalid warn log + // messages. Adding the delay here is used to wait for the RCP chip starts up to avoid outputing invalid + // log messages. usleep(static_cast(mSpiResetDelay) * kUsecPerMsec); + + return OT_ERROR_NONE; } otError SpiInterface::Init(const Url::Url &aRadioUrl) @@ -182,12 +193,6 @@ otError SpiInterface::Init(const Url::Url &aRadioUrl) InitResetPin(spiGpioResetDevice, spiGpioResetLine); InitSpiDev(aRadioUrl.GetPath(), spiMode, spiSpeed); - // Reset RCP chip. - TriggerReset(); - - // Waiting for the RCP chip starts up. - usleep(static_cast(spiResetDelay) * kUsecPerMsec); - return OT_ERROR_NONE; } @@ -800,6 +805,12 @@ otError SpiInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) otError error = OT_ERROR_NONE; VerifyOrExit(aLength < (kMaxFrameSize - kSpiFrameHeaderSize), error = OT_ERROR_NO_BUFS); + + if (ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength)) + { + ResetStates(); + } + VerifyOrExit(!mSpiTxIsReady, error = OT_ERROR_BUSY); memcpy(&mSpiTxFrameBuffer[kSpiFrameHeaderSize], aFrame, aLength); diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp index 988d13fe3..0f3242e35 100644 --- a/src/posix/platform/spi_interface.hpp +++ b/src/posix/platform/spi_interface.hpp @@ -147,17 +147,13 @@ 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. + * This method hardware resets the RCP. + * + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. * */ - void OnRcpReset(void); - - /** - * This method is called when RCP is reset to recreate the connection with it. - * Intentionally empty. - * - */ - otError ResetConnection(void) { return OT_ERROR_NONE; } + otError HardwareReset(void); /** * This method returns the RCP interface metrics. @@ -168,6 +164,7 @@ public: const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } private: + void ResetStates(void); 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); void SetGpioValue(int aFd, uint8_t aValue); diff --git a/src/posix/platform/vendor_interface.hpp b/src/posix/platform/vendor_interface.hpp index 4b2011c55..8c82c204d 100644 --- a/src/posix/platform/vendor_interface.hpp +++ b/src/posix/platform/vendor_interface.hpp @@ -144,19 +144,13 @@ public: uint32_t GetBusSpeed(void) const; /** - * This method is called when RCP failure detected and resets internal states of the interface. + * This method hardware resets the RCP. + * + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. * */ - void OnRcpReset(void); - - /** - * This method is called when RCP is reset to recreate the connection with it. - * - * @retval OT_ERROR_NONE Reset the connection successfully. - * @retval OT_ERROR_FAILED Failed to reset the connection. - * - */ - otError ResetConnection(void); + otError HardwareReset(void); /** * This method returns the RCP interface metrics. diff --git a/src/posix/platform/vendor_interface_example.cpp b/src/posix/platform/vendor_interface_example.cpp index 6ca5f0a13..ebf6b7869 100644 --- a/src/posix/platform/vendor_interface_example.cpp +++ b/src/posix/platform/vendor_interface_example.cpp @@ -101,9 +101,11 @@ void VendorInterface::Deinit(void) uint32_t VendorInterface::GetBusSpeed(void) const { return 1000000; } -void VendorInterface::OnRcpReset(void) +otError VendorInterface::HardwareReset(void) { // TODO: Implement vendor code here. + + return OT_ERROR_NOT_IMPLEMENTED; } void VendorInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout) @@ -142,13 +144,6 @@ otError VendorInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) return OT_ERROR_NONE; } -otError VendorInterface::ResetConnection(void) -{ - // TODO: Implement vendor code here. - - return OT_ERROR_NONE; -} - const otRcpInterfaceMetrics *VendorInterface::GetRcpInterfaceMetrics(void) { // TODO: Implement vendor code here.