[sub-mac] generalize timed transmission handling and states (#13460)

This commit simplifies and generalizes target TX time handling in `SubMac`:

- Encapsulates `TxDelay` and `TxDelayBaseTime` in `TxFrameProperties` behind
  `IsTargetTxTimeSpecified()`, `GetTargetTxTime()`, `ClearTargetTxTime()`, and
  `SetTargetTxTime()`, moving low-level delay accessors to private scope.
- Introduces derived config `OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE` to generalize
  timed TX handling across CSL transmitter and Wakeup (Thread Direct).
- Renames `SubMac` state `kStateCslTransmit` to `kStateTimedTransmit` and
  replaces `kCslTransmitTimeAhead` with `kTimedTxLeadTime`.
- Updates `CslTxScheduler` and `WakeupTxScheduler` to use new methods
  like `SetTargetTxTime()`.
This commit is contained in:
Abtin Keshavarzian
2026-08-04 23:27:20 -07:00
committed by GitHub
parent 0313eb7b72
commit 91474ad3da
7 changed files with 85 additions and 64 deletions
+1 -2
View File
@@ -171,8 +171,7 @@ public:
mTxFrame802154.SetIsSecurityProcessed(false);
mTxFrame802154.SetCsmaCaEnabled(true); // Set to true by default, only set to `false` for CSL transmission
mTxFrame802154.SetIsHeaderUpdated(false);
mTxFrame802154.SetTxDelay(0);
mTxFrame802154.SetTxDelayBaseTime(0);
mTxFrame802154.ClearTargetTxTime();
mTxFrame802154.SetTxPower(Radio::kInvalidPower);
mTxFrame802154.SetCslIePresent(false);
#endif
+26 -33
View File
@@ -116,7 +116,7 @@ SubMac::Capabilities SubMac::GetCaps(void) const
#endif
{
caps |= (kCapAckTimeout | kCapCsmaBackoff | kCapTransmitRetries | kCapEnergyScan | kCapTransmitSec);
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
caps |= kCapTransmitTiming;
#endif
}
@@ -295,8 +295,8 @@ Error SubMac::Send(void)
{
case kStateDisabled:
case kStateCsmaBackoff:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
case kStateTimedTransmit:
#endif
case kStateTransmit:
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
@@ -392,24 +392,19 @@ exit:
void SubMac::StartCsmaBackoff(void)
{
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
if (mTransmitFrame.GetTxDelay() != 0 || mTransmitFrame.GetTxDelayBaseTime() != 0)
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
if (mTransmitFrame.IsTargetTxTimeSpecified())
{
SetState(kStateCslTransmit);
SetState(kStateTimedTransmit);
if (ShouldHandleTransmitTargetTime())
if (ShouldHandle(kCapTransmitTiming))
{
static constexpr uint32_t kAheadTime =
kCcaSampleInterval + kCslTransmitTimeAhead + Radio::kHeaderShrDuration;
Radio::Time32 txStart = mTransmitFrame.GetTargetTxTime() - kTimedTxLeadTime;
Radio::Time32 radioNow = Get<Radio::Radio>().GetNowAsTime32();
Radio::Time32 radioNow = Get<Radio::Radio>().GetNowAsTime32();
Radio::Time32 txStartTime = mTransmitFrame.GetTxDelayBaseTime();
txStartTime += (mTransmitFrame.GetTxDelay() - kAheadTime);
if (Radio::IsTimeStrictlyBefore(radioNow, txStartTime))
if (Radio::IsTimeStrictlyBefore(radioNow, txStart))
{
StartTimer(txStartTime - radioNow);
StartTimer(txStart - radioNow);
ExitNow();
}
@@ -419,7 +414,7 @@ void SubMac::StartCsmaBackoff(void)
BeginTransmit();
ExitNow();
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
#endif // OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
SetState(kStateCsmaBackoff);
@@ -468,8 +463,8 @@ void SubMac::BeginTransmit(void)
{
Error error;
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateCslTransmit);
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateTimedTransmit);
#else
VerifyOrExit(mState == kStateCsmaBackoff);
#endif
@@ -483,14 +478,14 @@ void SubMac::BeginTransmit(void)
error = Get<Radio::Radio>().Transmit(mTransmitFrame);
if (error == kErrorInvalidState && mTransmitFrame.GetTxDelay() > 0)
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
if (error == kErrorInvalidState && mTransmitFrame.IsTargetTxTimeSpecified())
{
// Platform `transmit_at` fails and we send the frame directly.
mTransmitFrame.SetTxDelay(0);
mTransmitFrame.SetTxDelayBaseTime(0);
mTransmitFrame.ClearTargetTxTime();
error = Get<Radio::Radio>().Transmit(mTransmitFrame);
}
#endif
SuccessOrAssert(error);
@@ -714,8 +709,8 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
case kStateDisabled:
case kStateCsmaBackoff:
case kStateTransmit:
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
case kStateTimedTransmit:
#endif
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
case kStateDelayBeforeRetx:
@@ -796,10 +791,8 @@ void SubMac::HandleTimer(void)
{
switch (mState)
{
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
case kStateCslTransmit:
BeginTransmit();
break;
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
case kStateTimedTransmit:
#endif
case kStateCsmaBackoff:
BeginTransmit();
@@ -1078,7 +1071,7 @@ const char *SubMac::StateToString(State aState)
_(kStateCsmaBackoff, "CsmaBackoff") \
_(kStateTransmit, "Transmit") \
_(kStateEnergyScan, "EnergyScan") \
DelayBeforeRetxStateMapList(_) CslTxStateMapList(_) RadioSampleMapList(_)
DelayBeforeRetxStateMapList(_) TimedTxStateMapList(_) RadioSampleMapList(_)
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
#define DelayBeforeRetxStateMapList(_) _(kStateDelayBeforeRetx, "DelayBeforeRetx")
@@ -1086,10 +1079,10 @@ const char *SubMac::StateToString(State aState)
#define DelayBeforeRetxStateMapList(_)
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
#define CslTxStateMapList(_) _(kStateCslTransmit, "CslTransmit")
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
#define TimedTxStateMapList(_) _(kStateTimedTransmit, "TimedTransmit")
#else
#define CslTxStateMapList(_)
#define TimedTxStateMapList(_)
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
+25 -10
View File
@@ -60,6 +60,20 @@ namespace ot {
namespace Mac {
//----------------------------------------------------------------------------------------------------------------------
// Derived configs
#ifdef OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
#error "OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE MUST NOT be defined directly. It is derived from other configs"
#endif
#define OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE \
(OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || \
((OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE) && OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE))
//----------------------------------------------------------------------------------------------------------------------
// Config validity checks
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION < OT_THREAD_VERSION_1_2)
#error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE."
#endif
@@ -81,6 +95,8 @@ namespace Mac {
#error "OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is required for OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE."
#endif
//----------------------------------------------------------------------------------------------------------------------
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
class LinkRaw;
#endif
@@ -519,8 +535,7 @@ private:
ConditionalCap(kCapTransmitRetries, OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE) |
ConditionalCap(kCapCsmaBackoff, OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE) |
ConditionalCap(kCapTransmitSec, OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE) |
ConditionalCap(kCapTransmitTiming,
OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE &&OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE) |
ConditionalCap(kCapTransmitTiming, OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE) |
ConditionalCap(kCapSleepToTx, OPENTHREAD_RADIO);
#undef ConditionalCap
@@ -538,8 +553,8 @@ private:
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
kStateDelayBeforeRetx, // Delay before retx
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
kStateCslTransmit, // CSL transmission.
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
kStateTimedTransmit, // Timed TX (e.g., for CSL)
#endif
#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
@@ -563,11 +578,12 @@ private:
static constexpr uint32_t kWedReceiveTimeAfter = OPENTHREAD_CONFIG_WED_RECEIVE_TIME_AFTER;
#endif
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
// CSL transmitter would schedule delayed transmission `kCslTransmitTimeAhead` earlier
// than expected delayed transmit time. The value is in usec.
// Only for radios not supporting kCapTransmitTiming.
static constexpr uint32_t kCslTransmitTimeAhead = OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD;
#if OT_CONFIG_MAC_TARGET_TIME_TX_ENABLE
// Lead time (in microseconds) to schedule a delayed tx earlier
// than expected target tx time. Only used when radio does not
// itself support `kCapTransmitTiming`.
static constexpr uint32_t kTimedTxLeadTime =
OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD + kCcaSampleInterval + Radio::kHeaderShrDuration;
#endif
void Init(void);
@@ -578,7 +594,6 @@ private:
bool ShouldHandleAckTimeout(void) const { return ShouldHandle(kCapAckTimeout); }
bool ShouldHandleRetries(void) const { return ShouldHandle(kCapTransmitRetries); }
bool ShouldHandleEnergyScan(void) const { return ShouldHandle(kCapEnergyScan); }
bool ShouldHandleTransmitTargetTime(void) const { return ShouldHandle(kCapTransmitTiming); }
bool ShouldHandleCsmaBackOff(void) const;
void ProcessTransmitSecurity(void);
+4 -3
View File
@@ -83,7 +83,8 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames)
Mac::Address source;
uint32_t radioTxDelay;
uint32_t rendezvousTimeUs;
TimeMicro nowUs = TimerMicro::GetNow();
TimeMicro nowUs = TimerMicro::GetNow();
Radio::Time64 radioNow = Get<Radio::Radio>().GetNow();
Mac::ConnectionIe *connectionIe;
VerifyOrExit(mIsRunning);
@@ -100,8 +101,8 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames)
VerifyOrExit(frame->GenerateWakeupFrame(Get<Mac::Mac>().GetPanId(), mWakeupRequest, source) == kErrorNone,
frame = nullptr);
frame->SetTxDelayBaseTime(Get<Radio::Radio>().GetNowAsTime32());
frame->SetTxDelay(radioTxDelay);
frame->SetTargetTxTime(radioNow + radioTxDelay, radioNow);
frame->SetCsmaCaEnabled(kWakeupFrameTxCca);
frame->SetMaxCsmaBackoffs(0);
frame->SetMaxFrameRetries(0);
+26 -13
View File
@@ -449,34 +449,47 @@ public:
#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
/**
* Gets the TX delay field for the frame.
* Indicates whether or not a target transmission time is specified for the frame.
*
* @returns The delay time for the TX frame in microseconds.
* @retval TRUE If a target transmission time is specified.
* @retval FALSE If no target transmission time is specified.
*/
uint32_t GetTxDelay(void) const { return AsFrame().mInfo.mTxInfo.mTxDelay; }
bool IsTargetTxTimeSpecified(void) const { return GetTxDelay() != 0 || GetTxDelayBaseTime() != 0; }
/**
* Set TX delay field for the frame.
* Gets the target transmission time for the frame as a `Time32`.
*
* @param[in] aTxDelay The delay time for the TX frame.
* @returns The target transmission time.
*/
void SetTxDelay(uint32_t aTxDelay) { AsFrame().mInfo.mTxInfo.mTxDelay = aTxDelay; }
Time32 GetTargetTxTime(void) const { return GetTxDelayBaseTime() + GetTxDelay(); }
/**
* Gets the TX delay base time field for the frame.
*
* @returns The delay base time for the TX frame as a `Time32`.
* Clears the target transmission time for the frame (sets delay and base time to zero).
*/
Time32 GetTxDelayBaseTime(void) const { return AsFrame().mInfo.mTxInfo.mTxDelayBaseTime; }
void ClearTargetTxTime(void)
{
SetTxDelayBaseTime(0);
SetTxDelay(0);
}
/**
* Set TX delay base time field for the frame.
* Sets the target transmission time and base reference time for the frame.
*
* @param[in] aTxDelayBaseTime The delay base time for the TX frame.
* @param[in] aTargetTxTime The target transmission time.
* @param[in] aBaseTime The base reference time.
*/
void SetTxDelayBaseTime(Time32 aTxDelayBaseTime) { AsFrame().mInfo.mTxInfo.mTxDelayBaseTime = aTxDelayBaseTime; }
void SetTargetTxTime(Time64 aTargetTxTime, Time64 aBaseTime)
{
SetTxDelayBaseTime(ConvertTime64To32(aBaseTime));
SetTxDelay(static_cast<uint32_t>(aTargetTxTime - aBaseTime));
}
private:
uint32_t GetTxDelay(void) const { return AsFrame().mInfo.mTxInfo.mTxDelay; }
void SetTxDelay(uint32_t aTxDelay) { AsFrame().mInfo.mTxInfo.mTxDelay = aTxDelay; }
Time32 GetTxDelayBaseTime(void) const { return AsFrame().mInfo.mTxInfo.mTxDelayBaseTime; }
void SetTxDelayBaseTime(Time32 aTxDelayBaseTime) { AsFrame().mInfo.mTxInfo.mTxDelayBaseTime = aTxDelayBaseTime; }
Frame &AsFrame(void) { return *static_cast<TxFrameType *>(this); }
const Frame &AsFrame(void) const { return *static_cast<const TxFrameType *>(this); }
};
+2 -1
View File
@@ -52,7 +52,8 @@ namespace Radio {
#define OT_CONFIG_RADIO_TIME_ENABLE \
(OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE || \
OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE || OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || \
OPENTHREAD_CONFIG_TIME_SYNC_ENABLE)
OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || \
((OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE) && OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE))
class Radio;
+1 -2
View File
@@ -290,8 +290,7 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames)
ExitNow();
}
frame->SetTxDelayBaseTime(Radio::ConvertTime64To32(mCslTxNeighbor->GetLastRxTimestamp()));
frame->SetTxDelay(static_cast<uint32_t>(mNeighborCslWindow - mCslTxNeighbor->GetLastRxTimestamp()));
frame->SetTargetTxTime(mNeighborCslWindow, mCslTxNeighbor->GetLastRxTimestamp());
frame->SetCsmaCaEnabled(true);
exit: