diff --git a/src/ncp/ncp_host.cpp b/src/ncp/ncp_host.cpp index 84055875bef..a1e20ddcfbc 100644 --- a/src/ncp/ncp_host.cpp +++ b/src/ncp/ncp_host.cpp @@ -145,6 +145,14 @@ void NcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatase } } +void NcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver) +{ + OT_UNUSED_VARIABLE(aEnabled); + + // TODO: Implement SetThreadEnabled under NCP mode. + mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); }); +} + void NcpHost::Process(const MainloopContext &aMainloop) { mSpinelDriver.Process(&aMainloop); diff --git a/src/ncp/ncp_host.hpp b/src/ncp/ncp_host.hpp index 6bb18f77d07..b7d3d095f81 100644 --- a/src/ncp/ncp_host.hpp +++ b/src/ncp/ncp_host.hpp @@ -87,6 +87,7 @@ class NcpHost : public MainloopProcessor, public ThreadHost, public NcpNetworkPr void Leave(const AsyncResultReceiver &aReceiver) override; void ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs, const AsyncResultReceiver aReceiver) override; + void SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver) override; CoprocessorType GetCoprocessorType(void) override { return OT_COPROCESSOR_NCP; } const char *GetCoprocessorVersion(void) override; const char *GetInterfaceName(void) const override { return mConfig.mInterfaceName; } diff --git a/src/ncp/rcp_host.cpp b/src/ncp/rcp_host.cpp index a7eb6e526da..b4def76408f 100644 --- a/src/ncp/rcp_host.cpp +++ b/src/ncp/rcp_host.cpp @@ -295,6 +295,8 @@ void RcpHost::Deinit(void) OtNetworkProperties::SetInstance(nullptr); mThreadStateChangedCallbacks.clear(); mResetHandlers.clear(); + + mSetThreadEnabledReceiver = nullptr; } void RcpHost::HandleStateChanged(otChangedFlags aFlags) @@ -417,6 +419,56 @@ void RcpHost::ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatase mTaskRunner.Post([aReceiver](void) { aReceiver(OT_ERROR_NOT_IMPLEMENTED, "Not implemented!"); }); } +void RcpHost::SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver) +{ + otError error = OT_ERROR_NONE; + bool receiveResultHere = true; + + VerifyOrExit(mInstance != nullptr, error = OT_ERROR_INVALID_STATE); + VerifyOrExit(mSetThreadEnabledReceiver == nullptr, error = OT_ERROR_BUSY); + + if (aEnabled) + { + otOperationalDatasetTlvs datasetTlvs; + + if (otDatasetGetActiveTlvs(mInstance, &datasetTlvs) != OT_ERROR_NOT_FOUND && datasetTlvs.mLength > 0 && + otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_DISABLED) + { + SuccessOrExit(error = otIp6SetEnabled(mInstance, true)); + SuccessOrExit(error = otThreadSetEnabled(mInstance, true)); + } + } + else + { + SuccessOrExit(error = otThreadDetachGracefully(mInstance, DisableThreadAfterDetach, this)); + mSetThreadEnabledReceiver = aReceiver; + receiveResultHere = false; + } + +exit: + if (receiveResultHere) + { + mTaskRunner.Post([aReceiver, error](void) { aReceiver(error, ""); }); + } +} + +void RcpHost::DisableThreadAfterDetach(void *aContext) +{ + static_cast(aContext)->DisableThreadAfterDetach(); +} + +void RcpHost::DisableThreadAfterDetach(void) +{ + otError error = OT_ERROR_NONE; + std::string errorMsg; + + SuccessOrExit(error = otThreadSetEnabled(mInstance, false), errorMsg = "Failed to disable Thread stack"); + SuccessOrExit(error = otIp6SetEnabled(mInstance, false), errorMsg = "Failed to disable Thread interface"); + +exit: + SafeInvokeAndClear(mSetThreadEnabledReceiver, error, errorMsg); +} + /* * Provide, if required an "otPlatLog()" function */ diff --git a/src/ncp/rcp_host.hpp b/src/ncp/rcp_host.hpp index 79f441a16af..bdf3118d0e5 100644 --- a/src/ncp/rcp_host.hpp +++ b/src/ncp/rcp_host.hpp @@ -207,6 +207,7 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro void Leave(const AsyncResultReceiver &aRecevier) override; void ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs, const AsyncResultReceiver aReceiver) override; + void SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver) override; CoprocessorType GetCoprocessorType(void) override { @@ -219,6 +220,15 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro } private: + static void SafeInvokeAndClear(AsyncResultReceiver &aReceiver, otError aError, const std::string &aErrorInfo = "") + { + if (aReceiver) + { + aReceiver(aError, aErrorInfo); + aReceiver = nullptr; + } + } + static void HandleStateChanged(otChangedFlags aFlags, void *aContext) { static_cast(aContext)->HandleStateChanged(aFlags); @@ -238,6 +248,9 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro void HandleBackboneRouterNdProxyEvent(otBackboneRouterNdProxyEvent aEvent, const otIp6Address *aAddress); #endif + static void DisableThreadAfterDetach(void *aContext); + void DisableThreadAfterDetach(void); + bool IsAutoAttachEnabled(void); void DisableAutoAttach(void); @@ -252,6 +265,8 @@ class RcpHost : public MainloopProcessor, public ThreadHost, public OtNetworkPro std::vector mThreadStateChangedCallbacks; bool mEnableAutoAttach = false; + AsyncResultReceiver mSetThreadEnabledReceiver; + #if OTBR_ENABLE_FEATURE_FLAGS // The applied FeatureFlagList in ApplyFeatureFlagList call, used for debugging purpose. std::string mAppliedFeatureFlagListBytes; diff --git a/src/ncp/thread_host.hpp b/src/ncp/thread_host.hpp index 30b8e45a03d..f0677bcdc37 100644 --- a/src/ncp/thread_host.hpp +++ b/src/ncp/thread_host.hpp @@ -136,6 +136,20 @@ class ThreadHost : virtual public NetworkProperties virtual void ScheduleMigration(const otOperationalDatasetTlvs &aPendingOpDatasetTlvs, const AsyncResultReceiver aReceiver) = 0; + /** + * This method enables/disables the Thread network. + * + * 1. If there is an ongoing 'SetThreadEnabled' operation, no action will be taken and @p aReceiver + * will be invoked with error OT_ERROR_BUSY. + * 2. If the host hasn't been initialized, @p aReceiver will be invoked with error OT_ERROR_INVALID_STATE. + * 3. When @p aEnabled is false, this method will first trigger a graceful detach and then disable Thread + * network interface and the stack. + * + * @param[in] aEnabled true to enable and false to disable. + * @param[in] aReceiver A receiver to get the async result of this operation. + */ + virtual void SetThreadEnabled(bool aEnabled, const AsyncResultReceiver aReceiver) = 0; + /** * Returns the co-processor type. */