mirror of
https://github.com/espressif/openthread.git
synced 2026-09-15 21:50:08 +00:00
[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.
This commit is contained in:
@@ -520,7 +520,7 @@ exit:
|
||||
|
||||
uint32_t otLinkCslGetTimeout(otInstance *aInstance)
|
||||
{
|
||||
return static_cast<Instance *>(aInstance)->Get<Mac::Mac>().GetCslTimeout();
|
||||
return static_cast<Instance *>(aInstance)->Get<Mle::MleRouter>().GetCslTimeout();
|
||||
}
|
||||
|
||||
otError otLinkCslSetTimeout(otInstance *aInstance, uint32_t aTimeout)
|
||||
@@ -529,7 +529,7 @@ otError otLinkCslSetTimeout(otInstance *aInstance, uint32_t aTimeout)
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
VerifyOrExit(kMaxCslTimeout >= aTimeout, error = kErrorInvalidArgs);
|
||||
instance.Get<Mac::Mac>().SetCslTimeout(aTimeout);
|
||||
instance.Get<Mle::MleRouter>().SetCslTimeout(aTimeout);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
|
||||
@@ -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<Mle::MleRouter>().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<Mle::MleRouter>().GetTimeout()) -
|
||||
static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
|
||||
uint32_t period = Time::SecToMsec(Get<Mle::MleRouter>().GetTimeout());
|
||||
uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
if (Get<Mac::Mac>().IsCslEnabled())
|
||||
{
|
||||
period = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().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<MeshForwarder>().CalcIePresent(nullptr);
|
||||
|
||||
if (iePresent)
|
||||
{
|
||||
fcf |= Mac::Frame::kFcfIePresent;
|
||||
}
|
||||
|
||||
fcf |= Get<MeshForwarder>().CalcFrameVersion(Get<NeighborTable>().FindNeighbor(dst), iePresent);
|
||||
|
||||
if (dst.IsExtended())
|
||||
{
|
||||
fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt;
|
||||
src.SetExtended(Get<Mac::Mac>().GetExtAddress());
|
||||
}
|
||||
else
|
||||
{
|
||||
fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort;
|
||||
src.SetShort(Get<Mac::Mac>().GetShortAddress());
|
||||
}
|
||||
|
||||
frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
|
||||
|
||||
if (frame->IsDstPanIdPresent())
|
||||
{
|
||||
frame->SetDstPanId(Get<Mac::Mac>().GetPanId());
|
||||
}
|
||||
|
||||
frame->SetSrcAddr(src);
|
||||
frame->SetDstAddr(dst);
|
||||
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
|
||||
if (iePresent)
|
||||
{
|
||||
Get<MeshForwarder>().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
|
||||
|
||||
@@ -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;
|
||||
|
||||
+8
-84
@@ -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<MeshForwarder>().CalcIePresent(nullptr);
|
||||
|
||||
#if OPENTHREAD_CONFIG_MULTI_RADIO
|
||||
RadioType radio;
|
||||
|
||||
SuccessOrExit(Get<DataPollSender>().GetPollDestinationAddress(dst, radio));
|
||||
frame = &mLinks.GetTxFrames().GetTxFrame(radio);
|
||||
#else
|
||||
SuccessOrExit(Get<DataPollSender>().GetPollDestinationAddress(dst));
|
||||
frame = &mLinks.GetTxFrames().GetTxFrame();
|
||||
#endif
|
||||
|
||||
fcf = Frame::kFcfFrameMacCmd | Frame::kFcfPanidCompression | Frame::kFcfAckRequest | Frame::kFcfSecurityEnabled;
|
||||
|
||||
if (iePresent)
|
||||
{
|
||||
fcf |= Frame::kFcfIePresent;
|
||||
}
|
||||
|
||||
fcf |= Get<MeshForwarder>().CalcFrameVersion(Get<NeighborTable>().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<MeshForwarder>().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<DataPollSender>().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<MeshForwarder>().HandleSentFrame(aFrame, aError);
|
||||
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
||||
if (aError == kErrorNone && Get<Mle::Mle>().GetParent().IsEnhancedKeepAliveSupported() &&
|
||||
aFrame.GetSecurityEnabled() && aAckFrame != nullptr)
|
||||
{
|
||||
Get<DataPollSender>().ProcessFrame(*aAckFrame);
|
||||
}
|
||||
Get<DataPollSender>().ProcessTxDone(aFrame, aAckFrame, aError);
|
||||
#endif
|
||||
PerformNextOperation();
|
||||
break;
|
||||
@@ -2112,7 +2049,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError)
|
||||
}
|
||||
#endif
|
||||
|
||||
Get<DataPollSender>().ProcessFrame(*aFrame);
|
||||
Get<DataPollSender>().ProcessRxFrame(*aFrame);
|
||||
|
||||
if (neighbor != nullptr)
|
||||
{
|
||||
@@ -2521,6 +2458,8 @@ void Mac::SetCslPeriod(uint16_t aPeriod)
|
||||
{
|
||||
mLinks.GetSubMac().SetCslPeriod(aPeriod);
|
||||
|
||||
Get<DataPollSender>().RecalculatePollPeriod();
|
||||
|
||||
if (IsCslEnabled())
|
||||
{
|
||||
IgnoreError(Get<Radio>().EnableCsl(GetCslPeriod(), &Get<Mle::Mle>().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<Mle::Mle>().ScheduleChildUpdateRequest();
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
bool Mac::IsCslEnabled(void) const
|
||||
{
|
||||
return (GetCslPeriod() > 0) && !GetRxOnWhenIdle() && Get<Mle::MleRouter>().IsChild() &&
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<SubMac>().HandleCslTimer();
|
||||
|
||||
+17
-35
@@ -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`.
|
||||
|
||||
@@ -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<Mac::Mac>().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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
+21
-2
@@ -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<Mac::Mac>().IsCslEnabled());
|
||||
return Tlv::Append<CslTimeoutTlv>(aMessage, Get<Mac::Mac>().GetCslTimeout() == 0 ? mTimeout
|
||||
: Get<Mac::Mac>().GetCslTimeout());
|
||||
return Tlv::Append<CslTimeoutTlv>(aMessage, mCslTimeout == 0 ? mTimeout : mCslTimeout);
|
||||
}
|
||||
|
||||
void Mle::SetCslTimeout(uint32_t aTimeout)
|
||||
{
|
||||
VerifyOrExit(mCslTimeout != aTimeout);
|
||||
|
||||
mCslTimeout = aTimeout;
|
||||
|
||||
Get<DataPollSender>().RecalculatePollPeriod();
|
||||
|
||||
if (Get<Mac::Mac>().IsCslEnabled())
|
||||
{
|
||||
ScheduleChildUpdateRequest();
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user