[radio] add OT_RADIO_CAPS_RX_ON_WHEN_IDLE capability (#9554) (#9554)

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.
This commit is contained in:
Eduardo Montoya
2023-11-14 11:46:10 +01:00
committed by GitHub
parent 86215fc31c
commit 193e77e40e
18 changed files with 175 additions and 22 deletions
+1 -1
View File
@@ -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
+34 -1
View File
@@ -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`.
@@ -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
@@ -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
*
+10
View File
@@ -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
*
+2 -2
View File
@@ -414,7 +414,7 @@ void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
#endif
}
mLinks.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous);
mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
UpdateIdleMode();
exit:
@@ -2103,7 +2103,7 @@ void Mac::SetPromiscuous(bool aPromiscuous)
mShouldDelaySleep = false;
#endif
mLinks.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous);
mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
UpdateIdleMode();
}
+5 -5
View File
@@ -402,17 +402,17 @@ public:
}
/**
* 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);
}
/**
+33 -8
View File
@@ -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
@@ -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;
@@ -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;
@@ -211,17 +229,22 @@ exit:
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;
}
@@ -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()));
}
@@ -978,6 +1001,8 @@ exit:
return swTxDelay;
}
bool SubMac::ShouldHandleTransitionToSleep(void) const { return (mRxOnWhenIdle || !RadioSupportsRxOnWhenIdle()); }
void SubMac::SetState(State aState)
{
if (mState != aState)
+6 -4
View File
@@ -278,12 +278,12 @@ public:
}
/**
* 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.
@@ -604,6 +604,7 @@ private:
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;
@@ -611,6 +612,7 @@ private:
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);
@@ -642,7 +644,7 @@ private:
uint8_t mTransmitRetries;
ShortAddress mShortAddress;
ExtAddress mExtAddress;
bool mRxOnWhenBackoff : 1;
bool mRxOnWhenIdle : 1;
#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
bool mRadioFilterEnabled : 1;
#endif
+1
View File
@@ -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
+12
View File
@@ -434,6 +434,14 @@ public:
*/
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.
*
@@ -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)
@@ -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; }
+6
View File
@@ -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
View File
@@ -70,6 +70,7 @@ RadioSpinel::RadioSpinel(void)
, mRxSensitivity(0)
, mState(kStateDisabled)
, mIsPromiscuous(false)
, mRxOnWhenIdle(true)
, mIsReady(false)
, mSupportsLogStream(false)
, mSupportsResetToBootloader(false)
@@ -872,6 +873,18 @@ exit:
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;
@@ -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
+13
View File
@@ -103,6 +103,18 @@ public:
*/
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.
*
@@ -1067,6 +1079,7 @@ private:
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.
+1
View File
@@ -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"},
+12 -1
View File
@@ -420,7 +420,7 @@
* Please see section "Spinel definition compatibility guideline" for more details.
*
*/
#define SPINEL_RCP_API_VERSION 9
#define SPINEL_RCP_API_VERSION 10
/**
* @def SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION
@@ -2128,6 +2128,17 @@ enum
*/
SPINEL_PROP_MAC_DATA_POLL_PERIOD = SPINEL_PROP_MAC__BEGIN + 10,
/// MAC RxOnWhenIdle mode
/** Format: `b`
*
* Set to true to enable RxOnWhenIdle or false to disable it.
* When True, the radio is expected to stay in receive state during
* idle periods. When False, the radio is expected to switch to sleep
* state during idle periods.
*
*/
SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE = SPINEL_PROP_MAC__BEGIN + 11,
SPINEL_PROP_MAC__END = 0x40,
SPINEL_PROP_MAC_EXT__BEGIN = 0x1300,
+12
View File
@@ -1460,6 +1460,18 @@ exit:
return error;
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE>(void)
{
bool enabled;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadBool(enabled));
otPlatRadioSetRxOnWhenIdle(mInstance, enabled);
exit:
return error;
}
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_15_4_PANID>(void)
{
return mEncoder.WriteUint16(otLinkGetPanId(mInstance));
+1
View File
@@ -430,6 +430,7 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey)
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_PROMISCUOUS_MODE),
#if OPENTHREAD_MTD || OPENTHREAD_FTD
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_DATA_POLL_PERIOD),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_IF_UP),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_STACK_UP),
OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NET_ROLE),
+6
View File
@@ -260,6 +260,12 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnabled)
OT_UNUSED_VARIABLE(aEnabled);
}
void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnabled)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aEnabled);
}
bool otPlatRadioIsEnabled(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
+2
View File
@@ -126,6 +126,8 @@ OT_TOOL_WEAK void otPlatRadioSetShortAddress(otInstance *, uint16_t) {}
OT_TOOL_WEAK void otPlatRadioSetPromiscuous(otInstance *, bool) {}
OT_TOOL_WEAK void otPlatRadioSetRxOnWhenIdle(otInstance *, bool) {}
OT_TOOL_WEAK bool otPlatRadioIsEnabled(otInstance *) { return true; }
OT_TOOL_WEAK otError otPlatRadioEnable(otInstance *) { return OT_ERROR_NONE; }