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()