Skip to content

Commit

Permalink
[mac] skip CSMA backoff when disabled (openthread#10838)
Browse files Browse the repository at this point in the history
This commit updates SubMac to skip CSMA backoff when TX frame disables
it.
  • Loading branch information
bukepo authored Oct 15, 2024
1 parent 0066d56 commit b0790b8
Show file tree
Hide file tree
Showing 2 changed files with 126 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/core/mac/sub_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

#include <openthread/platform/time.h>

#include "common/code_utils.hpp"
#include "instance/instance.hpp"

namespace ot {
Expand Down Expand Up @@ -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<LinkRaw>().IsEnabled());
Expand Down
131 changes: 124 additions & 7 deletions tests/gtest/radio_spinel_rcp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <openthread/platform/radio.h>

#include "common/error.hpp"
#include "gmock/gmock.h"
#include "mac/mac_frame.hpp"
#include "mac/mac_types.hpp"

Expand All @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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<Mac::Frame *>(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<Mac::Frame *>(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);
}

0 comments on commit b0790b8

Please sign in to comment.