From d2fa779d876503e6082419629c61eadbc1259d5d Mon Sep 17 00:00:00 2001 From: Eduardo Montoya Date: Tue, 1 Oct 2024 11:16:48 +0200 Subject: [PATCH] [mac] enable wake-up frame periodic sniffing When `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is enabled: - APIs are available to configure and enable wake-up frames sniffing. - `SubMac::HandleWedTimer` periodically schedules receive slots on wake-up channel. - Wake-up frames are processed. - Upon reception of a wake-up frame, WUL is stopped. --- include/openthread/instance.h | 2 +- include/openthread/link.h | 62 +++++++++++++ src/cli/README.md | 69 +++++++++++++-- src/cli/cli.cpp | 115 +++++++++++++++++++++++++ src/core/BUILD.gn | 1 + src/core/CMakeLists.txt | 2 + src/core/api/link_api.cpp | 61 +++++++++++++ src/core/config/wakeup.h | 18 ++++ src/core/mac/mac.cpp | 148 +++++++++++++++++++++++++++++++- src/core/mac/mac.hpp | 63 +++++++++++++- src/core/mac/mac_links.hpp | 19 ++++ src/core/mac/sub_mac.cpp | 9 ++ src/core/mac/sub_mac.hpp | 33 ++++++- src/core/mac/sub_mac_wed.cpp | 87 +++++++++++++++++++ src/core/radio/radio.hpp | 46 ++++++---- src/core/thread/key_manager.cpp | 12 +++ src/core/thread/key_manager.hpp | 13 +++ src/core/thread/tmf.hpp | 1 - 18 files changed, 733 insertions(+), 28 deletions(-) create mode 100644 src/core/mac/sub_mac_wed.cpp diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 3e3c0105913a..56d63bb35d67 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -52,7 +52,7 @@ extern "C" { * * @note This number versions both OpenThread platform and user APIs. */ -#define OPENTHREAD_API_VERSION (451) +#define OPENTHREAD_API_VERSION (452) /** * @addtogroup api-instance diff --git a/include/openthread/link.h b/include/openthread/link.h index f0bce98e1116..b1c3194ffdf1 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -1126,6 +1126,68 @@ uint8_t otLinkGetWakeupChannel(otInstance *aInstance); */ otError otLinkSetWakeupChannel(otInstance *aInstance, uint8_t aChannel); +/** + * Enables or disables listening for wake-up frames. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnable true to enable listening for wake-up frames, or false otherwise. + * + * @retval OT_ERROR_NONE Successfully enabled / disabled the listening for wake-up frames. + * @retval OT_ERROR_INVALID_STATE Could not enable listening for wake-up frames due to bad configuration. + */ +otError otLinkWedListenSetEnabled(otInstance *aInstance, bool aEnable); + +/** + * Returns whether listening for wake-up frames is enabled. + * + * @retval TRUE If listening for wake-up frames is enabled. + * @retval FALSE If listening for wake-up frames is not enabled. + */ +bool otLinkIsWedListenEnabled(otInstance *aInstance); + +/** + * Gets the WED listen interval in microseconds. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The WED listen interval in microseconds. + */ +uint32_t otLinkGetWedListenInterval(otInstance *aInstance); + +/** + * Sets the WED listen interval in microseconds. + * + * The WED listen interval must be a multiple of `OT_LINK_CSL_PERIOD_TEN_SYMBOLS_UNIT_IN_USEC`, otherwise + * `OT_ERROR_INVALID_ARGS` is returned. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aInterval The WED listen interval in microseconds. + * + * @retval OT_ERROR_NONE Successfully set the WED listen interval. + * @retval OT_ERROR_INVALID_ARGS Invalid WED listen interval. + */ +otError otLinkSetWedListenInterval(otInstance *aInstance, uint32_t aInterval); + +/** + * Gets the WED listen duration. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The WED listen duration in microseconds. + */ +uint16_t otLinkGetWedListenDuration(otInstance *aInstance); + +/** + * Sets the WED listen duration in microseconds. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDuration The WED listen duration in microseconds. + * + * @retval OT_ERROR_NONE Successfully set the WED listen duration. + * @retval OT_ERROR_INVALID_ARGS Invalid WED listen duration. + */ +otError otLinkSetWedListenDuration(otInstance *aInstance, uint16_t aDuration); + /** * @} */ diff --git a/src/cli/README.md b/src/cli/README.md index fbf037d96f8b..3dadfe3a9769 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -133,6 +133,7 @@ Done - [vendor](#vendor-name) - [verhoeff](#verhoeff-calculate) - [version](#version) +- [wakeup](#wakeup) - [wakeupchannel](#wakeupchannel) ## OpenThread Command Details @@ -4393,15 +4394,73 @@ Factory Diagnostics module is enabled only when building OpenThread with `OPENTH [diag]: ../../src/core/diags/README.md -### wakeupchannel +### wakeup -Get the wake-up channel. +Get the Wake-up End Device listen configuration. -Requires `OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE` or `OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE`. +`OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is required. + +```bash +> wakeup +channel: 12 +interval: 1000000us +duration: 8000us +Done +``` + +### wakeup enable + +Enable the WED listening feature. + +`OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is required. + +```bash +> wakeup enable +Done +``` + +### wakeup disable + +Disable the WED listening feature. + +`OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is required. + +```bash +> wakeup disable +Done +``` + +### wakeup state + +Shows the WED listening state, among `disabled` and `enabled`. + +`OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is required. + +```bash +> wakeup state +enabled +Done +``` + +### wakeup interval \ + +Set the WED listen interval in microseconds. Disable WED listening by setting this parameter to `0`. + +`OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is required. + +```bash +> wakeup interval 1000000 +Done +``` + +### wakeup duration \ + +Set the WED listen duration in microseconds. + +`OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE` is required. ```bash -> wakeupchannel -12 +> wakeup duration 8000 Done ``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 9f1710b0ebe5..c01082726dfa 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -8219,6 +8219,115 @@ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otLinkGetWakeupChannel, otLinkSetWakeupChannel); } + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + bool enable; + + /** + * @cli wakeup + * @code + * wakeup + * channel: 12 + * interval: 1000000us + * duration: 8000us + * Done + * @endcode + * @par + * Gets the wake-up listening configuration. + * @sa otLinkGetWakeupChannel + * @sa otLinkGetWedListenInterval + * @sa otLinkGetWedListenDuration + */ + if (aArgs[0].IsEmpty()) + { + OutputLine("channel: %u", otLinkGetWakeupChannel(GetInstancePtr())); + OutputLine("interval: %luus", ToUlong(otLinkGetWedListenInterval(GetInstancePtr()))); + OutputLine("duration: %luus", ToUlong(otLinkGetWedListenDuration(GetInstancePtr()))); + } + /** + * @cli wakeup (enable,disable) + * @code + * wakeup enable + * Done + * @endcode + * @code + * wakeup disable + * Done + * @endcode + * @par api_copy + * #otLinkWedListenSetEnabled + */ + else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE) + { + error = otLinkWedListenSetEnabled(GetInstancePtr(), enable); + } + /** + * @cli wakeup interval + * @code + * wakeup interval 1000000 + * Done + * @endcode + * @cparam wakeup interval @ca{interval} + * @par api_copy + * #otLinkSetWedListenInterval + */ + else if (aArgs[0] == "interval") + { + error = ProcessSet(aArgs + 1, otLinkSetWedListenInterval); + } + /** + * @cli wakeup duration + * @code + * wakeup duration 8000 + * Done + * @endcode + * @cparam wakeup duration @ca{duration} + * @par api_copy + * #otLinkSetWedListenDuration + */ + else if (aArgs[0] == "duration") + { + error = ProcessSet(aArgs + 1, otLinkSetWedListenDuration); + } + /** + * @cli wakeup state + * @code + * wakeup state + * disabled + * Done + * @endcode + * @code + * wakeup state + * enabled + * Done + * @endcode + * @par + * Prints current wake-up listening link state. + * #otLinkIsWedListenEnabled + */ + else if (aArgs[0] == "state") + { + if (otLinkIsWedListenEnabled(GetInstancePtr())) + { + OutputLine("enabled"); + } + else + { + OutputLine("disabled"); + } + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE #endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE #endif // OPENTHREAD_FTD || OPENTHREAD_MTD @@ -8530,6 +8639,12 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD CmdEntry("version"), +#if OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_PERIPHERAL_ENABLE + CmdEntry("wakeup"), + CmdEntry("wakeupchannel"), +#endif +#endif }; #undef CmdEntry diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index ac5f933891ba..228a1c327dd5 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -508,6 +508,7 @@ openthread_core_files = [ "mac/sub_mac.hpp", "mac/sub_mac_callbacks.cpp", "mac/sub_mac_csl_receiver.cpp", + "mac/sub_mac_wed.cpp", "meshcop/announce_begin_client.cpp", "meshcop/announce_begin_client.hpp", "meshcop/border_agent.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0089c42ecf9a..26dec232b606 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -146,6 +146,7 @@ set(COMMON_SOURCES mac/sub_mac.cpp mac/sub_mac_callbacks.cpp mac/sub_mac_csl_receiver.cpp + mac/sub_mac_wed.cpp meshcop/announce_begin_client.cpp meshcop/border_agent.cpp meshcop/commissioner.cpp @@ -295,6 +296,7 @@ set(RADIO_COMMON_SOURCES mac/sub_mac.cpp mac/sub_mac_callbacks.cpp mac/sub_mac_csl_receiver.cpp + mac/sub_mac_wed.cpp radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index 3935b7eee289..7cf7b30bda72 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -504,3 +504,64 @@ otError otLinkGetRegion(otInstance *aInstance, uint16_t *aRegionCode) return error; } + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +otError otLinkWedListenSetEnabled(otInstance *aInstance, bool aEnable) +{ + Error error = kErrorInvalidState; + + VerifyOrExit(otLinkGetWedListenInterval(aInstance) > otLinkGetWedListenDuration(aInstance)); + + error = AsCoreType(aInstance).Get().WedListenEnable(aEnable); + +exit: + return error; +} + +bool otLinkIsWedListenEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsWedListenEnabled(); +} + +uint32_t otLinkGetWedListenInterval(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetWedListenInterval(); +} + +otError otLinkSetWedListenInterval(otInstance *aInstance, uint32_t aInterval) +{ + Error error = kErrorNone; + uint16_t intervalInTenSymbolsUnit; + + if (aInterval == 0) + { + intervalInTenSymbolsUnit = 0; + } + else + { + VerifyOrExit((aInterval % kUsPerTenSymbols) == 0, error = kErrorInvalidArgs); + intervalInTenSymbolsUnit = ClampToUint16(aInterval / kUsPerTenSymbols); + } + + AsCoreType(aInstance).Get().SetWedListenInterval(intervalInTenSymbolsUnit); + +exit: + return error; +} + +uint16_t otLinkGetWedListenDuration(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetWedListenDuration(); +} + +otError otLinkSetWedListenDuration(otInstance *aInstance, uint16_t aDuration) +{ + Error error = kErrorNone; + + VerifyOrExit(aDuration >= kMinWedListenDuration, error = kErrorInvalidArgs); + AsCoreType(aInstance).Get().SetWedListenDuration(aDuration); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE diff --git a/src/core/config/wakeup.h b/src/core/config/wakeup.h index 0dd52386f8df..c9f37cccbbc9 100644 --- a/src/core/config/wakeup.h +++ b/src/core/config/wakeup.h @@ -63,6 +63,24 @@ #define OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_WED_LISTEN_INTERVAL + * + * The default WED listen interval in microseconds. + */ +#ifndef OPENTHREAD_CONFIG_WED_LISTEN_INTERVAL +#define OPENTHREAD_CONFIG_WED_LISTEN_INTERVAL 1000000 +#endif + +/** + * @def OPENTHREAD_CONFIG_WED_LISTEN_DURATION + * + * The default WED listen duration in microseconds. + */ +#ifndef OPENTHREAD_CONFIG_WED_LISTEN_DURATION +#define OPENTHREAD_CONFIG_WED_LISTEN_DURATION 8000 +#endif + /** * @} */ diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 2a119cda2d17..59cea13ae3db 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -59,6 +59,9 @@ Mac::Mac(Instance &aInstance) #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS , mShouldDelaySleep(false) , mDelayingSleep(false) +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + , mWedListenEnabled(false) #endif , mOperation(kOperationIdle) , mPendingOperations(0) @@ -83,6 +86,10 @@ Mac::Mac(Instance &aInstance) , mCslPeriod(0) #endif , mWakeupChannel(OPENTHREAD_CONFIG_DEFAULT_WAKEUP_CHANNEL) +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + , mWedListenInterval(kDefaultWedListenInterval) + , mWedListenDuration(kDefaultWedListenDuration) +#endif , mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union , mScanHandlerContext(nullptr) , mLinks(aInstance) @@ -1587,8 +1594,27 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig break; case Frame::kKeyIdMode2: - macKey = &mMode2KeyMaterial; - extAddress = &AsCoreType(&sMode2ExtAddress); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (aFrame.IsWakeupFrame()) + { + uint32_t sequence; + + // TODO: Avoid generating a new key if a wake-up frame was recently received already + + IgnoreError(aFrame.GetKeyId(keyid)); + sequence = BigEndian::ReadUint32(aFrame.GetKeySource()); + VerifyOrExit(((sequence & 0x7f) + 1) == keyid, error = kErrorSecurity); + + macKey = (sequence == keyManager.GetCurrentKeySequence()) ? mLinks.GetCurrentMacKey(aFrame) + : &keyManager.GetTemporaryMacKey(sequence); + extAddress = &aSrcAddr.GetExtended(); + } + else +#endif + { + macKey = &mMode2KeyMaterial; + extAddress = &AsCoreType(&sMode2ExtAddress); + } break; default: @@ -1970,6 +1996,12 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) mCounters.mRxData++; break; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + case Frame::kTypeMultipurpose: + SuccessOrExit(error = HandleWakeupFrame(*aFrame)); + OT_FALL_THROUGH; +#endif + default: mCounters.mRxOther++; ExitNow(); @@ -2314,6 +2346,14 @@ void Mac::SetCslChannel(uint8_t aChannel) void Mac::SetCslPeriod(uint16_t aPeriod) { +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (IsWedListenEnabled() && aPeriod != 0) + { + WedListenEnable(false); + LogWarn("Disabling wake-up frame listening due to CSL period change"); + } +#endif + mCslPeriod = aPeriod; UpdateCsl(); } @@ -2414,10 +2454,114 @@ Error Mac::SetWakeupChannel(uint8_t aChannel) VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs); mWakeupChannel = aChannel; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + UpdateWakeupListening(); +#endif + exit: return error; } #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +void Mac::SetWedListenInterval(uint32_t aInterval) +{ + mWedListenInterval = aInterval; + UpdateWakeupListening(); +} + +void Mac::SetWedListenDuration(uint16_t aDuration) +{ + mWedListenDuration = aDuration; + UpdateWakeupListening(); +} + +Error Mac::WedListenEnable(bool aEnable) +{ + Error error = kErrorNone; + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (aEnable == true && IsCslEnabled()) + { + LogWarn("Cannot enable wake-up frame listening while CSL is enabled"); + ExitNow(error = kErrorInvalidState); + } +#endif + + if (aEnable == mWedListenEnabled) + { + LogInfo("Listening for wake up frames was already %s", aEnable ? "started" : "stopped"); + ExitNow(); + } + + mWedListenEnabled = aEnable; + UpdateWakeupListening(); + + LogInfo("Listening for wake up frames %s: chan:%u, addr:%s", aEnable ? "started" : "stopped", mWakeupChannel, + GetExtAddress().ToString().AsCString()); + +exit: + return error; +} + +void Mac::UpdateWakeupListening(void) +{ + uint32_t interval = mWedListenEnabled ? mWedListenInterval : 0; + uint16_t duration = mWedListenEnabled ? mWedListenDuration : 0; + uint8_t channel = mWakeupChannel ? mWakeupChannel : mPanChannel; + + mLinks.UpdateWakeupListening(interval, duration, channel); +} + +Error Mac::HandleWakeupFrame(const RxFrame &aFrame) +{ + Error error = kErrorNone; + const ConnectionIe *connectionIe; + uint32_t rvTimeUs; + uint64_t rvTimestampUs; + uint32_t attachDelayMs; + uint64_t radioNowUs; + uint8_t retryInterval; + uint8_t retryCount; + + VerifyOrExit(mWedListenEnabled && aFrame.IsWakeupFrame()); + connectionIe = aFrame.GetConnectionIe(); + retryInterval = connectionIe->GetRetryInterval(); + retryCount = connectionIe->GetRetryCount(); + VerifyOrExit(retryInterval > 0 && retryCount > 0, error = kErrorInvalidArgs); + + radioNowUs = otPlatRadioGetNow(&GetInstance()); + rvTimeUs = aFrame.GetRendezvousTimeIe()->GetRendezvousTime() * kUsPerTenSymbols; + rvTimestampUs = aFrame.GetTimestamp() + kRadioHeaderPhrDuration + aFrame.GetLength() * kOctetDuration + rvTimeUs; + if (rvTimestampUs > radioNowUs + OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US) + { + attachDelayMs = static_cast(rvTimestampUs - radioNowUs - OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US); + attachDelayMs = attachDelayMs / 1000; + } + else + { + attachDelayMs = 0; + } + +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + { + uint32_t frameCounter; + + IgnoreError(aFrame.GetFrameCounter(frameCounter)); + LogInfo("Received wake-up frame, fc:%lu, rendezvous:%luus, retries:%u/%u", ToUlong(frameCounter), + ToUlong(rvTimeUs), retryCount, retryInterval); + } +#endif + + // Stop receiving more wake up frames + IgnoreError(WedListenEnable(false)); + + // TODO: start MLE attach process with the WC + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 931e27006384..f2787f8a2dd9 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -89,6 +89,9 @@ constexpr uint8_t kTxNumBcast = OPENTHREAD_CONFIG_MAC_TX_NUM_BCAST; ///< Num of constexpr uint16_t kMinCslIePeriod = OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD; +constexpr uint32_t kDefaultWedListenInterval = OPENTHREAD_CONFIG_WED_LISTEN_INTERVAL; +constexpr uint16_t kDefaultWedListenDuration = OPENTHREAD_CONFIG_WED_LISTEN_DURATION; + /** * Defines the function pointer called on receiving an IEEE 802.15.4 Beacon during an Active Scan. */ @@ -699,6 +702,54 @@ class Mac : public InstanceLocator, private NonCopyable Error SetWakeupChannel(uint8_t aChannel); #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Gets the WED listen interval. + * + * @returns WED listen interval in microseconds. + */ + uint32_t GetWedListenInterval(void) const { return mWedListenInterval; } + + /** + * Sets the WED listen interval. + * + * @param[in] aInterval The WED listen interval in microseconds. + */ + void SetWedListenInterval(uint32_t aInterval); + + /** + * Gets the WED listen duration. + * + * @returns WED listen duration in us. + */ + uint16_t GetWedListenDuration(void) const { return mWedListenDuration; } + + /** + * Sets the WED listen duration. + * + * @param[in] aDuration The WED listen duration in us. + */ + void SetWedListenDuration(uint16_t aDuration); + + /** + * Enables/disables listening for wake-up frames. + * + * @param[in] aEnable TRUE to enable listening for wake-up frames, FALSE otherwise + * + * @retval kErrorNone Successfully enabled/disabled listening for wake-up frames. + * @retval kErrorInvalidState Could not enable/disable listening for wake-up frames. + */ + Error WedListenEnable(bool aEnable); + + /** + * Returns whether listening for wake-up frames is enabled. + * + * @retval TRUE If listening for wake-up frames is enabled. + * @retval FALSE If listening for wake-up frames is not enabled. + */ + bool IsWedListenEnabled(void) const { return mWedListenEnabled; } +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + private: static constexpr uint16_t kMaxCcaSampleCount = OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW; @@ -786,6 +837,10 @@ class Mac : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE void ProcessEnhAckProbing(const RxFrame &aFrame, const Neighbor &aNeighbor); +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + Error HandleWakeupFrame(const RxFrame &aFrame); + void UpdateWakeupListening(void); #endif static const char *OperationToString(Operation aOperation); @@ -803,6 +858,9 @@ class Mac : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS bool mShouldDelaySleep : 1; bool mDelayingSleep : 1; +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + bool mWedListenEnabled : 1; #endif Operation mOperation; uint16_t mPendingOperations; @@ -829,7 +887,10 @@ class Mac : public InstanceLocator, private NonCopyable uint16_t mCslPeriod; #endif uint8_t mWakeupChannel; - +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + uint32_t mWedListenInterval; + uint16_t mWedListenDuration; +#endif union { ActiveScanHandler mActiveScanHandler; diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index 4da89c0b79ca..7ca0d9ff8ee1 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -473,6 +473,25 @@ class Links : public InstanceLocator } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Configures wake-up listening parameters in all radios. + * + * @param[in] aInterval The WED listen interval in microseconds. + * @param[in] aDuration The WED listen duration in microseconds. + * @param[in] aChannel The wake-up channel. + */ + void UpdateWakeupListening(uint32_t aInterval, uint16_t aDuration, uint8_t aChannel) + { + OT_UNUSED_VARIABLE(aInterval); + OT_UNUSED_VARIABLE(aDuration); + OT_UNUSED_VARIABLE(aChannel); +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.UpdateWakeupListening(aInterval, aDuration, aChannel); +#endif + } +#endif + /** * Transitions all radio links to Receive. * diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 9021a1dc0dfb..2188a60ebd27 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -53,6 +53,9 @@ SubMac::SubMac(Instance &aInstance) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE , mCslTimer(aInstance, SubMac::HandleCslTimer) #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + , mWedTimer(aInstance, SubMac::HandleWedTimer) +#endif { #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE mCslParentAccuracy.Init(); @@ -90,6 +93,9 @@ void SubMac::Init(void) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE CslInit(); #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + WedInit(); +#endif } otRadioCaps SubMac::GetCaps(void) const @@ -203,6 +209,9 @@ Error SubMac::Disable(void) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE mCslTimer.Stop(); #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + mWedTimer.Stop(); +#endif mTimer.Stop(); SuccessOrExit(error = Get().Sleep()); diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 3e7ff39635c0..a1915a4c0932 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -480,6 +480,17 @@ class SubMac : public InstanceLocator, private NonCopyable bool IsRadioFilterEnabled(void) const { return mRadioFilterEnabled; } #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Configures wake-up listening parameters in all radios. + * + * @param[in] aInterval The WED listen interval in microseconds. + * @param[in] aDuration The WED listen duration in microseconds. + * @param[in] aChannel The wake-up channel. + */ + void UpdateWakeupListening(uint32_t aInterval, uint16_t aDuration, uint8_t aChannel); +#endif + private: #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE void CslInit(void); @@ -492,6 +503,11 @@ class SubMac : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE void LogReceived(RxFrame *aFrame); #endif +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + void WedInit(void); + static void HandleWedTimer(Timer &aTimer); + void HandleWedTimer(void); #endif static constexpr uint8_t kCsmaMinBe = 3; // macMinBE (IEEE 802.15.4-2006). @@ -530,16 +546,21 @@ class SubMac : public InstanceLocator, private NonCopyable #endif }; -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE // Radio on times needed before and after MHR time for proper frame detection static constexpr uint32_t kMinReceiveOnAhead = OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AHEAD; static constexpr uint32_t kMinReceiveOnAfter = OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AFTER; - // CSL receivers would wake up `kCslReceiveTimeAhead` earlier + // CSL/wake-up listening receivers would wake up `kCslReceiveTimeAhead` earlier // than expected sample window. The value is in usec. static constexpr uint32_t kCslReceiveTimeAhead = OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD; #endif +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + // Margin to be applied to detect wake-up listening receive slots overlaps with CSL slots, in us + static constexpr uint32_t kWakeupListeningOverlapMargin = 600; +#endif + #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE // CSL transmitter would schedule delayed transmission `kCslTransmitTimeAhead` earlier // than expected delayed transmit time. The value is in usec. @@ -635,6 +656,14 @@ class SubMac : public InstanceLocator, private NonCopyable CslAccuracy mCslParentAccuracy; // The parent's CSL accuracy (clock accuracy and uncertainty). TimerMicro mCslTimer; #endif + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + uint32_t mWedListenInterval; // The WED listen interval, in microseconds. + uint16_t mWedListenDuration; // The WED listen duration, in microseconds. + uint8_t mWakeupChannel; // The wake-up sample channel. + TimeMicro mWedSampleTime; // The WED sample time of the current interval. + TimerMicro mWedTimer; +#endif }; /** diff --git a/src/core/mac/sub_mac_wed.cpp b/src/core/mac/sub_mac_wed.cpp new file mode 100644 index 000000000000..b02790f6010f --- /dev/null +++ b/src/core/mac/sub_mac_wed.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the Wake-up End Device of the subset of IEEE 802.15.4 MAC primitives. + */ + +#include "sub_mac.hpp" + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + +#include "instance/instance.hpp" + +namespace ot { +namespace Mac { + +RegisterLogModule("SubMac"); + +void SubMac::WedInit(void) +{ + mWedListenInterval = 0; + mWedTimer.Stop(); +} + +void SubMac::UpdateWakeupListening(uint32_t aInterval, uint16_t aDuration, uint8_t aChannel) +{ + OT_ASSERT(RadioSupportsReceiveTiming()); + + VerifyOrExit(aInterval != mWedListenInterval || aDuration != mWedListenDuration || aChannel != mWakeupChannel); + + mWedListenInterval = aInterval; + mWedListenDuration = aDuration; + mWakeupChannel = aChannel; + mWedTimer.Stop(); + + if (mWedListenInterval > 0) + { + mWedSampleTime = TimeMicro(otPlatRadioGetNow(&GetInstance())) + kCslReceiveTimeAhead; + HandleWedTimer(); + } + +exit: + return; +} + +void SubMac::HandleWedTimer(Timer &aTimer) { aTimer.Get().HandleWedTimer(); } + +void SubMac::HandleWedTimer(void) +{ + if (RadioSupportsReceiveTiming() && (mState != kStateDisabled)) + { + IgnoreError(Get().ReceiveAt(mWakeupChannel, mWedSampleTime.GetValue(), mWedListenDuration)); + } + mWedSampleTime += mWedListenInterval; + mWedTimer.FireAt(mWedSampleTime - kCslReceiveTimeAhead); +} + +} // namespace Mac +} // namespace ot + +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 4f7353fd83a6..d63f2bc1104d 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -51,6 +51,7 @@ namespace ot { static constexpr uint32_t kUsPerTenSymbols = OT_US_PER_TEN_SYMBOLS; ///< Time for 10 symbols in units of microseconds static constexpr uint32_t kRadioHeaderShrDuration = 160; ///< Duration of SHR in us static constexpr uint32_t kRadioHeaderPhrDuration = 32; ///< Duration of PHR in us +static constexpr uint32_t kOctetDuration = 32; ///< Duration of one octet in us static constexpr int8_t kRadioPowerInvalid = OT_RADIO_POWER_INVALID; ///< Invalid TX power value @@ -62,6 +63,13 @@ static constexpr uint64_t kMinCslPeriod = OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD static constexpr uint64_t kMaxCslTimeout = OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT; #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +/** + * Minimum WED listen duration supported in us. + */ +static constexpr uint32_t kMinWedListenDuration = 100; +#endif + /** * @addtogroup core-radio * @@ -473,14 +481,7 @@ class Radio : public InstanceLocator, private NonCopyable */ Error Receive(uint8_t aChannel); -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - /** - * Updates the CSL sample time in radio. - * - * @param[in] aCslSampleTime The CSL sample time. - */ - void UpdateCslSampleTime(uint32_t aCslSampleTime); - +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE /** * Schedules a radio reception window at a specific time and duration. * @@ -492,6 +493,15 @@ class Radio : public InstanceLocator, private NonCopyable * @retval kErrorFailed The receive window could not be scheduled. */ Error ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration); +#endif + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * Updates the CSL sample time in radio. + * + * @param[in] aCslSampleTime The CSL sample time. + */ + void UpdateCslSampleTime(uint32_t aCslSampleTime); /** * Enables CSL sampling in radio. @@ -894,12 +904,7 @@ inline Error Radio::Receive(uint8_t aChannel) return otPlatRadioReceive(GetInstancePtr(), aChannel); } -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime) -{ - otPlatRadioUpdateCslSampleTime(GetInstancePtr(), aCslSampleTime); -} - +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDuration) { Error error = otPlatRadioReceiveAt(GetInstancePtr(), aChannel, aStart, aDuration); @@ -911,6 +916,13 @@ inline Error Radio::ReceiveAt(uint8_t aChannel, uint32_t aStart, uint32_t aDurat #endif return error; } +#endif + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime) +{ + otPlatRadioUpdateCslSampleTime(GetInstancePtr(), aCslSampleTime); +} inline Error Radio::EnableCsl(uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr) { @@ -1015,11 +1027,13 @@ inline Error Radio::Sleep(void) { return kErrorNone; } inline Error Radio::Receive(uint8_t) { return kErrorNone; } +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t) { return kErrorNone; } +#endif + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE inline void Radio::UpdateCslSampleTime(uint32_t) {} -inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t) { return kErrorNone; } - inline Error Radio::EnableCsl(uint32_t, otShortAddress aShortAddr, const otExtAddress *) { return kErrorNotImplemented; diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index 8c83bd91be94..8b8eb9073af6 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -400,6 +400,18 @@ const Mle::KeyMaterial &KeyManager::GetTemporaryMleKey(uint32_t aKeySequence) return mTemporaryMleKey; } +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +const Mle::KeyMaterial &KeyManager::GetTemporaryMacKey(uint32_t aKeySequence) +{ + HashKeys hashKeys; + + ComputeKeys(aKeySequence, hashKeys); + mTemporaryMacKey.SetFrom(hashKeys.GetMacKey()); + + return mTemporaryMacKey; +} +#endif + #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE const Mac::KeyMaterial &KeyManager::GetTemporaryTrelMacKey(uint32_t aKeySequence) { diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index 85dbb24fe5d2..2aafa3658245 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -358,6 +358,15 @@ class KeyManager : public InstanceLocator, private NonCopyable */ const Mle::KeyMaterial &GetTemporaryMleKey(uint32_t aKeySequence); + /** + * Returns a temporary MAC key Material computed from the given key sequence. + * + * @param[in] aKeySequence The key sequence value. + * + * @returns The temporary MAC key. + */ + const Mle::KeyMaterial &GetTemporaryMacKey(uint32_t aKeySequence); + #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE /** * Returns the current MAC Frame Counter value for 15.4 radio link. @@ -598,6 +607,10 @@ class KeyManager : public InstanceLocator, private NonCopyable Mle::KeyMaterial mMleKey; Mle::KeyMaterial mTemporaryMleKey; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + Mle::KeyMaterial mTemporaryMacKey; +#endif + #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE Mac::KeyMaterial mTrelKey; Mac::KeyMaterial mTemporaryTrelKey; diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp index d95afb4f638e..d33a3e3828e7 100644 --- a/src/core/thread/tmf.hpp +++ b/src/core/thread/tmf.hpp @@ -98,7 +98,6 @@ class MessageInfo : public InstanceLocator, public Ip6::MessageInfo /** * Sets the local socket address to RLOC address and the peer socket address to leader RLOC. -q * */ void SetSockAddrToRlocPeerAddrToLeaderRloc(void);