Skip to content

Commit

Permalink
[radio] add OT_RADIO_CAPS_RX_ON_WHEN_IDLE capability (openthread#9554)
Browse files Browse the repository at this point in the history
Add a new `OT_RADIO_CAPS_RX_ON_WHEN_IDLE` radio capability which lets
OpenThread know what the radio idle state operation will be: receive
or sleep.

This allows to save power on sleepy devices since the active --> idle
transition is much faster, specially for the cases in which there is
some kind of serialization between OpenThread core and the radio
driver.
  • Loading branch information
edmont committed Nov 3, 2023
1 parent 5058500 commit ddc7edd
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE 1
#endif

#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE 1
#endif

#endif // OPENTHREAD_RADIO

#ifndef OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
Expand Down
2 changes: 1 addition & 1 deletion include/openthread/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (370)
#define OPENTHREAD_API_VERSION (371)

/**
* @addtogroup api-instance
Expand Down
35 changes: 34 additions & 1 deletion include/openthread/platform/radio.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ enum
* The value is a bit-field indicating the capabilities supported by the radio. See `OT_RADIO_CAPS_*` definitions.
*
*/
typedef uint8_t otRadioCaps;
typedef uint16_t otRadioCaps;

/**
* Defines constants that are used to indicate different radio capabilities. See `otRadioCaps`.
Expand All @@ -136,6 +136,7 @@ enum
OT_RADIO_CAPS_TRANSMIT_SEC = 1 << 5, ///< Radio supports tx security.
OT_RADIO_CAPS_TRANSMIT_TIMING = 1 << 6, ///< Radio supports tx at specific time.
OT_RADIO_CAPS_RECEIVE_TIMING = 1 << 7, ///< Radio supports rx at specific time.
OT_RADIO_CAPS_RX_ON_WHEN_IDLE = 1 << 8, ///< Radio supports RxOnWhenIdle handling.
};

#define OT_PANID_BROADCAST 0xffff ///< IEEE 802.15.4 Broadcast PAN ID
Expand Down Expand Up @@ -621,6 +622,38 @@ bool otPlatRadioGetPromiscuous(otInstance *aInstance);
*/
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable);

/**
* Sets the rx-on-when-idle state to the radio platform.
*
* There are a few situations that the radio can enter sleep state if the device is in rx-off-when-idle state but
* it's hard and costly for the SubMac to identify these situations and instruct the radio to enter sleep:
*
* - Finalization of a regular frame reception task, provided that:
* - The frame is received without errors and passes the filtering and it's not an spurious ACK.
* - ACK is not requested or transmission of ACK is not possible due to internal conditions.
* - Finalization of a frame transmission or transmission of an ACK frame, when ACK is not requested in the transmitted
* frame.
* - Finalization of the reception operation of a requested ACK due to:
* - ACK timeout expiration.
* - Reception of an invalid ACK or not an ACK frame.
* - Reception of the proper ACK, unless the transmitted frame was a Data Request Command and the frame pending bit
* on the received ACK is set to true. In this case the radio platform implementation SHOULD keep the receiver on
* until a determined timeout which triggers an idle period start.`OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT` can be
* taken as a reference for this.
* - Finalization of a stand alone CCA task.
* - Finalization of a CCA operation with busy result during CSMA/CA procedure.
* - Finalization of an Energy Detection task.
* - Finalization of a radio reception window scheduled with `otPlatRadioReceiveAt`.
*
* If a platform supports `OT_RADIO_CAPS_RX_ON_WHEN_IDLE` it must also support `OT_RADIO_CAPS_CSMA_BACKOFF` and handle
* idle periods after CCA as described above.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aEnable TRUE to keep radio in Receive state, FALSE to put to Sleep state during idle periods.
*
*/
void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable);

/**
* Update MAC keys and key index
*
Expand Down
10 changes: 10 additions & 0 deletions src/core/config/mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,16 @@
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE 0
#endif

/**
* @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE
*
* Define to 1 to enable software rx off when idle switching.
*
*/
#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE
#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE 0
#endif

/**
* @def OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
*
Expand Down
4 changes: 2 additions & 2 deletions src/core/mac/mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
#endif
}

mLinks.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous);
mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
UpdateIdleMode();

exit:
Expand Down Expand Up @@ -2103,7 +2103,7 @@ void Mac::SetPromiscuous(bool aPromiscuous)
mShouldDelaySleep = false;
#endif

mLinks.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous);
mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
UpdateIdleMode();
}

Expand Down
10 changes: 5 additions & 5 deletions src/core/mac/mac_links.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -402,17 +402,17 @@ class Links : public InstanceLocator
}

/**
* Indicates whether radio should stay in Receive or Sleep during CSMA backoff.
* Indicates whether radio should stay in Receive or Sleep during idle periods.
*
* @param[in] aRxOnWhenBackoff TRUE to keep radio in Receive, FALSE to put to Sleep during CSMA backoff.
* @param[in] aRxOnWhenIdle TRUE to keep radio in Receive, FALSE to put to Sleep during idle periods.
*
*/
void SetRxOnWhenBackoff(bool aRxOnWhenBackoff)
void SetRxOnWhenIdle(bool aRxOnWhenIdle)
{
#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
mSubMac.SetRxOnWhenBackoff(aRxOnWhenBackoff);
mSubMac.SetRxOnWhenIdle(aRxOnWhenIdle);
#endif
OT_UNUSED_VARIABLE(aRxOnWhenBackoff);
OT_UNUSED_VARIABLE(aRxOnWhenIdle);
}

/**
Expand Down
41 changes: 33 additions & 8 deletions src/core/mac/sub_mac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void SubMac::Init(void)
mTransmitRetries = 0;
mShortAddress = kShortAddrInvalid;
mExtAddress.Clear();
mRxOnWhenBackoff = true;
mRxOnWhenIdle = true;
mEnergyScanMaxRssi = Radio::kInvalidRssi;
mEnergyScanEndTime = Time{0};
#if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
Expand Down Expand Up @@ -141,10 +141,14 @@ otRadioCaps SubMac::GetCaps(void) const
caps |= OT_RADIO_CAPS_RECEIVE_TIMING;
#endif

#if OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE
caps |= OT_RADIO_CAPS_RX_ON_WHEN_IDLE;
#endif

#else
caps = OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES |
OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
OT_RADIO_CAPS_RECEIVE_TIMING;
OT_RADIO_CAPS_RECEIVE_TIMING | OT_RADIO_CAPS_RX_ON_WHEN_IDLE;
#endif

return caps;
Expand Down Expand Up @@ -176,6 +180,20 @@ void SubMac::SetExtAddress(const ExtAddress &aExtAddress)
LogDebg("RadioExtAddress: %s", mExtAddress.ToString().AsCString());
}

void SubMac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
{
mRxOnWhenIdle = aRxOnWhenIdle;

if (RadioSupportsRxOnWhenIdle())
{
#if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
Get<Radio>().SetRxOnWhenIdle(mRxOnWhenIdle);
#endif
}

LogDebg("RxOnWhenIdle: %d", mRxOnWhenIdle);
}

Error SubMac::Enable(void)
{
Error error = kErrorNone;
Expand Down Expand Up @@ -211,17 +229,22 @@ Error SubMac::Disable(void)

Error SubMac::Sleep(void)
{
Error error = Get<Radio>().Sleep();
Error error = kErrorNone;

VerifyOrExit(ShouldHandleTransitionToSleep());

error = Get<Radio>().Sleep();

exit:
if (error != kErrorNone)
{
LogWarn("RadioSleep() failed, error: %s", ErrorToString(error));
ExitNow();
}
else
{
SetState(kStateSleep);
}

SetState(kStateSleep);

exit:
return error;
}

Expand Down Expand Up @@ -507,7 +530,7 @@ void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);

if (mRxOnWhenBackoff)
if (mRxOnWhenIdle)
{
IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
}
Expand Down Expand Up @@ -970,6 +993,8 @@ bool SubMac::ShouldHandleTransmitTargetTime(void) const
return swTxDelay;
}

bool SubMac::ShouldHandleTransitionToSleep(void) const { return (mRxOnWhenIdle || !RadioSupportsRxOnWhenIdle()); }

void SubMac::SetState(State aState)
{
if (mState != aState)
Expand Down
10 changes: 6 additions & 4 deletions src/core/mac/sub_mac.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,12 +278,12 @@ class SubMac : public InstanceLocator, private NonCopyable
}

/**
* Indicates whether radio should stay in Receive or Sleep during CSMA backoff.
* Indicates whether radio should stay in Receive or Sleep during idle periods.
*
* @param[in] aRxOnWhenBackoff TRUE to keep radio in Receive, FALSE to put to Sleep during CSMA backoff.
* @param[in] aRxOnWhenIdle TRUE to keep radio in Receive, FALSE to put to Sleep during idle periods.
*
*/
void SetRxOnWhenBackoff(bool aRxOnWhenBackoff) { mRxOnWhenBackoff = aRxOnWhenBackoff; }
void SetRxOnWhenIdle(bool aRxOnWhenIdle);

/**
* Enables the radio.
Expand Down Expand Up @@ -604,13 +604,15 @@ class SubMac : public InstanceLocator, private NonCopyable
bool RadioSupportsEnergyScan(void) const { return ((mRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN) != 0); }
bool RadioSupportsTransmitTiming(void) const { return ((mRadioCaps & OT_RADIO_CAPS_TRANSMIT_TIMING) != 0); }
bool RadioSupportsReceiveTiming(void) const { return ((mRadioCaps & OT_RADIO_CAPS_RECEIVE_TIMING) != 0); }
bool RadioSupportsRxOnWhenIdle(void) const { return ((mRadioCaps & OT_RADIO_CAPS_RX_ON_WHEN_IDLE) != 0); }

bool ShouldHandleTransmitSecurity(void) const;
bool ShouldHandleCsmaBackOff(void) const;
bool ShouldHandleAckTimeout(void) const;
bool ShouldHandleRetries(void) const;
bool ShouldHandleEnergyScan(void) const;
bool ShouldHandleTransmitTargetTime(void) const;
bool ShouldHandleTransitionToSleep(void) const;

void ProcessTransmitSecurity(void);
void SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId);
Expand Down Expand Up @@ -642,7 +644,7 @@ class SubMac : public InstanceLocator, private NonCopyable
uint8_t mTransmitRetries;
ShortAddress mShortAddress;
ExtAddress mExtAddress;
bool mRxOnWhenBackoff : 1;
bool mRxOnWhenIdle : 1;
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
bool mRadioFilterEnabled : 1;
#endif
Expand Down
1 change: 1 addition & 0 deletions src/core/radio/radio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ void Radio::Init(void)
SetMacFrameCounter(0);

SetPromiscuous(false);
SetRxOnWhenIdle(true);
#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
}
#endif // OPENTHREAD_RADIO
Expand Down
12 changes: 12 additions & 0 deletions src/core/radio/radio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,14 @@ class Radio : public InstanceLocator, private NonCopyable
*/
void SetPromiscuous(bool aEnable);

/**
* Indicates whether radio should stay in Receive or Sleep during idle periods.
*
* @param[in] aEnable TRUE to keep radio in Receive, FALSE to put to Sleep during idle periods.
*
*/
void SetRxOnWhenIdle(bool aEnable);

/**
* Returns the current state of the radio.
*
Expand Down Expand Up @@ -833,6 +841,8 @@ inline bool Radio::GetPromiscuous(void) { return otPlatRadioGetPromiscuous(GetIn

inline void Radio::SetPromiscuous(bool aEnable) { otPlatRadioSetPromiscuous(GetInstancePtr(), aEnable); }

inline void Radio::SetRxOnWhenIdle(bool aEnable) { otPlatRadioSetRxOnWhenIdle(GetInstancePtr(), aEnable); }

inline otRadioState Radio::GetState(void) { return otPlatRadioGetState(GetInstancePtr()); }

inline Error Radio::Enable(void)
Expand Down Expand Up @@ -974,6 +984,8 @@ inline bool Radio::GetPromiscuous(void) { return false; }

inline void Radio::SetPromiscuous(bool) {}

inline void Radio::SetRxOnWhenIdle(bool) {}

inline otRadioState Radio::GetState(void) { return OT_RADIO_STATE_DISABLED; }

inline Error Radio::Enable(void) { return kErrorNone; }
Expand Down
6 changes: 6 additions & 0 deletions src/core/radio/radio_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,9 @@ OT_TOOL_WEAK otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChanne

return kErrorNotImplemented;
}

OT_TOOL_WEAK void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aEnable);
}
18 changes: 18 additions & 0 deletions src/lib/spinel/radio_spinel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ RadioSpinel::RadioSpinel(void)
, mRxSensitivity(0)
, mState(kStateDisabled)
, mIsPromiscuous(false)
, mRxOnWhenIdle(true)
, mIsReady(false)
, mSupportsLogStream(false)
, mSupportsResetToBootloader(false)
Expand Down Expand Up @@ -872,6 +873,18 @@ otError RadioSpinel::SetPromiscuous(bool aEnable)
return error;
}

otError RadioSpinel::SetRxOnWhenIdle(bool aEnable)
{
otError error = OT_ERROR_NONE;

VerifyOrExit(mRxOnWhenIdle != aEnable);
SuccessOrExit(error = Set(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, SPINEL_DATATYPE_BOOL_S, aEnable));
mRxOnWhenIdle = aEnable;

exit:
return error;
}

otError RadioSpinel::SetShortAddress(uint16_t aAddress)
{
otError error = OT_ERROR_NONE;
Expand Down Expand Up @@ -2135,6 +2148,11 @@ void RadioSpinel::RestoreProperties(void)
}
#endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE

if ((mRadioCaps & OT_RADIO_CAPS_RX_ON_WHEN_IDLE) != 0)
{
SuccessOrDie(Set(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, SPINEL_DATATYPE_BOOL_S, mRxOnWhenIdle));
}

CalcRcpTimeOffset();
}
#endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0
Expand Down
13 changes: 13 additions & 0 deletions src/lib/spinel/radio_spinel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,18 @@ class RadioSpinel
*/
otError SetPromiscuous(bool aEnable);

/**
* Sets the status of RxOnWhenIdle mode.
*
* @param[in] aEnable Whether to enable or disable RxOnWhenIdle mode.
*
* @retval OT_ERROR_NONE Succeeded.
* @retval OT_ERROR_BUSY Failed due to another operation is on going.
* @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver.
*
*/
otError SetRxOnWhenIdle(bool aEnable);

/**
* Sets the Short Address for address filtering.
*
Expand Down Expand Up @@ -1067,6 +1079,7 @@ class RadioSpinel

State mState;
bool mIsPromiscuous : 1; ///< Promiscuous mode.
bool mRxOnWhenIdle : 1; ///< RxOnWhenIdle mode.
bool mIsReady : 1; ///< NCP ready.
bool mSupportsLogStream : 1; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format.
bool mSupportsResetToBootloader : 1; ///< RCP supports resetting into bootloader mode.
Expand Down
1 change: 1 addition & 0 deletions src/lib/spinel/spinel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1263,6 +1263,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key)
{SPINEL_PROP_MAC_PROMISCUOUS_MODE, "MAC_PROMISCUOUS_MODE"},
{SPINEL_PROP_MAC_ENERGY_SCAN_RESULT, "MAC_ENERGY_SCAN_RESULT"},
{SPINEL_PROP_MAC_DATA_POLL_PERIOD, "MAC_DATA_POLL_PERIOD"},
{SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, "MAC_RX_ON_WHEN_IDLE_MODE"},
{SPINEL_PROP_MAC_ALLOWLIST, "MAC_ALLOWLIST"},
{SPINEL_PROP_MAC_ALLOWLIST_ENABLED, "MAC_ALLOWLIST_ENABLED"},
{SPINEL_PROP_MAC_EXTENDED_ADDR, "MAC_EXTENDED_ADDR"},
Expand Down
Loading

0 comments on commit ddc7edd

Please sign in to comment.