mirror of
https://github.com/espressif/openthread.git
synced 2026-10-28 10:14:30 +00:00
[sub-mac] introduce SubMac::ListenScheduler (#13690)
This commit introduces `ListenScheduler` in `SubMac` to coordinate timed receive windows (`TimedRx`) across different mechanisms (such as CSL receiver and future Thread Direct wakeup or peer listeners). Key changes: - Add `NextRadioFireTime` to track the current synced time and the earliest target radio fire time (`Radio::Time64`), and to schedule a `TimerMicro` by converting radio time to local timer time. - Update `SubMac::ProcessTimedRx()` to use `NextRadioFireTime`. - Add `SubMac::ReceiveAt(const TimedRx &)` and `TimedRx::operator==` to cancel pending timed RX when passed an unspecified window and skip redundant updates when the pending or active `TimedRx` is unchanged. - Add `ListenScheduler` (and `SchedInfo`) to evaluate active listeners, schedule the earliest upcoming `TimedRx`, and manage the transition timer. - Refactor `CslReceiver` to maintain `mCurWindow` and `mNextWindow` and delegate timer and `ReceiveAt()` scheduling to `ListenScheduler`.
This commit is contained in:
+171
-36
@@ -51,6 +51,9 @@ SubMac::SubMac(Instance &aInstance)
|
||||
, mRadioCaps(Get<Radio::Radio>().GetCaps())
|
||||
, mTransmitFrame(Get<Radio::Radio>().GetTransmitBuffer())
|
||||
, mCallbacks(aInstance)
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
, mListenScheduler(aInstance)
|
||||
#endif
|
||||
, mTimer(aInstance)
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
, mCslReceiver(aInstance)
|
||||
@@ -89,6 +92,7 @@ void SubMac::Init(void)
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
mActiveTimedRx.Clear();
|
||||
mPendingTimedRx.Clear();
|
||||
mListenScheduler.Stop();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
@@ -212,6 +216,10 @@ Error SubMac::Enable(void)
|
||||
|
||||
SetState(kStateSleep);
|
||||
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
mListenScheduler.Start();
|
||||
#endif
|
||||
|
||||
exit:
|
||||
SuccessOrAssert(error);
|
||||
return error;
|
||||
@@ -224,10 +232,7 @@ Error SubMac::Disable(void)
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
mActiveTimedRx.Clear();
|
||||
mPendingTimedRx.Clear();
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
mCslReceiver.Stop();
|
||||
mListenScheduler.Stop();
|
||||
#endif
|
||||
|
||||
mTimer.Stop();
|
||||
@@ -847,9 +852,16 @@ void SubMac::HandleEnergyScanDone(int8_t aMaxRssi)
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
|
||||
void SubMac::ReceiveAt(Radio::Time64 aStartTime, uint32_t aDuration, uint8_t aChannel)
|
||||
{
|
||||
TimedRx timedRx;
|
||||
|
||||
timedRx.Init(aStartTime, aDuration, aChannel);
|
||||
ReceiveAt(timedRx);
|
||||
}
|
||||
|
||||
void SubMac::ReceiveAt(const TimedRx &aTimedRx)
|
||||
{
|
||||
Radio::SyncedTime now;
|
||||
TimedRx timedRx;
|
||||
|
||||
VerifyOrExit(mState != kStateDisabled);
|
||||
|
||||
@@ -860,18 +872,29 @@ void SubMac::ReceiveAt(Radio::Time64 aStartTime, uint32_t aDuration, uint8_t aCh
|
||||
}
|
||||
#endif
|
||||
|
||||
timedRx.Init(aStartTime, aDuration, aChannel);
|
||||
if (!aTimedRx.IsSpecified())
|
||||
{
|
||||
CancelPendingReceiveAt();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
now.SetToNow(Get<Radio::Radio>());
|
||||
|
||||
VerifyOrExit(!timedRx.HasEnded(now));
|
||||
VerifyOrExit(!aTimedRx.HasEnded(now));
|
||||
|
||||
if (!ShouldHandle(kCapReceiveTiming))
|
||||
{
|
||||
timedRx.ScheduleOnRadio(Get<Radio::Radio>());
|
||||
aTimedRx.ScheduleOnRadio(Get<Radio::Radio>());
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
VerifyOrExit(mPendingTimedRx != aTimedRx);
|
||||
|
||||
if (!mPendingTimedRx.IsSpecified())
|
||||
{
|
||||
VerifyOrExit(aTimedRx != mActiveTimedRx);
|
||||
}
|
||||
|
||||
if (mPendingTimedRx.IsSpecified() && mPendingTimedRx.HasStarted(now) && !mPendingTimedRx.HasEnded(now))
|
||||
{
|
||||
// Before replacing `mPendingTimedRx` check if the existing one
|
||||
@@ -891,7 +914,7 @@ void SubMac::ReceiveAt(Radio::Time64 aStartTime, uint32_t aDuration, uint8_t aCh
|
||||
}
|
||||
}
|
||||
|
||||
mPendingTimedRx = timedRx;
|
||||
mPendingTimedRx = aTimedRx;
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
@@ -948,20 +971,15 @@ void SubMac::ProcessTimedRx(void)
|
||||
// timed RX, scheduling the timer for upcoming windows, or putting
|
||||
// the radio to sleep when active reception window ends.
|
||||
|
||||
Radio::Time64 fireTime = Radio::kMaxTime64;
|
||||
Radio::SyncedTime now;
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
now.SetToNow(Get<Radio::Radio>());
|
||||
NextRadioFireTime nextFireTime(GetInstance());
|
||||
|
||||
// Start pending `TimedRx` if due, or clear it if missed.
|
||||
|
||||
if (mPendingTimedRx.IsSpecified())
|
||||
{
|
||||
if (mPendingTimedRx.HasStarted(now))
|
||||
if (mPendingTimedRx.HasStarted(nextFireTime.GetNow()))
|
||||
{
|
||||
if (!mPendingTimedRx.HasEnded(now))
|
||||
if (!mPendingTimedRx.HasEnded(nextFireTime.GetNow()))
|
||||
{
|
||||
StartPendingTimedRx();
|
||||
}
|
||||
@@ -970,13 +988,13 @@ void SubMac::ProcessTimedRx(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
fireTime = mPendingTimedRx.GetStartTime();
|
||||
nextFireTime.UpdateIfEarlier(mPendingTimedRx.GetStartTime());
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrExit(mActiveTimedRx.IsSpecified());
|
||||
|
||||
if (!mActiveTimedRx.HasEnded(now))
|
||||
if (!mActiveTimedRx.HasEnded(nextFireTime.GetNow()))
|
||||
{
|
||||
if (mState != kStateTimedReceive)
|
||||
{
|
||||
@@ -984,7 +1002,7 @@ void SubMac::ProcessTimedRx(void)
|
||||
SetState(kStateTimedReceive);
|
||||
}
|
||||
|
||||
fireTime = Min(fireTime, mActiveTimedRx.GetEndTime());
|
||||
nextFireTime.UpdateIfEarlier(mActiveTimedRx.GetEndTime());
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
@@ -1005,22 +1023,7 @@ void SubMac::ProcessTimedRx(void)
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
if (fireTime != Radio::kMaxTime64)
|
||||
{
|
||||
// Schedule the timer to fire at a target radio time `fireTime`,
|
||||
// using the synced reference `now` to translate radio time to
|
||||
// local time.
|
||||
|
||||
uint32_t delay = 0;
|
||||
|
||||
if (fireTime > now.GetAsTime64())
|
||||
{
|
||||
delay = ClampToUint32(fireTime - now.GetAsTime64());
|
||||
}
|
||||
|
||||
StartTimerAt(now.GetAsLocalTimeMicro(), delay);
|
||||
}
|
||||
nextFireTime.ScheduleTimer(mTimer);
|
||||
}
|
||||
|
||||
void SubMac::TimedRx::Init(Radio::Time64 aStartTime, uint32_t aDuration, uint8_t aChannel)
|
||||
@@ -1038,6 +1041,17 @@ void SubMac::TimedRx::ScheduleOnRadio(Radio::Radio &aRadio) const
|
||||
LogWarnOnError(error, "Radio::ReceiveAt()");
|
||||
}
|
||||
|
||||
bool SubMac::TimedRx::operator==(const TimedRx &aOther) const
|
||||
{
|
||||
bool matches = false;
|
||||
|
||||
VerifyOrExit(mIsSpecified && aOther.mIsSpecified);
|
||||
matches = (mStartTime == aOther.mStartTime) && (mDuration == aOther.mDuration) && (mChannel == aOther.mChannel);
|
||||
|
||||
exit:
|
||||
return matches;
|
||||
}
|
||||
|
||||
#endif // OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
|
||||
void SubMac::HandleTimer(void)
|
||||
@@ -1259,5 +1273,126 @@ const char *SubMac::StateToString(State aState)
|
||||
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// NextRadioFireTime
|
||||
|
||||
SubMac::NextRadioFireTime::NextRadioFireTime(Instance &aInstance)
|
||||
: mFireTime(Radio::kMaxTime64)
|
||||
{
|
||||
mNow.SetToNow(aInstance.Get<Radio::Radio>());
|
||||
}
|
||||
|
||||
void SubMac::NextRadioFireTime::ScheduleTimer(TimerMicro &aTimer) const
|
||||
{
|
||||
// Schedule `aTimer` to fire at the radio time `mFireTime`
|
||||
// using the synced reference `mNow` to translate radio time to
|
||||
// local time.
|
||||
|
||||
uint32_t delay;
|
||||
|
||||
if (mFireTime == Radio::kMaxTime64)
|
||||
{
|
||||
aTimer.Stop();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
delay = (mFireTime <= mNow.GetAsTime64()) ? 0 : ClampToUint32(mFireTime - mNow.GetAsTime64());
|
||||
|
||||
aTimer.StartAt(mNow.GetAsLocalTimeMicro(), delay);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// ListenScheduler
|
||||
|
||||
SubMac::ListenScheduler::ListenScheduler(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mTimer(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
void SubMac::ListenScheduler::Stop(void) { mTimer.Stop(); }
|
||||
|
||||
void SubMac::ListenScheduler::Schedule(void)
|
||||
{
|
||||
SchedInfo info(GetInstance());
|
||||
|
||||
VerifyOrExit(Get<SubMac>().IsEnabled());
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
ScheduleCsl(info);
|
||||
#endif
|
||||
|
||||
Get<SubMac>().ReceiveAt(info.GetTimedRx());
|
||||
|
||||
info.ScheduleNextFireTimeOn(mTimer);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
|
||||
void SubMac::ListenScheduler::ScheduleCsl(SchedInfo &aInfo)
|
||||
{
|
||||
CslReceiver &cslReceiver = Get<SubMac>().mCslReceiver;
|
||||
|
||||
VerifyOrExit(cslReceiver.IsEnabled());
|
||||
|
||||
// Advance the CSL window once within `kCslTransitionToNextAheadTime`
|
||||
// of the next window's start time, and set the next timer fire time
|
||||
// to that transition point. Both current and next windows are then
|
||||
// considered for timed RX (selecting the earliest one that has not
|
||||
// yet ended).
|
||||
|
||||
while (cslReceiver.GetNextWindow().GetStartTime() <= aInfo.GetNow().GetAsTime64() + kCslTransitionToNextAheadTime)
|
||||
{
|
||||
cslReceiver.AdvanceToNextWindow();
|
||||
}
|
||||
|
||||
aInfo.UpdateNextFireTime(cslReceiver.GetNextWindow().GetStartTime() - kCslTransitionToNextAheadTime);
|
||||
|
||||
aInfo.UpdateTimedRxIfEarlier(cslReceiver.GetNextWindow());
|
||||
aInfo.UpdateTimedRxIfEarlier(cslReceiver.GetCurWindow());
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
// ListenScheduler::SchedInfo
|
||||
|
||||
SubMac::ListenScheduler::SchedInfo::SchedInfo(Instance &aInstance)
|
||||
: mNextFireTime(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
void SubMac::ListenScheduler::SchedInfo::UpdateTimedRxIfEarlier(const TimedRx &aTimedRx)
|
||||
{
|
||||
// Updates `mTimedRx` to `aTimedRx` if `aTimedRx` is specified, has
|
||||
// not yet ended, and starts earlier than the current `mTimedRx`.
|
||||
|
||||
VerifyOrExit(aTimedRx.IsSpecified());
|
||||
VerifyOrExit(!aTimedRx.HasEnded(GetNow()));
|
||||
|
||||
if (mTimedRx.IsSpecified())
|
||||
{
|
||||
VerifyOrExit(aTimedRx.GetStartTime() < mTimedRx.GetStartTime());
|
||||
}
|
||||
|
||||
mTimedRx = aTimedRx;
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
|
||||
} // namespace Mac
|
||||
} // namespace ot
|
||||
|
||||
+84
-24
@@ -42,6 +42,7 @@
|
||||
|
||||
#include "common/callback.hpp"
|
||||
#include "common/clearable.hpp"
|
||||
#include "common/equatable.hpp"
|
||||
#include "common/locator.hpp"
|
||||
#include "common/non_copyable.hpp"
|
||||
#include "common/timer.hpp"
|
||||
@@ -597,7 +598,27 @@ private:
|
||||
#endif
|
||||
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
class TimedRx : public Clearable<TimedRx>
|
||||
class NextRadioFireTime
|
||||
{
|
||||
// Tracks the current synced time (`mNow`) and the earliest
|
||||
// upcoming target radio fire time (`Radio::Time64`). Used to
|
||||
// schedule a `TimerMicro` by converting the target radio time
|
||||
// to local timer time relative to `mNow`.
|
||||
|
||||
public:
|
||||
explicit NextRadioFireTime(Instance &aInstance);
|
||||
|
||||
const Radio::SyncedTime &GetNow(void) const { return mNow; }
|
||||
|
||||
void UpdateIfEarlier(Radio::Time64 aFireTime) { mFireTime = Min(mFireTime, aFireTime); }
|
||||
void ScheduleTimer(TimerMicro &aTimer) const;
|
||||
|
||||
private:
|
||||
Radio::SyncedTime mNow;
|
||||
Radio::Time64 mFireTime;
|
||||
};
|
||||
|
||||
class TimedRx : public Clearable<TimedRx>, public Unequatable<TimedRx>
|
||||
{
|
||||
public:
|
||||
TimedRx(void) { Clear(); }
|
||||
@@ -606,10 +627,12 @@ private:
|
||||
bool IsSpecified(void) const { return mIsSpecified; }
|
||||
Radio::Time64 GetStartTime(void) const { return mStartTime; }
|
||||
Radio::Time64 GetEndTime(void) const { return mStartTime + mDuration; }
|
||||
uint32_t GetDuration(void) const { return mDuration; }
|
||||
uint8_t GetChannel(void) const { return mChannel; }
|
||||
bool HasStarted(const Radio::SyncedTime &aNow) const { return mStartTime <= aNow.GetAsTime64(); }
|
||||
bool HasEnded(const Radio::SyncedTime &aNow) const { return GetEndTime() <= aNow.GetAsTime64(); }
|
||||
void ScheduleOnRadio(Radio::Radio &aRadio) const;
|
||||
bool operator==(const TimedRx &aOther) const;
|
||||
|
||||
private:
|
||||
Radio::Time64 mStartTime;
|
||||
@@ -617,53 +640,90 @@ private:
|
||||
uint8_t mChannel;
|
||||
bool mIsSpecified;
|
||||
};
|
||||
#endif
|
||||
|
||||
void HandleListenSchedulerTimer(void) { mListenScheduler.HandleTimer(); }
|
||||
|
||||
class ListenScheduler : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
explicit ListenScheduler(Instance &aInstance);
|
||||
|
||||
void Start(void) { Schedule(); }
|
||||
void Stop(void);
|
||||
void HandleTimer(void) { Schedule(); }
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
void HandleCslReceiverTimer(void) { mCslReceiver.HandleTimer(); }
|
||||
void UpdateOnCslChange(void) { Schedule(); }
|
||||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kCslTransitionToNextAheadTime = OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD;
|
||||
|
||||
class SchedInfo
|
||||
{
|
||||
public:
|
||||
explicit SchedInfo(Instance &aInstance);
|
||||
|
||||
const Radio::SyncedTime &GetNow(void) const { return mNextFireTime.GetNow(); }
|
||||
|
||||
const TimedRx &GetTimedRx(void) const { return mTimedRx; }
|
||||
void UpdateTimedRxIfEarlier(const TimedRx &aTimedRx);
|
||||
void UpdateNextFireTime(Radio::Time64 aTime) { mNextFireTime.UpdateIfEarlier(aTime); }
|
||||
void ScheduleNextFireTimeOn(TimerMicro &aTimer) { mNextFireTime.ScheduleTimer(aTimer); }
|
||||
|
||||
private:
|
||||
NextRadioFireTime mNextFireTime;
|
||||
TimedRx mTimedRx;
|
||||
};
|
||||
|
||||
void Schedule(void);
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
void ScheduleCsl(SchedInfo &aInfo);
|
||||
#endif
|
||||
|
||||
using ListenTimer = TimerMicroIn<SubMac, &SubMac::HandleListenSchedulerTimer>;
|
||||
|
||||
ListenTimer mTimer;
|
||||
};
|
||||
|
||||
#endif // OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
class CslReceiver : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
explicit CslReceiver(Instance &aInstance);
|
||||
|
||||
void Init(void);
|
||||
void Stop(void) { mTimer.Stop(); }
|
||||
bool IsEnabled(void) const { return mPeriod > 0; }
|
||||
void SetParams(uint16_t aPeriod, uint8_t aChannel, ShortAddress aShortAddr, const ExtAddress &aExtAddr);
|
||||
void ProcessTxDone(const TxFrame::ParseInfo &aFrameInfo, RxFrame *aAckFrame);
|
||||
void ProcessRxFrame(const RxFrame &aFrame);
|
||||
void HandleTimer(void) { ScheduleSampleWindow(); }
|
||||
void AdvanceToNextWindow(void);
|
||||
|
||||
const TimedRx &GetCurWindow(void) const { return mCurWindow; }
|
||||
const TimedRx &GetNextWindow(void) const { return mNextWindow; }
|
||||
const CslAccuracy &GetParentAccuracy(void) const { return mParentAccuracy; }
|
||||
void SetParentAccuracy(const CslAccuracy &aCslAccuracy) { mParentAccuracy = aCslAccuracy; }
|
||||
|
||||
private:
|
||||
static constexpr uint32_t kMinReceiveOnAhead = OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AHEAD;
|
||||
static constexpr uint32_t kMinReceiveOnAfter = OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AFTER;
|
||||
static constexpr uint32_t kReceiveTimeAhead = OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD;
|
||||
|
||||
struct Window
|
||||
{
|
||||
Radio::SyncedTime mStartTime;
|
||||
uint32_t mDuration;
|
||||
};
|
||||
|
||||
void RestartTimerAfterSyncUpdate(void);
|
||||
void SetLastSyncToNow(void);
|
||||
void ScheduleSampleWindow(void);
|
||||
void DetermineWindow(const Radio::SyncedTime &aSampleTime, Window &aWindow) const;
|
||||
void Schedule(void);
|
||||
void UpdateCurAndNextWindows(void);
|
||||
void DetermineWindow(const Radio::SyncedTime &aSampleTime, TimedRx &aTimedRx) const;
|
||||
uint32_t DetermineClockDrift(uint32_t aIntervalUs) const;
|
||||
bool IsEnabled(void) const { return mPeriod > 0; }
|
||||
void LogReceived(const RxFrame &aFrame);
|
||||
|
||||
using CslTimer = TimerMicroIn<SubMac, &SubMac::HandleCslReceiverTimer>;
|
||||
|
||||
uint16_t mPeriod;
|
||||
uint8_t mChannel;
|
||||
uint16_t mPeerShort;
|
||||
Radio::SyncedTime mSampleTime;
|
||||
CslAccuracy mParentAccuracy;
|
||||
CslTimer mTimer;
|
||||
TimedRx mCurWindow;
|
||||
TimedRx mNextWindow;
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
|
||||
TimeMicro mLastSync;
|
||||
#else
|
||||
@@ -674,11 +734,10 @@ private:
|
||||
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
||||
|
||||
void Init(void);
|
||||
|
||||
bool IsEnabled(void) const { return mState != kStateDisabled; }
|
||||
bool RadioSupports(Capability aCapability) const { return (mRadioCaps & aCapability) != 0; }
|
||||
bool ShouldHandle(Capability aCapability) const;
|
||||
bool ShouldHandleCsmaBackoff(void) const;
|
||||
|
||||
void ProcessTransmitSecurity(TxFrame::ParseInfo &aFrameInfo);
|
||||
void ReprocessSecurityForRetx(TxFrame::ParseInfo &aFrameInfo);
|
||||
void SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyIndex);
|
||||
@@ -688,7 +747,6 @@ private:
|
||||
void SampleRssi(void);
|
||||
void StartTimer(uint32_t aDelayUs);
|
||||
void StartTimerAt(Time aStartTime, uint32_t aDelayUs);
|
||||
|
||||
void HandleReceiveDone(RxFrame *aFrame, Error aError);
|
||||
void HandleTransmitStarted(TxFrame &aFrame);
|
||||
void HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError);
|
||||
@@ -697,6 +755,7 @@ private:
|
||||
void HandleTimer(void);
|
||||
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
void ReceiveAt(const TimedRx &aTimedRx);
|
||||
void StartPendingTimedRx(void);
|
||||
void ProcessTimedRx(void);
|
||||
#endif
|
||||
@@ -732,8 +791,9 @@ private:
|
||||
uint8_t mRetxDelayBackoffExponent;
|
||||
#endif
|
||||
#if OT_CONFIG_MAC_TARGET_TIME_RX_ENABLE
|
||||
TimedRx mActiveTimedRx;
|
||||
TimedRx mPendingTimedRx;
|
||||
TimedRx mActiveTimedRx;
|
||||
TimedRx mPendingTimedRx;
|
||||
ListenScheduler mListenScheduler;
|
||||
#endif
|
||||
|
||||
SubMacTimer mTimer;
|
||||
|
||||
@@ -44,7 +44,6 @@ RegisterLogModule("SubMac");
|
||||
|
||||
SubMac::CslReceiver::CslReceiver(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mTimer(aInstance)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
@@ -56,7 +55,8 @@ void SubMac::CslReceiver::Init(void)
|
||||
mPeerShort = 0;
|
||||
mParentAccuracy.Init();
|
||||
mSampleTime.Clear();
|
||||
mTimer.Stop();
|
||||
mCurWindow.Clear();
|
||||
mNextWindow.Clear();
|
||||
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
|
||||
mLastSync.SetValue(0);
|
||||
#else
|
||||
@@ -64,23 +64,6 @@ void SubMac::CslReceiver::Init(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void SubMac::CslReceiver::RestartTimerAfterSyncUpdate(void)
|
||||
{
|
||||
if (mTimer.IsRunning())
|
||||
{
|
||||
uint32_t periodUs = CslPeriodToUsec(mPeriod);
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
// Rewind sample times by one period. `ScheduleSampleWindow()` will add
|
||||
// this period back, effectively re-evaluating the current CSL period's
|
||||
// schedule using the updated mLastSync.
|
||||
mSampleTime -= periodUs;
|
||||
|
||||
ScheduleSampleWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void SubMac::CslReceiver::ProcessTxDone(const TxFrame::ParseInfo &aFrameInfo, RxFrame *aAckFrame)
|
||||
{
|
||||
// Actual synchronization timestamp should be from the sent frame instead of the current time.
|
||||
@@ -92,7 +75,7 @@ void SubMac::CslReceiver::ProcessTxDone(const TxFrame::ParseInfo &aFrameInfo, Rx
|
||||
VerifyOrExit(aFrameInfo.Has<CslIe>());
|
||||
|
||||
SetLastSyncToNow();
|
||||
RestartTimerAfterSyncUpdate();
|
||||
Schedule();
|
||||
|
||||
exit:
|
||||
return;
|
||||
@@ -114,7 +97,7 @@ void SubMac::CslReceiver::ProcessRxFrame(const RxFrame &aFrame)
|
||||
#else
|
||||
mLastSync = aFrame.GetTimestamp();
|
||||
#endif
|
||||
RestartTimerAfterSyncUpdate();
|
||||
Schedule();
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -126,18 +109,23 @@ void SubMac::CslReceiver::SetParams(uint16_t aPeriod,
|
||||
ShortAddress aShortAddr,
|
||||
const ExtAddress &aExtAddr)
|
||||
{
|
||||
mChannel = aChannel;
|
||||
|
||||
VerifyOrExit((aPeriod != mPeriod) || (aShortAddr != mPeerShort));
|
||||
if ((aPeriod == mPeriod) && (aShortAddr == mPeerShort))
|
||||
{
|
||||
// Handle a CSL channel change only
|
||||
VerifyOrExit(mChannel != aChannel);
|
||||
mChannel = aChannel;
|
||||
VerifyOrExit(IsEnabled());
|
||||
Schedule();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
mPeriod = aPeriod;
|
||||
mChannel = aChannel;
|
||||
mPeerShort = aShortAddr;
|
||||
|
||||
IgnoreError(Get<Radio::Radio>().EnableCsl(aPeriod, aShortAddr, aExtAddr));
|
||||
|
||||
mPeriod = aPeriod;
|
||||
|
||||
mTimer.Stop();
|
||||
|
||||
if (mPeriod > 0)
|
||||
if (IsEnabled())
|
||||
{
|
||||
mSampleTime.SetToNow(Get<Radio::Radio>());
|
||||
|
||||
@@ -146,37 +134,60 @@ void SubMac::CslReceiver::SetParams(uint16_t aPeriod,
|
||||
#else
|
||||
mLastSync = mSampleTime.GetAsTime64();
|
||||
#endif
|
||||
ScheduleSampleWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
Get<SubMac>().CancelPendingReceiveAt();
|
||||
}
|
||||
|
||||
Schedule();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void SubMac::CslReceiver::ScheduleSampleWindow(void)
|
||||
void SubMac::CslReceiver::Schedule(void)
|
||||
{
|
||||
Window window;
|
||||
|
||||
DetermineWindow(mSampleTime, window);
|
||||
Get<SubMac>().ReceiveAt(window.mStartTime.GetAsTime64(), window.mDuration, mChannel);
|
||||
|
||||
LogDebg("CSL window start %lu, duration %lu", ToUlong(window.mStartTime.GetAsTime32()), ToUlong(window.mDuration));
|
||||
|
||||
// Advance sample time to the next CSL period, update the radio
|
||||
// sample time on `Radio` and schedule the timer ahead of the
|
||||
// next window's start time.
|
||||
|
||||
mSampleTime += CslPeriodToUsec(mPeriod);
|
||||
Get<Radio::Radio>().UpdateCslSampleTime(mSampleTime.GetAsTime32());
|
||||
DetermineWindow(mSampleTime, window);
|
||||
mTimer.FireAt(window.mStartTime.GetAsLocalTimeMicro() - kReceiveTimeAhead);
|
||||
UpdateCurAndNextWindows();
|
||||
Get<SubMac>().mListenScheduler.UpdateOnCslChange();
|
||||
}
|
||||
|
||||
void SubMac::CslReceiver::DetermineWindow(const Radio::SyncedTime &aSampleTime, Window &aWindow) const
|
||||
void SubMac::CslReceiver::AdvanceToNextWindow(void)
|
||||
{
|
||||
VerifyOrExit(IsEnabled());
|
||||
|
||||
mSampleTime += CslPeriodToUsec(mPeriod);
|
||||
UpdateCurAndNextWindows();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void SubMac::CslReceiver::UpdateCurAndNextWindows(void)
|
||||
{
|
||||
Radio::SyncedTime nextSampleTime;
|
||||
|
||||
if (!IsEnabled())
|
||||
{
|
||||
mCurWindow.Clear();
|
||||
mNextWindow.Clear();
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
DetermineWindow(mSampleTime, mCurWindow);
|
||||
|
||||
nextSampleTime = mSampleTime;
|
||||
nextSampleTime += CslPeriodToUsec(mPeriod);
|
||||
|
||||
Get<Radio::Radio>().UpdateCslSampleTime(nextSampleTime.GetAsTime32());
|
||||
|
||||
DetermineWindow(nextSampleTime, mNextWindow);
|
||||
|
||||
LogDebg("CSL windows (start, dur) - cur (%lu, %lu), next (%lu, %lu)",
|
||||
ToUlong(Radio::ConvertTime64To32(mCurWindow.GetStartTime())), ToUlong(mCurWindow.GetDuration()),
|
||||
ToUlong(Radio::ConvertTime64To32(mNextWindow.GetStartTime())), ToUlong(mNextWindow.GetDuration()));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void SubMac::CslReceiver::DetermineWindow(const Radio::SyncedTime &aSampleTime, TimedRx &aTimedRx) const
|
||||
{
|
||||
/*
|
||||
* CSL sample timing diagram:
|
||||
@@ -215,10 +226,7 @@ void SubMac::CslReceiver::DetermineWindow(const Radio::SyncedTime &aSampleTime,
|
||||
ahead = Min(guardInterval + kMinReceiveOnAhead, halfPeriod);
|
||||
after = Min(guardInterval + kMinReceiveOnAfter, halfPeriod);
|
||||
|
||||
aWindow.mStartTime = aSampleTime;
|
||||
aWindow.mStartTime -= ahead;
|
||||
|
||||
aWindow.mDuration = ahead + after;
|
||||
aTimedRx.Init(aSampleTime.GetAsTime64() - ahead, ahead + after, mChannel);
|
||||
}
|
||||
|
||||
uint32_t SubMac::CslReceiver::DetermineClockDrift(uint32_t aIntervalUs) const
|
||||
@@ -241,8 +249,6 @@ void SubMac::CslReceiver::SetLastSyncToNow(void)
|
||||
void SubMac::CslReceiver::LogReceived(const RxFrame &aFrame)
|
||||
{
|
||||
RxFrame::ParseInfo frameInfo;
|
||||
Radio::SyncedTime sampleTime;
|
||||
Window window;
|
||||
uint32_t margin;
|
||||
Radio::Time64 timestamp;
|
||||
uint32_t deviation;
|
||||
@@ -258,28 +264,23 @@ void SubMac::CslReceiver::LogReceived(const RxFrame &aFrame)
|
||||
|
||||
VerifyOrExit((Get<SubMac>().mState == kStateTimedReceive) || (Get<SubMac>().mState == kStateSleep));
|
||||
|
||||
sampleTime = mSampleTime;
|
||||
sampleTime -= CslPeriodToUsec(mPeriod);
|
||||
|
||||
DetermineWindow(sampleTime, window);
|
||||
|
||||
// The `kMinReceiveOnAhead` is not considered for the margin since
|
||||
// it has no impact on understanding possible deviation errors
|
||||
// between transmitter and receiver.
|
||||
|
||||
margin = sampleTime.GetAsTime32() - window.mStartTime.GetAsTime32();
|
||||
margin = mSampleTime.GetAsTime32() - Radio::ConvertTime64To32(mCurWindow.GetStartTime());
|
||||
margin -= Min(margin, kMinReceiveOnAhead);
|
||||
|
||||
timestamp = aFrame.GetTimestamp() + Radio::kHeaderPhrDuration;
|
||||
|
||||
if (timestamp >= sampleTime.GetAsTime64())
|
||||
if (timestamp >= mSampleTime.GetAsTime64())
|
||||
{
|
||||
deviation = ClampToUint32(timestamp - sampleTime.GetAsTime64());
|
||||
deviation = ClampToUint32(timestamp - mSampleTime.GetAsTime64());
|
||||
signChar = '+';
|
||||
}
|
||||
else
|
||||
{
|
||||
deviation = ClampToUint32(sampleTime.GetAsTime64() - timestamp);
|
||||
deviation = ClampToUint32(mSampleTime.GetAsTime64() - timestamp);
|
||||
signChar = '-';
|
||||
}
|
||||
|
||||
@@ -296,7 +297,7 @@ void SubMac::CslReceiver::LogReceived(const RxFrame &aFrame)
|
||||
// expected sample time. This can be due to clock drift and/or
|
||||
// CSL Phase rounding error.
|
||||
|
||||
LogAt(logLevel, "Expected sample time %lu, margin ±%lu, deviation %c%lu", ToUlong(sampleTime.GetAsTime32()),
|
||||
LogAt(logLevel, "Expected sample time %lu, margin ±%lu, deviation %c%lu", ToUlong(mSampleTime.GetAsTime32()),
|
||||
ToUlong(margin), signChar, ToUlong(deviation));
|
||||
|
||||
exit:
|
||||
|
||||
Reference in New Issue
Block a user