[spinel] add SPINEL_PROP_MAC_RX_AT (#10921)

This commit adds receive at functionality for spinel for host to
schedule reception on RCP.
This commit is contained in:
Yakun Xu
2024-11-18 08:53:55 -08:00
committed by GitHub
parent 0579a4d3af
commit 9277bce08e
8 changed files with 169 additions and 17 deletions
+14
View File
@@ -1727,6 +1727,20 @@ exit:
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;
+16
View File
@@ -569,6 +569,22 @@ public:
*/
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.
*
+11
View File
@@ -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,
+26
View File
@@ -46,6 +46,7 @@
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "lib/spinel/spinel.h"
#include "radio/radio.hpp"
namespace ot {
@@ -1685,6 +1686,31 @@ exit:
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)
{
+1
View File
@@ -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),
+41 -14
View File
@@ -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>
@@ -124,11 +122,15 @@ void FakePlatform::StartMilliAlarm(uint32_t aT0, uint32_t aDt)
void FakePlatform::StopMilliAlarm() { mMilliAlarmStart = kAlarmStop; }
void FakePlatform::ProcessAlarm(uint64_t &aTimeout)
{
uint64_t end = mNow + aTimeout;
template <> void FakePlatform::HandleSchedule<&FakePlatform::mReceiveAtStart>() { mChannel = mReceiveAtChannel; }
uint64_t *alarm = &end;
template <> void FakePlatform::HandleSchedule<&FakePlatform::mReceiveAtEnd>() { mChannel = 0; }
void FakePlatform::ProcessSchedules(uint64_t &aTimeout)
{
uint64_t guard = mNow + aTimeout;
uint64_t *alarm = &guard;
#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
if (mMicroAlarmStart < *alarm)
{
@@ -139,6 +141,14 @@ void FakePlatform::ProcessAlarm(uint64_t &aTimeout)
{
alarm = &mMilliAlarmStart;
}
if (mReceiveAtStart < *alarm)
{
alarm = &mReceiveAtStart;
}
else if (mReceiveAtEnd < *alarm)
{
alarm = &mReceiveAtEnd;
}
if (mNow < *alarm)
{
@@ -146,17 +156,29 @@ void FakePlatform::ProcessAlarm(uint64_t &aTimeout)
mNow = *alarm;
}
*alarm = kAlarmStop;
if (alarm == &guard)
{
// nonthing scheduled within this period.
}
else if (alarm == &mReceiveAtEnd)
{
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
if (alarm == &mMicroAlarmStart)
else if (alarm == &mMicroAlarmStart)
{
otPlatAlarmMicroFired(mInstance);
FakePlatform::HandleSchedule<&FakePlatform::mMicroAlarmStart>();
}
else
#endif
if (alarm == &mMilliAlarmStart)
{
otPlatAlarmMilliFired(mInstance);
}
}
uint64_t FakePlatform::Run(uint64_t aTimeoutInUs)
@@ -167,7 +189,7 @@ uint64_t FakePlatform::Run(uint64_t aTimeoutInUs)
}
else
{
ProcessAlarm(aTimeoutInUs);
ProcessSchedules(aTimeoutInUs);
}
return aTimeoutInUs;
@@ -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);
+32 -3
View File
@@ -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>
@@ -77,7 +79,16 @@ public:
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;
@@ -98,7 +109,7 @@ public:
virtual uint64_t GetEui64() const { return 0; }
protected:
void ProcessAlarm(uint64_t &aTimeout);
void ProcessSchedules(uint64_t &aTimeout);
static constexpr uint64_t kAlarmStop = 0xffffffffffffffffUL;
@@ -115,15 +126,33 @@ protected:
#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
View File
@@ -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);
}