[csl] simplify states and scheduling (#7783)

After enabling the scheduling of delayed reception slots in #7677
they would be configured even when the device switched to MED mode.

This commit introduces some simplifications around the CSL states
and scheduling:
- Maintain the CSL timer stopped if not in CSL Receiver mode.
- Keep the CSL Period and Channel values in the MAC in order to use
  them as an indication of CSL Receiver mode in the Sub-Mac.
- Simplify CSL states with `mIsCslSampling` variable.
- Use a simplified interaction between MAC and SubMac:
  - `SubMac::UpdateCsl` for CSL config or stopping CSL sampling.
  - `SubMac::CslSample` for start/maintain CSL sampling.
This commit is contained in:
Eduardo Montoya
2022-07-06 19:58:01 +02:00
committed by GitHub
parent 2057ca8846
commit 289bbebb9c
6 changed files with 154 additions and 224 deletions
+39 -26
View File
@@ -92,6 +92,10 @@ Mac::Mac(Instance &aInstance)
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
, mCslTxFireTime(TimeMilli::kMaxDuration)
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
, mCslChannel(0)
, mCslPeriod(0)
#endif
#endif
, mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union
, mScanHandlerContext(nullptr)
@@ -413,6 +417,10 @@ Error Mac::SetPanChannel(uint8_t aChannel)
mRadioChannel = mPanChannel;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
UpdateCsl();
#endif
UpdateIdleMode();
exit:
@@ -553,7 +561,7 @@ void Mac::UpdateIdleMode(void)
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (IsCslEnabled())
{
mLinks.CslSample(mRadioChannel);
mLinks.CslSample();
ExitNow();
}
#endif
@@ -2257,52 +2265,57 @@ exit:
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void Mac::SetCslChannel(uint8_t aChannel)
void Mac::UpdateCsl(void)
{
VerifyOrExit(GetCslChannel() != aChannel);
uint16_t period;
uint8_t channel;
mLinks.GetSubMac().SetCslChannel(aChannel);
mLinks.GetSubMac().SetCslChannelSpecified(aChannel != 0);
VerifyOrExit(IsCslSupported());
if (IsCslEnabled())
period = Get<Mle::Mle>().IsRxOnWhenIdle() ? 0 : GetCslPeriod();
channel = GetCslChannel() ? GetCslChannel() : mRadioChannel;
if (mLinks.UpdateCsl(period, channel, Get<Mle::Mle>().GetParent().GetRloc16(),
&Get<Mle::Mle>().GetParent().GetExtAddress()))
{
Get<Mle::Mle>().ScheduleChildUpdateRequest();
Get<DataPollSender>().RecalculatePollPeriod();
if (period)
{
Get<Mle::Mle>().ScheduleChildUpdateRequest();
}
UpdateIdleMode();
}
exit:
return;
}
void Mac::SetCslChannel(uint8_t aChannel)
{
mCslChannel = aChannel;
UpdateCsl();
}
void Mac::SetCslPeriod(uint16_t aPeriod)
{
mLinks.GetSubMac().SetCslPeriod(aPeriod);
Get<DataPollSender>().RecalculatePollPeriod();
if ((GetCslPeriod() == 0) || IsCslEnabled())
{
IgnoreError(Get<Radio>().EnableCsl(GetCslPeriod(), Get<Mle::Mle>().GetParent().GetRloc16(),
&Get<Mle::Mle>().GetParent().GetExtAddress()));
}
if (IsCslEnabled())
{
Get<Mle::Mle>().ScheduleChildUpdateRequest();
}
UpdateIdleMode();
mCslPeriod = aPeriod;
UpdateCsl();
}
bool Mac::IsCslEnabled(void) const
{
return !GetRxOnWhenIdle() && IsCslCapable();
return !Get<Mle::Mle>().IsRxOnWhenIdle() && IsCslCapable();
}
bool Mac::IsCslCapable(void) const
{
return (GetCslPeriod() > 0) && Get<Mle::MleRouter>().IsChild() &&
Get<Mle::Mle>().GetParent().IsEnhancedKeepAliveSupported();
return (GetCslPeriod() > 0) && IsCslSupported();
}
bool Mac::IsCslSupported(void) const
{
return Get<Mle::MleRouter>().IsChild() && Get<Mle::Mle>().GetParent().IsEnhancedKeepAliveSupported();
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
+18 -6
View File
@@ -583,7 +583,7 @@ public:
* @returns CSL channel.
*
*/
uint8_t GetCslChannel(void) const { return mLinks.GetSubMac().GetCslChannel(); }
uint8_t GetCslChannel(void) const { return mCslChannel; }
/**
* This method sets the CSL channel.
@@ -594,12 +594,10 @@ public:
void SetCslChannel(uint8_t aChannel);
/**
* This method indicates if CSL channel has been explicitly specified by the upper layer.
*
* @returns If CSL channel has been specified.
* This method centralizes CSL state switching conditions evaluating, configuring SubMac accordingly.
*
*/
bool IsCslChannelSpecified(void) const { return mLinks.GetSubMac().IsCslChannelSpecified(); }
void UpdateCsl(void);
/**
* This method gets the CSL period.
@@ -607,7 +605,7 @@ public:
* @returns CSL period in units of 10 symbols.
*
*/
uint16_t GetCslPeriod(void) const { return mLinks.GetSubMac().GetCslPeriod(); }
uint16_t GetCslPeriod(void) const { return mCslPeriod; }
/**
* This method sets the CSL period.
@@ -635,6 +633,15 @@ public:
*/
bool IsCslCapable(void) const;
/**
* This method indicates whether the device is connected to a parent which supports CSL.
*
* @retval TRUE If parent supports CSL.
* @retval FALSE If parent does not support CSL.
*
*/
bool IsCslSupported(void) const;
/**
* This method returns CSL parent clock accuracy, in ± ppm.
*
@@ -820,6 +827,11 @@ private:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
TimeMilli mCslTxFireTime;
#endif
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// When Mac::mCslChannel is 0, it indicates that CSL channel has not been specified by the upper layer.
uint8_t mCslChannel;
uint16_t mCslPeriod;
#endif
union
+31 -7
View File
@@ -457,18 +457,42 @@ public:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method transitions all radios link to CSL sample state.
* This method configures CSL parameters in all radios.
*
* CSL sample state is only applicable and used for 15.4 radio link. Other link are transitioned to sleep state.
* @param[in] aPeriod The CSL period.
* @param[in] aChannel The CSL channel.
* @param[in] aShortAddr The short source address of CSL receiver's peer.
* @param[in] aExtAddr The extended source address of CSL receiver's peer.
*
* @retval TRUE if CSL Period or CSL Channel changed.
* @retval FALSE if CSL Period and CSL Channel did not change.
*
* @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.
*/
void CslSample(uint8_t aPanChannel)
bool UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShortAddr, const otExtAddress *aExtAddr)
{
OT_UNUSED_VARIABLE(aPanChannel);
bool retval = false;
OT_UNUSED_VARIABLE(aPeriod);
OT_UNUSED_VARIABLE(aChannel);
OT_UNUSED_VARIABLE(aShortAddr);
OT_UNUSED_VARIABLE(aExtAddr);
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
IgnoreError(mSubMac.CslSample(aPanChannel));
retval = mSubMac.UpdateCsl(aPeriod, aChannel, aShortAddr, aExtAddr);
#endif
return retval;
}
/**
* This method transitions all radios link to CSL sample state, given that a non-zero CSL period is configured.
*
* CSL sample state is only applicable and used for 15.4 radio link. Other link are transitioned to sleep state
* when CSL period is non-zero.
*
*/
void CslSample(void)
{
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
mSubMac.CslSample();
#endif
#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
mTrel.Sleep();
+41 -96
View File
@@ -95,12 +95,11 @@ void SubMac::Init(void)
mTimer.Stop();
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
mCslPeriod = 0;
mCslChannel = 0;
mIsCslChannelSpecified = false;
mCslSampleTime = TimeMicro{0};
mCslLastSync = TimeMicro{0};
mCslState = kCslIdle;
mCslPeriod = 0;
mCslChannel = 0;
mIsCslSampling = false;
mCslSampleTime = TimeMicro{0};
mCslLastSync = TimeMicro{0};
mCslTimer.Stop();
#endif
}
@@ -201,6 +200,10 @@ Error SubMac::Disable(void)
{
Error error;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
mCslTimer.Stop();
#endif
mTimer.Stop();
SuccessOrExit(error = Get<Radio>().Sleep());
SuccessOrExit(error = Get<Radio>().Disable());
@@ -254,43 +257,26 @@ exit:
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
Error SubMac::CslSample(uint8_t aPanChannel)
void SubMac::CslSample(void)
{
Error error = kErrorNone;
if (!IsCslChannelSpecified())
{
mCslChannel = aPanChannel;
}
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
VerifyOrExit(!mRadioFilterEnabled, error = Get<Radio>().Sleep());
VerifyOrExit(!mRadioFilterEnabled, IgnoreError(Get<Radio>().Sleep()));
#endif
switch (mCslState)
{
case kCslSample:
error = Get<Radio>().Receive(mCslChannel);
break;
case kCslSleep:
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
error = Get<Radio>().Sleep(); // Don't actually sleep for debugging
#endif
break;
case kCslIdle:
ExitNow(error = kErrorInvalidState);
default:
OT_ASSERT(false);
}
SetState(kStateCslSample);
exit:
if (error != kErrorNone)
if (mIsCslSampling && !RadioSupportsReceiveTiming())
{
LogWarn("CslSample() failed, error: %s", ErrorToString(error));
IgnoreError(Get<Radio>().Receive(mCslChannel));
ExitNow();
}
return error;
#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
@@ -318,7 +304,7 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError)
#if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
// Split the log into two lines for RTT to output
LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState),
CslStateToString(mCslState), static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
mIsCslSampling ? "CslSample" : "CslSleep", static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
LogDebg("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(),
static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue());
#endif
@@ -1055,63 +1041,35 @@ const char *SubMac::StateToString(State aState)
return kStateStrings[aState];
}
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
const char *SubMac::CslStateToString(CslState aCslState)
{
static const char *const kCslStateStrings[] = {
"CslIdle", // (0) kCslIdle
"CslSample", // (1) kCslSample
"CslSleep", // (2) kCslSleep
};
static_assert(kCslIdle == 0, "kCslIdle value is incorrect");
static_assert(kCslSample == 1, "kCslSample value is incorrect");
static_assert(kCslSleep == 2, "kCslSleep value is incorrect");
return kCslStateStrings[aCslState];
}
#endif
// LCOV_EXCL_STOP
//---------------------------------------------------------------------------------------------------------------------
// CSL Receiver methods
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
void SubMac::SetCslChannel(uint8_t aChannel)
bool SubMac::UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShortAddr, const otExtAddress *aExtAddr)
{
bool diffPeriod = aPeriod != mCslPeriod;
bool diffChannel = aChannel != mCslChannel;
bool retval = diffPeriod || diffChannel;
VerifyOrExit(retval);
mCslChannel = aChannel;
}
void SubMac::SetCslPeriod(uint16_t aPeriod)
{
VerifyOrExit(mCslPeriod != aPeriod);
VerifyOrExit(diffPeriod);
mCslPeriod = aPeriod;
IgnoreError(Get<Radio>().EnableCsl(aPeriod, aShortAddr, aExtAddr));
mCslTimer.Stop();
if (mCslPeriod > 0)
{
mCslSampleTime = TimeMicro(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
mCslState = kCslSleep;
mIsCslSampling = false;
HandleCslTimer();
}
else
{
mCslState = kCslIdle;
if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Sleep());
SetState(kStateSleep);
}
}
LogDebg("CSL Period: %u", mCslPeriod);
exit:
return;
return retval;
}
void SubMac::HandleCslTimer(Timer &aTimer)
@@ -1135,11 +1093,9 @@ void SubMac::HandleCslTimer(void)
GetCslWindowEdges(timeAhead, timeAfter);
switch (mCslState)
if (mIsCslSampling)
{
case kCslSample:
mCslState = kCslSleep;
mIsCslSampling = false;
mCslTimer.FireAt(mCslSampleTime - timeAhead);
if (mState == kStateCslSample)
{
@@ -1148,9 +1104,9 @@ void SubMac::HandleCslTimer(void)
#endif
LogDebg("CSL sleep %u", mCslTimer.GetNow().GetValue());
}
break;
case kCslSleep:
}
else
{
if (RadioSupportsReceiveTiming())
{
mCslSampleTime += periodUs;
@@ -1160,33 +1116,22 @@ void SubMac::HandleCslTimer(void)
else
{
mCslTimer.FireAt(mCslSampleTime + timeAfter);
mCslState = kCslSample;
mIsCslSampling = true;
mCslSampleTime += periodUs;
}
Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
if (RadioSupportsReceiveTiming())
if (RadioSupportsReceiveTiming() && (mState != kStateDisabled))
{
if (mState != kStateDisabled && mCslChannel)
{
IgnoreError(Get<Radio>().ReceiveAt(mCslChannel, mCslSampleTime.GetValue() - periodUs - timeAhead,
timeAhead + timeAfter));
}
IgnoreError(Get<Radio>().ReceiveAt(mCslChannel, mCslSampleTime.GetValue() - periodUs - timeAhead,
timeAhead + timeAfter));
}
else if (mState == kStateCslSample)
{
IgnoreError(Get<Radio>().Receive(mCslChannel));
LogDebg("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter);
}
break;
case kCslIdle:
break;
default:
OT_ASSERT(false);
break;
}
}
+19 -70
View File
@@ -395,69 +395,27 @@ public:
int8_t GetNoiseFloor(void);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
/**
* This method lets `SubMac` start CSL sample.
* This method configures CSL parameters in 'SubMac'.
*
* `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] aPeriod The CSL period.
* @param[in] aChannel The CSL channel.
* @param[in] aShortAddr The short source address of CSL receiver's peer.
* @param[in] aExtAddr The extended source address of CSL receiver's peer.
*
* @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.
* @retval TRUE if CSL Period or CSL Channel changed.
* @retval FALSE if CSL Period and CSL Channel did not change.
*
*/
Error CslSample(uint8_t aPanChannel);
bool UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShortAddr, const otExtAddress *aExtAddr);
/**
* This method gets the CSL channel.
* This method lets `SubMac` start CSL sample mode given a configured non-zero CSL period.
*
* @returns CSL channel.
* `SubMac` would switch the radio state between `Receive` and `Sleep` according the CSL timer.
*
*/
uint8_t GetCslChannel(void) const { return mCslChannel; }
/**
* This method sets the CSL channel.
*
* @param[in] aChannel The CSL channel. `0` to set CSL Channel unspecified.
*
*/
void SetCslChannel(uint8_t aChannel);
/**
* This method indicates if CSL channel has been explicitly specified by the upper layer.
*
* @returns If CSL channel has been specified.
*
*/
bool IsCslChannelSpecified(void) const { return mIsCslChannelSpecified; }
/**
* This method sets the flag representing if CSL channel has been specified.
*
*/
void SetCslChannelSpecified(bool aIsSpecified) { mIsCslChannelSpecified = aIsSpecified; }
/**
* This method gets the CSL period.
*
* @returns CSL period.
*
*/
uint16_t GetCslPeriod(void) const { return mCslPeriod; }
/**
* This method sets the CSL period.
*
* @param[in] aPeriod The CSL period in 10 symbols.
*
*/
void SetCslPeriod(uint16_t aPeriod);
void CslSample(void);
/**
* This method returns CSL parent clock accuracy, in ± ppm.
@@ -623,12 +581,6 @@ private:
// CSL receivers would wake up `kCslReceiveTimeAhead` earlier
// than expected sample window. The value is in usec.
static constexpr uint32_t kCslReceiveTimeAhead = OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD;
enum CslState : uint8_t{
kCslIdle, // CSL receiver is not started.
kCslSample, // Sampling CSL channel.
kCslSleep, // Radio in sleep.
};
#endif
/**
@@ -674,9 +626,6 @@ private:
void SetState(State aState);
static const char *StateToString(State aState);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
static const char *CslStateToString(CslState aCslState);
#endif
otRadioCaps mRadioCaps;
State mState;
@@ -709,14 +658,14 @@ private:
#endif
#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 (only when `mIsCslChannelSpecified` is `true`).
uint8_t mIsCslChannelSpecified : 1; // Whether the CSL channel was explicitly set
TimeMicro mCslSampleTime; // The CSL sample time of the current period.
TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization.
uint8_t mCslParentAccuracy; // Drift of timer used for scheduling CSL tx by the parent, in ± ppm.
uint8_t mCslParentUncert; // Uncertainty of the scheduling CSL of tx by the parent, in ±10 us units.
CslState mCslState;
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.
TimeMicro mCslSampleTime; // The CSL sample time of the current period.
TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization.
uint8_t mCslParentAccuracy; // Drift of timer used for scheduling CSL tx by the parent, in ± ppm.
uint8_t mCslParentUncert; // Uncertainty of the scheduling CSL of tx by the parent, in ±10 us units.
TimerMicro mCslTimer;
#endif
};
+6 -19
View File
@@ -641,12 +641,6 @@ bool Mle::IsRouterOrLeader(void) const
void Mle::SetStateDetached(void)
{
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().IsCslEnabled())
{
IgnoreError(Get<Radio>().EnableCsl(0, GetParent().GetRloc16(), &GetParent().GetExtAddress()));
}
#endif
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
Get<BackboneRouter::Local>().Reset();
#endif
@@ -676,6 +670,9 @@ void Mle::SetStateDetached(void)
#if OPENTHREAD_FTD
Get<Ip6::Mpl>().SetTimerExpirations(0);
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
Get<Mac::Mac>().UpdateCsl();
#endif
}
void Mle::SetStateChild(uint16_t aRloc16)
@@ -726,12 +723,7 @@ void Mle::SetStateChild(uint16_t aRloc16)
mPreviousParentRloc = mParent.GetRloc16();
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (Get<Mac::Mac>().IsCslCapable())
{
uint32_t period = IsRxOnWhenIdle() ? 0 : Get<Mac::Mac>().GetCslPeriod();
IgnoreError(Get<Radio>().EnableCsl(period, GetParent().GetRloc16(), &GetParent().GetExtAddress()));
ScheduleChildUpdateRequest();
}
Get<Mac::Mac>().UpdateCsl();
#endif
}
@@ -1834,12 +1826,7 @@ void Mle::ScheduleMessageTransmissionTimer(void)
case kChildUpdateRequestActive:
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// CSL transmitter may respond in next CSL cycle.
// This condition IsCslCapable() && !IsRxOnWhenIdle() is used instead of
// IsCslEnabled because during transitions SSED -> MED and MED -> SSED
// there is a delay in synchronisation of IsRxOnWhenIdle residing in MAC
// and in MLE, which causes below datapoll interval to be calculated incorrectly.
if (Get<Mac::Mac>().IsCslCapable() && !IsRxOnWhenIdle())
if (Get<Mac::Mac>().IsCslEnabled())
{
ExitNow(interval = Get<Mac::Mac>().GetCslPeriod() * kUsPerTenSymbols / 1000 +
static_cast<uint32_t>(kUnicastRetransmissionDelay));
@@ -4767,7 +4754,7 @@ Error Mle::TxMessage::AppendCslChannelTlv(void)
// in CSL Channel TLV, if CSL channel is not specified, we don't append CSL Channel TLV.
// And on transmitter side, it would also set CSL Channel for the child to `0` if it doesn't find a CSL Channel
// TLV.
VerifyOrExit(Get<Mac::Mac>().IsCslChannelSpecified());
VerifyOrExit(Get<Mac::Mac>().GetCslChannel());
cslChannel.Init();
cslChannel.SetChannelPage(0);