From e4f5aa89d0eda61d096ad98186eeacb73e101f69 Mon Sep 17 00:00:00 2001 From: pvanhorn Date: Tue, 1 May 2018 11:18:34 -0700 Subject: [PATCH] [mac] add start/stop the MAC according to the state of the netif (#2688) To ensure proper and expedient shutdown of Openthread when the Interface is brought down, this change provides a mechanism to gracefully stop the MAC layer operations. Scans are stopped by preventing any new channel change. New Transmissions are stopped. Existing transmissions are allowed to complete. Any pending operations are cleared. --- include/openthread/link.h | 26 ++++++++++++++++++ src/core/api/link_api.cpp | 21 ++++++++++++++ src/core/common/instance.cpp | 1 + src/core/mac/mac.cpp | 47 +++++++++++++++++++++++++++----- src/core/mac/mac.hpp | 20 ++++++++++++++ src/core/thread/thread_netif.cpp | 2 ++ 6 files changed, 110 insertions(+), 7 deletions(-) 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);