Skip to content

Commit

Permalink
[spinel] add SPINEL_PROP_MAC_RX_AT (#10921)
Browse files Browse the repository at this point in the history
This commit adds receive at functionality for spinel for host to
schedule reception on RCP.
  • Loading branch information
bukepo authored Nov 18, 2024
1 parent 0579a4d commit 9277bce
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 16 deletions.
14 changes: 14 additions & 0 deletions src/lib/spinel/radio_spinel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,20 @@ otError RadioSpinel::Receive(uint8_t aChannel)
return error;
}

otError RadioSpinel::ReceiveAt(uint64_t aWhen, uint32_t aDuration, uint8_t aChannel)
{
otError error = OT_ERROR_NONE;

EXPECT(mState != kStateDisabled, error = OT_ERROR_INVALID_STATE);

error = Set(SPINEL_PROP_MAC_RX_AT, SPINEL_DATATYPE_UINT64_S SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_UINT8_S, aWhen,
aDuration, aChannel);
EXPECT_NO_ERROR(error);

exit:
return error;
}

otError RadioSpinel::Sleep(void)
{
otError error = OT_ERROR_NONE;
Expand Down
16 changes: 16 additions & 0 deletions src/lib/spinel/radio_spinel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,22 @@ class RadioSpinel : private Logger
*/
otError Receive(uint8_t aChannel);

/**
* Schedule a radio reception window at a specific time and duration.
*
* @param[in] aWhen The receive window start time in the local
* radio clock, see `otPlatRadioGetNow`. The radio
* receiver SHALL be on and ready to receive the first
* symbol of a frame's SHR at the window start time.
* @param[in] aDuration The receive window duration, in microseconds, as
* measured by the local radio clock.
* @param[in] aChannel The channel to use for receiving.
*
* @retval OT_ERROR_NONE Successfully scheduled the reception.
* @retval OT_ERROR_INVALID_STATE The radio was disabled.
*/
otError ReceiveAt(uint64_t aWhen, uint32_t aDuration, uint8_t aChannel);

/**
* Switches the radio state from Receive to Sleep.
*
Expand Down
11 changes: 11 additions & 0 deletions src/lib/spinel/spinel.h
Original file line number Diff line number Diff line change
Expand Up @@ -2101,6 +2101,17 @@ enum
*/
SPINEL_PROP_MAC_15_4_ALT_SADDR = SPINEL_PROP_MAC__BEGIN + 12,

/// MAC Receive At
/** Format: `XLC`
*
* Schedule a radio reception window at a specific time and duration.
*
* `X`: The receive window start time.
* `L`: The receive window duration.
* `C`: The receive channel.
*/
SPINEL_PROP_MAC_RX_AT = SPINEL_PROP_MAC__BEGIN + 13,

SPINEL_PROP_MAC__END = 0x40,

SPINEL_PROP_MAC_EXT__BEGIN = 0x1300,
Expand Down
26 changes: 26 additions & 0 deletions src/ncp/ncp_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@

#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "lib/spinel/spinel.h"
#include "radio/radio.hpp"

namespace ot {
Expand Down Expand Up @@ -1685,6 +1686,31 @@ template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_RAW_STREAM_ENABLE
return error;
}

template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_RX_AT>(void)
{
otError error = OT_ERROR_NONE;
uint64_t when;
uint32_t duration;
uint8_t channel;

SuccessOrExit(error = mDecoder.ReadUint64(when));
SuccessOrExit(error = mDecoder.ReadUint32(duration));
SuccessOrExit(error = mDecoder.ReadUint8(channel));

{
uint64_t now = otPlatRadioGetNow(mInstance);
uint32_t start;

VerifyOrExit(when > now && (when - now) < UINT32_MAX, error = OT_ERROR_INVALID_ARGS);

start = when - now;
error = otPlatRadioReceiveAt(mInstance, channel, start, duration);
}

exit:
return error;
}

#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_RCP_CSL_ACCURACY>(void)
{
Expand Down
1 change: 1 addition & 0 deletions src/ncp/ncp_base_dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_15_4_ALT_SADDR),
#endif
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_RX_AT),
#if OPENTHREAD_MTD || OPENTHREAD_FTD
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_IF_UP),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_STACK_UP),
Expand Down
53 changes: 40 additions & 13 deletions tests/gtest/fake_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@
#include <openthread/instance.h>
#include <openthread/tasklet.h>
#include <openthread/tcat.h>
#include <openthread/platform/alarm-micro.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/ble.h>
#include <openthread/platform/diag.h>
#include <openthread/platform/dso_transport.h>
Expand Down Expand Up @@ -124,11 +122,15 @@ void FakePlatform::StartMilliAlarm(uint32_t aT0, uint32_t aDt)

void FakePlatform::StopMilliAlarm() { mMilliAlarmStart = kAlarmStop; }

void FakePlatform::ProcessAlarm(uint64_t &aTimeout)
template <> void FakePlatform::HandleSchedule<&FakePlatform::mReceiveAtStart>() { mChannel = mReceiveAtChannel; }

template <> void FakePlatform::HandleSchedule<&FakePlatform::mReceiveAtEnd>() { mChannel = 0; }

void FakePlatform::ProcessSchedules(uint64_t &aTimeout)
{
uint64_t end = mNow + aTimeout;
uint64_t guard = mNow + aTimeout;

uint64_t *alarm = &end;
uint64_t *alarm = &guard;
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
if (mMicroAlarmStart < *alarm)
{
Expand All @@ -139,24 +141,44 @@ void FakePlatform::ProcessAlarm(uint64_t &aTimeout)
{
alarm = &mMilliAlarmStart;
}
if (mReceiveAtStart < *alarm)
{
alarm = &mReceiveAtStart;
}
else if (mReceiveAtEnd < *alarm)
{
alarm = &mReceiveAtEnd;
}

if (mNow < *alarm)
{
aTimeout -= *alarm - mNow;
mNow = *alarm;
}
*alarm = kAlarmStop;
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
if (alarm == &mMicroAlarmStart)

if (alarm == &guard)
{
otPlatAlarmMicroFired(mInstance);
// nonthing scheduled within this period.
}
else
#endif
if (alarm == &mMilliAlarmStart)
else if (alarm == &mReceiveAtEnd)
{
otPlatAlarmMilliFired(mInstance);
FakePlatform::HandleSchedule<&FakePlatform::mReceiveAtEnd>();
}
else if (alarm == &mReceiveAtStart)
{
FakePlatform::HandleSchedule<&FakePlatform::mReceiveAtStart>();
}
else if (alarm == &mMilliAlarmStart)
{
FakePlatform::HandleSchedule<&FakePlatform::mMilliAlarmStart>();
}
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
else if (alarm == &mMicroAlarmStart)
{
FakePlatform::HandleSchedule<&FakePlatform::mMicroAlarmStart>();
}
#endif
}

uint64_t FakePlatform::Run(uint64_t aTimeoutInUs)
Expand All @@ -167,7 +189,7 @@ uint64_t FakePlatform::Run(uint64_t aTimeoutInUs)
}
else
{
ProcessAlarm(aTimeoutInUs);
ProcessSchedules(aTimeoutInUs);
}

return aTimeoutInUs;
Expand Down Expand Up @@ -356,6 +378,11 @@ otError otPlatRadioSleep(otInstance *) { return OT_ERROR_NONE; }

otError otPlatRadioReceive(otInstance *, uint8_t aChannel) { return FakePlatform::CurrentPlatform().Receive(aChannel); }

otError otPlatRadioReceiveAt(otInstance *, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
return FakePlatform::CurrentPlatform().ReceiveAt(aChannel, aStart, aDuration);
}

otError otPlatRadioTransmit(otInstance *, otRadioFrame *aFrame)
{
return FakePlatform::CurrentPlatform().Transmit(aFrame);
Expand Down
35 changes: 32 additions & 3 deletions tests/gtest/fake_platform.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@

#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/platform/alarm-micro.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/time.h>

Expand Down Expand Up @@ -77,7 +79,16 @@ class FakePlatform
uint8_t GetReceiveChannel(void) const { return mChannel; }
virtual otRadioFrame *GetTransmitBuffer() { return &mTransmitFrame; }
virtual otError Transmit(otRadioFrame *aFrame);
virtual otError Receive(uint8_t aChannel)
virtual otError ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
mReceiveAtChannel = aChannel;
mReceiveAtStart = mNow + aStart;
mReceiveAtEnd = mReceiveAtStart + aDuration;

return OT_ERROR_NONE;
}

virtual otError Receive(uint8_t aChannel)
{
mChannel = aChannel;
return OT_ERROR_NONE;
Expand All @@ -98,7 +109,7 @@ class FakePlatform
virtual uint64_t GetEui64() const { return 0; }

protected:
void ProcessAlarm(uint64_t &aTimeout);
void ProcessSchedules(uint64_t &aTimeout);

static constexpr uint64_t kAlarmStop = 0xffffffffffffffffUL;

Expand All @@ -115,15 +126,33 @@ class FakePlatform
#endif
uint64_t mMilliAlarmStart = kAlarmStop;

uint64_t mReceiveAtStart = kAlarmStop;
uint64_t mReceiveAtEnd = kAlarmStop;

template <uint64_t FakePlatform::*T> void HandleSchedule();

otRadioFrame mTransmitFrame;
uint8_t mTransmitBuffer[OT_RADIO_FRAME_MAX_SIZE];
uint8_t mChannel = 0;
uint8_t mChannel = 0;
uint8_t mReceiveAtChannel = 0;

uint8_t mFlash[kFlashSwapSize * kFlashSwapNum];

std::map<uint32_t, std::vector<std::vector<uint8_t>>> mSettings;
};

template <> inline void FakePlatform::HandleSchedule<&FakePlatform::mMilliAlarmStart>()
{
otPlatAlarmMilliFired(mInstance);
}

#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
template <> inline void FakePlatform::HandleSchedule<&FakePlatform::mMicroAlarmStart>()
{
otPlatAlarmMicroFired(mInstance);
}
#endif

} // namespace ot

#endif // OT_GTEST_FAKE_PLATFORM_HPP_
28 changes: 28 additions & 0 deletions tests/gtest/radio_spinel_rcp_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,31 @@ TEST(RadioSpinelTransmit, shouldNotCauseSwitchingToRxAfterTxDoneIfNotRxOnWhenIdl
platform.GoInMs(1000);
EXPECT_EQ(platform.GetReceiveChannel(), 11);
}

TEST(RadioSpinelReceiveAt, shouldReceiveAtGiveRadioTime)
{
class MockPlatform : public FakeCoprocessorPlatform
{
public:
MOCK_METHOD(otError, ReceiveAt, (uint8_t aChannel, uint32_t aStart, uint32_t aDuration), (override));
};

MockPlatform platform;

ON_CALL(platform, ReceiveAt)
.WillByDefault([&platform](uint8_t aChannel, uint32_t aStart, uint32_t aDuration) -> otError {
return platform.FakePlatform::ReceiveAt(aChannel, aStart, aDuration);
});

EXPECT_CALL(platform, ReceiveAt).Times(1);

ASSERT_EQ(platform.mRadioSpinel.Enable(FakePlatform::CurrentInstance()), kErrorNone);
ASSERT_EQ(platform.mRadioSpinel.SetRxOnWhenIdle(false), kErrorNone);
ASSERT_EQ(platform.mRadioSpinel.ReceiveAt(100000, 10000, 11), kErrorNone);
platform.GoInUs(100000);
EXPECT_EQ(platform.GetReceiveChannel(), 0);
platform.GoInUs(1);
EXPECT_EQ(platform.GetReceiveChannel(), 11);
platform.GoInUs(10000);
EXPECT_EQ(platform.GetReceiveChannel(), 0);
}

0 comments on commit 9277bce

Please sign in to comment.