From 03caf1536873be02ffadcc24491cf6ef917797ac Mon Sep 17 00:00:00 2001 From: Eduardo Montoya Date: Wed, 27 Nov 2024 15:48:29 +0100 Subject: [PATCH] [wed] send Parent Request when receiving a wake-up frame This commit introduces a basic `CslNeighborTable` which currently assumes a single ehanced CSL capable neighbor: the Wake-up Parent. When a wake-up frame is received: - Schedule modified Parent Request to Wake-up Parent - Suppress Announce messages - Suppress data polling - Pause MLE attach backoff --- include/openthread/thread.h | 2 +- src/core/BUILD.gn | 2 + src/core/CMakeLists.txt | 1 + src/core/common/locator.hpp | 2 +- src/core/common/message.hpp | 20 ++ src/core/config/mac.h | 19 ++ src/core/mac/mac.cpp | 39 ++- src/core/mac/mac.hpp | 1 + src/core/thread/csl_tx_scheduler.hpp | 16 ++ src/core/thread/enh_csl_neighbor_table.cpp | 62 +++++ src/core/thread/enh_csl_neighbor_table.hpp | 89 ++++++ src/core/thread/indirect_sender.cpp | 301 +++++++++++++++++---- src/core/thread/indirect_sender.hpp | 27 +- src/core/thread/mesh_forwarder.cpp | 24 +- src/core/thread/mesh_forwarder_ftd.cpp | 20 +- src/core/thread/mesh_forwarder_mtd.cpp | 21 +- src/core/thread/mle.cpp | 191 ++++++++++++- src/core/thread/mle.hpp | 73 ++++- src/core/thread/mle_types.hpp | 17 +- src/core/thread/neighbor.hpp | 3 + 20 files changed, 838 insertions(+), 92 deletions(-) create mode 100644 src/core/thread/enh_csl_neighbor_table.cpp create mode 100644 src/core/thread/enh_csl_neighbor_table.hpp diff --git a/include/openthread/thread.h b/include/openthread/thread.h index 6c5bf2b5e25..f86efafac84 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -1142,7 +1142,7 @@ uint32_t otThreadGetStoreFrameCounterAhead(otInstance *aInstance); * @param[in] aWakeupIntervalUs An interval between consecutive wake-up frames (in microseconds). * @param[in] aWakeupDurationMs Duration of the wake-up sequence (in milliseconds). * @param[in] aCallback A pointer to function that is called when the wake-up succeeds or fails. - * @param[in] aContext A pointer to callback application-specific context. + * @param[in] aCallbackContext A pointer to callback application-specific context. * * @retval OT_ERROR_NONE Successfully started the wake-up. * @retval OT_ERROR_INVALID_STATE Another attachment request is still in progress. diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 09fb562b907..6fa4900b9e3 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -643,6 +643,8 @@ openthread_core_files = [ "thread/dua_manager.hpp", "thread/energy_scan_server.cpp", "thread/energy_scan_server.hpp", + "thread/enh_csl_neighbor_table.cpp", + "thread/enh_csl_neighbor_table.hpp", "thread/indirect_sender.cpp", "thread/indirect_sender.hpp", "thread/indirect_sender_frame_context.hpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index af0046b64cd..c0fab253e1a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -214,6 +214,7 @@ set(COMMON_SOURCES thread/discover_scanner.cpp thread/dua_manager.cpp thread/energy_scan_server.cpp + thread/enh_csl_neighbor_table.cpp thread/indirect_sender.cpp thread/key_manager.cpp thread/link_metrics.cpp diff --git a/src/core/common/locator.hpp b/src/core/common/locator.hpp index 8b1f8221742..0e8c0e04521 100644 --- a/src/core/common/locator.hpp +++ b/src/core/common/locator.hpp @@ -81,7 +81,7 @@ template class GetProvider * * @returns A reference to the `Type` object of the instance. */ - template inline Type &Get(void) const; // Implemented in `locator_getters.hpp`. + template inline Type &Get(void) const; protected: GetProvider(void) = default; diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 4441af5804c..e16e267c845 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -202,6 +202,9 @@ class Buffer : public otMessageBuffer, public LinkedListEntry #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE bool mTimeSync : 1; // Whether the message is also used for time sync purpose. +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + bool mIsEnhCslNeighbor : 1; // Whether the message is for an enhanced CSL neighbor. #endif uint8_t mPriority : 2; // The message priority level (higher value is higher priority). uint8_t mOrigin : 2; // The origin of the message. @@ -1469,6 +1472,23 @@ class Message : public otMessage, public Buffer, public GetProvider #endif // #if OPENTHREAD_CONFIG_MULTI_RADIO +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Indicates whether the message is destined for an enhanced CSL neighbor. + * + * @retval TRUE If the message is destined for an enhanced CSL neighbor. + * @retval FALSE If the message is not destined for an enhanced CSL neighbor. + */ + bool IsForEnhancedCslNeighbor(void) const { return GetMetadata().mIsEnhCslNeighbor; } + + /** + * Sets whether the message is destined for an enhanced CSL neighbor. + * + * @param[in] aIsEnhCslNeighbor TRUE if the message is destined for an enhanced CSL neighbor, FALSE otherwise. + */ + void SetForEnhancedCslNeighbor(bool aIsEnhCslNeighbor) { GetMetadata().mIsEnhCslNeighbor = aIsEnhCslNeighbor; } +#endif + protected: class ConstIterator : public ItemPtrIterator { diff --git a/src/core/config/mac.h b/src/core/config/mac.h index d2359a91db2..84db7472804 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -558,6 +558,25 @@ #define OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT 100 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_CSL_WAKEUP_INTERVAL + * + * Periodicity of wake-up frame transmission by WC (in units of 10 symbols). + */ +#ifndef OPENTHREAD_CONFIG_MAC_CSL_WAKEUP_INTERVAL +#define OPENTHREAD_CONFIG_MAC_CSL_WAKEUP_INTERVAL 47 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_ENH_CSL_TX_ATTEMPTS + * + * Maximum number of TX attempts for the enhanced CSL communication before considering the peer de-synchronized. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_ENH_CSL_TX_ATTEMPTS +#define OPENTHREAD_CONFIG_MAC_ENH_CSL_TX_ATTEMPTS 8 +#endif + /** * @} */ diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 0ea79cefc2d..3e2c12025a7 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -1655,7 +1655,8 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig { uint32_t sequence; - // TODO: Avoid generating a new key if a wake-up frame was recently received already + // Avoid generating a new key if a wake-up frame was recently received already + VerifyOrExit(!Get().IsWakeupParentPresent(), error = kErrorInvalidState); IgnoreError(aFrame.GetKeyId(keyid)); sequence = BigEndian::ReadUint32(aFrame.GetKeySource()); @@ -2475,6 +2476,8 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) CslNeighbor *neighbor = nullptr; const CslIe *csl; + OT_UNUSED_VARIABLE(aSrcAddr); + VerifyOrExit(aFrame.IsVersion2015() && aFrame.GetSecurityEnabled()); csl = aFrame.GetCslIe(); @@ -2482,8 +2485,14 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) #if OPENTHREAD_FTD neighbor = Get().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid); -#else - OT_UNUSED_VARIABLE(aSrcAddr); +#endif + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (neighbor == nullptr) + { + neighbor = Get().GetWakeupParent(); + VerifyOrExit(neighbor->GetExtAddress() == aSrcAddr.GetExtended()); + } #endif VerifyOrExit(neighbor != nullptr); @@ -2620,7 +2629,8 @@ void Mac::UpdateWakeupListening(void) Error Mac::HandleWakeupFrame(const RxFrame &aFrame) { - Error error = kErrorNone; + Error error = kErrorNone; + constexpr uint32_t kWakeupIntervalUs = kDefaultWedListenInterval * kUsPerTenSymbols; const ConnectionIe *connectionIe; uint32_t rvTimeUs; uint64_t rvTimestampUs; @@ -2628,6 +2638,8 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) uint64_t radioNowUs; uint8_t retryInterval; uint8_t retryCount; + Address parentAddress; + CslNeighbor * parent; VerifyOrExit(mWakeupListenEnabled && aFrame.IsWakeupFrame()); connectionIe = aFrame.GetConnectionIe(); @@ -2662,8 +2674,23 @@ Error Mac::HandleWakeupFrame(const RxFrame &aFrame) // Stop receiving more wake up frames IgnoreError(SetWakeupListenEnabled(false)); - // TODO: start MLE attach process with the WC - OT_UNUSED_VARIABLE(attachDelayMs); + IgnoreError(aFrame.GetSrcAddr(parentAddress)); + Get().AddWakeupParent(parentAddress.GetExtended(), TimerMilli::GetNow() + attachDelayMs, + kWakeupIntervalUs * retryInterval * retryCount / 1000); + + parent = Get().GetWakeupParent(); + OT_ASSERT(parent != nullptr); + parent->SetCslPeriod(kDefaultWedListenInterval * retryInterval); + parent->SetCslPhase(0); + parent->SetCslSynchronized(true); + parent->SetCslLastHeard(TimerMilli::GetNow()); + // Rendezvous time is the time when the WC begins listening for the connection request from the awakened WED. + // Since EnhCslSender schedules a frame's PHR at `LastRxTimestamp + Phase + n*Period`, increase the timestamp + // by SHR length and the CSL uncertainty to make sure SHR begins while the WC is already listening. + parent->SetLastRxTimestamp(rvTimestampUs + kRadioHeaderShrDuration + Get().GetCslUncertainty() * 10); + parent->SetEnhCslMaxTxAttempts(retryCount); + + Get().AttachToWakeupParent(); exit: return error; diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index bb0ab49978b..ce6a7fcb1d5 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -97,6 +97,7 @@ constexpr uint16_t kMinCslIePeriod = OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD; constexpr uint32_t kDefaultWedListenInterval = OPENTHREAD_CONFIG_WED_LISTEN_INTERVAL; constexpr uint32_t kDefaultWedListenDuration = OPENTHREAD_CONFIG_WED_LISTEN_DURATION; +constexpr uint16_t kDefaultWakeupInterval = OPENTHREAD_CONFIG_MAC_CSL_WAKEUP_INTERVAL; /** * Defines the function pointer called on receiving an IEEE 802.15.4 Beacon during an Active Scan. diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 13fb800e27c..2524b69fdf4 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -64,6 +64,9 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable public: static constexpr uint8_t kMaxCslTriggeredTxAttempts = OPENTHREAD_CONFIG_MAC_MAX_TX_ATTEMPTS_INDIRECT_POLLS; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + static constexpr uint8_t kMaxEnhCslTriggeredTxAttempts = OPENTHREAD_CONFIG_MAC_ENH_CSL_TX_ATTEMPTS; +#endif /** * Defines all the neighbor info required for scheduling CSL transmissions. @@ -98,6 +101,15 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable uint64_t GetLastRxTimestamp(void) const { return mLastRxTimestamp; } void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; } +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + uint8_t GetEnhCslMaxTxAttempts(void) const + { + return mCslMaxTxAttempts != 0 ? mCslMaxTxAttempts : kMaxEnhCslTriggeredTxAttempts; + } + void SetEnhCslMaxTxAttempts(uint8_t txAttempts) { mCslMaxTxAttempts = txAttempts; } + void ResetEnhCslMaxTxAttempts() { mCslMaxTxAttempts = 0; } +#endif + private: uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts. bool mCslSynchronized : 1; ///< Indicates whether or not the child is CSL synchronized. @@ -153,6 +165,10 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable uint64_t mLastRxTimestamp; ///< Radio clock time when last frame containing CSL IE was received, in microseconds. +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + uint8_t mCslMaxTxAttempts; ///< Override for the maximum number of enhanced CSL triggered tx attempts. +#endif + static_assert(kMaxCslTriggeredTxAttempts < (1 << 7), "mCslTxAttempts cannot fit max!"); }; diff --git a/src/core/thread/enh_csl_neighbor_table.cpp b/src/core/thread/enh_csl_neighbor_table.cpp new file mode 100644 index 00000000000..269e5adc970 --- /dev/null +++ b/src/core/thread/enh_csl_neighbor_table.cpp @@ -0,0 +1,62 @@ +/* + * 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 includes definitions for enhanced CSL neighbor table. + */ + +#include "enh_csl_neighbor_table.hpp" + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + +#include "instance/instance.hpp" + +namespace ot { + +CslNeighborTable::CslNeighborTable(Instance &aInstance) +{ + for (CslNeighbor &cslNeighbor : mCslNeighbors) + { + cslNeighbor.Init(aInstance); + } +} + +CslNeighbor *CslNeighborTable::GetNewCslNeighbor(void) +{ + return mCslNeighbors; +} + +CslNeighbor *CslNeighborTable::GetFirstCslNeighbor(void) +{ + return mCslNeighbors; +} + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE diff --git a/src/core/thread/enh_csl_neighbor_table.hpp b/src/core/thread/enh_csl_neighbor_table.hpp new file mode 100644 index 00000000000..4949378d4ff --- /dev/null +++ b/src/core/thread/enh_csl_neighbor_table.hpp @@ -0,0 +1,89 @@ +/* + * 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 includes definitions for the CSL neighbors table. + */ + +#ifndef ENH_CSL_NEIGHBOR_TABLE_HPP_ +#define ENH_CSL_NEIGHBOR_TABLE_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + +#include "common/const_cast.hpp" +#include "common/iterator_utils.hpp" +#include "common/locator.hpp" +#include "common/non_copyable.hpp" +#include "thread/neighbor.hpp" + +namespace ot { + +/** + * Represents the CSL neighbors table. + */ +class CslNeighborTable : private NonCopyable +{ +public: + /** + * Initializes a `CslNeighborTable` instance. + * + * @param[in] aInstance A reference to the OpenThread instance. + */ + explicit CslNeighborTable(Instance &aInstance); + + /** + * Gets a new/unused `CslNeighbor` entry from the enhanced CSL neighbor table. + * + * @note The returned neighbor entry will be cleared (`memset` to zero). + * + * @returns A pointer to a new `CslNeighbor` entry, or `nullptr` if all `CslNeighbor` entries are in use. + */ + CslNeighbor *GetNewCslNeighbor(void); + + /** + * Gets the first `CslNeighbor` entry in the enhanced CSL neighbor table. + * + * @returns A pointer to the first `CslNeighbor` entry, or `nullptr` if the table is empty. + */ + CslNeighbor *GetFirstCslNeighbor(void); + +private: + // static constexpr uint16_t kMaxCslNeighbors = OPENTHREAD_CONFIG_MLE_MAX_ENH_CSL_NEIGHBORS; + static constexpr uint16_t kMaxCslNeighbors = 1; + + CslNeighbor mCslNeighbors[kMaxCslNeighbors]; +}; + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + +#endif // ENH_CSL_NEIGHBOR_TABLE_HPP_ diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index b18bbf6ea7b..f4e14680413 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -88,7 +88,82 @@ void IndirectSender::Stop(void) mEnabled = false; } +uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, CslNeighbor &aNeighbor, Message &aMessage) +{ + Ip6::Header ip6Header; + Mac::Addresses macAddrs; + uint16_t directTxOffset; + uint16_t nextOffset; + + // Determine the MAC source and destination addresses. + + IgnoreError(aMessage.Read(0, ip6Header)); + + Get().GetMacSourceAddress(ip6Header.GetSource(), macAddrs.mSource); + + if (ip6Header.GetDestination().IsLinkLocalUnicast()) + { + Get().GetMacDestinationAddress(ip6Header.GetDestination(), macAddrs.mDestination); + } + else + { + aNeighbor.GetMacAddress(macAddrs.mDestination); + } + + // Prepare the data frame from previous neighbor's indirect offset. + + directTxOffset = aMessage.GetOffset(); + aMessage.SetOffset(aNeighbor.GetIndirectFragmentOffset()); + + nextOffset = Get().PrepareDataFrameWithNoMeshHeader(aFrame, aMessage, macAddrs); + + aMessage.SetOffset(directTxOffset); + + return nextOffset; +} + +void IndirectSender::UpdateIndirectMessage(CslNeighbor &aNeighbor) +{ + Message *message = nullptr; + +#if OPENTHREAD_FTD + if (IsChild(aNeighbor)) + { + message = FindQueuedMessageForSleepyChild(static_cast(aNeighbor), AcceptAnyMessage); + } + else +#endif + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + { + message = FindQueuedMessageForWedNeighbor(aNeighbor); + } +#endif + + + aNeighbor.SetWaitingForMessageUpdate(false); + aNeighbor.SetIndirectMessage(message); + aNeighbor.SetIndirectFragmentOffset(0); + aNeighbor.SetIndirectTxSuccess(true); + +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + mCslTxScheduler.Update(); +#endif + + if (message != nullptr) + { + Mac::Address neighborAddress; + + aNeighbor.GetMacAddress(neighborAddress); + Get().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, kErrorNone, &neighborAddress); + } +} + #if OPENTHREAD_FTD +bool IndirectSender::IsChild(const Neighbor &aNeighbor) const +{ + return Get().Contains(static_cast(aNeighbor)); +} void IndirectSender::AddMessageForSleepyChild(Message &aMessage, Child &aChild) { @@ -300,28 +375,6 @@ void IndirectSender::HandleFrameChangeDone(Child &aChild) return; } -void IndirectSender::UpdateIndirectMessage(Child &aChild) -{ - Message *message = FindQueuedMessageForSleepyChild(aChild, AcceptAnyMessage); - - aChild.SetWaitingForMessageUpdate(false); - aChild.SetIndirectMessage(message); - aChild.SetIndirectFragmentOffset(0); - aChild.SetIndirectTxSuccess(true); - -#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - mCslTxScheduler.Update(); -#endif - - if (message != nullptr) - { - Mac::Address childAddress; - - aChild.GetMacAddress(childAddress); - Get().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, kErrorNone, &childAddress); - } -} - Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild) { Error error = kErrorNone; @@ -357,34 +410,9 @@ Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &a uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, Message &aMessage) { - Ip6::Header ip6Header; - Mac::Addresses macAddrs; - uint16_t directTxOffset; uint16_t nextOffset; - // Determine the MAC source and destination addresses. - - IgnoreError(aMessage.Read(0, ip6Header)); - - Get().GetMacSourceAddress(ip6Header.GetSource(), macAddrs.mSource); - - if (ip6Header.GetDestination().IsLinkLocalUnicast()) - { - Get().GetMacDestinationAddress(ip6Header.GetDestination(), macAddrs.mDestination); - } - else - { - aChild.GetMacAddress(macAddrs.mDestination); - } - - // Prepare the data frame from previous child's indirect offset. - - directTxOffset = aMessage.GetOffset(); - aMessage.SetOffset(aChild.GetIndirectFragmentOffset()); - - nextOffset = Get().PrepareDataFrameWithNoMeshHeader(aFrame, aMessage, macAddrs); - - aMessage.SetOffset(directTxOffset); + nextOffset = PrepareDataFrame(aFrame, static_cast(aChild), aMessage); // Set `FramePending` if there are more queued messages (excluding // the current one being sent out) for the child (note `> 1` check). @@ -572,6 +600,152 @@ bool IndirectSender::AcceptSupervisionMessage(const Message &aMessage) #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +void IndirectSender::AddMessageForEnhCslNeighbor(Message &aMessage, CslNeighbor &aNeighbor) +{ + OT_UNUSED_VARIABLE(aNeighbor); + + aMessage.SetForEnhancedCslNeighbor(true); +} + +Error IndirectSender::PrepareFrameForEnhCslNeighbor(Mac::TxFrame &aFrame, FrameContext &aContext, CslNeighbor &aNeighbor) +{ + Error error = kErrorNone; + Message *message = aNeighbor.GetIndirectMessage(); + VerifyOrExit(message != nullptr, error = kErrorInvalidState); + + switch (message->GetType()) + { + case Message::kTypeIp6: + aContext.mMessageNextOffset = PrepareDataFrame(aFrame, aNeighbor, *message); + break; + + default: + error = kErrorNotImplemented; + break; + } + +exit: + return error; +} + +Message *IndirectSender::FindQueuedMessageForWedNeighbor(CslNeighbor &aNeighbor) +{ + Message *match = nullptr; + + OT_UNUSED_VARIABLE(aNeighbor); + + for (Message &message : Get().mSendQueue) + { + if (message.IsForEnhancedCslNeighbor()) + { + match = &message; + break; + } + } + + return match; +} + +void IndirectSender::HandleSentFrameToWedNeighbor(const Mac::TxFrame &aFrame, + const FrameContext &aContext, + Error aError, + CslNeighbor &aNeighbor) +{ + Message *message = aNeighbor.GetIndirectMessage(); + uint16_t nextOffset = aContext.mMessageNextOffset; + + VerifyOrExit(mEnabled); + + switch (aError) + { + case kErrorNone: + break; + + case kErrorNoAck: + case kErrorChannelAccessFailure: + case kErrorAbort: + + aNeighbor.SetIndirectTxSuccess(false); + +#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE + // We set the nextOffset to end of message, since there is no need to + // send any remaining fragments in the message to the neighbor, if all tx + // attempts of current frame already failed. + + if (message != nullptr) + { + nextOffset = message->GetLength(); + } +#endif + break; + + default: + OT_ASSERT(false); + } + + if ((message != nullptr) && (nextOffset < message->GetLength())) + { + aNeighbor.SetIndirectFragmentOffset(nextOffset); +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + mCslTxScheduler.Update(); +#endif + ExitNow(); + } + + if (message != nullptr) + { + // The indirect tx of this message to the neighbor is done. + + Error txError = aError; + Mac::Address macDest; + + aNeighbor.SetIndirectMessage(nullptr); + aNeighbor.GetLinkInfo().AddMessageTxStatus(aNeighbor.GetIndirectTxSuccess()); + +#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE + + // When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is + // disabled, all fragment frames of a larger message are + // sent even if the transmission of an earlier fragment fail. + // Note that `GetIndirectTxSuccess() tracks the tx success of + // the entire message to the child, while `txError = aError` + // represents the error status of the last fragment frame + // transmission. + + if (!aNeighbor.GetIndirectTxSuccess() && (txError == kErrorNone)) + { + txError = kErrorFailed; + } +#endif + + if (!aFrame.IsEmpty()) + { + IgnoreError(aFrame.GetDstAddr(macDest)); + Get().LogMessage(MeshForwarder::kMessageTransmit, *message, txError, &macDest); + } + + if (message->GetType() == Message::kTypeIp6) + { + if (aNeighbor.GetIndirectTxSuccess()) + { + Get().mIpCounters.mTxSuccess++; + } + else + { + Get().mIpCounters.mTxFailure++; + } + } + + } + + UpdateIndirectMessage(aNeighbor); + +exit: + return; +} +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE Error IndirectSender::PrepareFrameForCslNeighbor(Mac::TxFrame &aFrame, @@ -581,14 +755,23 @@ Error IndirectSender::PrepareFrameForCslNeighbor(Mac::TxFrame &aFrame, Error error = kErrorNotFound; #if OPENTHREAD_FTD - // `CslNeighbor` can only be a `Child` for now, but can be changed later. - error = PrepareFrameForChild(aFrame, aContext, static_cast(aCslNeighbor)); + if (IsChild(aCslNeighbor)) + { + error = PrepareFrameForChild(aFrame, aContext, static_cast(aCslNeighbor)); + VerifyOrExit(error != kErrorNone); + } +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + error = PrepareFrameForEnhCslNeighbor(aFrame, aContext, aCslNeighbor); + ExitNow(); #else OT_UNUSED_VARIABLE(aFrame); OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aCslNeighbor); + ExitNow(); #endif +exit: return error; } @@ -598,17 +781,29 @@ void IndirectSender::HandleSentFrameToCslNeighbor(const Mac::TxFrame &aFrame, CslNeighbor &aCslNeighbor) { #if OPENTHREAD_FTD - HandleSentFrameToChild(aFrame, aContext, aError, static_cast(aCslNeighbor)); + if (IsChild(aCslNeighbor)) + { + HandleSentFrameToChild(aFrame, aContext, aError, static_cast(aCslNeighbor)); + ExitNow(); + } +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + HandleSentFrameToWedNeighbor(aFrame, aContext, aError, aCslNeighbor); + ExitNow(); #else OT_UNUSED_VARIABLE(aFrame); OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aError); OT_UNUSED_VARIABLE(aCslNeighbor); + ExitNow(); #endif + +exit: + return; } -#endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE +#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE } // namespace ot diff --git a/src/core/thread/indirect_sender.hpp b/src/core/thread/indirect_sender.hpp index fcc120f9379..055614b1afa 100644 --- a/src/core/thread/indirect_sender.hpp +++ b/src/core/thread/indirect_sender.hpp @@ -257,6 +257,16 @@ class IndirectSender : public InstanceLocator, public IndirectSenderBase, privat #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Adds a message for indirect transmission to a WED neighbor. + * + * @param[in] aMessage The message to add. + * @param[in] aNeighbor The neighbor for indirect transmission. + */ + void AddMessageForEnhCslNeighbor(Message &aMessage, CslNeighbor &aCslNeighbor); +#endif + private: #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE // Callbacks from `CslTxScheduler` @@ -267,13 +277,18 @@ class IndirectSender : public InstanceLocator, public IndirectSenderBase, privat CslNeighbor &aCslNeighbor); #endif + uint16_t PrepareDataFrame(Mac::TxFrame &aFrame, CslNeighbor &aNeighbor, Message &aMessage); + void HandleSentFrameToNeighbor(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, CslNeighbor &aNeighbor); + void UpdateIndirectMessage(CslNeighbor &aNeighbor); + #if OPENTHREAD_FTD + bool IsChild(const Neighbor &aNeighbor) const; + // Callbacks from `DataPollHandler` Error PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild); void HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, Child &aChild); void HandleFrameChangeDone(Child &aChild); - void UpdateIndirectMessage(Child &aChild); void RequestMessageUpdate(Child &aChild); uint16_t PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, Message &aMessage); void PrepareEmptyFrame(Mac::TxFrame &aFrame, Child &aChild, bool aAckRequest); @@ -283,6 +298,16 @@ class IndirectSender : public InstanceLocator, public IndirectSenderBase, privat static bool AcceptSupervisionMessage(const Message &aMessage); #endif // OPENTHREAD_FTD + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + Error PrepareFrameForEnhCslNeighbor(Mac::TxFrame &aFrame, FrameContext &aContext, CslNeighbor &aNeighbor); + Message *FindQueuedMessageForWedNeighbor(CslNeighbor &aNeighbor); + void HandleSentFrameToWedNeighbor(const Mac::TxFrame &aFrame, + const FrameContext &aContext, + Error aError, + CslNeighbor &aNeighbor); +#endif + bool mEnabled; #if OPENTHREAD_FTD SourceMatchController mSourceMatchController; diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 9999b36be59..291499afa2e 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -686,14 +686,27 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle) { Get().SetRxOnWhenIdle(aRxOnWhenIdle); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + // Data polls not allowed in enhanced CSL mode + if (!Get().IsWakeupParentPresent()) +#endif + { + if (aRxOnWhenIdle) + { + mDataPollSender.StopPolling(); + } + else + { + mDataPollSender.StartPolling(); + } + } + if (aRxOnWhenIdle) { - mDataPollSender.StopPolling(); Get().Stop(); } else { - mDataPollSender.StartPolling(); Get().Start(); } } @@ -1083,7 +1096,12 @@ uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame &aFrame, if (nextOffset < aMessage.GetLength()) { - aFrame.SetFramePending(true); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (!Get().IsWakeupParentPresent()) +#endif + { + aFrame.SetFramePending(true); + } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE aMessage.SetTimeSync(false); #endif diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 270a41b3074..40af59bc666 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -45,10 +45,22 @@ void MeshForwarder::SendMessage(OwnedPtr aMessagePtr) { Message &message = *aMessagePtr.Release(); - message.SetOffset(0); - message.SetDatagramTag(0); - message.SetTimestampToNow(); - mSendQueue.Enqueue(message); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + CslNeighbor *cslNeighbor = Get().GetWakeupParent(); + + if ((cslNeighbor != nullptr) && cslNeighbor->IsCslSynchronized()) + { + mIndirectSender.AddMessageForEnhCslNeighbor(message, *cslNeighbor); + } + else +#endif + { + message.SetOffset(0); + message.SetDatagramTag(0); + message.SetTimestampToNow(); + } + +mSendQueue.Enqueue(message); switch (message.GetType()) { diff --git a/src/core/thread/mesh_forwarder_mtd.cpp b/src/core/thread/mesh_forwarder_mtd.cpp index 9dec26b43b8..1dc99200750 100644 --- a/src/core/thread/mesh_forwarder_mtd.cpp +++ b/src/core/thread/mesh_forwarder_mtd.cpp @@ -35,16 +35,29 @@ #if OPENTHREAD_MTD +#include "instance/instance.hpp" + namespace ot { void MeshForwarder::SendMessage(OwnedPtr aMessagePtr) { Message &message = *aMessagePtr.Release(); - message.SetDirectTransmission(); - message.SetOffset(0); - message.SetDatagramTag(0); - message.SetTimestampToNow(); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + CslNeighbor *cslNeighbor = Get().GetWakeupParent(); + + if ((cslNeighbor != nullptr) && cslNeighbor->IsCslSynchronized()) + { + mIndirectSender.AddMessageForEnhCslNeighbor(message, *cslNeighbor); + } + else +#endif + { + message.SetDirectTransmission(); + message.SetOffset(0); + message.SetDatagramTag(0); + message.SetTimestampToNow(); + } mSendQueue.Enqueue(message); mScheduleTransmissionTask.Post(); diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index e3d184f04aa..1826eafc035 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -91,6 +91,9 @@ Mle::Mle(Instance &aInstance) , mWedAttachState(kWedDetached) , mWedAttachTimer(aInstance) #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + , mCslNeighborTable(aInstance) +#endif { mParent.Init(aInstance); mParentCandidate.Init(aInstance); @@ -175,8 +178,7 @@ Error Mle::Start(StartMode aMode) Get().SubscribeMulticast(mRealmLocalAllThreadNodes); SetRloc16(GetRloc16()); - - mAttachCounter = 0; + ResetAttachCounter(); Get().Start(); @@ -341,6 +343,45 @@ void Mle::SetAttachState(AttachState aState) return; } +void Mle::ResetAttachCounter(void) +{ +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + VerifyOrExit(!IsWakeupParentPresent()); + +#if OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_ENABLE + mAttachFireTime = TimeMilli(0); +#endif +#endif + + mAttachCounter = 0; + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +exit: + return; +#endif +} + +void Mle::IncrementAttachCounter(void) +{ +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + VerifyOrExit(!IsWakeupParentPresent()); +#endif + + mAttachCounter++; + + if (mAttachCounter == 0) + { + mAttachCounter--; + } + + mCounters.mAttachAttempts++; + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +exit: + return; +#endif +} + void Mle::Restore(void) { Settings::NetworkInfo networkInfo; @@ -563,7 +604,7 @@ void Mle::Attach(AttachMode aMode) if (!IsDetached()) { - mAttachCounter = 0; + ResetAttachCounter(); } if (mReattachState == kReattachStart) @@ -619,13 +660,34 @@ void Mle::Attach(AttachMode aMode) return; } -uint32_t Mle::GetAttachStartDelay(void) const +uint32_t Mle::GetAttachStartDelay(void) { uint32_t delay = 1; uint32_t jitter; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + TimeMilli nowMs = TimerMilli::GetNow(); +#endif VerifyOrExit(IsDetached()); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (IsWakeupParentPresent()) + { + delay = (mWakeupParentAttachTime > nowMs) ? (mWakeupParentAttachTime - nowMs) : 0; + ExitNow(); + } +#if OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_ENABLE + else if (mAttachFireTime.GetValue() > 0) + { + // Resume previously scheduled attach attempt. + delay = (mAttachFireTime > nowMs) ? (mAttachFireTime - nowMs) : 0; + mAttachCounter = (mAttachCounter > 0) ? (mAttachCounter - 1) : mAttachCounter; + mAttachFireTime = TimeMilli(0); + ExitNow(); + } +#endif +#endif + if (mAttachCounter == 0) { delay = 1 + Random::NonCrypto::GetUint32InRange(0, kParentRequestRouterTimeout); @@ -851,7 +913,7 @@ Error Mle::SetDeviceMode(DeviceMode aDeviceMode) if (shouldReattach) { - mAttachCounter = 0; + ResetAttachCounter(); IgnoreError(BecomeDetached()); ExitNow(); } @@ -859,7 +921,7 @@ Error Mle::SetDeviceMode(DeviceMode aDeviceMode) if (IsDetached()) { - mAttachCounter = 0; + ResetAttachCounter(); SetStateDetached(); Attach(kAnyPartition); } @@ -1080,6 +1142,12 @@ void Mle::InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo) aNeighbor.SetLastHeard(TimerMilli::GetNow()); } +void Mle::InitParentCandidate(Mac::ExtAddress &aAddress) +{ + mParentCandidate.Clear(); + mParentCandidate.SetExtAddress(aAddress); +} + void Mle::ScheduleChildUpdateRequestIfMtdChild(void) { if (IsChild() && !IsFullThreadDevice()) @@ -1315,6 +1383,16 @@ Error Mle::DetermineParentRequestType(ParentRequestType &aType) const OT_ASSERT(mAttachState == kAttachStateParentRequest); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (IsWakeupParentPresent()) + { + aType = kToWakeupParent; + VerifyOrExit(TimerMilli::GetNow() < mWakeupParentAttachTime + mWakeupParentAttachWindow, + error = kErrorNotFound); + ExitNow(); + } +#endif + if (mAttachMode == kSelectedParent) { aType = kToSelectedRouter; @@ -1448,7 +1526,7 @@ void Mle::HandleAttachTimer(void) switch (mAttachState) { case kAttachStateIdle: - mAttachCounter = 0; + ResetAttachCounter(); break; case kAttachStateProcessAnnounce: @@ -1456,8 +1534,17 @@ void Mle::HandleAttachTimer(void) break; case kAttachStateStart: - LogNote("Attach attempt %d, %s %s", mAttachCounter, AttachModeToString(mAttachMode), - ReattachStateToString(mReattachState)); +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (IsWakeupParentPresent()) + { + LogNote("Attach attempt to Wake-up Parent"); + } + else +#endif + { + LogNote("Attach attempt %d, %s %s", mAttachCounter, AttachModeToString(mAttachMode), + ReattachStateToString(mReattachState)); + } SetAttachState(kAttachStateParentRequest); mParentCandidate.SetState(Neighbor::kStateInvalid); @@ -1482,6 +1569,12 @@ void Mle::HandleAttachTimer(void) case kToRoutersAndReeds: delay = kParentRequestReedTimeout; break; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + case kToWakeupParent: + delay = kWakeupParentParentRespTimeout; + delay += mWakeupParentAttachTime + mWakeupParentAttachWindow - TimerMilli::GetNow(); + break; +#endif } break; @@ -1538,6 +1631,10 @@ bool Mle::PrepareAnnounceState(void) bool shouldAnnounce = false; Mac::ChannelMask channelMask; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + VerifyOrExit(!IsWakeupParentPresent()); +#endif + VerifyOrExit(!IsChild() && (mReattachState == kReattachStop) && (Get().IsPartiallyComplete() || !IsFullThreadDevice())); @@ -1646,6 +1743,12 @@ void Mle::SendParentRequest(ParentRequestType aType) case kToRoutersAndReeds: scanMask = ScanMaskTlv::kRouterFlag | ScanMaskTlv::kEndDeviceFlag; break; + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + case kToWakeupParent: + scanMask = ScanMaskTlv::kRouterFlag | ScanMaskTlv::kEndDeviceFlag; + break; +#endif } VerifyOrExit((message = NewMleMessage(kCommandParentRequest)) != nullptr, error = kErrorNoBufs); @@ -1678,6 +1781,14 @@ void Mle::SendParentRequest(ParentRequestType aType) destination.SetToLinkLocalAddress(mParentSearch.GetSelectedParent().GetExtAddress()); } else +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + if (aType == kToWakeupParent) + { + destination.SetToLinkLocalAddress(GetWakeupParent()->GetExtAddress()); + SuccessOrExit(error = message->AppendCslClockAccuracyTlv()); + } + else #endif { destination.SetToLinkLocalAllRoutersMulticast(); @@ -1695,6 +1806,13 @@ void Mle::SendParentRequest(ParentRequestType aType) case kToRoutersAndReeds: Log(kMessageSend, kTypeParentRequestToRoutersReeds, destination); break; + +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + case kToWakeupParent: + Log(kMessageSend, kTypeParentRequestToWakeupCoord, destination); + LogInfo("Sent Parent Request FC: %lu", ToUlong(Get().GetMleFrameCounter() - 1)); + break; +#endif } exit: @@ -2129,6 +2247,9 @@ void Mle::SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, Annou MeshCoP::Timestamp activeTimestamp; TxMessage *message = nullptr; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + VerifyOrExit(!IsWakeupParentPresent(), error = kErrorInvalidState); +#endif VerifyOrExit(Get().GetSupportedChannelMask().ContainsChannel(aChannel), error = kErrorInvalidArgs); VerifyOrExit((message = NewMleMessage(kCommandAnnounce)) != nullptr, error = kErrorNoBufs); message->SetLinkSecurityEnabled(true); @@ -3681,7 +3802,7 @@ void Mle::HandleAnnounce(RxInfo &aRxInfo) mAlternatePanId = panId; SetAttachState(kAttachStateProcessAnnounce); mAttachTimer.Start(kAnnounceProcessTimeout); - mAttachCounter = 0; + ResetAttachCounter(); LogNote("Delay processing Announce - channel %d, panid 0x%02x", channel, panId); } @@ -3840,6 +3961,9 @@ void Mle::InformPreviousParent(void) Message *message = nullptr; Ip6::MessageInfo messageInfo; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + VerifyOrExit(!IsWakeupParentPresent()); +#endif VerifyOrExit((message = Get().NewMessage(0)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->SetLength(0)); @@ -4149,6 +4273,9 @@ const char *Mle::MessageTypeToString(MessageType aType) #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE "Time Sync", // (31) kTypeTimeSync +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + "Parent Request", // kTypeParentRequestToWakeupCoord #endif }; @@ -5485,6 +5612,50 @@ Error Mle::RxMessage::ReadRouteTlv(RouteTlv &aRouteTlv) const } #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +void Mle::AddWakeupParent(const Mac::ExtAddress &aParent, TimeMilli aAttachTime, uint32_t aAttachWindowMs) +{ + CslNeighbor *parent = nullptr; + + parent = mCslNeighborTable.GetNewCslNeighbor(); + parent->SetExtAddress(aParent); + + mWakeupParentAttachTime = aAttachTime; + mWakeupParentAttachWindow = aAttachWindowMs; +} + +CslNeighbor *Mle::GetWakeupParent(void) +{ + return mCslNeighborTable.GetFirstCslNeighbor(); +} + +void Mle::AttachToWakeupParent() +{ + CslNeighbor * parent = GetWakeupParent(); + + OT_ASSERT(parent != nullptr); + +#if OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_ENABLE + if (mAttachTimer.IsRunning()) + { + mAttachFireTime = mAttachTimer.GetFireTime(); + } +#endif + + mPreviousRole = mRole; + SetStateDetached(); + mParent.Clear(); + SetRloc16(Mac::kShortAddrInvalid); + // Clear the current network data to avoid needless address registrations as the Wake-up Parent acts as a + // leader and establishes new network data, anyway. + Get().Reset(); + + InitParentCandidate(parent->GetExtAddress()); + + Attach(kAnyPartition); +} +#endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + //--------------------------------------------------------------------------------------------------------------------- // ParentCandidate diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 6d9652f4df2..c6c18c2f26e 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -51,6 +51,7 @@ #include "meshcop/meshcop.hpp" #include "net/udp6.hpp" #include "thread/child.hpp" +#include "thread/enh_csl_neighbor_table.hpp" #include "thread/link_metrics.hpp" #include "thread/link_metrics_tlvs.hpp" #include "thread/mle_tlvs.hpp" @@ -117,6 +118,9 @@ class Mle : public InstanceLocator, private NonCopyable friend class ot::LinkMetrics::Initiator; #endif friend class ot::UnitTester; +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + friend class ot::Mac::Mac; +#endif public: typedef otDetachGracefullyCallback DetachCallback; ///< Callback to signal end of graceful detach. @@ -750,6 +754,38 @@ class Mle : public InstanceLocator, private NonCopyable void *aCallbackContext); #endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * Returns whether the Thread interface is currently communicating to a Wake-up Parent. + * + * @retval TRUE If the Thread interface is communicating to a Wake-up Parent. + * @retval FALSE If the Thread interface is not communicating to a Wake-up Parent. + */ + bool IsWakeupParentPresent() const { return mWakeupParentAttachWindow > 0; } + + /** + * Adds a Wake-up Parent to the list of potential parents. + * + * @param[in] aParent The extended address of the Wake-up Parent. + * @param[in] aAttachTime The time when the Thread interface attached to the Wake-up Parent. + * @param[in] aAttachWindowMs The time window in milliseconds during which the Thread interface can attach to the + * Wake-up Parent. + */ + void AddWakeupParent(const Mac::ExtAddress &aParent, TimeMilli aAttachTime, uint32_t aAttachWindowMs); + + /** + * Returns the Wake-up Parent that the Thread interface is currently communicating to. + * + * @returns The Wake-up Parent that the Thread interface is currently communicating to. + */ + CslNeighbor *GetWakeupParent(void); + + /** + * Starts the process of attaching to a Wake-up Parent, if previously configured with `AddWakeupParent`. + */ + void AttachToWakeupParent(); +#endif + private: //------------------------------------------------------------------------------------------------------------------ // Constants @@ -886,6 +922,9 @@ class Mle : public InstanceLocator, private NonCopyable kToRouters, // Parent Request to routers only. kToRoutersAndReeds, // Parent Request to all routers and REEDs. kToSelectedRouter, // Parent Request to a selected router (e.g., by `ParentSearch` module). +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + kToWakeupParent, // Parent Request unicast to a known Wake-up Parent device. +#endif }; enum ChildUpdateRequestMode : uint8_t // Used in `SendChildUpdateRequest()` @@ -948,6 +987,9 @@ class Mle : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE kTypeTimeSync, +#endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + kTypeParentRequestToWakeupCoord, #endif }; @@ -1094,6 +1136,26 @@ class Mle : public InstanceLocator, private NonCopyable Class mClass; // The message class (authoritative, peer, or unknown). }; + /* + * Resets the attach counter. + * + */ + void ResetAttachCounter(void); + + /** + * Increments the attach counter. + * + */ + void IncrementAttachCounter(void); + + /* + * Initialize parent candidate. + * + * @param[in] aAddress The MAC Address of the parent candidate. + * + */ + void InitParentCandidate(Mac::ExtAddress &aAddress); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #if OPENTHREAD_FTD @@ -1343,7 +1405,7 @@ class Mle : public InstanceLocator, private NonCopyable Error HandleLeaderData(RxInfo &aRxInfo); void ProcessAnnounce(void); bool HasUnregisteredAddress(void); - uint32_t GetAttachStartDelay(void) const; + uint32_t GetAttachStartDelay(void); void SendParentRequest(ParentRequestType aType); Error SendChildIdRequest(void); Error GetNextAnnounceChannel(uint8_t &aChannel) const; @@ -1515,6 +1577,15 @@ class Mle : public InstanceLocator, private NonCopyable WedAttachTimer mWedAttachTimer; Callback mWakeupCallback; #endif +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +#if OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_ENABLE + TimeMilli mAttachFireTime; +#endif + DeviceRole mPreviousRole; + TimeMilli mWakeupParentAttachTime; + uint32_t mWakeupParentAttachWindow; + CslNeighborTable mCslNeighborTable; +#endif }; } // namespace Mle diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index 95f52a7c685..c266b999e25 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -75,14 +75,15 @@ namespace Mle { constexpr uint16_t kUdpPort = 19788; ///< MLE UDP Port -constexpr uint16_t kMaxChildren = OPENTHREAD_CONFIG_MLE_MAX_CHILDREN; ///< Maximum number of children -constexpr uint16_t kMinChildId = 1; ///< Minimum Child ID -constexpr uint16_t kMaxChildId = 511; ///< Maximum Child ID -constexpr uint8_t kMaxRouters = OPENTHREAD_CONFIG_MLE_MAX_ROUTERS; ///< Maximum number of routers -constexpr uint8_t kMaxRouterId = OT_NETWORK_MAX_ROUTER_ID; ///< Max Router ID -constexpr uint8_t kInvalidRouterId = kMaxRouterId + 1; ///< Value indicating invalid Router ID -constexpr uint8_t kRouterIdOffset = 10; ///< Bit offset of router ID in RLOC16 -constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; ///< Invalid RLOC16. +constexpr uint16_t kMaxChildren = OPENTHREAD_CONFIG_MLE_MAX_CHILDREN; ///< Maximum number of children +constexpr uint32_t kWakeupParentParentRespTimeout = 500; ///< Max delay for receiving a Parent Response from WC (ms) +constexpr uint16_t kMinChildId = 1; ///< Minimum Child ID +constexpr uint16_t kMaxChildId = 511; ///< Maximum Child ID +constexpr uint8_t kMaxRouters = OPENTHREAD_CONFIG_MLE_MAX_ROUTERS; ///< Maximum number of routers +constexpr uint8_t kMaxRouterId = OT_NETWORK_MAX_ROUTER_ID; ///< Max Router ID +constexpr uint8_t kInvalidRouterId = kMaxRouterId + 1; ///< Value indicating invalid Router ID +constexpr uint8_t kRouterIdOffset = 10; ///< Bit offset of router ID in RLOC16 +constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; ///< Invalid RLOC16. #if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE constexpr uint8_t kMaxRouteCost = 127; ///< Maximum path cost diff --git a/src/core/thread/neighbor.hpp b/src/core/thread/neighbor.hpp index b7e90e647f1..3a5820d049f 100644 --- a/src/core/thread/neighbor.hpp +++ b/src/core/thread/neighbor.hpp @@ -783,6 +783,9 @@ class CslNeighbor : public Neighbor public CslTxScheduler::NeighborInfo #endif { +#if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + friend class CslNeighborTable; +#endif }; } // namespace ot