From b0790b8ff0c7c8f889ebb07461c01744a09c394b Mon Sep 17 00:00:00 2001 From: Yakun Xu Date: Wed, 16 Oct 2024 02:20:39 +0800 Subject: [PATCH] [mac] skip CSMA backoff when disabled (#10838) This commit updates SubMac to skip CSMA backoff when TX frame disables it. --- src/core/mac/sub_mac.cpp | 3 +- tests/gtest/radio_spinel_rcp_test.cpp | 131 ++++++++++++++++++++++++-- 2 files changed, 126 insertions(+), 8 deletions(-) diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 0363b5055..67323bed4 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -37,6 +37,7 @@ #include +#include "common/code_utils.hpp" #include "instance/instance.hpp" namespace ot { @@ -798,7 +799,7 @@ bool SubMac::ShouldHandleCsmaBackOff(void) const { bool swCsma = true; - VerifyOrExit(!RadioSupportsCsmaBackoff(), swCsma = false); + VerifyOrExit(mTransmitFrame.IsCsmaCaEnabled() && !RadioSupportsCsmaBackoff(), swCsma = false); #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE VerifyOrExit(Get().IsEnabled()); diff --git a/tests/gtest/radio_spinel_rcp_test.cpp b/tests/gtest/radio_spinel_rcp_test.cpp index 5c7b83438..4795ee483 100644 --- a/tests/gtest/radio_spinel_rcp_test.cpp +++ b/tests/gtest/radio_spinel_rcp_test.cpp @@ -32,6 +32,7 @@ #include #include "common/error.hpp" +#include "gmock/gmock.h" #include "mac/mac_frame.hpp" #include "mac/mac_types.hpp" @@ -40,16 +41,17 @@ using namespace ot; +using ::testing::AnyNumber; using ::testing::Truly; -class MockPlatform : public FakeCoprocessorPlatform -{ -public: - MOCK_METHOD(otError, Transmit, (otRadioFrame * aFrame), (override)); -}; - TEST(RadioSpinelTransmit, shouldPassDesiredTxPowerToRadioPlatform) { + class MockPlatform : public FakeCoprocessorPlatform + { + public: + MOCK_METHOD(otError, Transmit, (otRadioFrame * aFrame), (override)); + }; + MockPlatform platform; constexpr Mac::PanId kSrcPanId = 0x1234; @@ -59,7 +61,7 @@ TEST(RadioSpinelTransmit, shouldPassDesiredTxPowerToRadioPlatform) constexpr int8_t kTxPower = 100; uint8_t frameBuffer[OT_RADIO_FRAME_MAX_SIZE]; - Mac::TxFrame txFrame; + Mac::TxFrame txFrame{}; txFrame.mPsdu = frameBuffer; @@ -132,3 +134,118 @@ TEST(RadioSpinelTransmit, shouldCauseSwitchingToRxChannelAfterTxDone) platform.GoInMs(1000); EXPECT_EQ(platform.GetReceiveChannel(), 25); } + +TEST(RadioSpinelTransmit, shouldSkipCsmaCaWhenDisabled) +{ + class MockPlatform : public FakeCoprocessorPlatform + { + public: + MOCK_METHOD(otError, Transmit, (otRadioFrame * aFrame), (override)); + MOCK_METHOD(otError, Receive, (uint8_t aChannel), (override)); + }; + + MockPlatform platform; + + constexpr Mac::PanId kSrcPanId = 0x1234; + constexpr Mac::PanId kDstPanId = 0x4321; + constexpr uint8_t kDstAddr[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + constexpr uint16_t kSrcAddr = 0xac00; + constexpr int8_t kTxPower = 100; + + uint8_t frameBuffer[OT_RADIO_FRAME_MAX_SIZE]; + Mac::TxFrame txFrame{}; + + txFrame.mPsdu = frameBuffer; + + { + Mac::TxFrame::Info frameInfo; + + frameInfo.mType = Mac::Frame::kTypeData; + frameInfo.mVersion = Mac::Frame::kVersion2006; + frameInfo.mAddrs.mSource.SetShort(kSrcAddr); + frameInfo.mAddrs.mDestination.SetExtended(kDstAddr); + frameInfo.mPanIds.SetSource(kSrcPanId); + frameInfo.mPanIds.SetDestination(kDstPanId); + frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; + + frameInfo.PrepareHeadersIn(txFrame); + } + + txFrame.mInfo.mTxInfo.mCsmaCaEnabled = false; + txFrame.mChannel = 11; + + EXPECT_CALL(platform, Transmit(Truly([](otRadioFrame *aFrame) -> bool { + Mac::Frame &frame = *static_cast(aFrame); + return frame.mInfo.mTxInfo.mCsmaCaEnabled == false; + }))) + .Times(1); + + EXPECT_CALL(platform, Receive).Times(AnyNumber()); + // Receive(11) will be called exactly once to prepare for TX because the fake platform doesn't support sleep-to-tx + // capability. + EXPECT_CALL(platform, Receive(11)).Times(1); + + ASSERT_EQ(platform.mRadioSpinel.Enable(FakePlatform::CurrentInstance()), kErrorNone); + ASSERT_EQ(platform.mRadioSpinel.Receive(12), kErrorNone); + ASSERT_EQ(platform.mRadioSpinel.Transmit(txFrame), kErrorNone); + + platform.GoInMs(1000); +} + +TEST(RadioSpinelTransmit, shouldPerformCsmaCaWhenEnabled) +{ + class MockPlatform : public FakeCoprocessorPlatform + { + public: + MOCK_METHOD(otError, Transmit, (otRadioFrame * aFrame), (override)); + MOCK_METHOD(otError, Receive, (uint8_t aChannel), (override)); + }; + + MockPlatform platform; + + constexpr Mac::PanId kSrcPanId = 0x1234; + constexpr Mac::PanId kDstPanId = 0x4321; + constexpr uint8_t kDstAddr[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + constexpr uint16_t kSrcAddr = 0xac00; + constexpr int8_t kTxPower = 100; + + uint8_t frameBuffer[OT_RADIO_FRAME_MAX_SIZE]; + Mac::TxFrame txFrame{}; + + txFrame.mPsdu = frameBuffer; + + { + Mac::TxFrame::Info frameInfo; + + frameInfo.mType = Mac::Frame::kTypeData; + frameInfo.mVersion = Mac::Frame::kVersion2006; + frameInfo.mAddrs.mSource.SetShort(kSrcAddr); + frameInfo.mAddrs.mDestination.SetExtended(kDstAddr); + frameInfo.mPanIds.SetSource(kSrcPanId); + frameInfo.mPanIds.SetDestination(kDstPanId); + frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32; + + frameInfo.PrepareHeadersIn(txFrame); + } + + txFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; + txFrame.mChannel = 11; + + EXPECT_CALL(platform, Transmit(Truly([](otRadioFrame *aFrame) -> bool { + Mac::Frame &frame = *static_cast(aFrame); + return frame.mInfo.mTxInfo.mCsmaCaEnabled == true; + }))) + .Times(1); + + EXPECT_CALL(platform, Receive).Times(AnyNumber()); + // Receive(11) will be called exactly twice: + // 1. one time to prepare for TX because the fake platform doesn't support sleep-to-tx capability. + // 2. one time in CSMA backoff because rx-on-when-idle is true. + EXPECT_CALL(platform, Receive(11)).Times(2); + + ASSERT_EQ(platform.mRadioSpinel.Enable(FakePlatform::CurrentInstance()), kErrorNone); + ASSERT_EQ(platform.mRadioSpinel.Receive(12), kErrorNone); + ASSERT_EQ(platform.mRadioSpinel.Transmit(txFrame), kErrorNone); + + platform.GoInMs(1000); +}