diff --git a/include/openthread/link.h b/include/openthread/link.h index 6d44e537b..da3dc8641 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -628,6 +628,32 @@ otError otLinkSetPromiscuous(otInstance *aInstance, bool aPromiscuous); */ uint16_t otLinkGetCcaFailureRate(otInstance *aInstance); +/** + * This function enables or disables the link layer. + * + * @note The link layer may only be enabled / disabled when the Thread Interface is disabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnable true to enable the link layer, or false otherwise. + * + * @retval OT_ERROR_NONE Successfully enabled / disabled the link layer. + * @retval OT_ERROR_INVALID_STATE Could not disable the link layer because + * the Thread interface is enabled. + * + */ +otError otLinkSetEnabled(otInstance *aInstance, bool aEnable); + +/** + * This function indicates whether or not the link layer is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval true Link layer is enabled. + * @retval false Link layer is not enabled. + * + */ +bool otLinkIsEnabled(otInstance *aInstance); + /** * @} * diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index aaed41955..639b2f321 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -289,6 +289,27 @@ exit: return error; } +otError otLinkSetEnabled(otInstance *aInstance, bool aEnable) +{ + otError error = OT_ERROR_NONE; + Instance &instance = *static_cast(aInstance); + + // cannot disable the link layer if the Thread interface is enabled + VerifyOrExit(instance.GetThreadNetif().IsUp() == false, error = OT_ERROR_INVALID_STATE); + + error = instance.GetThreadNetif().GetMac().SetEnabled(aEnable); + +exit: + return error; +} + +bool otLinkIsEnabled(otInstance *aInstance) +{ + Instance &instance = *static_cast(aInstance); + + return instance.GetThreadNetif().GetMac().IsEnabled(); +} + const otMacCounters *otLinkGetCounters(otInstance *aInstance) { Instance &instance = *static_cast(aInstance); diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index d1cff1412..c25eb377a 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -173,6 +173,7 @@ void Instance::Finalize(void) IgnoreReturnValue(otThreadSetEnabled(this, false)); IgnoreReturnValue(otIp6SetEnabled(this, false)); + IgnoreReturnValue(otLinkSetEnabled(this, false)); exit: return; diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index de9a634c2..cff81bb5d 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -194,6 +194,7 @@ Mac::Mac(Instance &aInstance) , mKeyIdMode2FrameCounter(0) , mCcaSuccessRateTracker() , mCcaSampleCount(0) + , mEnabled(true) { GenerateExtAddress(&mExtAddress); @@ -212,6 +213,7 @@ otError Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveSc { otError error = OT_ERROR_NONE; + VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE); VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = OT_ERROR_BUSY); mActiveScanHandler = aHandler; @@ -231,6 +233,7 @@ otError Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergySc { otError error = OT_ERROR_NONE; + VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE); VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = OT_ERROR_BUSY); mEnergyScanHandler = aHandler; @@ -309,7 +312,14 @@ exit: otError Mac::UpdateScanChannel(void) { - return mScanChannelMask.GetNextChannel(mScanChannel); + otError error; + + VerifyOrExit(mEnabled, error = OT_ERROR_ABORT); + + error = mScanChannelMask.GetNextChannel(mScanChannel); + +exit: + return error; } void Mac::PerformActiveScan(void) @@ -540,6 +550,7 @@ otError Mac::SendFrameRequest(Sender &aSender) { otError error = OT_ERROR_NONE; + VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE); VerifyOrExit(mSendTail != &aSender && aSender.mNext == NULL, error = OT_ERROR_ALREADY); if (mSendHead == NULL) @@ -563,6 +574,7 @@ otError Mac::SendOutOfBandFrameRequest(otRadioFrame *aOobFrame) { otError error = OT_ERROR_NONE; + VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE); VerifyOrExit(mOobFrame == NULL, error = OT_ERROR_ALREADY); mOobFrame = static_cast(aOobFrame); @@ -649,10 +661,21 @@ void Mac::PerformOperation(void) { VerifyOrExit(mOperation == kOperationIdle); + if (!mEnabled) + { + mPendingWaitingForData = false; + mPendingTransmitOobFrame = false; + mPendingActiveScan = false; + mPendingEnergyScan = false; + mPendingTransmitBeacon = false; + mPendingTransmitData = false; + mOobFrame = NULL; + ExitNow(); + } + // `WaitingForData` should be checked before any other pending // operations since radio should remain in receive mode after // a data poll ack indicating a pending frame from parent. - if (mPendingWaitingForData) { mPendingWaitingForData = false; @@ -999,6 +1022,8 @@ void Mac::BeginTransmit(void) bool applyTransmitSecurity = true; Frame & sendFrame(*GetOperationFrame()); + VerifyOrExit(mEnabled, error = OT_ERROR_ABORT); + #if OPENTHREAD_CONFIG_DISABLE_CCA_ON_LAST_ATTEMPT // Disable CCA for the last attempt @@ -1225,7 +1250,7 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr otDumpDebgMac(GetInstance(), "TX ERR", sendFrame.GetHeader(), 16); - if (!RadioSupportsRetries() && mTransmitAttempts < sendFrame.GetMaxTxAttempts()) + if (mEnabled && !RadioSupportsRetries() && mTransmitAttempts < sendFrame.GetMaxTxAttempts()) { mCounters.mTxRetry++; StartCsmaBackoff(); @@ -1301,7 +1326,7 @@ void Mac::HandleTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otEr if (sendFrame.IsDataRequestCommand()) { - if (framePending) + if (mEnabled && framePending) { mReceiveTimer.Start(kDataPollTimeout); StartOperation(kOperationWaitingForData); @@ -1684,6 +1709,7 @@ void Mac::HandleReceivedFrame(Frame *aFrame, otError aError) VerifyOrExit(error == OT_ERROR_NONE); VerifyOrExit(aFrame != NULL, error = OT_ERROR_NO_FRAME_RECEIVED); + VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE); aFrame->SetSecurityValid(false); @@ -1987,11 +2013,11 @@ otError Mac::HandleMacCommand(Frame &aFrame) mCounters.mRxBeaconRequest++; otLogInfoMac(GetInstance(), "Received Beacon Request"); - if (mBeaconsEnabled + if (mEnabled && (mBeaconsEnabled #if OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_WHEN_JOINABLE - || IsBeaconJoinable() + || IsBeaconJoinable() #endif // OPENTHREAD_CONFIG_ENABLE_BEACON_RSP_WHEN_JOINABLE - ) + )) { StartOperation(kOperationTransmitBeacon); } @@ -2045,6 +2071,13 @@ bool Mac::RadioSupportsRetries(void) return (otPlatRadioGetCaps(&GetInstance()) & OT_RADIO_CAPS_TRANSMIT_RETRIES) != 0; } +otError Mac::SetEnabled(bool aEnable) +{ + mEnabled = aEnable; + + return OT_ERROR_NONE; +} + void Mac::FillMacCountersTlv(NetworkDiagnostic::MacCountersTlv &aMacCounters) const { aMacCounters.SetIfInUnknownProtos(mCounters.mRxOther); diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index eceb96756..fc43a88b2 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -786,6 +786,25 @@ public: */ uint16_t GetCcaFailureRate(void) const { return mCcaSuccessRateTracker.GetFailureRate(); } + /** + * This method Starts/Stops the Link layer. It may only be used when the Netif Interface is down + * + * @param[in] aEnable The requested State for the MAC layer. true - Start, false - Stop. + * + * @retval OT_ERROR_NONE The operation succeeded or the new State equals the current State. + * + */ + otError SetEnabled(bool aEnable); + + /** + * This method indicates whether or not the link layer is enabled. + * + * @retval true Link layer is enabled. + * @retval false Link layer is not enabled. + * + */ + bool IsEnabled(void) { return mEnabled; } + private: enum { @@ -924,6 +943,7 @@ private: SuccessRateTracker mCcaSuccessRateTracker; uint16_t mCcaSampleCount; + bool mEnabled; }; /** diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 9e6df942e..5230fb4dd 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -111,6 +111,8 @@ otError ThreadNetif::Up(void) { if (!mIsUp) { + // Enable the MAC just in case it was disabled while the Interface was down. + mMac.SetEnabled(true); GetIp6().AddNetif(*this); mMeshForwarder.Start(); mCoap.Start(kCoapUdpPort);