diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 92a516a90..90ce3e27a 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -261,6 +261,7 @@ ot_option(OT_UPTIME OPENTHREAD_CONFIG_UPTIME_ENABLE "uptime") ot_option(OT_VERHOEFF_CHECKSUM OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE "verhoeff checksum") ot_option(OT_WAKEUP_COORDINATOR OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE "wake-up coordinator") ot_option(OT_WAKEUP_END_DEVICE OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE "wake-up end device") +ot_option(OT_SPINEL_CP_RESET_FAIL_CALLBACK_ENABLE OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE "CP reset failure callback") option(OT_DOC "build OpenThread documentation") message(STATUS "- - - - - - - - - - - - - - - - ") diff --git a/script/check-posix-build-cmake b/script/check-posix-build-cmake index 63f67c683..965da3f22 100755 --- a/script/check-posix-build-cmake +++ b/script/check-posix-build-cmake @@ -76,7 +76,7 @@ main() fi reset_source - build -DOT_POSIX_RCP_VENDOR_BUS=ON "$@" + build -DOT_POSIX_RCP_VENDOR_BUS=ON -DOT_SPINEL_CP_RESET_FAIL_CALLBACK_ENABLE=ON "$@" } main "$@" diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index 632fcfc42..fb99a63be 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -163,4 +163,13 @@ #define OPENTHREAD_SPINEL_CONFIG_RCP_TX_WAIT_TIME_SECS 5 #endif +/** + * @def OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE + * + * Enables Co-processor reset failure callback in Spinel driver + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE +#define OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE 0 +#endif + #endif // OPENTHREAD_SPINEL_CONFIG_H_ diff --git a/src/lib/spinel/spinel_driver.cpp b/src/lib/spinel/spinel_driver.cpp index d88d55df9..9ef7e87a1 100644 --- a/src/lib/spinel/spinel_driver.cpp +++ b/src/lib/spinel/spinel_driver.cpp @@ -52,6 +52,10 @@ SpinelDriver::SpinelDriver(void) , mSpinelVersionMajor(-1) , mSpinelVersionMinor(-1) , mIsCoprocessorReady(false) +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE + , mCoprocessorResetFailureCallback(nullptr) + , mCoprocessorResetFailureContext(nullptr) +#endif { memset(mVersion, 0, sizeof(mVersion)); @@ -119,6 +123,15 @@ exit: return error; } +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE +void SpinelDriver::SetCoprocessorResetFailureCallback(otSpinelDriverCoprocessorResetFailureCallback aCallback, + void *aContext) +{ + mCoprocessorResetFailureCallback = aCallback; + mCoprocessorResetFailureContext = aContext; +} +#endif + void SpinelDriver::ResetCoprocessor(bool aSoftwareReset) { bool hardwareReset; @@ -129,7 +142,13 @@ void SpinelDriver::ResetCoprocessor(bool aSoftwareReset) VerifyOrExit(!mIsCoprocessorReady, resetDone = true); #endif - mWaitingKey = SPINEL_PROP_LAST_STATUS; + // Before resetting the co-processor, mIsCoprocessorReady must be set to false. + // Otherwise, in some cases, the host may misinterpret the reset result. + // For example, if mIsCoprocessorReady is true when calling ResetCoprocessor, + // and the reset fails but the co-processor sends some invalid data, `WaitResponse` + // may mistakenly return OT_ERROR_NONE, causing the host to believe the reset succeeded. + mIsCoprocessorReady = false; + mWaitingKey = SPINEL_PROP_LAST_STATUS; if (aSoftwareReset && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (WaitResponse() == OT_ERROR_NONE)) { @@ -160,6 +179,12 @@ exit: if (!resetDone) { LogCrit("Failed to reset co-processor!"); +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE + if (mCoprocessorResetFailureCallback) + { + mCoprocessorResetFailureCallback(mCoprocessorResetFailureContext); + } +#endif DieNow(OT_EXIT_FAILURE); } } diff --git a/src/lib/spinel/spinel_driver.hpp b/src/lib/spinel/spinel_driver.hpp index 076106204..8f34c7043 100644 --- a/src/lib/spinel/spinel_driver.hpp +++ b/src/lib/spinel/spinel_driver.hpp @@ -215,6 +215,27 @@ public: */ spinel_iid_t GetIid(void) { return mIid; } +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE + /** + * A callback type for handling Co-processor reset failure of spinel driver. + * + * @param[in] aContext A pointer to the user context. + */ + typedef void (*otSpinelDriverCoprocessorResetFailureCallback)(void *aContext); + + /** + * Registers a callback to handle Co-processor reset failure of Spinel driver. + * + * This function is used to register a callback to handle Co-processor reset failure. + * When the Spinel driver fails to reset the Co-processor through both software and + * hardware resets, the user can handle the error through the callback(such as OTA). + * + * @param[in] aCallback The callback. + * @param[in] aContext A pointer to the user context. + */ + void SetCoprocessorResetFailureCallback(otSpinelDriverCoprocessorResetFailureCallback aCallback, void *aContext); +#endif + private: static constexpr uint16_t kMaxSpinelFrame = SPINEL_FRAME_MAX_SIZE; static constexpr uint16_t kVersionStringSize = 128; @@ -314,6 +335,11 @@ private: char mVersion[kVersionStringSize]; Array mCoprocessorCaps; + +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE + otSpinelDriverCoprocessorResetFailureCallback mCoprocessorResetFailureCallback; + void *mCoprocessorResetFailureContext; +#endif }; } // namespace Spinel diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index fa16e103a..2f9497809 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -444,6 +444,13 @@ void platformResolverUpdateFdSet(otSysMainloopContext *aContext); */ void platformResolverProcess(const otSysMainloopContext *aContext); +/** + * The callback for coprocessor reset failure. + * + * @param[in] aContext A pointer to the mainloop context. + */ +void platformCoprocessorResetFailed(void *aContext); + #ifdef __cplusplus } #endif diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index ea9cd350f..3e98998ef 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -73,6 +73,10 @@ Radio::Radio(void) { } +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE +OT_TOOL_WEAK void platformCoprocessorResetFailed(void *) {} +#endif + void Radio::Init(const char *aUrl) { bool resetRadio; @@ -107,6 +111,10 @@ void Radio::Init(const char *aUrl) resetRadio = !mRadioUrl.HasParam("no-reset"); skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check"); +#if OPENTHREAD_SPINEL_CONFIG_COPROCESSOR_RESET_FAILURE_CALLBACK_ENABLE + GetSpinelDriver().SetCoprocessorResetFailureCallback(platformCoprocessorResetFailed, this); +#endif + mRadioSpinel.SetCallbacks(callbacks); mRadioSpinel.Init(skipCompatibilityCheck, resetRadio, &GetSpinelDriver(), (skipCompatibilityCheck ? 0 : kRequiredRadioCaps), aEnableRcpTimeSync);