diff --git a/src/core/config/mac.h b/src/core/config/mac.h index e24442589..f4821a0c6 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -85,6 +85,40 @@ #define OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_INDIRECT 0 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + * + * Define as 1 to add random backoff delay in between frame transmission retries when the previous attempt resulted in + * no-ack error. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY +#define OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT + * + * Specifies the minimum backoff exponent to start with when adding random delay in between frame transmission + * retries on no-ack error. It is applicable only when `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY` + * is enabled. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT +#define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT + * + * Specifies the maximum backoff exponent when adding random delay in between frame transmission retries on no-ack + * error. It is applicable only when `OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY` is enabled. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT +#define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT 5 +#endif + /** * @def OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE * diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 73e9ae53a..a09a5378b 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -78,6 +78,9 @@ void SubMac::Init(void) mRxOnWhenBackoff = true; mEnergyScanMaxRssi = kInvalidRssiValue; mEnergyScanEndTime = Time{0}; +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent; +#endif #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE mRadioFilterEnabled = false; @@ -342,6 +345,9 @@ Error SubMac::Send(void) case kStateCslTransmit: #endif case kStateTransmit: +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + case kStateDelayBeforeRetx: +#endif case kStateEnergyScan: ExitNow(error = kErrorInvalidState); OT_UNREACHABLE_CODE(break); @@ -363,8 +369,14 @@ Error SubMac::Send(void) #endif ProcessTransmitSecurity(); + mCsmaBackoffs = 0; mTransmitRetries = 0; + +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent; +#endif + StartCsmaBackoff(); exit: @@ -414,8 +426,7 @@ exit: void SubMac::StartCsmaBackoff(void) { - uint32_t backoff; - uint32_t backoffExponent = kCsmaMinBe + mTransmitRetries + mCsmaBackoffs; + uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs; #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0) @@ -444,6 +455,7 @@ void SubMac::StartCsmaBackoff(void) ExitNow(); } #endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + SetState(kStateCsmaBackoff); VerifyOrExit(ShouldHandleCsmaBackOff(), BeginTransmit()); @@ -453,8 +465,18 @@ void SubMac::StartCsmaBackoff(void) backoffExponent = kCsmaMaxBe; } - backoff = Random::NonCrypto::GetUint32InRange(0, static_cast(1UL << backoffExponent)); - backoff *= (kUnitBackoffPeriod * OT_RADIO_SYMBOL_TIME); + StartTimerForBackoff(backoffExponent); + +exit: + return; +} + +void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent) +{ + uint32_t backoff; + + backoff = Random::NonCrypto::GetUint32InRange(0, static_cast(1UL << aBackoffExponent)); + backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime); if (mRxOnWhenBackoff) { @@ -471,8 +493,12 @@ void SubMac::StartCsmaBackoff(void) mTimer.Start(backoff / 1000UL); #endif -exit: - return; +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + if (mState == kStateDelayBeforeRetx) + { + LogDebg("Delaying retx for %u usec (be=%d)", backoff, aBackoffExponent); + } +#endif } void SubMac::BeginTransmit(void) @@ -593,6 +619,17 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro { mTransmitRetries++; aFrame.SetIsARetransmission(true); + +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + if (aError == kErrorNoAck) + { + SetState(kStateDelayBeforeRetx); + StartTimerForBackoff(mRetxDelayBackOffExponent); + mRetxDelayBackOffExponent = OT_MIN(mRetxDelayBackOffExponent + 1, kRetxDelayMaxBackoffExponent); + ExitNow(); + } +#endif + StartCsmaBackoff(); ExitNow(); } @@ -672,6 +709,9 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) case kStateTransmit: #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE case kStateCslTransmit: +#endif +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + case kStateDelayBeforeRetx: #endif case kStateEnergyScan: ExitNow(error = kErrorInvalidState); @@ -769,6 +809,12 @@ void SubMac::HandleTimer(void) HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck); break; +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + case kStateDelayBeforeRetx: + StartCsmaBackoff(); + break; +#endif + case kStateEnergyScan: SampleRssi(); break; @@ -968,11 +1014,14 @@ const char *SubMac::StateToString(State aState) "CsmaBackoff", // (3) kStateCsmaBackoff "Transmit", // (4) kStateTransmit "EnergyScan", // (5) kStateEnergyScan +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + "DelayBeforeRetx", // (6) kStateDelayBeforeRetx +#endif #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - "CslTransmit", // (6) kStateCslTransmit + "CslTransmit", // (7) kStateCslTransmit #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - "CslSample", // (7) kStateCslSample + "CslSample", // (8) kStateCslSample #endif }; @@ -982,15 +1031,25 @@ const char *SubMac::StateToString(State aState) static_assert(kStateCsmaBackoff == 3, "kStateCsmaBackoff value is not correct"); static_assert(kStateTransmit == 4, "kStateTransmit value is not correct"); static_assert(kStateEnergyScan == 5, "kStateEnergyScan value is not correct"); + +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + static_assert(kStateDelayBeforeRetx == 6, "kStateDelayBeforeRetx value is not correct"); #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + static_assert(kStateCslTransmit == 7, "kStateCslTransmit value is not correct"); +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + static_assert(kStateCslSample == 8, "kStateCslSample value is not correct"); +#endif +#elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + static_assert(kStateCslSample == 7, "kStateCslSample value is not correct"); +#endif + +#elif !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE static_assert(kStateCslTransmit == 6, "kStateCslTransmit value is not correct"); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE static_assert(kStateCslSample == 7, "kStateCslSample value is not correct"); #endif -#else -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +#elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE static_assert(kStateCslSample == 6, "kStateCslSample value is not correct"); -#endif #endif return kStateStrings[aState]; diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 7155fa13c..4e243e97b 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -584,6 +584,11 @@ private: static constexpr uint32_t kAckTimeout = 16; // Timeout for waiting on an ACK (in msec). static constexpr uint32_t kCcaSampleInterval = 128; // CCA sample interval, 128 usec. +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + static constexpr uint8_t kRetxDelayMinBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MIN_BACKOFF_EXPONENT; + static constexpr uint8_t kRetxDelayMaxBackoffExponent = OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT; +#endif + #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE static constexpr uint32_t kEnergyScanRssiSampleInterval = 128; // RSSI sample interval for energy scan, 128 usec #else @@ -598,6 +603,9 @@ private: kStateCsmaBackoff, // CSMA backoff before transmission. kStateTransmit, // Radio is transmitting. kStateEnergyScan, // Energy scan. +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + kStateDelayBeforeRetx, // Delay before retx +#endif #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE kStateCslTransmit, // CSL transmission. #endif @@ -650,6 +658,7 @@ private: void ProcessTransmitSecurity(void); void SignalFrameCounterUsed(uint32_t aFrameCounter); void StartCsmaBackoff(void); + void StartTimerForBackoff(uint8_t aBackoffExponent); void BeginTransmit(void); void SampleRssi(void); @@ -689,6 +698,9 @@ private: KeyMaterial mNextKey; uint32_t mFrameCounter; uint8_t mKeyId; +#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY + uint8_t mRetxDelayBackOffExponent; +#endif #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE TimerMicro mTimer; #else diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 7947ae086..fd8e56143 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -81,6 +81,7 @@ class Radio : public InstanceLocator, private NonCopyable friend class Instance; public: + static constexpr uint32_t kSymbolTime = OT_RADIO_SYMBOL_TIME; #if (OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT && OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT) static constexpr uint16_t kNumChannelPages = 2; static constexpr uint32_t kSupportedChannels =