[sub-mac] WED performs periodic sampling by calling Sleep() and Receive() (#11318)

The WED listener only supports calling the `Radio::ReceiveAt()` for
periodic sampling.

This commit adds support for the WED to perform periodic sampling by calling
`Radio::Sleep()` and `Radio::Receive()`.
This commit is contained in:
Zhanglong Xia
2025-03-27 09:10:37 -07:00
committed by GitHub
parent 9398342b49
commit 8d41a1d124
4 changed files with 173 additions and 54 deletions
+104 -11
View File
@@ -240,10 +240,10 @@ Error SubMac::Sleep(void)
{
Error error = kErrorNone;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (IsCslEnabled())
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
if (IsRadioSampleEnabled())
{
CslSample();
RadioSample();
}
else
#endif
@@ -343,8 +343,8 @@ Error SubMac::Send(void)
#endif
case kStateSleep:
case kStateReceive:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
case kStateCslSample:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
case kStateRadioSample:
#endif
break;
@@ -721,8 +721,8 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
case kStateReceive:
case kStateSleep:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
case kStateCslSample:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
case kStateRadioSample:
#endif
break;
}
@@ -1029,6 +1029,99 @@ void SubMac::StartTimerAt(Time aStartTime, uint32_t aDelayUs)
#endif
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
void SubMac::RadioSample(void)
{
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
VerifyOrExit(!mRadioFilterEnabled, IgnoreError(Get<Radio>().Sleep()));
#endif
SetState(kStateRadioSample);
if (!RadioSupportsReceiveTiming())
{
UpdateRadioSampleState();
}
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
exit:
#endif
return;
}
bool SubMac::IsRadioSampleEnabled(void) const
{
bool ret = false;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
ret = IsCslEnabled();
#endif
#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
ret = ret || mIsWedEnabled;
#endif
return ret;
}
/*
* The radio state (receive/sleep) is determined by the request from both CSL and WED:
* 1. If both CSL and WED request to enter sleep state, the radio is set to sleep state.
* 2. If either CSL or WED requests to enter the receive state and the other requests to enter sleep state, the radio
* is set to receive state using the channel that is requested to enter the receive state.
* 3. If both CSL and WED request to enter the receive state, the radio is set to the receive state using the CSL
* channel.
*
* The diagram below illustrates how to set the radio state based on the request of WED and CSL.
*
* CSL ------========------------========------------========------------========---
* ^ ^
* | |
* | mIsCslSampling=false
* mIsCslSampling=true
*
* WED -----------++++++++----------------++++++++----------------++++++++----------
* ^ ^
* | |
* | mIsWedSampling=false
* mIsWedSampling=true
*
* Radio ------========+++++-------========-++++++++---========-----+++++++========---
* ^ ^ ^
* | | |
* | | Radio::Sleep()
* | Radio::Receive(WedCh)
* Radio::Receive(CslCh)
*/
void SubMac::UpdateRadioSampleState(void)
{
VerifyOrExit(mState == kStateRadioSample);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (mIsCslSampling)
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
ExitNow();
}
#endif
#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
if (mIsWedSampling)
{
IgnoreError(Get<Radio>().Receive(mWakeupChannel));
ExitNow();
}
#endif
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
IgnoreError(Get<Radio>().Sleep()); // Don't actually sleep for debugging
#endif
exit:
return;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
// LCOV_EXCL_START
const char *SubMac::StateToString(State aState)
@@ -1046,8 +1139,8 @@ const char *SubMac::StateToString(State aState)
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
"CslTransmit", // (7) kStateCslTransmit
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
"CslSample", // (8) kStateCslSample
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
"RadioSample", // (8) kStateRadioSample
#endif
};
@@ -1067,8 +1160,8 @@ const char *SubMac::StateToString(State aState)
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
ValidateNextEnum(kStateCslTransmit);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
ValidateNextEnum(kStateCslSample);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
ValidateNextEnum(kStateRadioSample);
#endif
};
+16 -5
View File
@@ -501,7 +501,6 @@ public:
private:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void CslInit(void);
void CslSample(void);
void UpdateCslLastSyncTimestamp(TxFrame &aFrame, RxFrame *aAckFrame);
void UpdateCslLastSyncTimestamp(RxFrame *aFrame, Error aError);
static void HandleCslTimer(Timer &aTimer);
@@ -520,6 +519,8 @@ private:
void WedInit(void);
static void HandleWedTimer(Timer &aTimer);
void HandleWedTimer(void);
void HandleWedReceiveAt(void);
void HandleWedReceiveOrSleep(void);
#endif
static constexpr uint8_t kCsmaMinBe = 3; // macMinBE (IEEE 802.15.4-2006).
@@ -553,8 +554,9 @@ private:
#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
kStateCslTransmit, // CSL transmission.
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
kStateCslSample, // CSL receive.
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
kStateRadioSample, // Mac layer has requested the SubMac to enter sleep state, but the SubMac is in the periodic
// sample state.
#endif
};
@@ -628,6 +630,12 @@ private:
void SetState(State aState);
static const char *StateToString(State aState);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
bool IsRadioSampleEnabled(void) const;
void UpdateRadioSampleState(void);
void RadioSample(void);
#endif
using SubMacTimer =
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
TimerMicroIn<SubMac, &SubMac::HandleTimer>;
@@ -664,8 +672,8 @@ private:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
uint16_t mCslPeriod; // The CSL sample period, in units of 10 symbols (160 microseconds).
uint8_t mCslChannel : 7; // The CSL sample channel.
bool mIsCslSampling : 1; // Indicates that the radio is receiving in CSL state for platforms not supporting
// delayed reception.
bool mIsCslSampling : 1; // Indicates that the current time is in CSL sample window
// for platforms not supporting `Radio::ReceiveAt()`.
uint16_t mCslPeerShort; // The CSL peer short address.
uint32_t mCslSampleTimeRadio; // The CSL sample time of the current period based on radio time (lower 32-bit).
TimeMicro mCslSampleTimeLocal; // The CSL sample time of the current period based on local time.
@@ -675,6 +683,9 @@ private:
#endif
#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
bool mIsWedSampling : 1; // Indicates that the current time is in WED's sample window
// for platforms not supporting `Radio::ReceiveAt()`.
bool mIsWedEnabled : 1; // Indicates if the WED is enabled.
uint32_t mWakeupListenInterval; // The wake-up listen interval, in microseconds.
uint32_t mWakeupListenDuration; // The wake-up listen duration, in microseconds.
uint8_t mWakeupChannel; // The wake-up sample channel.
+12 -33
View File
@@ -86,28 +86,6 @@ exit:
return;
}
void SubMac::CslSample(void)
{
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
VerifyOrExit(!mRadioFilterEnabled, IgnoreError(Get<Radio>().Sleep()));
#endif
SetState(kStateCslSample);
if (mIsCslSampling && !RadioSupportsReceiveTiming())
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
ExitNow();
}
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
IgnoreError(Get<Radio>().Sleep()); // Don't actually sleep for debugging
#endif
exit:
return;
}
void SubMac::SetCslParams(uint16_t aPeriod, uint8_t aChannel, ShortAddress aShortAddr, const ExtAddress &aExtAddr)
{
bool diffPeriod = aPeriod != mCslPeriod;
@@ -119,18 +97,24 @@ void SubMac::SetCslParams(uint16_t aPeriod, uint8_t aChannel, ShortAddress aShor
mCslChannel = aChannel;
VerifyOrExit(diffPeriod || diffPeer);
mCslPeriod = aPeriod;
mCslPeerShort = aShortAddr;
IgnoreError(Get<Radio>().EnableCsl(aPeriod, aShortAddr, aExtAddr));
mIsCslSampling = false;
mCslPeriod = aPeriod;
mCslTimer.Stop();
if (mCslPeriod > 0)
{
mCslSampleTimeRadio = static_cast<uint32_t>(Get<Radio>().GetNow());
mCslSampleTimeLocal = TimerMicro::GetNow();
mIsCslSampling = false;
HandleCslTimer();
}
else if (!RadioSupportsReceiveTiming())
{
UpdateRadioSampleState();
}
exit:
return;
@@ -213,11 +197,8 @@ void SubMac::HandleCslReceiveOrSleep(uint32_t aTimeAhead, uint32_t aTimeAfter)
{
mIsCslSampling = false;
mCslTimer.FireAt(mCslSampleTimeLocal - aTimeAhead);
if (mState == kStateCslSample)
if (mState == kStateRadioSample)
{
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
IgnoreError(Get<Radio>().Sleep()); // Don't actually sleep for debugging
#endif
LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue()));
}
}
@@ -236,13 +217,11 @@ void SubMac::HandleCslReceiveOrSleep(uint32_t aTimeAhead, uint32_t aTimeAfter)
mCslSampleTimeLocal += periodUs;
Get<Radio>().UpdateCslSampleTime(mCslSampleTimeRadio);
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
}
LogCslWindow(winStart, winDuration);
}
UpdateRadioSampleState();
}
void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter)
@@ -313,7 +292,7 @@ void SubMac::LogReceived(RxFrame *aFrame)
mIsCslSampling ? "CslSample" : "CslSleep",
ToUlong(static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp)));
VerifyOrExit(mState == kStateCslSample);
VerifyOrExit(mState == kStateRadioSample);
GetCslWindowEdges(ahead, after);
ahead -= kMinReceiveOnAhead + kCslReceiveTimeAhead;
+41 -5
View File
@@ -44,17 +44,20 @@ RegisterLogModule("SubMac");
void SubMac::WedInit(void)
{
mIsWedSampling = false;
mIsWedEnabled = false;
mWakeupListenInterval = 0;
mWedTimer.Stop();
}
void SubMac::UpdateWakeupListening(bool aEnable, uint32_t aInterval, uint32_t aDuration, uint8_t aChannel)
{
VerifyOrExit(RadioSupportsReceiveTiming());
mWakeupListenInterval = aInterval;
mWakeupListenDuration = aDuration;
mWakeupChannel = aChannel;
mIsWedSampling = false;
mIsWedEnabled = aEnable;
mWedTimer.Stop();
if (aEnable)
@@ -64,14 +67,27 @@ void SubMac::UpdateWakeupListening(bool aEnable, uint32_t aInterval, uint32_t aD
HandleWedTimer();
}
exit:
return;
else if (!RadioSupportsReceiveTiming())
{
UpdateRadioSampleState();
}
}
void SubMac::HandleWedTimer(Timer &aTimer) { aTimer.Get<SubMac>().HandleWedTimer(); }
void SubMac::HandleWedTimer(void)
{
if (RadioSupportsReceiveTiming())
{
HandleWedReceiveAt();
}
else
{
HandleWedReceiveOrSleep();
}
}
void SubMac::HandleWedReceiveAt(void)
{
mWedSampleTime += mWakeupListenInterval;
mWedSampleTimeRadio += mWakeupListenInterval;
@@ -84,6 +100,26 @@ void SubMac::HandleWedTimer(void)
}
}
void SubMac::HandleWedReceiveOrSleep(void)
{
TimeMilli fireTime;
mIsWedSampling = !mIsWedSampling;
if (mIsWedSampling)
{
fireTime = mWedSampleTime + mWakeupListenDuration + kMinReceiveOnAfter;
}
else
{
mWedSampleTime += mWakeupListenInterval;
fireTime = mWedSampleTime - kMinReceiveOnAhead;
}
mWedTimer.FireAt(fireTime);
UpdateRadioSampleState();
}
} // namespace Mac
} // namespace ot