diff --git a/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h b/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h index a41610ed4..a312d84bb 100644 --- a/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h +++ b/examples/platforms/nrf528xx/nrf52840/openthread-core-nrf52840-config.h @@ -268,6 +268,16 @@ #define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0 #endif +/** + * @def OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW + * + * CSL sample window in units of 10 symbols. + * + */ +#ifndef OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW +#define OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW 30 +#endif + /* * Suppress the ARMCC warning on unreachable statement, * e.g. break after assert(false) or ExitNow() macro. diff --git a/examples/platforms/nrf528xx/src/radio.c b/examples/platforms/nrf528xx/src/radio.c index 9021796ca..ac21307f7 100644 --- a/examples/platforms/nrf528xx/src/radio.c +++ b/examples/platforms/nrf528xx/src/radio.c @@ -41,6 +41,8 @@ #include #include +#include + #include "utils/code_utils.h" #include "utils/mac_frame.h" @@ -56,6 +58,7 @@ #include #include +#include #include #include @@ -109,6 +112,12 @@ static uint32_t sEnergyDetectionTime; static uint8_t sEnergyDetectionChannel; static int8_t sEnergyDetected; +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +static uint32_t sCslPeriod; +static uint32_t sCslSampleTime; +static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI}; +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + typedef enum { kPendingEventSleep, // Requested to enter Sleep state. @@ -413,7 +422,7 @@ otError otPlatRadioSleep(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - if (nrf_802154_sleep()) + if (nrf_802154_sleep_if_idle()) { clearPendingEvents(); } @@ -818,7 +827,7 @@ void nrf5RadioProcess(otInstance *aInstance) if (isPendingEventSet(kPendingEventSleep)) { - if (nrf_802154_sleep()) + if (nrf_802154_sleep_if_idle()) { resetPendingEvent(kPendingEventSleep); } @@ -943,6 +952,17 @@ void nrf_802154_receive_failed(nrf_802154_rx_error_t error) setPendingEvent(kPendingEventReceiveFailed); } +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +static uint16_t getCslPhase() +{ + uint32_t curTime = otPlatAlarmMicroGetNow(); + uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS; + uint32_t diff = ((sCslSampleTime % cslPeriodInUs) - (curTime % cslPeriodInUs) + cslPeriodInUs) % cslPeriodInUs; + + return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS); +} +#endif + void nrf_802154_tx_ack_started(uint8_t *p_data) { // Check if the frame pending bit is set in ACK frame. @@ -950,6 +970,16 @@ void nrf_802154_tx_ack_started(uint8_t *p_data) #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 // Update IE and secure Enh-ACK. +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (sCslPeriod > 0) + { + otRadioFrame ackFrame; + ackFrame.mPsdu = (uint8_t *)(p_data + 1); + ackFrame.mLength = p_data[0]; + otMacFrameSetCslIe(&ackFrame, sCslPeriod, getCslPhase()); + } +#endif + txAckProcessSecurity(p_data); #endif } @@ -1046,6 +1076,13 @@ void nrf_802154_tx_started(const uint8_t *aFrame) } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (sCslPeriod > 0) + { + otMacFrameSetCslIe(&sTransmitFrame, (uint16_t)sCslPeriod, getCslPhase()); + } +#endif + #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 otEXPECT(otMacFrameIsSecurityEnabled(&sTransmitFrame) && otMacFrameIsKeyIdMode1(&sTransmitFrame) && !sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed); @@ -1084,6 +1121,13 @@ uint32_t nrf_802154_random_get(void) return otRandomNonCryptoGetUint32(); } +uint64_t otPlatRadioGetNow(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return otPlatTimeGet(); +} + #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 void otPlatRadioSetMacKey(otInstance * aInstance, uint8_t aKeyIdMode, @@ -1118,3 +1162,58 @@ void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCoun CRITICAL_REGION_EXIT(); } #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +static void updateIeData(otInstance *aInstance, const uint8_t *aShortAddr, const uint8_t *aExtAddr) +{ + int8_t offset = 0; + uint8_t ackIeData[OT_ACK_IE_MAX_SIZE]; + + if (sCslPeriod > 0) + { + memcpy(ackIeData, sCslIeHeader, OT_IE_HEADER_SIZE); + offset += OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE; // reserve space for CSL IE + } + + if (offset > 0) + { + nrf_802154_ack_data_set(aShortAddr, false, ackIeData, offset, NRF_802154_ACK_DATA_IE); + nrf_802154_ack_data_set(aExtAddr, true, ackIeData, offset, NRF_802154_ACK_DATA_IE); + } + else + { + nrf_802154_ack_data_clear(aShortAddr, false, NRF_802154_ACK_DATA_IE); + nrf_802154_ack_data_clear(aExtAddr, true, NRF_802154_ACK_DATA_IE); + } +} + +otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const otExtAddress *aExtAddr) +{ + otError error = OT_ERROR_NONE; + uint8_t parentExtAddr[OT_EXT_ADDRESS_SIZE]; + uint8_t parentShortAddress[SHORT_ADDRESS_SIZE]; + const uint8_t *shortAddress; + + sCslPeriod = aCslPeriod; + + for (uint32_t i = 0; i < sizeof(parentExtAddr); i++) + { + parentExtAddr[i] = aExtAddr->m8[sizeof(parentExtAddr) - i - 1]; + } + + shortAddress = nrf_802154_pib_short_address_get(); + memcpy(parentShortAddress, shortAddress, SHORT_ADDRESS_SIZE); + parentShortAddress[0] &= 0xfc; + + updateIeData(aInstance, parentShortAddress, parentExtAddr); + + return error; +} + +void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) +{ + OT_UNUSED_VARIABLE(aInstance); + + sCslSampleTime = aCslSampleTime; +} +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 222c8b9b5..d39534c49 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -120,7 +120,7 @@ static uint8_t sAckIeDataLength = 0; #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -static const uint8_t sCslIeHeader[OT_IE_HEADER_IE_SIZE] = {0x04, 0x0d}; +static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI}; static uint32_t sCslSampleTime; static uint32_t sCslPeriod; #endif @@ -1073,8 +1073,8 @@ static otError updateIeData(otInstance *aInstance) if (sCslPeriod > 0) { - memcpy(sAckIeData, sCslIeHeader, OT_IE_HEADER_IE_SIZE); - offset += OT_IE_HEADER_IE_SIZE + OT_CSL_IE_SIZE; // reserve space for CSL IE + memcpy(sAckIeData, sCslIeHeader, OT_IE_HEADER_SIZE); + offset += OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE; // reserve space for CSL IE } sAckIeDataLength = offset; diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index a6db1fa39..7b0c039a3 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -149,11 +149,14 @@ typedef uint16_t otShortAddress; */ enum { - OT_IE_HEADER_IE_SIZE = 2, ///< Size of IE header in bytes. - OT_CSL_IE_SIZE = 4, ///< Size of CSL IE content in bytes. - OT_ACK_IE_MAX_SIZE = 16, ///< Max length for header IE in ACK. + OT_IE_HEADER_SIZE = 2, ///< Size of IE header in bytes. + OT_CSL_IE_SIZE = 4, ///< Size of CSL IE content in bytes. + OT_ACK_IE_MAX_SIZE = 16, ///< Max length for header IE in ACK. }; +#define CSL_IE_HEADER_BYTES_LO 0x04 ///< Fixed CSL IE header first byte +#define CSL_IE_HEADER_BYTES_HI 0x0d ///< Fixed CSL IE header second byte + /** * @struct otExtAddress * diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index bec0fe679..6b5ea92fb 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -2281,10 +2281,10 @@ exit: void Mac::SetCslPeriod(uint16_t aPeriod) { mSubMac.SetCslPeriod(aPeriod); - IgnoreError(Get().EnableCsl(GetCslPeriod(), &Get().GetParent().GetExtAddress())); if (IsCslEnabled()) { + IgnoreError(Get().EnableCsl(GetCslPeriod(), &Get().GetParent().GetExtAddress())); Get().ScheduleChildUpdateRequest(); } diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 0e76ef201..03d04c45e 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -253,10 +253,11 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, otError aError) #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE if (aFrame != nullptr && aError == OT_ERROR_NONE) { - otLogDebgMac( - "Received frame in state (SubMac %s, CSL %d), timestamp %lu, target sample start time %u, time drift %d", - StateToString(mState), mCslState, aFrame->mInfo.mRxInfo.mTimestamp, mCslSampleTime.GetValue(), - static_cast(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue()); + // Split the log into two lines for RTT to output + otLogDebgMac("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState), + CslStateToString(mCslState), static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); + otLogDebgMac("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(), + static_cast(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue()); } #endif @@ -404,7 +405,7 @@ void SubMac::BeginTransmit(void) VerifyOrExit(mState == kStateCsmaBackoff, OT_NOOP); #endif - mTransmitFrame.SetCsmaCaEnabled(mTransmitFrame.mInfo.mTxInfo.mPeriod != 0); + mTransmitFrame.SetCsmaCaEnabled(mTransmitFrame.mInfo.mTxInfo.mPeriod == 0); if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0) { @@ -858,6 +859,30 @@ const char *SubMac::StateToString(State aState) return str; } +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +const char *SubMac::CslStateToString(CslState aCslState) +{ + const char *str = "Unknown"; + + switch (aCslState) + { + case kCslIdle: + str = "CslIdle"; + break; + case kCslSample: + str = "CslSample"; + break; + case kCslSleep: + str = "kCslSleep"; + break; + default: + break; + } + + return str; +} +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + // LCOV_EXCL_STOP //--------------------------------------------------------------------------------------------------------------------- diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 7c08fcdf4..b00579a70 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -520,6 +520,26 @@ private: #endif }; +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * The SSED sample window in units of 10 symbols. + * + */ + enum : uint32_t{ + kCslSampleWindow = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * kUsPerTenSymbols, + }; + + /** + * CSL state, always updated by `mCslTimer`. + * + */ + enum CslState : uint8_t{ + kCslIdle = 0, ///< CSL receiver is not started. + kCslSample, ///< Sampling CSL channel. + kCslSleep, ///< Radio in sleep. + }; +#endif + bool RadioSupportsCsmaBackoff(void) const { return ((mRadioCaps & (OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES)) != 0); @@ -553,6 +573,9 @@ private: void SetState(State aState); static const char *StateToString(State aState); +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + static const char *CslStateToString(CslState aCslState); +#endif otRadioCaps mRadioCaps; State mState; @@ -579,24 +602,6 @@ private: #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - /** - * The SSED sample window in units of 10 symbols. - * - */ - enum : uint32_t{ - kCslSampleWindow = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * kUsPerTenSymbols, - }; - - /** - * CSL state, always updated by `mCslTimer`. - * - */ - enum CslState : uint8_t{ - kCslIdle = 0, ///< CSL receiver is not started. - kCslSample, ///< Sampling CSL channel. - kCslSleep, ///< Radio in sleep. - }; - uint32_t mCslTimeout; ///< The CSL synchronized timeout in seconds. TimeMicro mCslSampleTime; ///< The CSL sample time of the current period. uint16_t mCslPeriod; ///< The CSL sample period, in units of 10 symbols (160 microseconds). diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index fb90279f1..f1beb9295 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -686,6 +686,7 @@ void Mle::SetStateChild(uint16_t aRloc16) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { + IgnoreError(Get().EnableCsl(Get().GetCslPeriod(), &GetParent().GetExtAddress())); ScheduleChildUpdateRequest(); } #endif