Skip to content

Commit

Permalink
[radio] add OT_RADIO_CAPS_RX_ON_WHEN_IDLE capability (#9554) (#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 authored Nov 14, 2023
1 parent 86215fc commit 193e77e
Show file tree
Hide file tree
Showing 18 changed files with 175 additions and 22 deletions.
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 @@ -511,7 +534,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 @@ -978,6 +1001,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 193e77e

Please sign in to comment.