From d514373832c0ed254cebd22507eaeae50de769b6 Mon Sep 17 00:00:00 2001 From: Jintao Lin Date: Wed, 14 Apr 2021 04:02:00 +0800 Subject: [PATCH] [low-power] resync SSED with its parent after retransmission (#6342) Per 15.4 spec, transmitter cannot modify frame content in retransmission which will leads to out-of-date CSL IE being sent to CSL transmitter. This commit fixes this issue by, 1. Send a follow up data poll with CSL IE to resync the CSL transmitter. 2. Send periodic data poll with CSL IE to keep SSED's parent in sync. 3. Move PrepareDataRequest to data_poll_sender. 4. Add test cases to cover. --- src/core/api/link_api.cpp | 4 +- src/core/mac/data_poll_sender.cpp | 142 ++++++++++++++++-- src/core/mac/data_poll_sender.hpp | 69 +++++---- src/core/mac/mac.cpp | 92 +----------- src/core/mac/mac.hpp | 17 --- src/core/mac/sub_mac.cpp | 6 - src/core/mac/sub_mac.hpp | 52 +++---- src/core/thread/mesh_forwarder.cpp | 4 +- src/core/thread/mesh_forwarder.hpp | 2 +- src/core/thread/mle.cpp | 23 ++- src/core/thread/mle.hpp | 21 +++ .../thread-cert/v1_2_test_csl_transmission.py | 25 +++ 12 files changed, 268 insertions(+), 189 deletions(-) diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index 67d019a53..b5063fe68 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -520,7 +520,7 @@ exit: uint32_t otLinkCslGetTimeout(otInstance *aInstance) { - return static_cast(aInstance)->Get().GetCslTimeout(); + return static_cast(aInstance)->Get().GetCslTimeout(); } otError otLinkCslSetTimeout(otInstance *aInstance, uint32_t aTimeout) @@ -529,7 +529,7 @@ otError otLinkCslSetTimeout(otInstance *aInstance, uint32_t aTimeout) Instance &instance = *static_cast(aInstance); VerifyOrExit(kMaxCslTimeout >= aTimeout, error = kErrorInvalidArgs); - instance.Get().SetCslTimeout(aTimeout); + instance.Get().SetCslTimeout(aTimeout); exit: return error; diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index b1f3176db..674f2a13c 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -262,7 +262,12 @@ void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError) otLogInfoMac("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter, kMaxPollRetxAttempts); +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (mPollTxFailureCounter < + ((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts)) +#else if (mPollTxFailureCounter < kMaxPollRetxAttempts) +#endif { if (!mRetxMode) { @@ -314,7 +319,7 @@ exit: return; } -void DataPollSender::ProcessFrame(const Mac::RxFrame &aFrame) +void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame) { VerifyOrExit(mEnabled); @@ -324,17 +329,52 @@ void DataPollSender::ProcessFrame(const Mac::RxFrame &aFrame) { IgnoreError(SendDataPoll()); } -#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 - else if (aFrame.IsAck()) - { - ResetKeepAliveTimer(); - } -#endif exit: return; } +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 +void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError) +{ + bool sendDataPoll = false; + + VerifyOrExit(mEnabled); + VerifyOrExit(Get().GetParent().IsEnhancedKeepAliveSupported()); + VerifyOrExit(aFrame.GetSecurityEnabled()); + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (aFrame.mInfo.mTxInfo.mIsARetx && (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)) + { + // For retransmission frame, use a data poll to resync its parent with correct CSL phase + sendDataPoll = true; + } +#endif + + if (aError == kErrorNone && aAckFrame != nullptr) + { + mPollTimeoutCounter = 0; + + if (aAckFrame->GetFramePending()) + { + sendDataPoll = true; + } + else + { + ResetKeepAliveTimer(); + } + } + + if (sendDataPoll) + { + IgnoreError(SendDataPoll()); + } + +exit: + return; +} +#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + void DataPollSender::RecalculatePollPeriod(void) { if (mEnabled) @@ -496,8 +536,92 @@ void DataPollSender::HandlePollTimer(Timer &aTimer) uint32_t DataPollSender::GetDefaultPollPeriod(void) const { - return Time::SecToMsec(Get().GetTimeout()) - - static_cast(kRetxPollPeriod) * kMaxPollRetxAttempts; + uint32_t period = Time::SecToMsec(Get().GetTimeout()); + uint32_t pollAhead = static_cast(kRetxPollPeriod) * kMaxPollRetxAttempts; + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (Get().IsCslEnabled()) + { + period = OT_MIN(period, Time::SecToMsec(Get().GetCslTimeout())); + } +#endif + + if (period > pollAhead) + { + period -= pollAhead; + } + + return period; +} + +Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames) +{ + Mac::TxFrame *frame = nullptr; + Mac::Address src, dst; + uint16_t fcf; + bool iePresent; + +#if OPENTHREAD_CONFIG_MULTI_RADIO + Mac::RadioType radio; + + SuccessOrExit(GetPollDestinationAddress(dst, radio)); + frame = &aTxFrames.GetTxFrame(radio); +#else + SuccessOrExit(GetPollDestinationAddress(dst)); + frame = &aTxFrames.GetTxFrame(); +#endif + + fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfAckRequest | + Mac::Frame::kFcfSecurityEnabled; + + iePresent = Get().CalcIePresent(nullptr); + + if (iePresent) + { + fcf |= Mac::Frame::kFcfIePresent; + } + + fcf |= Get().CalcFrameVersion(Get().FindNeighbor(dst), iePresent); + + if (dst.IsExtended()) + { + fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt; + src.SetExtended(Get().GetExtAddress()); + } + else + { + fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort; + src.SetShort(Get().GetShortAddress()); + } + + frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32); + + if (frame->IsDstPanIdPresent()) + { + frame->SetDstPanId(Get().GetPanId()); + } + + frame->SetSrcAddr(src); + frame->SetDstAddr(dst); +#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT + if (iePresent) + { + Get().AppendHeaderIe(nullptr, *frame); + } + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) + { + // Disable frame retransmission when the data poll has CSL IE included + aTxFrames.SetMaxFrameRetries(0); + } +#endif +#endif + + IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest)); + +exit: + return frame; } } // namespace ot diff --git a/src/core/mac/data_poll_sender.hpp b/src/core/mac/data_poll_sender.hpp index 28f7c2111..33991f8d6 100644 --- a/src/core/mac/data_poll_sender.hpp +++ b/src/core/mac/data_poll_sender.hpp @@ -40,6 +40,7 @@ #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/timer.hpp" +#include "mac/mac.hpp" #include "mac/mac_frame.hpp" #include "thread/topology.hpp" @@ -130,31 +131,6 @@ public: */ uint32_t GetExternalPollPeriod(void) const { return mExternalPollPeriod; } -#if OPENTHREAD_CONFIG_MULTI_RADIO - /** - * This method gets the destination MAC address for a data poll frame. - * - * @param[out] aDest Reference to a `MAC::Address` to output the poll destination address (on success). - * @param[out] aRadioType Reference to a `Mac::RadioType` to output the link type (on success). - * - * @retval kErrorNone @p aDest and @p aRadioType were updated successfully. - * @retval kErrorAbort Abort the data poll transmission (not currently attached to any parent). - * - */ - Error GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const; -#else - /** - * This method gets the destination MAC address for a data poll frame. - * - * @param[out] aDest Reference to a `MAC::Address` to output the poll destination address (on success). - * - * @retval kErrorNone @p aDest was updated successfully. - * @retval kErrorAbort Abort the data poll transmission (not currently attached to any parent). - * - */ - Error GetPollDestinationAddress(Mac::Address &aDest) const; -#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO - /** * This method informs the data poll sender of success/error status of a previously requested poll frame * transmission. @@ -178,16 +154,27 @@ public: void HandlePollTimeout(void); /** - * This method informs the data poll sender to process a MAC frame. + * This method informs the data poll sender to process a received MAC frame. * - * 1. Data Frame: send an immediate data poll if "frame pending" is set. - * 2. Ack Frame for a secured data frame in version 1.2 or newer: send an immediate data poll if - * "frame pending" is set, otherwise reset the keep-alive timer for sending next poll. - * - * @param[in] aFrame The frame to process. + * @param[in] aFrame A reference to the received frame to process. * */ - void ProcessFrame(const Mac::RxFrame &aFrame); + void ProcessRxFrame(const Mac::RxFrame &aFrame); + +#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + /** + * This method informs the data poll sender to process a transmitted MAC frame. + * + * @param[in] aFrame A reference to the frame that was transmitted. + * @param[in] aAckFrame A pointer to the ACK frame, nullptr if no ACK was received. + * @param[in] aError kErrorNone when the frame was transmitted successfully, + * kErrorNoAck when the frame was transmitted but no ACK was received, + * kErrorChannelAccessFailure when the tx failed due to activity on the channel, + * kErrorAbort when transmission was aborted for other reasons. + * + */ + void ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError); +#endif /** * This method asks the data poll sender to recalculate the poll period. @@ -261,12 +248,25 @@ public: */ uint32_t GetDefaultPollPeriod(void) const; + /** + * This method prepares and returns a data request command frame. + * + * @param[in] aTxFrames The set of TxFrames for all radio links. + * + * @returns The data poll frame. + * + */ + Mac::TxFrame *PrepareDataRequest(Mac::TxFrames &aTxFrames); + private: enum { kQuickPollsAfterTimeout = 5, ///< Maximum number of quick data poll tx in case of back-to-back poll timeouts. kMaxPollRetxAttempts = OPENTHREAD_CONFIG_FAILED_CHILD_TRANSMISSIONS, ///< Maximum number of retransmit attempts ///< of data poll (mac data request). + kMaxCslPollRetxAttempts = + OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT, ///< Maximum number of retransmit attempts of data + ///< poll with CSL IE (mac data request). }; enum PollPeriodSelector @@ -286,6 +286,11 @@ private: uint32_t CalculatePollPeriod(void) const; const Neighbor &GetParent(void) const; static void HandlePollTimer(Timer &aTimer); +#if OPENTHREAD_CONFIG_MULTI_RADIO + Error GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const; +#else + Error GetPollDestinationAddress(Mac::Address &aDest) const; +#endif TimeMilli mTimerStartTime; uint32_t mPollPeriod; diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 6176258ed..43d9d02f9 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -897,65 +897,6 @@ void Mac::FinishOperation(void) mOperation = kOperationIdle; } -TxFrame *Mac::PrepareDataRequest(void) -{ - TxFrame *frame = nullptr; - Address src, dst; - uint16_t fcf; - bool iePresent = Get().CalcIePresent(nullptr); - -#if OPENTHREAD_CONFIG_MULTI_RADIO - RadioType radio; - - SuccessOrExit(Get().GetPollDestinationAddress(dst, radio)); - frame = &mLinks.GetTxFrames().GetTxFrame(radio); -#else - SuccessOrExit(Get().GetPollDestinationAddress(dst)); - frame = &mLinks.GetTxFrames().GetTxFrame(); -#endif - - fcf = Frame::kFcfFrameMacCmd | Frame::kFcfPanidCompression | Frame::kFcfAckRequest | Frame::kFcfSecurityEnabled; - - if (iePresent) - { - fcf |= Frame::kFcfIePresent; - } - - fcf |= Get().CalcFrameVersion(Get().FindNeighbor(dst), iePresent); - - if (dst.IsExtended()) - { - fcf |= Frame::kFcfDstAddrExt | Frame::kFcfSrcAddrExt; - src.SetExtended(GetExtAddress()); - } - else - { - fcf |= Frame::kFcfDstAddrShort | Frame::kFcfSrcAddrShort; - src.SetShort(GetShortAddress()); - } - - frame->InitMacHeader(fcf, Frame::kKeyIdMode1 | Frame::kSecEncMic32); - - if (frame->IsDstPanIdPresent()) - { - frame->SetDstPanId(GetPanId()); - } - - frame->SetSrcAddr(src); - frame->SetDstAddr(dst); -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (iePresent) - { - Get().AppendHeaderIe(nullptr, *frame); - } -#endif - - IgnoreError(frame->SetCommandId(Frame::kMacCmdDataRequest)); - -exit: - return frame; -} - TxFrame *Mac::PrepareBeaconRequest(void) { TxFrame &frame = mLinks.GetTxFrames().GetBroadcastTxFrame(); @@ -1175,12 +1116,12 @@ void Mac::BeginTransmit(void) break; case kOperationTransmitPoll: - frame = PrepareDataRequest(); + txFrames.SetChannel(mRadioChannel); + txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); + txFrames.SetMaxFrameRetries(mMaxFrameRetriesDirect); + frame = Get().PrepareDataRequest(txFrames); VerifyOrExit(frame != nullptr); - frame->SetChannel(mRadioChannel); frame->SetSequence(mDataSequence++); - frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); - frame->SetMaxFrameRetries(mMaxFrameRetriesDirect); break; case kOperationTransmitDataDirect: @@ -1632,11 +1573,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) FinishOperation(); Get().HandleSentFrame(aFrame, aError); #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 - if (aError == kErrorNone && Get().GetParent().IsEnhancedKeepAliveSupported() && - aFrame.GetSecurityEnabled() && aAckFrame != nullptr) - { - Get().ProcessFrame(*aAckFrame); - } + Get().ProcessTxDone(aFrame, aAckFrame, aError); #endif PerformNextOperation(); break; @@ -2112,7 +2049,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) } #endif - Get().ProcessFrame(*aFrame); + Get().ProcessRxFrame(*aFrame); if (neighbor != nullptr) { @@ -2521,6 +2458,8 @@ void Mac::SetCslPeriod(uint16_t aPeriod) { mLinks.GetSubMac().SetCslPeriod(aPeriod); + Get().RecalculatePollPeriod(); + if (IsCslEnabled()) { IgnoreError(Get().EnableCsl(GetCslPeriod(), &Get().GetParent().GetExtAddress())); @@ -2530,21 +2469,6 @@ void Mac::SetCslPeriod(uint16_t aPeriod) UpdateIdleMode(); } -void Mac::SetCslTimeout(uint32_t aTimeout) -{ - VerifyOrExit(GetCslTimeout() != aTimeout); - - mLinks.GetSubMac().SetCslTimeout(aTimeout); - - if (IsCslEnabled()) - { - Get().ScheduleChildUpdateRequest(); - } - -exit: - return; -} - bool Mac::IsCslEnabled(void) const { return (GetCslPeriod() > 0) && !GetRxOnWhenIdle() && Get().IsChild() && diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index b5a62077f..78a69d179 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -720,22 +720,6 @@ public: */ void SetCslPeriod(uint16_t aPeriod); - /** - * This method gets the CSL timeout. - * - * @returns CSL timeout in seconds. - * - */ - uint32_t GetCslTimeout(void) const { return mLinks.GetSubMac().GetCslTimeout(); } - - /** - * This method sets the CSL timeout. - * - * @param[in] aTimeout The CSL timeout in seconds. - * - */ - void SetCslTimeout(uint32_t aTimeout); - /** * This method indicates whether CSL is started at the moment. * @@ -805,7 +789,6 @@ private: void StartOperation(Operation aOperation); void FinishOperation(void); void PerformNextOperation(void); - TxFrame *PrepareDataRequest(void); TxFrame *PrepareBeaconRequest(void); TxFrame *PrepareBeacon(void); bool ShouldSendBeacon(void) const; diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index e77adfb41..ce71c8a17 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -67,7 +67,6 @@ SubMac::SubMac(Instance &aInstance) , mKeyId(0) , mTimer(aInstance, SubMac::HandleTimer) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - , mCslTimeout(OPENTHREAD_CONFIG_CSL_TIMEOUT) , mCslPeriod(0) , mCslChannel(0) , mIsCslChannelSpecified(false) @@ -972,11 +971,6 @@ exit: return; } -void SubMac::SetCslTimeout(uint32_t aTimeout) -{ - mCslTimeout = aTimeout; -} - void SubMac::HandleCslTimer(Timer &aTimer) { aTimer.Get().HandleCslTimer(); diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 48352d1a8..dabbe87a5 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -319,25 +319,6 @@ public: */ Error Receive(uint8_t aChannel); -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - /** - * This method lets `SubMac` start CSL sample. - * - * `SubMac` would switch the radio state between `Receive` and `Sleep` according the CSL timer. When CslSample is - * started, `mState` will become `kStateCslSample`. But it could be doing `Sleep` or `Receive` at this moment - * (depending on `mCslState`). - * - * @param[in] aPanChannel The current phy channel used by the device. This param will only take effect when CSL - * channel hasn't been explicitly specified. - * - * @retval kErrorNone Successfully entered CSL operation (sleep or receive according to CSL timer). - * @retval kErrorBusy The radio was transmitting. - * @retval kErrorInvalidState The radio was disabled. - * - */ - Error CslSample(uint8_t aPanChannel); -#endif - /** * This method gets the radio transmit frame. * @@ -398,6 +379,23 @@ public: #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * This method lets `SubMac` start CSL sample. + * + * `SubMac` would switch the radio state between `Receive` and `Sleep` according the CSL timer. When CslSample is + * started, `mState` will become `kStateCslSample`. But it could be doing `Sleep` or `Receive` at this moment + * (depending on `mCslState`). + * + * @param[in] aPanChannel The current phy channel used by the device. This param will only take effect when CSL + * channel hasn't been explicitly specified. + * + * @retval kErrorNone Successfully entered CSL operation (sleep or receive according to CSL timer). + * @retval kErrorBusy The radio was transmitting. + * @retval kErrorInvalidState The radio was disabled. + * + */ + Error CslSample(uint8_t aPanChannel); + /** * This method gets the CSL channel. * @@ -444,21 +442,6 @@ public: */ void SetCslPeriod(uint16_t aPeriod); - /** - * This method gets the CSL timeout. - * - * @returns CSL timeout - * - */ - uint32_t GetCslTimeout(void) const { return mCslTimeout; } - - /** - * This method sets the CSL timeout. - * - * @param[in] aTimeout The CSL timeout in seconds. - * - */ - void SetCslTimeout(uint32_t aTimeout); #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** @@ -643,7 +626,6 @@ private: #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds. uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds). uint8_t mCslChannel : 7; ///< The actually CSL sample channel. If `mIsCslChannelSpecified` is 0, this should be ///< equal to the Pan channel of `Mac`. diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index f56b1ad0c..4e39c73be 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -1536,18 +1536,20 @@ bool MeshForwarder::CalcIePresent(const Message *aMessage) OT_UNUSED_VARIABLE(aMessage); +#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE iePresent |= (aMessage != nullptr && aMessage->IsTimeSync()); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE iePresent |= Get().IsCslEnabled(); +#endif #endif return iePresent; } #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT -void MeshForwarder::AppendHeaderIe(const Message *aMessage, Mac::Frame &aFrame) +void MeshForwarder::AppendHeaderIe(const Message *aMessage, Mac::TxFrame &aFrame) { uint8_t index = 0; bool iePresent = false; diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index beed901bf..3aa543737 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -482,7 +482,7 @@ private: bool CalcIePresent(const Message *aMessage); uint16_t CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent); #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - void AppendHeaderIe(const Message *aMessage, Mac::Frame &aFrame); + void AppendHeaderIe(const Message *aMessage, Mac::TxFrame &aFrame); #endif void PauseMessageTransmissions(void) { mTxPaused = true; } diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 66394717d..4967c4208 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -93,6 +93,9 @@ Mle::Mle(Instance &aInstance) , mReceivedResponseFromParent(false) , mSocket(aInstance) , mTimeout(kMleEndDeviceTimeout) +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + , mCslTimeout(OPENTHREAD_CONFIG_CSL_TIMEOUT) +#endif #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH , mPreviousParentRloc(Mac::kShortAddrInvalid) #endif @@ -1451,8 +1454,24 @@ exit: Error Mle::AppendCslTimeout(Message &aMessage) { OT_ASSERT(Get().IsCslEnabled()); - return Tlv::Append(aMessage, Get().GetCslTimeout() == 0 ? mTimeout - : Get().GetCslTimeout()); + return Tlv::Append(aMessage, mCslTimeout == 0 ? mTimeout : mCslTimeout); +} + +void Mle::SetCslTimeout(uint32_t aTimeout) +{ + VerifyOrExit(mCslTimeout != aTimeout); + + mCslTimeout = aTimeout; + + Get().RecalculatePollPeriod(); + + if (Get().IsCslEnabled()) + { + ScheduleChildUpdateRequest(); + } + +exit: + return; } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 0f50e8fd5..69d20df2e 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -714,6 +714,24 @@ public: */ bool HasRestored(void) const { return mHasRestored; } +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * This method gets the CSL timeout. + * + * @returns CSL timeout + * + */ + uint32_t GetCslTimeout(void) const { return mCslTimeout; } + + /** + * This method sets the CSL timeout. + * + * @param[in] aTimeout The CSL timeout in seconds. + * + */ + void SetCslTimeout(uint32_t aTimeout); +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + protected: /** * MLE Command Types. @@ -1815,6 +1833,9 @@ private: Ip6::Udp::Socket mSocket; uint32_t mTimeout; +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + uint32_t mCslTimeout; +#endif #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH uint16_t mPreviousParentRloc; diff --git a/tests/scripts/thread-cert/v1_2_test_csl_transmission.py b/tests/scripts/thread-cert/v1_2_test_csl_transmission.py index f0c46e4e9..9d10986d6 100755 --- a/tests/scripts/thread-cert/v1_2_test_csl_transmission.py +++ b/tests/scripts/thread-cert/v1_2_test_csl_transmission.py @@ -101,6 +101,31 @@ class SSED_CslTransmission(thread_cert.TestCase): self.nodes[SSED_1].set_pollperiod(0) self.simulator.go(5) + # Check if data poll is not sent if data packet with CSL IE is sent to parent + self.nodes[SSED_1].set_csl_period(consts.CSL_DEFAULT_PERIOD) + self.simulator.go(consts.CSL_DEFAULT_TIMEOUT / 2) + self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED_1].get_rloc(), timeout=timeout)) + self.flush_all() + self.simulator.go(consts.CSL_DEFAULT_TIMEOUT / 2) + ssed_messages = self.simulator.get_messages_sent_by(SSED_1) + self.assertIsNone(ssed_messages.next_data_poll()) + + # Check if data poll is used to sync SSED's parent before CSL timeout + self.assertTrue(self.nodes[LEADER].ping(self.nodes[SSED_1].get_rloc(), timeout=timeout)) + self.flush_all() + self.simulator.go(consts.CSL_DEFAULT_TIMEOUT) + ssed_messages = self.simulator.get_messages_sent_by(SSED_1) + self.assertIsNotNone(ssed_messages.next_data_poll()) + + # Check if data poll is used to resync SSED's parent after retransmission + leader_rloc = self.nodes[LEADER].get_rloc() + self.nodes[LEADER].stop() + self.flush_all() + self.assertFalse(self.nodes[SSED_1].ping(leader_rloc, timeout=timeout)) + self.simulator.go(2) + ssed_messages = self.simulator.get_messages_sent_by(SSED_1) + self.assertIsNotNone(ssed_messages.next_data_poll()) + if __name__ == '__main__': unittest.main()