From a94fc444257a39f0fc3b7d72c4a067b948437675 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Tue, 7 Mar 2023 13:09:34 -0800 Subject: [PATCH] [radio] mechanism to switch to PAN channel more quickly on CSL tx (#8701) This commit adds `mRxChannelAfterTxDone` in `otRadioFrame` which specifies the RX channel after frame TX is done (after all retries and ack received). This is intended for situations where they may be delay in interactions between OT stack and radio, as example this is used in RCP and host architecture to make sure RCP switches to PAN channel more quickly. In particular, this can help with CSL tx to a sleepy child, where the child may use a different channel for CSL than the PAN channel. After frame tx, we want the radio/RCP to go back to PAN channel quickly to ensure that parent does not miss any tx from child afterwards, e.g., child responding to the earlier CSL transmitted frame from parent using PAN channel while radio still staying on CSL channel. This commit adds support for the `mRxChannelAfterTxDone` parameter in spinel and posix host `RadioSpinel`. This is added in a backward compatible way, i.e., if RCP is using a newer version but host is older and does not provide the new parameter, RCP will use `frame.mChannel` as default value for this, and if host is using a newer version, older RCP will ignore it. --- include/openthread/instance.h | 2 +- include/openthread/platform/radio.h | 21 +++++++++++++++ src/core/mac/mac_frame.hpp | 38 ++++++++++++++++++++++------ src/core/mac/sub_mac.cpp | 13 ++++++++++ src/core/thread/csl_tx_scheduler.cpp | 5 ++++ src/core/thread/mesh_forwarder.cpp | 1 + src/lib/spinel/radio_spinel_impl.hpp | 24 ++++++++++-------- src/lib/spinel/spinel.h | 22 +++++++++++----- src/ncp/ncp_base_radio.cpp | 18 +++++++------ 9 files changed, 109 insertions(+), 35 deletions(-) diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 5ad1b256a..2ee6c79f0 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (295) +#define OPENTHREAD_API_VERSION (296) /** * @addtogroup api-instance diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index 5d90e501a..240ce45bc 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -277,6 +277,27 @@ typedef struct otRadioFrame uint8_t mMaxCsmaBackoffs; ///< Maximum number of backoffs attempts before declaring CCA failure. uint8_t mMaxFrameRetries; ///< Maximum number of retries allowed after a transmission failure. + /** + * The RX channel after frame TX is done (after all frame retries - ack received, or timeout, or abort). + * + * Radio platforms can choose to fully ignore this. OT stack will make sure to call `otPlatRadioReceive()` + * with the desired RX channel after a frame TX is done and signaled in `otPlatRadioTxDone()` callback. + * Radio platforms that don't provide `OT_RADIO_CAPS_TRANSMIT_RETRIES` must always ignore this. + * + * This is intended for situations where there may be delay in interactions between OT stack and radio, as + * an example this is used in RCP/host architecture to make sure RCP switches to PAN channel more quickly. + * In particular, this can help with CSL tx to a sleepy child, where the child may use a different channel + * for CSL than the PAN channel. After frame tx, we want the radio/RCP to go back to the PAN channel + * quickly to ensure that parent does not miss tx from child afterwards, e.g., child responding to the + * earlier CSL transmitted frame from parent using PAN channel while radio still staying on CSL channel. + * + * The switch to the RX channel MUST happen after the frame TX is fully done, i.e., after all retries and + * when ack is received (when "Ack Request" flag is set on the TX frame) or ack timeout. Note that ack is + * expected on the same channel that frame is sent on. + * + */ + uint8_t mRxChannelAfterTxDone; + /** * Indicates whether frame counter and CSL IEs are properly updated in the header. * diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index edfb1a0da..bd21317bf 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -836,14 +836,6 @@ public: */ uint8_t GetChannel(void) const { return mChannel; } - /** - * This method sets the IEEE 802.15.4 channel used for transmission or reception. - * - * @param[in] aChannel The IEEE 802.15.4 channel used for transmission or reception. - * - */ - void SetChannel(uint8_t aChannel) { mChannel = aChannel; } - /** * This method returns the IEEE 802.15.4 PSDU length. * @@ -1262,6 +1254,36 @@ public: class TxFrame : public Frame { public: + /** + * This method sets the channel on which to send the frame. + * + * It also sets the `RxChannelAfterTxDone` to the same channel. + * + * @param[in] aChannel The channel used for transmission. + * + */ + void SetChannel(uint8_t aChannel) + { + mChannel = aChannel; + SetRxChannelAfterTxDone(aChannel); + } + + /** + * This method gets the RX channel after frame TX is done. + * + * @returns The RX channel after frame TX is done. + * + */ + uint8_t GetRxChannelAfterTxDone(void) const { return mInfo.mTxInfo.mRxChannelAfterTxDone; } + + /** + * This method sets the RX channel after frame TX is done. + * + * @param[in] aChannel The RX channel after frame TX is done. + * + */ + void SetRxChannelAfterTxDone(uint8_t aChannel) { mInfo.mTxInfo.mRxChannelAfterTxDone = aChannel; } + /** * This method returns the maximum number of backoffs the CSMA-CA algorithm will attempt before declaring a channel * access failure. diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index ba16d12c8..194647023 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -619,6 +619,19 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro SetState(kStateReceive); +#if OPENTHREAD_RADIO + if (aFrame.GetChannel() != aFrame.GetRxChannelAfterTxDone()) + { + // On RCP build, we switch immediately to the specified RX + // channel if it is different from the channel on which frame + // was sent. On FTD or MTD builds we don't need to do + // the same as the `Mac` will switch the channel from the + // `mCallbacks.TransmitDone()`. + + IgnoreError(Get().Receive(aFrame.GetRxChannelAfterTxDone())); + } +#endif + mCallbacks.TransmitDone(aFrame, aAckFrame, aError); exit: diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index b929e6651..390fd303c 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -214,6 +214,11 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames) frame->SetChannel(mCslTxChild->GetCslChannel() == 0 ? Get().GetPanChannel() : mCslTxChild->GetCslChannel()); + if (frame->GetChannel() != Get().GetPanChannel()) + { + frame->SetRxChannelAfterTxDone(Get().GetPanChannel()); + } + delay = GetNextCslTransmissionDelay(*mCslTxChild, txDelay, /* aAheadUs */ 0); // We make sure that delay is less than `mCslFrameRequestAheadUs` diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 07c676515..ccc6a6fbc 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -871,6 +871,7 @@ start: { case Message::kSubTypeMleAnnounce: aFrame.SetChannel(aMessage.GetChannel()); + aFrame.SetRxChannelAfterTxDone(Get().GetPanChannel()); panIds.mDestination = Mac::kPanIdBroadcast; break; diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index 155fc9012..d4d78f176 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -1980,21 +1980,23 @@ otError RadioSpinel::Transmit(otRadioFrame &a otPlatRadioTxStarted(mInstance, mTransmitFrame); error = Request(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW, - SPINEL_DATATYPE_DATA_WLEN_S // Frame data - SPINEL_DATATYPE_UINT8_S // Channel - SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs - SPINEL_DATATYPE_UINT8_S // MaxFrameRetries - SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled - SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated - SPINEL_DATATYPE_BOOL_S // IsARetx - SPINEL_DATATYPE_BOOL_S // SkipAes - SPINEL_DATATYPE_UINT32_S // TxDelay - SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime + SPINEL_DATATYPE_DATA_WLEN_S // Frame data + SPINEL_DATATYPE_UINT8_S // Channel + SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs + SPINEL_DATATYPE_UINT8_S // MaxFrameRetries + SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled + SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated + SPINEL_DATATYPE_BOOL_S // IsARetx + SPINEL_DATATYPE_BOOL_S // IsSecurityProcessed + SPINEL_DATATYPE_UINT32_S // TxDelay + SPINEL_DATATYPE_UINT32_S // TxDelayBaseTime + SPINEL_DATATYPE_UINT8_S, // RxChannelAfterTxDone mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel, mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries, mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled, mTransmitFrame->mInfo.mTxInfo.mIsHeaderUpdated, mTransmitFrame->mInfo.mTxInfo.mIsARetx, mTransmitFrame->mInfo.mTxInfo.mIsSecurityProcessed, - mTransmitFrame->mInfo.mTxInfo.mTxDelay, mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime); + mTransmitFrame->mInfo.mTxInfo.mTxDelay, mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime, + mTransmitFrame->mInfo.mTxInfo.mRxChannelAfterTxDone); if (error == OT_ERROR_NONE) { diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 6af510635..0324729ec 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -420,7 +420,7 @@ * Please see section "Spinel definition compatibility guideline" for more details. * */ -#define SPINEL_RCP_API_VERSION 8 +#define SPINEL_RCP_API_VERSION 9 /** * @def SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION @@ -3511,19 +3511,27 @@ enum * over the radio. This allows the caller to use the radio directly. * * The frame meta data for the `CMD_PROP_VALUE_SET` contains the following - * optional fields. Default values are used for all unspecified fields. + * fields. Default values are used for all unspecified fields. * - * `C` : Channel (for frame tx) + * `C` : Channel (for frame tx) - MUST be included. * `C` : Maximum number of backoffs attempts before declaring CCA failure * (use Thread stack default if not specified) * `C` : Maximum number of retries allowed after a transmission failure * (use Thread stack default if not specified) * `b` : Set to true to enable CSMA-CA for this packet, false otherwise. * (default true). - * `b` : Set to true to indicate it is a retransmission packet, false otherwise. - * (default false). - * `b` : Set to true to indicate that SubMac should skip AES processing, false otherwise. - * (default false). + * `b` : Set to true to indicate if header is updated - related to + * `mIsHeaderUpdated` in `otRadioFrame` (default false). + * `b` : Set to true to indicate it is a retransmission - related to + * `mIsARetx` in `otRadioFrame` (default false). + * `b` : Set to true to indicate security was processed on tx frame + * `mIsSecurityProcessed` in `otRadioFrame` (default false). + * `L` : TX delay interval used for CSL - related to `mTxDelay` in + * `otRadioFrame` (default zero). + * `L` : TX delay based time used for CSL - related to `mTxDelayBaseTime` + * in `otRadioFrame` (default zero). + * `C` : RX channel after TX done (default assumed to be same as + * channel in metadata) * */ SPINEL_PROP_STREAM_RAW = SPINEL_PROP_STREAM__BEGIN + 1, diff --git a/src/ncp/ncp_base_radio.cpp b/src/ncp/ncp_base_radio.cpp index 4cd679981..9ebb3e290 100644 --- a/src/ncp/ncp_base_radio.cpp +++ b/src/ncp/ncp_base_radio.cpp @@ -408,14 +408,15 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame) SuccessOrExit(error = mDecoder.ReadUint8(aFrame.mChannel)); // Set the default value for all optional parameters. - aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs = OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT; - aFrame.mInfo.mTxInfo.mMaxFrameRetries = OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT; - aFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; - aFrame.mInfo.mTxInfo.mIsHeaderUpdated = false; - aFrame.mInfo.mTxInfo.mIsARetx = false; - aFrame.mInfo.mTxInfo.mIsSecurityProcessed = false; - aFrame.mInfo.mTxInfo.mTxDelay = 0; - aFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0; + aFrame.mInfo.mTxInfo.mRxChannelAfterTxDone = aFrame.mChannel; + aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs = OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT; + aFrame.mInfo.mTxInfo.mMaxFrameRetries = OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT; + aFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; + aFrame.mInfo.mTxInfo.mIsHeaderUpdated = false; + aFrame.mInfo.mTxInfo.mIsARetx = false; + aFrame.mInfo.mTxInfo.mIsSecurityProcessed = false; + aFrame.mInfo.mTxInfo.mTxDelay = 0; + aFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0; // All the next parameters are optional. Note that even if the // decoder fails to parse any of optional parameters we still want to @@ -430,6 +431,7 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame) SuccessOrExit(mDecoder.ReadBool(isSecurityProcessed)); SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelay)); SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelayBaseTime)); + SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mRxChannelAfterTxDone)); aFrame.mInfo.mTxInfo.mCsmaCaEnabled = csmaEnable; aFrame.mInfo.mTxInfo.mIsHeaderUpdated = isHeaderUpdated; aFrame.mInfo.mTxInfo.mIsARetx = isARetx;