diff --git a/src/core/config/mac.h b/src/core/config/mac.h index f4821a0c6..6572dd0e8 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -119,6 +119,32 @@ #define OPENTHREAD_CONFIG_MAC_RETX_DELAY_MAX_BACKOFF_EXPONENT 5 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + * + * Define as 1 to enable collision avoidance delay feature, which adds a delay wait time after a successful frame tx + * to a neighbor which is expected to forward the frame. This delay is applied before the next direct frame tx (towards + * any neighbor) on an FTD. + * + * The delay interval is specified by `OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL` (in milliseconds). + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE +#define OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL + * + * Specifies the collision avoidance delay interval in milliseconds. This is added after a successful frame tx to a + * neighbor that is expected to forward the frame (when `OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE` is + * enabled). + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL +#define OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL 8 +#endif + /** * @def OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE * diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 05eddb515..7a1a18e8a 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -101,6 +101,10 @@ MeshForwarder::MeshForwarder(Instance &aInstance) , mEnabled(false) , mTxPaused(false) , mSendBusy(false) +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + , mDelayNextTx(false) + , mTxDelayTimer(aInstance, HandleTxDelayTimer) +#endif , mScheduleTransmissionTask(aInstance, MeshForwarder::ScheduleTransmissionTask) #if OPENTHREAD_FTD , mIndirectSender(aInstance) @@ -145,6 +149,11 @@ void MeshForwarder::Stop(void) mFragmentPriorityList.Clear(); #endif +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + mTxDelayTimer.Stop(); + mDelayNextTx = false; +#endif + mEnabled = false; mSendMessage = nullptr; Get().SetRxOnWhenIdle(false); @@ -237,6 +246,20 @@ void MeshForwarder::ResumeMessageTransmissions(void) } } +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE +void MeshForwarder::HandleTxDelayTimer(Timer &aTimer) +{ + aTimer.Get().HandleTxDelayTimer(); +} + +void MeshForwarder::HandleTxDelayTimer(void) +{ + mDelayNextTx = false; + mScheduleTransmissionTask.Post(); + LogDebg("Tx delay timer expired"); +} +#endif + void MeshForwarder::ScheduleTransmissionTask(Tasklet &aTasklet) { aTasklet.Get().ScheduleTransmissionTask(); @@ -246,6 +269,10 @@ void MeshForwarder::ScheduleTransmissionTask(void) { VerifyOrExit(!mSendBusy && !mTxPaused); +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + VerifyOrExit(!mDelayNextTx); +#endif + mSendMessage = PrepareNextDirectTransmission(); VerifyOrExit(mSendMessage != nullptr); @@ -996,6 +1023,18 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, Error aError) VerifyOrExit(mEnabled); +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + if (mDelayNextTx && (aError == kErrorNone)) + { + mTxDelayTimer.Start(kTxDelayInterval); + LogDebg("Start tx delay timer for %u msec", kTxDelayInterval); + } + else + { + mDelayNextTx = false; + } +#endif + if (!aFrame.IsEmpty()) { IgnoreError(aFrame.GetDstAddr(macDest)); diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index bbf6d62ee..603e567ef 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -334,6 +334,8 @@ private: static constexpr uint8_t kMeshHeaderFrameMtu = OT_RADIO_FRAME_MAX_SIZE; // Max MTU with a Mesh Header frame. static constexpr uint8_t kMeshHeaderFrameFcsSize = sizeof(uint16_t); // Frame FCS size for Mesh Header frame. + static constexpr uint32_t kTxDelayInterval = OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL; // In msec + enum MessageAction : uint8_t { kMessageReceive, // Indicates that the message was received. @@ -503,11 +505,15 @@ private: void PauseMessageTransmissions(void) { mTxPaused = true; } void ResumeMessageTransmissions(void); +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + static void HandleTxDelayTimer(Timer &aTimer); + void HandleTxDelayTimer(void); +#endif + void LogMessage(MessageAction aAction, const Message & aMessage, Error aError = kErrorNone, const Mac::Address *aAddress = nullptr); - void LogFrame(const char *aActionText, const Mac::Frame &aFrame, Error aError); void LogFragmentFrameDrop(Error aError, uint16_t aFrameLength, @@ -585,6 +591,10 @@ private: bool mEnabled : 1; bool mTxPaused : 1; bool mSendBusy : 1; +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + bool mDelayNextTx : 1; + TimerMilli mTxDelayTimer; +#endif Tasklet mScheduleTransmissionTask; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index ffe80bff9..ef1f60df1 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -403,6 +403,13 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) mMeshDest = meshHeader.GetDestination(); mMeshSource = meshHeader.GetSource(); +#if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + if (mMacDest.GetShort() != mMeshDest) + { + mDelayNextTx = true; + } +#endif + exit: return error; } @@ -616,6 +623,9 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage // destination is not neighbor mMacSource.SetShort(mMeshSource); mAddMeshHeader = true; +#if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + mDelayNextTx = true; +#endif } exit: