From ffb3fdf9bdc0e1aff3b10a6811eb385afec5c3ee Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 22 Nov 2019 15:34:53 -0800 Subject: [PATCH] [radio-selector] adding multi radio link support (#4440) This commit adds support for multi radio links in OpenThread core. A set of `OPENTHREAD_CONFIG_RADIO_LINK_*` definitions (in header `config/radio_link.h`) determines the supported radio link types. This commit adds a new class `RadioSelector` which selects the radio link for each message transmission. Per neighbor it tracks the supported radio types and a preference value for each type. `RadioSelector` updates the preference value based on the history of rx/tx events with the neighbor, e.g., a successful tx on a radio link increases the preference, whereas a failed tx attempt decreases it. A new class `Mac::Links` is added defining a layer between `Mac` and different radio links (e.g., `SubMac` for 802.15.4 radio type). Broadcast frames are sent in parallel over all radio links. A unicast transmission is sent over a single radio link at a time but on a tx failure it may be retransmitted over other radio links. This commit also adds the concept of deferred ack, allowing radio links report status of ack at a later time through a different callback. --- Android.mk | 3 + doc/ot_api_doc.h | 1 + include/openthread/link.h | 3 + include/openthread/platform/radio.h | 2 + src/core/BUILD.gn | 7 + src/core/CMakeLists.txt | 3 + src/core/Makefile.am | 7 + src/core/common/instance.hpp | 16 +- src/core/common/message.cpp | 3 + src/core/common/message.hpp | 50 +- src/core/config/radio_link.h | 75 +++ src/core/mac/data_poll_handler.cpp | 33 +- src/core/mac/data_poll_handler.hpp | 16 +- src/core/mac/data_poll_sender.cpp | 8 + src/core/mac/data_poll_sender.hpp | 14 + src/core/mac/mac.cpp | 439 +++++++++++----- src/core/mac/mac.hpp | 52 +- src/core/mac/mac_frame.cpp | 106 +++- src/core/mac/mac_frame.hpp | 45 +- src/core/mac/mac_links.cpp | 280 ++++++++++ src/core/mac/mac_links.hpp | 691 +++++++++++++++++++++++++ src/core/mac/mac_types.cpp | 144 ++++++ src/core/mac/mac_types.hpp | 310 +++++++++++ src/core/mac/sub_mac.hpp | 2 +- src/core/openthread-core-config.h | 1 + src/core/radio/radio.cpp | 9 +- src/core/radio/radio.hpp | 453 +++++++++++++--- src/core/radio/radio_platform.cpp | 118 ++++- src/core/radio/trel.cpp | 64 +++ src/core/radio/trel.hpp | 106 ++++ src/core/thread/csl_tx_scheduler.cpp | 43 +- src/core/thread/csl_tx_scheduler.hpp | 4 +- src/core/thread/discover_scanner.cpp | 10 +- src/core/thread/discover_scanner.hpp | 6 +- src/core/thread/key_manager.cpp | 86 ++- src/core/thread/key_manager.hpp | 87 +++- src/core/thread/mesh_forwarder.cpp | 215 ++++++-- src/core/thread/mesh_forwarder.hpp | 31 +- src/core/thread/mesh_forwarder_ftd.cpp | 26 +- src/core/thread/mle.cpp | 83 ++- src/core/thread/mle_router.cpp | 32 +- src/core/thread/mle_router.hpp | 3 +- src/core/thread/radio_selector.cpp | 378 ++++++++++++++ src/core/thread/radio_selector.hpp | 200 +++++++ src/core/thread/thread_netif.cpp | 3 + src/core/thread/thread_netif.hpp | 4 + src/core/thread/topology.cpp | 3 +- src/core/thread/topology.hpp | 79 ++- tests/unit/test_mac_frame.cpp | 7 +- 49 files changed, 3948 insertions(+), 413 deletions(-) create mode 100644 src/core/config/radio_link.h create mode 100644 src/core/mac/mac_links.cpp create mode 100644 src/core/mac/mac_links.hpp create mode 100644 src/core/radio/trel.cpp create mode 100644 src/core/radio/trel.hpp create mode 100644 src/core/thread/radio_selector.cpp create mode 100644 src/core/thread/radio_selector.hpp diff --git a/Android.mk b/Android.mk index 2732f18d8..9ef3f6ee5 100644 --- a/Android.mk +++ b/Android.mk @@ -215,6 +215,7 @@ LOCAL_SRC_FILES := \ src/core/mac/mac.cpp \ src/core/mac/mac_filter.cpp \ src/core/mac/mac_frame.cpp \ + src/core/mac/mac_links.cpp \ src/core/mac/mac_types.cpp \ src/core/mac/sub_mac.cpp \ src/core/mac/sub_mac_callbacks.cpp \ @@ -250,6 +251,7 @@ LOCAL_SRC_FILES := \ src/core/radio/radio.cpp \ src/core/radio/radio_callbacks.cpp \ src/core/radio/radio_platform.cpp \ + src/core/radio/trel.cpp \ src/core/thread/address_resolver.cpp \ src/core/thread/announce_begin_server.cpp \ src/core/thread/announce_sender.cpp \ @@ -278,6 +280,7 @@ LOCAL_SRC_FILES := \ src/core/thread/network_data_notifier.cpp \ src/core/thread/network_diagnostic.cpp \ src/core/thread/panid_query_server.cpp \ + src/core/thread/radio_selector.cpp \ src/core/thread/router_table.cpp \ src/core/thread/src_match_controller.cpp \ src/core/thread/thread_netif.cpp \ diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index 9ba5282fe..5f9f1444c 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -158,6 +158,7 @@ * @defgroup plat-spi-slave SPI Slave * @defgroup plat-time Time Service * @defgroup plat-toolchain Toolchain + * @defgroup plat-trel TREL * @defgroup plat-uart UART * * @} diff --git a/include/openthread/link.h b/include/openthread/link.h index a833f3a70..5171da74f 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -69,6 +69,9 @@ typedef struct otThreadLinkInfo // Applicable/Required only when time sync feature (`OPENTHREAD_CONFIG_TIME_SYNC_ENABLE`) is enabled. uint8_t mTimeSyncSeq; ///< The time sync sequence. int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds. + + // Applicable only when OPENTHREAD_CONFIG_MULTI_RADIO feature is enabled. + uint8_t mRadioType; ///< Radio link type. } otThreadLinkInfo; /** diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index e1cb45dd0..510152a48 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -217,6 +217,8 @@ typedef struct otRadioFrame uint16_t mLength; ///< Length of the PSDU. uint8_t mChannel; ///< Channel used to transmit/receive the frame. + uint8_t mRadioType; ///< Radio link type - should be ignored by radio driver. + /** * The union of transmit and receive information for a radio frame. */ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 7b3901ad6..eaaac1e80 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -423,6 +423,8 @@ openthread_core_files = [ "mac/mac_filter.hpp", "mac/mac_frame.cpp", "mac/mac_frame.hpp", + "mac/mac_links.cpp", + "mac/mac_links.hpp", "mac/mac_types.cpp", "mac/mac_types.hpp", "mac/sub_mac.cpp", @@ -494,6 +496,8 @@ openthread_core_files = [ "radio/radio.hpp", "radio/radio_callbacks.cpp", "radio/radio_platform.cpp", + "radio/trel.cpp", + "radio/trel.hpp", "thread/address_resolver.cpp", "thread/address_resolver.hpp", "thread/announce_begin_server.cpp", @@ -555,6 +559,8 @@ openthread_core_files = [ "thread/network_diagnostic_tlvs.hpp", "thread/panid_query_server.cpp", "thread/panid_query_server.hpp", + "thread/radio_selector.cpp", + "thread/radio_selector.hpp", "thread/router_table.cpp", "thread/router_table.hpp", "thread/src_match_controller.cpp", @@ -657,6 +663,7 @@ source_set("libopenthread_core_config") { "config/openthread-core-default-config.h", "config/parent_search.h", "config/platform.h", + "config/radio_link.h", "config/sntp_client.h", "config/time_sync.h", "config/tmf.h", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1c948d0ab..9668b2395 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -107,6 +107,7 @@ set(COMMON_SOURCES mac/mac.cpp mac/mac_filter.cpp mac/mac_frame.cpp + mac/mac_links.cpp mac/mac_types.cpp mac/sub_mac.cpp mac/sub_mac_callbacks.cpp @@ -143,6 +144,7 @@ set(COMMON_SOURCES radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp + radio/trel.cpp thread/address_resolver.cpp thread/announce_begin_server.cpp thread/announce_sender.cpp @@ -171,6 +173,7 @@ set(COMMON_SOURCES thread/network_data_notifier.cpp thread/network_diagnostic.cpp thread/panid_query_server.cpp + thread/radio_selector.cpp thread/router_table.cpp thread/src_match_controller.cpp thread/thread_netif.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 57de1cbee..3403d7049 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -184,6 +184,7 @@ SOURCES_COMMON = \ mac/mac.cpp \ mac/mac_filter.cpp \ mac/mac_frame.cpp \ + mac/mac_links.cpp \ mac/mac_types.cpp \ mac/sub_mac.cpp \ mac/sub_mac_callbacks.cpp \ @@ -220,6 +221,7 @@ SOURCES_COMMON = \ radio/radio.cpp \ radio/radio_callbacks.cpp \ radio/radio_platform.cpp \ + radio/trel.cpp \ thread/address_resolver.cpp \ thread/announce_begin_server.cpp \ thread/announce_sender.cpp \ @@ -248,6 +250,7 @@ SOURCES_COMMON = \ thread/network_data_notifier.cpp \ thread/network_diagnostic.cpp \ thread/panid_query_server.cpp \ + thread/radio_selector.cpp \ thread/router_table.cpp \ thread/src_match_controller.cpp \ thread/thread_netif.cpp \ @@ -398,6 +401,7 @@ HEADERS_COMMON = \ config/openthread-core-default-config.h \ config/parent_search.h \ config/platform.h \ + config/radio_link.h \ config/sntp_client.h \ config/time_sync.h \ config/tmf.h \ @@ -417,6 +421,7 @@ HEADERS_COMMON = \ mac/mac.hpp \ mac/mac_filter.hpp \ mac/mac_frame.hpp \ + mac/mac_links.hpp \ mac/mac_types.hpp \ mac/sub_mac.hpp \ meshcop/announce_begin_client.hpp \ @@ -452,6 +457,7 @@ HEADERS_COMMON = \ net/tcp.hpp \ net/udp6.hpp \ radio/radio.hpp \ + radio/trel.hpp \ thread/address_resolver.hpp \ thread/announce_begin_server.hpp \ thread/announce_sender.hpp \ @@ -485,6 +491,7 @@ HEADERS_COMMON = \ thread/network_diagnostic.hpp \ thread/network_diagnostic_tlvs.hpp \ thread/panid_query_server.hpp \ + thread/radio_selector.hpp \ thread/router_table.hpp \ thread/src_match_controller.hpp \ thread/thread_netif.hpp \ diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index 9d9ee552f..b328cb829 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -446,6 +446,13 @@ template <> inline MeshForwarder &Instance::Get(void) return mThreadNetif.mMeshForwarder; } +#if OPENTHREAD_CONFIG_MULTI_RADIO +template <> inline RadioSelector &Instance::Get(void) +{ + return mThreadNetif.mRadioSelector; +} +#endif + template <> inline Mle::Mle &Instance::Get(void) { return mThreadNetif.mMleRouter; @@ -500,9 +507,16 @@ template <> inline Mac::Mac &Instance::Get(void) template <> inline Mac::SubMac &Instance::Get(void) { - return mThreadNetif.mMac.mSubMac; + return mThreadNetif.mMac.mLinks.mSubMac; } +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +template <> inline Trel::Link &Instance::Get(void) +{ + return mThreadNetif.mMac.mLinks.mTrel; +} +#endif + #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE template <> inline Mac::Filter &Instance::Get(void) { diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index a1115baed..3aeb1c838 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -625,6 +625,9 @@ void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo) SetTimeSyncSeq(aLinkInfo.mTimeSyncSeq); SetNetworkTimeOffset(aLinkInfo.mNetworkTimeOffset); #endif +#if OPENTHREAD_CONFIG_MULTI_RADIO + SetRadioType(static_cast(aLinkInfo.mRadioType)); +#endif } bool Message::IsTimeSync(void) const diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 7fef09235..77a4ffa8e 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -176,8 +176,14 @@ struct MessageMetadata bool mMulticastLoop : 1; ///< Indicates whether or not this multicast message may be looped back. #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE bool mTimeSync : 1; ///< Indicates whether the message is also used for time sync purpose. - uint8_t mTimeSyncSeq; ///< The time sync sequence. int64_t mNetworkTimeOffset; ///< The time offset to the Thread network time, in microseconds. + uint8_t mTimeSyncSeq; ///< The time sync sequence. +#endif +#if OPENTHREAD_CONFIG_MULTI_RADIO + uint8_t mRadioType : 2; ///< The radio link type the message was received on, or should be sent on. + bool mIsRadioTypeSet : 1; ///< Indicates whether the radio type is set. + + static_assert(Mac::kNumRadioTypes <= (1 << 2), "mRadioType bitfield cannot store all radio type values"); #endif }; @@ -1116,6 +1122,48 @@ public: uint8_t GetTimeSyncSeq(void) const { return GetMetadata().mTimeSyncSeq; } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + /** + * This method indicates whether the radio type is set. + * + * @retval TRUE If the radio type is set. + * @retval FALSE If the radio type is not set. + * + */ + bool IsRadioTypeSet(void) const { return GetMetadata().mIsRadioTypeSet; } + + /** + * This method gets the radio link type the message was received on, or should be sent on. + * + * This method should be used only when `IsRadioTypeSet()` returns `true`. + * + * @returns The radio link type of the message. + * + */ + Mac::RadioType GetRadioType(void) const { return static_cast(GetMetadata().mRadioType); } + + /** + * This method sets the radio link type the message was received on, or should be sent on. + * + * @param[in] aRadioType A radio link type of the message. + * + */ + void SetRadioType(Mac::RadioType aRadioType) + { + GetMetadata().mIsRadioTypeSet = true; + GetMetadata().mRadioType = aRadioType; + } + + /** + * This method clears any previously set radio type on the message. + * + * After calling this method, `IsRadioTypeSet()` returns false until radio type is set (`SetRadioType()`). + * + */ + void ClearRadioType(void) { GetMetadata().mIsRadioTypeSet = false; } + +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO + private: /** * This method returns a pointer to the message pool to which this message belongs diff --git a/src/core/config/radio_link.h b/src/core/config/radio_link.h new file mode 100644 index 000000000..1179261b9 --- /dev/null +++ b/src/core/config/radio_link.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, 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 compile-time configurations for the radio links. + * + */ + +#ifndef CONFIG_RADIO_LINK_H_ +#define CONFIG_RADIO_LINK_H_ + +/** + * @def OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + * + * Set to 1 to enable support for IEEE802.15.4 radio link. + * + */ +#ifndef OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +#define OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + * + * Set to 1 to enable support for Thread Radio Encapsulation Link (TREL). + * + */ +#ifndef OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#define OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE 0 +#endif + +//-------------------------------------------------------------- + +#if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE && !OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#error "No radio link type is enabled - at least one radio link type should be enabled" +#endif + +#ifdef OPENTHREAD_CONFIG_MULTI_RADIO +#error "OPENTHREAD_CONFIG_MULTI_RADIO should not be defined directly." \ + "It is derived from CONFIG_RADIO_LINK__ENABLE options." +#endif + +#if (OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE) +#define OPENTHREAD_CONFIG_MULTI_RADIO 1 +#else +#define OPENTHREAD_CONFIG_MULTI_RADIO 0 +#endif + +#endif // CONFIG_RADIO_LINK_H_ diff --git a/src/core/mac/data_poll_handler.cpp b/src/core/mac/data_poll_handler.cpp index e5c050b53..cc939b6b5 100644 --- a/src/core/mac/data_poll_handler.cpp +++ b/src/core/mac/data_poll_handler.cpp @@ -138,6 +138,10 @@ void DataPollHandler::HandleDataPoll(Mac::RxFrame &aFrame) child->SetLastHeard(TimerMilli::GetNow()); child->ResetLinkFailures(); +#if OPENTHREAD_CONFIG_MULTI_RADIO + child->SetLastPollRadioType(aFrame.GetRadioType()); +#endif + indirectMsgCount = child->GetIndirectMessageCount(); otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount, @@ -167,13 +171,20 @@ exit: return; } -otError DataPollHandler::HandleFrameRequest(Mac::TxFrame &aFrame) +Mac::TxFrame *DataPollHandler::HandleFrameRequest(Mac::TxFrames &aTxFrames) { - otError error = OT_ERROR_NONE; + Mac::TxFrame *frame = nullptr; - VerifyOrExit(mIndirectTxChild != nullptr, error = OT_ERROR_ABORT); + VerifyOrExit(mIndirectTxChild != nullptr); - SuccessOrExit(error = mCallbacks.PrepareFrameForChild(aFrame, mFrameContext, *mIndirectTxChild)); +#if OPENTHREAD_CONFIG_MULTI_RADIO + frame = &aTxFrames.GetTxFrame(mIndirectTxChild->GetLastPollRadioType()); +#else + frame = &aTxFrames.GetTxFrame(); +#endif + + VerifyOrExit(mCallbacks.PrepareFrameForChild(*frame, mFrameContext, *mIndirectTxChild) == OT_ERROR_NONE, + frame = nullptr); if (mIndirectTxChild->GetIndirectTxAttempts() > 0) { @@ -181,22 +192,22 @@ otError DataPollHandler::HandleFrameRequest(Mac::TxFrame &aFrame) // child, we ensure to use the same frame counter, key id, and // data sequence number as the previous attempt. - aFrame.SetIsARetransmission(true); - aFrame.SetSequence(mIndirectTxChild->GetIndirectDataSequenceNumber()); + frame->SetIsARetransmission(true); + frame->SetSequence(mIndirectTxChild->GetIndirectDataSequenceNumber()); - if (aFrame.GetSecurityEnabled()) + if (frame->GetSecurityEnabled()) { - aFrame.SetFrameCounter(mIndirectTxChild->GetIndirectFrameCounter()); - aFrame.SetKeyId(mIndirectTxChild->GetIndirectKeyId()); + frame->SetFrameCounter(mIndirectTxChild->GetIndirectFrameCounter()); + frame->SetKeyId(mIndirectTxChild->GetIndirectKeyId()); } } else { - aFrame.SetIsARetransmission(false); + frame->SetIsARetransmission(false); } exit: - return error; + return frame; } void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, otError aError) diff --git a/src/core/mac/data_poll_handler.hpp b/src/core/mac/data_poll_handler.hpp index d1e140809..6d6285330 100644 --- a/src/core/mac/data_poll_handler.hpp +++ b/src/core/mac/data_poll_handler.hpp @@ -117,6 +117,11 @@ public: bool IsFrameReplacePending(void) const { return mFrameReplacePending; } void SetFrameReplacePending(bool aReplacePending) { mFrameReplacePending = aReplacePending; } +#if OPENTHREAD_CONFIG_MULTI_RADIO + Mac::RadioType GetLastPollRadioType(void) const { return mLastPollRadioType; } + void SetLastPollRadioType(Mac::RadioType aRadioType) { mLastPollRadioType = aRadioType; } +#endif + uint32_t mIndirectFrameCounter; // Frame counter for current indirect frame (used for retx). uint8_t mIndirectKeyId; // Key Id for current indirect frame (used for retx). uint8_t mIndirectDsn; // MAC level Data Sequence Number (DSN) for retx attempts. @@ -124,6 +129,9 @@ public: bool mDataPollPending : 1; // Indicates whether or not a Data Poll was received. bool mFramePurgePending : 1; // Indicates a pending purge request for the current indirect frame. bool mFrameReplacePending : 1; // Indicates a pending replace request for the current indirect frame. +#if OPENTHREAD_CONFIG_MULTI_RADIO + Mac::RadioType mLastPollRadioType; // The radio link last data poll frame was received on. +#endif static_assert(kMaxPollTriggeredTxAttempts < (1 << 5), "mIndirectTxAttempts cannot fit max!"); }; @@ -163,7 +171,7 @@ public: * @param[out] aContext A reference to a `FrameContext` where the context for the new frame would be placed. * @param[in] aChild The child for which to prepare the frame. * - * @retval OT_ERROR_NONE Frame was prepared successfully + * @retval OT_ERROR_NONE Frame was prepared successfully. * @retval OT_ERROR_ABORT Indirect transmission to child should be aborted (no frame for the child). * */ @@ -262,9 +270,9 @@ public: private: // Callbacks from MAC - void HandleDataPoll(Mac::RxFrame &aFrame); - otError HandleFrameRequest(Mac::TxFrame &aFrame); - void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError); + void HandleDataPoll(Mac::RxFrame &aFrame); + Mac::TxFrame *HandleFrameRequest(Mac::TxFrames &aTxFrames); + void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError); void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError, Child &aChild); void ProcessPendingPolls(void); diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index 7ef5858a0..0d36fd743 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -135,7 +135,11 @@ exit: return error; } +#if OPENTHREAD_CONFIG_MULTI_RADIO +otError DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const +#else otError DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const +#endif { otError error = OT_ERROR_NONE; const Neighbor &parent = GetParent(); @@ -153,6 +157,10 @@ otError DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const aDest.SetShort(parent.GetRloc16()); } +#if OPENTHREAD_CONFIG_MULTI_RADIO + aRadioType = Get().SelectPollFrameRadio(parent); +#endif + exit: return error; } diff --git a/src/core/mac/data_poll_sender.hpp b/src/core/mac/data_poll_sender.hpp index a7574a5a4..8eb9f0655 100644 --- a/src/core/mac/data_poll_sender.hpp +++ b/src/core/mac/data_poll_sender.hpp @@ -130,6 +130,19 @@ public: */ uint32_t GetExternalPollPeriod(void) const { return mExternalPollPeriod; } +#if OPENTHREAD_CONFIG_MULTI_RADIO + /** + * This method gets the destination MAC address for a data poll frame. + * + * @param[out] aDest Reference to a `MAC::Address` to output the poll destination address (on success). + * @param[out] aRadioType Reference to a `Mac::RadioType` to output the link type (on success). + * + * @retval OT_ERROR_NONE @p aDest and @p aRadioType were updated successfully. + * @retval OT_ERROR_ABORT Abort the data poll transmission (not currently attached to any parent). + * + */ + otError GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const; +#else /** * This method gets the destination MAC address for a data poll frame. * @@ -140,6 +153,7 @@ public: * */ otError GetPollDestinationAddress(Mac::Address &aDest) const; +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO /** * This method informs the data poll sender of success/error status of a previously requested poll frame diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index f98f45aa4..20cbfabec 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -114,12 +114,15 @@ Mac::Mac(Instance &aInstance) #endif , mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union , mScanHandlerContext(nullptr) - , mSubMac(aInstance) + , mLinks(aInstance) , mOperationTask(aInstance, Mac::HandleOperationTask, this) , mTimer(aInstance, Mac::HandleTimer, this) , mOobFrame(nullptr) , mKeyIdMode2FrameCounter(0) , mCcaSampleCount(0) +#if OPENTHREAD_CONFIG_MULTI_RADIO + , mTxError(OT_ERROR_NONE) +#endif { ExtAddress randomExtAddress; @@ -130,7 +133,7 @@ Mac::Mac(Instance &aInstance) mExtendedPanId.Clear(); SetEnabled(true); - IgnoreError(mSubMac.Enable()); + mLinks.Enable(); Get().UpdateKeyMaterial(); SetExtendedPanId(static_cast(sExtendedPanidInit)); @@ -293,7 +296,7 @@ void Mac::PerformActiveScan(void) } else { - mSubMac.SetPanId(mPanId); + mLinks.SetPanId(mPanId); FinishOperation(); ReportActiveScanResult(nullptr); PerformNextOperation(); @@ -330,14 +333,14 @@ void Mac::PerformEnergyScan(void) { while (true) { - IgnoreError(mSubMac.Receive(mScanChannel)); - ReportEnergyScanResult(mSubMac.GetRssi()); + mLinks.Receive(mScanChannel); + ReportEnergyScanResult(mLinks.GetRssi()); SuccessOrExit(error = UpdateScanChannel()); } } else { - error = mSubMac.EnergyScan(mScanChannel, mScanDuration); + error = mLinks.EnergyScan(mScanChannel, mScanDuration); } exit: @@ -407,7 +410,7 @@ void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle) #endif } - mSubMac.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous); + mLinks.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous); UpdateIdleMode(); exit: @@ -544,7 +547,7 @@ otError Mac::SetDomainName(const NameData &aNameData) void Mac::SetPanId(PanId aPanId) { SuccessOrExit(Get().Update(mPanId, aPanId, kEventThreadPanIdChanged)); - mSubMac.SetPanId(mPanId); + mLinks.SetPanId(mPanId); exit: return; @@ -664,16 +667,16 @@ void Mac::UpdateIdleMode(void) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (IsCslEnabled()) { - IgnoreError(mSubMac.CslSample(mRadioChannel)); + mLinks.CslSample(mRadioChannel); ExitNow(); } #endif - IgnoreError(mSubMac.Sleep()); + mLinks.Sleep(); otLogDebgMac("Idle mode: Radio sleeping"); } else { - IgnoreError(mSubMac.Receive(mRadioChannel)); + mLinks.Receive(mRadioChannel); otLogDebgMac("Idle mode: Radio receiving on channel %d", mRadioChannel); } @@ -877,7 +880,7 @@ void Mac::PerformNextOperation(void) break; case kOperationWaitingForData: - IgnoreError(mSubMac.Receive(mRadioChannel)); + mLinks.Receive(mRadioChannel); mTimer.Start(kDataPollTimeout); break; } @@ -892,14 +895,21 @@ void Mac::FinishOperation(void) mOperation = kOperationIdle; } -otError Mac::PrepareDataRequest(TxFrame &aFrame) +TxFrame *Mac::PrepareDataRequest(void) { - otError error = OT_ERROR_NONE; + TxFrame *frame = nullptr; Address src, dst; uint16_t fcf; - SuccessOrExit(error = Get().GetPollDestinationAddress(dst)); - VerifyOrExit(!dst.IsNone(), error = OT_ERROR_ABORT); +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioType radio; + + SuccessOrExit(Get().GetPollDestinationAddress(dst, radio)); + frame = &mLinks.GetTxFrames().GetTxFrame(radio); +#else + SuccessOrExit(Get().GetPollDestinationAddress(dst)); + frame = &mLinks.GetTxFrames().GetTxFrame(); +#endif fcf = Frame::kFcfFrameMacCmd | Frame::kFcfPanidCompression | Frame::kFcfAckRequest | Frame::kFcfSecurityEnabled; UpdateFrameControlField(nullptr, /* aIsTimeSync */ false, fcf); @@ -915,50 +925,54 @@ otError Mac::PrepareDataRequest(TxFrame &aFrame) src.SetShort(GetShortAddress()); } - aFrame.InitMacHeader(fcf, Frame::kKeyIdMode1 | Frame::kSecEncMic32); + frame->InitMacHeader(fcf, Frame::kKeyIdMode1 | Frame::kSecEncMic32); - if (aFrame.IsDstPanIdPresent()) + if (frame->IsDstPanIdPresent()) { - aFrame.SetDstPanId(GetPanId()); + frame->SetDstPanId(GetPanId()); } - aFrame.SetSrcAddr(src); - aFrame.SetDstAddr(dst); + frame->SetSrcAddr(src); + frame->SetDstAddr(dst); #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - IgnoreError(AppendHeaderIe(false, aFrame)); + IgnoreError(AppendHeaderIe(false, *frame)); #endif - IgnoreError(aFrame.SetCommandId(Frame::kMacCmdDataRequest)); + IgnoreError(frame->SetCommandId(Frame::kMacCmdDataRequest)); exit: - return error; + return frame; } -void Mac::PrepareBeaconRequest(TxFrame &aFrame) +TxFrame *Mac::PrepareBeaconRequest(void) { - uint16_t fcf = Frame::kFcfFrameMacCmd | Frame::kFcfDstAddrShort | Frame::kFcfSrcAddrNone; + TxFrame &frame = mLinks.GetTxFrames().GetBroadcastTxFrame(); + uint16_t fcf = Frame::kFcfFrameMacCmd | Frame::kFcfDstAddrShort | Frame::kFcfSrcAddrNone; - aFrame.InitMacHeader(fcf, Frame::kSecNone); - aFrame.SetDstPanId(kShortAddrBroadcast); - aFrame.SetDstAddr(kShortAddrBroadcast); - IgnoreError(aFrame.SetCommandId(Frame::kMacCmdBeaconRequest)); + frame.InitMacHeader(fcf, Frame::kSecNone); + frame.SetDstPanId(kShortAddrBroadcast); + frame.SetDstAddr(kShortAddrBroadcast); + IgnoreError(frame.SetCommandId(Frame::kMacCmdBeaconRequest)); otLogInfoMac("Sending Beacon Request"); + + return &frame; } -void Mac::PrepareBeacon(TxFrame &aFrame) +TxFrame *Mac::PrepareBeacon(void) { + TxFrame & frame = mLinks.GetTxFrames().GetBroadcastTxFrame(); uint8_t beaconLength; uint16_t fcf; Beacon * beacon = nullptr; BeaconPayload *beaconPayload = nullptr; fcf = Frame::kFcfFrameBeacon | Frame::kFcfDstAddrNone | Frame::kFcfSrcAddrExt; - aFrame.InitMacHeader(fcf, Frame::kSecNone); - IgnoreError(aFrame.SetSrcPanId(mPanId)); - aFrame.SetSrcAddr(GetExtAddress()); + frame.InitMacHeader(fcf, Frame::kSecNone); + IgnoreError(frame.SetSrcPanId(mPanId)); + frame.SetSrcAddr(GetExtAddress()); - beacon = reinterpret_cast(aFrame.GetPayload()); + beacon = reinterpret_cast(frame.GetPayload()); beacon->Init(); beaconLength = sizeof(*beacon); @@ -983,9 +997,11 @@ void Mac::PrepareBeacon(TxFrame &aFrame) beaconLength += sizeof(*beaconPayload); } - aFrame.SetPayloadLength(beaconLength); + frame.SetPayloadLength(beaconLength); LogBeacon("Sending", *beaconPayload); + + return &frame; } bool Mac::ShouldSendBeacon(void) const @@ -1048,9 +1064,33 @@ void Mac::ProcessTransmitSecurity(TxFrame &aFrame) break; case Frame::kKeyIdMode1: - // For MAC Key ID Mode 1, the security frame counter update and CCM* is done at SubMac or Radio depending on - // `OT_RADIO_CAPS_TRANSMIT_SEC`. + + // For 15.4 radio link, the AES CCM* and frame security counter (under MAC + // key ID mode 1) are managed by `SubMac` or `Radio` modules. +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +#if !OPENTHREAD_CONFIG_MULTI_RADIO ExitNow(); +#else + VerifyOrExit(aFrame.GetRadioType() != kRadioTypeIeee802154); +#endif +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + aFrame.SetAesKey(*mLinks.GetCurrentMacKey(aFrame)); + extAddress = &GetExtAddress(); + + // If the frame is marked as a retransmission, `MeshForwarder` which + // prepared the frame should set the frame counter and key id to the + // same values used in the earlier transmit attempt. For a new frame (not + // a retransmission), we get a new frame counter and key id from the key + // manager. + + if (!aFrame.IsARetransmission()) + { + mLinks.SetMacFrameCounter(aFrame); + aFrame.SetKeyId((keyManager.GetCurrentKeySequence() & 0x7f) + 1); + } +#endif break; case Frame::kKeyIdMode2: @@ -1088,76 +1128,87 @@ exit: void Mac::BeginTransmit(void) { - otError error = OT_ERROR_NONE; - TxFrame &sendFrame = mSubMac.GetTransmitFrame(); + TxFrame * frame = nullptr; + TxFrames &txFrames = mLinks.GetTxFrames(); + Address dstAddr; - VerifyOrExit(IsEnabled(), error = OT_ERROR_ABORT); - sendFrame.SetIsARetransmission(false); - sendFrame.SetIsSecurityProcessed(false); -#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - sendFrame.SetTxDelay(0); - sendFrame.SetTxDelayBaseTime(0); + txFrames.Clear(); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + mTxPendingRadioLinks.Clear(); + mTxError = OT_ERROR_ABORT; #endif - sendFrame.SetCsmaCaEnabled(true); // Set to true by default, only set to `false` for CSL transmission + + VerifyOrExit(IsEnabled()); switch (mOperation) { case kOperationActiveScan: - mSubMac.SetPanId(kPanIdBroadcast); - sendFrame.SetChannel(mScanChannel); - PrepareBeaconRequest(sendFrame); - sendFrame.SetSequence(0); - sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); - sendFrame.SetMaxFrameRetries(mMaxFrameRetriesDirect); + mLinks.SetPanId(kPanIdBroadcast); + frame = PrepareBeaconRequest(); + VerifyOrExit(frame != nullptr); + frame->SetChannel(mScanChannel); + frame->SetSequence(0); + frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); + frame->SetMaxFrameRetries(mMaxFrameRetriesDirect); break; case kOperationTransmitBeacon: - sendFrame.SetChannel(mRadioChannel); - PrepareBeacon(sendFrame); - sendFrame.SetSequence(mBeaconSequence++); - sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); - sendFrame.SetMaxFrameRetries(mMaxFrameRetriesDirect); + frame = PrepareBeacon(); + VerifyOrExit(frame != nullptr); + frame->SetChannel(mRadioChannel); + frame->SetSequence(mBeaconSequence++); + frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); + frame->SetMaxFrameRetries(mMaxFrameRetriesDirect); break; case kOperationTransmitPoll: - sendFrame.SetChannel(mRadioChannel); - SuccessOrExit(error = PrepareDataRequest(sendFrame)); - sendFrame.SetSequence(mDataSequence++); - sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); - sendFrame.SetMaxFrameRetries(mMaxFrameRetriesDirect); + frame = PrepareDataRequest(); + VerifyOrExit(frame != nullptr); + frame->SetChannel(mRadioChannel); + frame->SetSequence(mDataSequence++); + frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); + frame->SetMaxFrameRetries(mMaxFrameRetriesDirect); break; case kOperationTransmitDataDirect: - sendFrame.SetChannel(mRadioChannel); - sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); - sendFrame.SetMaxFrameRetries(mMaxFrameRetriesDirect); - SuccessOrExit(error = Get().HandleFrameRequest(sendFrame)); - sendFrame.SetSequence(mDataSequence++); + // Set channel and retry counts on all TxFrames before asking + // the next layer (`MeshForwarder`) to prepare the frame. This + // allows next layer to possibility change these parameters. + txFrames.SetChannel(mRadioChannel); + txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect); + txFrames.SetMaxFrameRetries(mMaxFrameRetriesDirect); + frame = Get().HandleFrameRequest(txFrames); + VerifyOrExit(frame != nullptr); + frame->SetSequence(mDataSequence++); break; #if OPENTHREAD_FTD case kOperationTransmitDataIndirect: - sendFrame.SetChannel(mRadioChannel); - sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsIndirect); - sendFrame.SetMaxFrameRetries(mMaxFrameRetriesIndirect); - SuccessOrExit(error = Get().HandleFrameRequest(sendFrame)); + txFrames.SetChannel(mRadioChannel); + txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsIndirect); + txFrames.SetMaxFrameRetries(mMaxFrameRetriesIndirect); + frame = Get().HandleFrameRequest(txFrames); + VerifyOrExit(frame != nullptr); // If the frame is marked as a retransmission, then data sequence number is already set. - if (!sendFrame.IsARetransmission()) + if (!frame->IsARetransmission()) { - sendFrame.SetSequence(mDataSequence++); + frame->SetSequence(mDataSequence++); } break; #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE case kOperationTransmitDataCsl: - sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsCsl); - sendFrame.SetMaxFrameRetries(kMaxFrameRetriesCsl); - SuccessOrExit(error = Get().HandleFrameRequest(sendFrame)); + txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsCsl); + txFrames.SetMaxFrameRetries(kMaxFrameRetriesCsl); + frame = Get().HandleFrameRequest(txFrames); + VerifyOrExit(frame != nullptr); + // If the frame is marked as a retransmission, then data sequence number is already set. - if (!sendFrame.IsARetransmission()) + if (!frame->IsARetransmission()) { - sendFrame.SetSequence(mDataSequence++); + frame->SetSequence(mDataSequence++); } break; @@ -1166,8 +1217,9 @@ void Mac::BeginTransmit(void) #endif // OPENTHREAD_FTD case kOperationTransmitOutOfBandFrame: - sendFrame.CopyFrom(*mOobFrame); - sendFrame.SetIsSecurityProcessed(true); + frame = &txFrames.GetBroadcastTxFrame(); + frame->CopyFrom(*mOobFrame); + frame->SetIsSecurityProcessed(true); break; default: @@ -1177,47 +1229,94 @@ void Mac::BeginTransmit(void) #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE { - uint8_t timeIeOffset = GetTimeIeOffset(sendFrame); + uint8_t timeIeOffset = GetTimeIeOffset(*frame); - sendFrame.SetTimeIeOffset(timeIeOffset); + frame->SetTimeIeOffset(timeIeOffset); if (timeIeOffset != 0) { - sendFrame.SetTimeSyncSeq(Get().GetTimeSyncSeq()); - sendFrame.SetNetworkTimeOffset(Get().GetNetworkTimeOffset()); + frame->SetTimeSyncSeq(Get().GetTimeSyncSeq()); + frame->SetNetworkTimeOffset(Get().GetNetworkTimeOffset()); } } #endif - if (!sendFrame.IsSecurityProcessed()) + if (!frame->IsSecurityProcessed()) { - ProcessTransmitSecurity(sendFrame); +#if OPENTHREAD_CONFIG_MULTI_RADIO + // Go through all selected radio link types for this tx and + // copy the frame into correct `TxFrame` for each radio type + // (if it is not already prepared), then process security for + // each radio type separately. This allows radio links to + // handle security differently, e.g., with different keys or + // link frame counters. + + for (uint8_t index = 0; index < OT_ARRAY_LENGTH(RadioTypes::kAllRadioTypes); index++) + { + RadioType radio = RadioTypes::kAllRadioTypes[index]; + + if (txFrames.GetSelectedRadioTypes().Contains(radio)) + { + TxFrame &txFrame = txFrames.GetTxFrame(radio); + + if (txFrame.IsEmpty()) + { + txFrame.CopyFrom(*frame); + } + + ProcessTransmitSecurity(txFrame); + } + } +#else + ProcessTransmitSecurity(*frame); +#endif } mBroadcastTransmitCount = 0; +#if OPENTHREAD_CONFIG_MULTI_RADIO + mTxPendingRadioLinks = txFrames.GetSelectedRadioTypes(); + + // If the "required radio type set" is empty,`mTxError` starts as + // `OT_ERROR_ABORT`. In this case, successful tx over any radio + // link is sufficient for overall tx to be considered successful. + // When the "required radio type set" is not empty, `mTxError` + // starts as `OT_ERROR_NONE` and we update it if tx over any link + // in the required set fails. + + if (!txFrames.GetRequiredRadioTypes().IsEmpty()) + { + mTxError = OT_ERROR_NONE; + } +#endif + #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS if (!mRxOnWhenIdle && !mPromiscuous) { - mShouldDelaySleep = sendFrame.GetFramePending(); + mShouldDelaySleep = frame->GetFramePending(); otLogDebgMac("Delay sleep for pending tx"); } #endif - error = mSubMac.Send(); +#if OPENTHREAD_CONFIG_MULTI_RADIO + mLinks.Send(*frame, mTxPendingRadioLinks); +#else + mLinks.Send(); +#endif exit: - if (error != OT_ERROR_NONE) + if (frame == nullptr) { - // If the sendFrame could not be prepared and the tx is being + // If the frame could not be prepared and the tx is being // aborted, we set the frame length to zero to mark it as empty. // The empty frame helps differentiate between an aborted tx due // to OpenThread itself not being able to prepare the frame, versus // the radio platform aborting the tx operation. - sendFrame.SetLength(0); - HandleTransmitDone(sendFrame, nullptr, OT_ERROR_ABORT); + frame = &txFrames.GetBroadcastTxFrame(); + frame->SetLength(0); + HandleTransmitDone(*frame, nullptr, OT_ERROR_ABORT); } } @@ -1364,7 +1463,12 @@ exit: void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError) { - if (!aFrame.IsEmpty()) +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (!aFrame.IsEmpty() +#if OPENTHREAD_CONFIG_MULTI_RADIO + && (aFrame.GetRadioType() == kRadioTypeIeee802154) +#endif + ) { Address dstAddr; @@ -1378,7 +1482,15 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError if (mBroadcastTransmitCount < kTxNumBcast) { - IgnoreError(mSubMac.Send()); +#if OPENTHREAD_CONFIG_MULTI_RADIO + { + RadioTypes radioTypes; + radioTypes.Add(kRadioTypeIeee802154); + mLinks.Send(aFrame, radioTypes); + } +#else + mLinks.Send(); +#endif ExitNow(); } @@ -1394,6 +1506,55 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError } #endif } +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (!aFrame.IsEmpty()) + { + RadioType radio = aFrame.GetRadioType(); + RadioTypes requriedRadios = mLinks.GetTxFrames().GetRequiredRadioTypes(); + + Get().UpdateOnSendDone(aFrame, aError); + + if (requriedRadios.IsEmpty()) + { + // If the "required radio type set" is empty, successful + // tx over any radio link is sufficient for overall tx to + // be considered successful. In this case `mTxError` + // starts as `OT_ERROR_ABORT` and we update it only when + // it is not already `OT_ERROR_NONE`. + + if (mTxError != OT_ERROR_NONE) + { + mTxError = aError; + } + } + else + { + // When the "required radio type set" is not empty we + // expect the successful frame tx on all links in this set + // to consider the overall tx successful. In this case, + // `mTxError` starts as `OT_ERROR_NONE` and we update it + // if tx over any link in the set fails. + + if (requriedRadios.Contains(radio) && (aError != OT_ERROR_NONE)) + { + otLogDebgMac("Frame tx failed on required radio link %s with error %s", RadioTypeToString(radio), + otThreadErrorToString(aError)); + + mTxError = aError; + } + } + + // Keep track of radio links on which the frame is sent + // and wait for all radio links to finish. + mTxPendingRadioLinks.Remove(radio); + + VerifyOrExit(mTxPendingRadioLinks.IsEmpty()); + + aError = mTxError; + } +#endif // Determine next action based on current operation. @@ -1439,9 +1600,9 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError mCounters.mTxDirectMaxRetryExpiry++; } #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE - else if (mSubMac.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT) + else if (mLinks.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT) { - mRetryHistogram.mTxDirectRetrySuccess[mSubMac.GetTransmitRetries()]++; + mRetryHistogram.mTxDirectRetrySuccess[mLinks.GetTransmitRetries()]++; } #endif @@ -1478,9 +1639,9 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError mCounters.mTxIndirectMaxRetryExpiry++; } #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE - else if (mSubMac.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT) + else if (mLinks.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT) { - mRetryHistogram.mTxIndirectRetrySuccess[mSubMac.GetTransmitRetries()]++; + mRetryHistogram.mTxIndirectRetrySuccess[mLinks.GetTransmitRetries()]++; } #endif @@ -1500,6 +1661,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, otError aError default: OT_ASSERT(false); + OT_UNREACHABLE_CODE(ExitNow()); // Added to suppress "unused label exit" warning (in TREL radio only). OT_UNREACHABLE_CODE(break); } @@ -1591,17 +1753,17 @@ otError Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Ne if (keyid == (keyManager.GetCurrentKeySequence() & 0x7f)) { keySequence = keyManager.GetCurrentKeySequence(); - macKey = &mSubMac.GetCurrentMacKey(); + macKey = mLinks.GetCurrentMacKey(aFrame); } else if (keyid == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f)) { keySequence = keyManager.GetCurrentKeySequence() - 1; - macKey = &mSubMac.GetPreviousMacKey(); + macKey = mLinks.GetTemporaryMacKey(aFrame, keySequence); } else if (keyid == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f)) { keySequence = keyManager.GetCurrentKeySequence() + 1; - macKey = &mSubMac.GetNextMacKey(); + macKey = mLinks.GetTemporaryMacKey(aFrame, keySequence); } else { @@ -1619,10 +1781,18 @@ otError Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Ne if (keySequence == aNeighbor->GetKeySequence()) { - // If frame counter is one off, then frame is a duplicate. - VerifyOrExit((frameCounter + 1) != aNeighbor->GetLinkFrameCounter(), error = OT_ERROR_DUPLICATED); + uint32_t neighborFrameCounter; - VerifyOrExit(frameCounter >= aNeighbor->GetLinkFrameCounter()); +#if OPENTHREAD_CONFIG_MULTI_RADIO + neighborFrameCounter = aNeighbor->GetLinkFrameCounters().Get(aFrame.GetRadioType()); +#else + neighborFrameCounter = aNeighbor->GetLinkFrameCounters().Get(); +#endif + + // If frame counter is one off, then frame is a duplicate. + VerifyOrExit((frameCounter + 1) != neighborFrameCounter, error = OT_ERROR_DUPLICATED); + + VerifyOrExit(frameCounter >= neighborFrameCounter); } } @@ -1650,11 +1820,21 @@ otError Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Ne aNeighbor->SetMleFrameCounter(0); } - aNeighbor->SetLinkFrameCounter(frameCounter + 1); -#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 - if ((frameCounter + 1) > aNeighbor->GetLinkAckFrameCounter()) +#if OPENTHREAD_CONFIG_MULTI_RADIO + aNeighbor->GetLinkFrameCounters().Set(aFrame.GetRadioType(), frameCounter + 1); +#else + aNeighbor->GetLinkFrameCounters().Set(frameCounter + 1); +#endif + +#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (aFrame.GetRadioType() == kRadioTypeIeee802154) +#endif { - aNeighbor->SetLinkAckFrameCounter(frameCounter + 1); + if ((frameCounter + 1) > aNeighbor->GetLinkAckFrameCounter()) + { + aNeighbor->SetLinkAckFrameCounter(frameCounter + 1); + } } #endif @@ -1730,15 +1910,15 @@ otError Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame) if (ackKeyId == (keyManager.GetCurrentKeySequence() & 0x7f)) { - macKey = &mSubMac.GetCurrentMacKey(); + macKey = &mLinks.GetSubMac().GetCurrentMacKey(); } else if (ackKeyId == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f)) { - macKey = &mSubMac.GetPreviousMacKey(); + macKey = &mLinks.GetSubMac().GetPreviousMacKey(); } else if (ackKeyId == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f)) { - macKey = &mSubMac.GetNextMacKey(); + macKey = &mLinks.GetSubMac().GetNextMacKey(); } else { @@ -1952,6 +2132,10 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, otError aError) } #endif } + +#if OPENTHREAD_CONFIG_MULTI_RADIO + Get().UpdateOnReceive(*neighbor, aFrame->GetRadioType(), /* aIsDuplicate */ false); +#endif } } @@ -2123,7 +2307,7 @@ void Mac::SetPromiscuous(bool aPromiscuous) mShouldDelaySleep = false; #endif - mSubMac.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous); + mLinks.SetRxOnWhenBackoff(mRxOnWhenIdle || mPromiscuous); UpdateIdleMode(); } @@ -2252,11 +2436,24 @@ void Mac::LogFrameRxFailure(const RxFrame *aFrame, otError aError) const void Mac::LogFrameTxFailure(const TxFrame &aFrame, otError aError, uint8_t aRetryCount, bool aWillRetx) const { - uint8_t maxAttempts = aFrame.GetMaxFrameRetries() + 1; - uint8_t curAttempt = aWillRetx ? (aRetryCount + 1) : maxAttempts; +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE && OPENTHREAD_CONFIG_MULTI_RADIO + if (aFrame.GetRadioType() == kRadioTypeIeee802154) +#elif OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (true) +#else + if (false) +#endif + { + uint8_t maxAttempts = aFrame.GetMaxFrameRetries() + 1; + uint8_t curAttempt = aWillRetx ? (aRetryCount + 1) : maxAttempts; - otLogInfoMac("Frame tx attempt %d/%d failed, error:%s, %s", curAttempt, maxAttempts, otThreadErrorToString(aError), - aFrame.ToInfoString().AsCString()); + otLogInfoMac("Frame tx attempt %d/%d failed, error:%s, %s", curAttempt, maxAttempts, + otThreadErrorToString(aError), aFrame.ToInfoString().AsCString()); + } + else + { + otLogInfoMac("Frame tx failed, error:%s, %s", otThreadErrorToString(aError), aFrame.ToInfoString().AsCString()); + } } void Mac::LogBeacon(const char *aActionText, const BeaconPayload &aBeaconPayload) const @@ -2305,8 +2502,8 @@ void Mac::SetCslChannel(uint8_t aChannel) { VerifyOrExit(GetCslChannel() != aChannel); - mSubMac.SetCslChannel(aChannel); - mSubMac.SetCslChannelSpecified(aChannel != 0); + mLinks.GetSubMac().SetCslChannel(aChannel); + mLinks.GetSubMac().SetCslChannelSpecified(aChannel != 0); if (IsCslEnabled()) { @@ -2318,7 +2515,7 @@ exit: void Mac::SetCslPeriod(uint16_t aPeriod) { - mSubMac.SetCslPeriod(aPeriod); + mLinks.GetSubMac().SetCslPeriod(aPeriod); if (IsCslEnabled()) { @@ -2333,7 +2530,7 @@ void Mac::SetCslTimeout(uint32_t aTimeout) { VerifyOrExit(GetCslTimeout() != aTimeout); - mSubMac.SetCslTimeout(aTimeout); + mLinks.GetSubMac().SetCslTimeout(aTimeout); if (IsCslEnabled()) { diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index 22cf65738..0e671b0e1 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -47,8 +47,10 @@ #include "mac/channel_mask.hpp" #include "mac/mac_filter.hpp" #include "mac/mac_frame.hpp" +#include "mac/mac_links.hpp" #include "mac/mac_types.hpp" #include "mac/sub_mac.hpp" +#include "radio/trel.hpp" #include "thread/key_manager.hpp" #include "thread/link_quality.hpp" @@ -262,7 +264,7 @@ public: * @returns A pointer to the IEEE 802.15.4 Extended Address. * */ - const ExtAddress &GetExtAddress(void) const { return mSubMac.GetExtAddress(); } + const ExtAddress &GetExtAddress(void) const { return mLinks.GetExtAddress(); } /** * This method sets the IEEE 802.15.4 Extended Address. @@ -270,7 +272,7 @@ public: * @param[in] aExtAddress A reference to the IEEE 802.15.4 Extended Address. * */ - void SetExtAddress(const ExtAddress &aExtAddress) { mSubMac.SetExtAddress(aExtAddress); } + void SetExtAddress(const ExtAddress &aExtAddress) { mLinks.SetExtAddress(aExtAddress); } /** * This method returns the IEEE 802.15.4 Short Address. @@ -278,7 +280,7 @@ public: * @returns The IEEE 802.15.4 Short Address. * */ - ShortAddress GetShortAddress(void) const { return mSubMac.GetShortAddress(); } + ShortAddress GetShortAddress(void) const { return mLinks.GetShortAddress(); } /** * This method sets the IEEE 802.15.4 Short Address. @@ -286,7 +288,7 @@ public: * @param[in] aShortAddress The IEEE 802.15.4 Short Address. * */ - void SetShortAddress(ShortAddress aShortAddress) { mSubMac.SetShortAddress(aShortAddress); } + void SetShortAddress(ShortAddress aShortAddress) { mLinks.SetShortAddress(aShortAddress); } /** * This method returns the IEEE 802.15.4 PAN Channel. @@ -573,7 +575,7 @@ public: */ void SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext) { - mSubMac.SetPcapCallback(aPcapCallback, aCallbackContext); + mLinks.SetPcapCallback(aPcapCallback, aCallbackContext); } /** @@ -647,7 +649,7 @@ public: * @returns The noise floor value in dBm. * */ - int8_t GetNoiseFloor(void) { return mSubMac.GetNoiseFloor(); } + int8_t GetNoiseFloor(void) { return mLinks.GetNoiseFloor(); } /** * This method returns the current CCA (Clear Channel Assessment) failure rate. @@ -684,7 +686,7 @@ public: * @returns CSL channel. * */ - uint8_t GetCslChannel(void) const { return mSubMac.GetCslChannel(); } + uint8_t GetCslChannel(void) const { return mLinks.GetSubMac().GetCslChannel(); } /** * This method sets the CSL channel. @@ -700,7 +702,7 @@ public: * @returns If CSL channel has been specified. * */ - bool IsCslChannelSpecified(void) const { return mSubMac.IsCslChannelSpecified(); } + bool IsCslChannelSpecified(void) const { return mLinks.GetSubMac().IsCslChannelSpecified(); } /** * This method gets the CSL period. @@ -708,7 +710,7 @@ public: * @returns CSL period in units of 10 symbols. * */ - uint16_t GetCslPeriod(void) const { return mSubMac.GetCslPeriod(); } + uint16_t GetCslPeriod(void) const { return mLinks.GetSubMac().GetCslPeriod(); } /** * This method sets the CSL period. @@ -724,7 +726,7 @@ public: * @returns CSL timeout in seconds. * */ - uint32_t GetCslTimeout(void) const { return mSubMac.GetCslTimeout(); } + uint32_t GetCslTimeout(void) const { return mLinks.GetSubMac().GetCslTimeout(); } /** * This method sets the CSL timeout. @@ -827,17 +829,18 @@ private: #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 otError ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame); #endif - void UpdateIdleMode(void); - void StartOperation(Operation aOperation); - void FinishOperation(void); - void PerformNextOperation(void); - otError PrepareDataRequest(TxFrame &aFrame); - void PrepareBeaconRequest(TxFrame &aFrame); - void PrepareBeacon(TxFrame &aFrame); - bool ShouldSendBeacon(void) const; - bool IsJoinable(void) const; - void BeginTransmit(void); - bool HandleMacCommand(RxFrame &aFrame); + + void UpdateIdleMode(void); + void StartOperation(Operation aOperation); + void FinishOperation(void); + void PerformNextOperation(void); + TxFrame *PrepareDataRequest(void); + TxFrame *PrepareBeaconRequest(void); + TxFrame *PrepareBeacon(void); + bool ShouldSendBeacon(void) const; + bool IsJoinable(void) const; + void BeginTransmit(void); + bool HandleMacCommand(RxFrame &aFrame); static void HandleTimer(Timer &aTimer); void HandleTimer(void); @@ -926,7 +929,7 @@ private: void *mScanHandlerContext; - SubMac mSubMac; + Links mLinks; Tasklet mOperationTask; TimerMilli mTimer; TxFrame * mOobFrame; @@ -938,6 +941,11 @@ private: RetryHistogram mRetryHistogram; #endif +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioTypes mTxPendingRadioLinks; + otError mTxError; +#endif + #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE Filter mFilter; #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index f10e48657..2fe3ce57f 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -37,6 +37,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" +#include "radio/trel.hpp" #if !OPENTHREAD_RADIO || OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE #include "crypto/aes_ccm.hpp" #endif @@ -970,25 +971,106 @@ exit: } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO +uint16_t Frame::GetMtu(void) const +{ + uint16_t mtu; + + switch (GetRadioType()) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + case kRadioTypeIeee802154: + mtu = OT_RADIO_FRAME_MAX_SIZE; + break; +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + case kRadioTypeTrel: + mtu = Trel::Link::kMtuSize; + break; +#endif + + default: + OT_ASSERT(false); + } + + return mtu; +} + +uint8_t Frame::GetFcsSize(void) const +{ + uint8_t fcsSize; + + switch (GetRadioType()) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + case kRadioTypeIeee802154: + fcsSize = k154FcsSize; + break; +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + case kRadioTypeTrel: + fcsSize = Trel::Link::kFcsSize; + break; +#endif + + default: + OT_ASSERT(false); + } + + return fcsSize; +} + +#elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +uint16_t Frame::GetMtu(void) const +{ + return Trel::Link::kMtuSize; +} + +uint8_t Frame::GetFcsSize(void) const +{ + return Trel::Link::kFcsSize; +} +#endif + void TxFrame::CopyFrom(const TxFrame &aFromFrame) { uint8_t * psduBuffer = mPsdu; otRadioIeInfo *ieInfoBuffer = mInfo.mTxInfo.mIeInfo; +#if OPENTHREAD_CONFIG_MULTI_RADIO + uint8_t radioType = mRadioType; +#endif memcpy(this, &aFromFrame, sizeof(Frame)); - // Set the original buffer pointers back on the frame - // which were overwritten by above `memcpy()`. + // Set the original buffer pointers (and link type) back on + // the frame (which were overwritten by above `memcpy()`). mPsdu = psduBuffer; mInfo.mTxInfo.mIeInfo = ieInfoBuffer; +#if OPENTHREAD_CONFIG_MULTI_RADIO + mRadioType = radioType; +#endif + memcpy(mPsdu, aFromFrame.mPsdu, aFromFrame.mLength); // mIeInfo may be null when TIME_SYNC is not enabled. #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE memcpy(mInfo.mTxInfo.mIeInfo, aFromFrame.mInfo.mTxInfo.mIeInfo, sizeof(otRadioIeInfo)); #endif + +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (mRadioType != aFromFrame.GetRadioType()) + { + // Frames associated with different radio link types can have + // different FCS size. We adjust the PSDU length after the + // copy to account for this. + + SetLength(aFromFrame.GetPsduLength() - aFromFrame.GetFcsSize() + GetFcsSize()); + } +#endif } void TxFrame::ProcessTransmitAesCcm(const ExtAddress &aExtAddress) @@ -1010,7 +1092,7 @@ void TxFrame::ProcessTransmitAesCcm(const ExtAddress &aExtAddress) Crypto::AesCcm::GenerateNonce(aExtAddress, frameCounter, securityLevel, nonce); aesCcm.SetKey(GetAesKey()); - tagLength = GetFooterLength() - Frame::kFcsSize; + tagLength = GetFooterLength() - GetFcsSize(); aesCcm.Init(GetHeaderLength(), GetPayloadLength(), tagLength, nonce, sizeof(nonce)); aesCcm.Header(GetHeader(), GetHeaderLength()); @@ -1161,12 +1243,12 @@ otError RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const Key & return OT_ERROR_NONE; #else - otError error = OT_ERROR_SECURITY; - uint32_t frameCounter = 0; - uint8_t securityLevel; - uint8_t nonce[Crypto::AesCcm::kNonceSize]; - uint8_t tag[kMaxMicSize]; - uint8_t tagLength; + otError error = OT_ERROR_SECURITY; + uint32_t frameCounter = 0; + uint8_t securityLevel; + uint8_t nonce[Crypto::AesCcm::kNonceSize]; + uint8_t tag[kMaxMicSize]; + uint8_t tagLength; Crypto::AesCcm aesCcm; VerifyOrExit(GetSecurityEnabled(), error = OT_ERROR_NONE); @@ -1177,7 +1259,7 @@ otError RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const Key & Crypto::AesCcm::GenerateNonce(aExtAddress, frameCounter, securityLevel, nonce); aesCcm.SetKey(aMacKey); - tagLength = GetFooterLength() - Frame::kFcsSize; + tagLength = GetFooterLength() - GetFcsSize(); aesCcm.Init(GetHeaderLength(), GetPayloadLength(), tagLength, nonce, sizeof(nonce)); aesCcm.Header(GetHeader(), GetHeaderLength()); @@ -1264,6 +1346,10 @@ Frame::InfoString Frame::ToInfoString(void) const dst.ToString().AsCString(), GetSecurityEnabled() ? "yes" : "no", GetAckRequest() ? "yes" : "no")); +#if OPENTHREAD_CONFIG_MULTI_RADIO + IgnoreError(string.Append(", radio:%s", RadioTypeToString(GetRadioType()))); +#endif + return string; } diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index fea5c59e6..959cb67fb 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -261,13 +261,12 @@ class Frame : public otRadioFrame public: enum { - kMtu = OT_RADIO_FRAME_MAX_SIZE, kFcfSize = sizeof(uint16_t), kDsnSize = sizeof(uint8_t), kSecurityControlSize = sizeof(uint8_t), kFrameCounterSize = sizeof(uint32_t), kCommandIdSize = sizeof(uint8_t), - kFcsSize = sizeof(uint16_t), + k154FcsSize = sizeof(uint16_t), kFcfFrameBeacon = 0 << 0, kFcfFrameData = 1 << 0, @@ -334,9 +333,9 @@ public: kHeaderIeCsl = 0x1a, kHeaderIeTermination2 = 0x7f, - kInfoStringSize = 110, ///< Max chars needed for the info string representation (@sa ToInfoString()). + kImmAckLength = kFcfSize + kDsnSize + k154FcsSize, - kImmAckLength = kFcfSize + kDsnSize + kFcsSize, + kInfoStringSize = 128, ///< Max chars needed for the info string representation (@sa ToInfoString()). }; /** @@ -952,13 +951,38 @@ public: #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT +#if OPENTHREAD_CONFIG_MULTI_RADIO + /** + * This method gets the radio link type of the frame. + * + * @returns Frame's radio link type. + * + */ + RadioType GetRadioType(void) const { return static_cast(mRadioType); } + + /** + * This method sets the radio link type of the frame. + * + * @param[in] aRadioType A radio link type. + * + */ + void SetRadioType(RadioType aRadioType) { mRadioType = static_cast(aRadioType); } +#endif + /** * This method returns the maximum transmission unit size (MTU). * * @returns The maximum transmission unit (MTU). * */ - uint16_t GetMtu(void) const { return kMtu; } + uint16_t GetMtu(void) const +#if !OPENTHREAD_CONFIG_MULTI_RADIO && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + { + return OT_RADIO_FRAME_MAX_SIZE; + } +#else + ; +#endif /** * This method returns the FCS size. @@ -966,7 +990,14 @@ public: * @returns This method returns the FCS size. * */ - uint16_t GetFcsSize(void) const { return kFcsSize; } + uint8_t GetFcsSize(void) const +#if !OPENTHREAD_CONFIG_MULTI_RADIO && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + { + return k154FcsSize; + } +#else + ; +#endif /** * This method returns information about the frame object as an `InfoString` object. @@ -1213,7 +1244,7 @@ public: void SetAesKey(const Mac::Key &aAesKey) { mInfo.mTxInfo.mAesKey = &aAesKey; } /** - * This method copies the PSDU and all attributes from another frame. + * This method copies the PSDU and all attributes (except for frame link type) from another frame. * * @note This method performs a deep copy meaning the content of PSDU buffer from the given frame is copied into * the PSDU buffer of the current frame. diff --git a/src/core/mac/mac_links.cpp b/src/core/mac/mac_links.cpp new file mode 100644 index 000000000..5b4126294 --- /dev/null +++ b/src/core/mac/mac_links.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2019, 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 MAC radio links. + */ + +#include "mac_links.hpp" + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" + +namespace ot { +namespace Mac { + +//--------------------------------------------------------------------------------------------------------------------- +// TxFrames + +TxFrames::TxFrames(Instance &aInstance) + : InstanceLocator(aInstance) +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + , mTxFrame802154(aInstance.Get().GetTransmitFrame()) +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + , mTxFrameTrel(aInstance.Get().GetTransmitFrame()) +#endif +#if OPENTHREAD_CONFIG_MULTI_RADIO + , mSelectedRadioTypes() +#endif +{ +} + +#if OPENTHREAD_CONFIG_MULTI_RADIO + +TxFrame &TxFrames::GetTxFrame(RadioType aRadioType) +{ + TxFrame *frame = nullptr; + + switch (aRadioType) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + case kRadioTypeIeee802154: + frame = &mTxFrame802154; + break; +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + case kRadioTypeTrel: + frame = &mTxFrameTrel; + break; +#endif + } + + mSelectedRadioTypes.Add(aRadioType); + + return *frame; +} + +TxFrame &TxFrames::GetTxFrame(RadioTypes aRadioTypes) +{ + // Return the TxFrame among all set of `aRadioTypes` with the smallest MTU. + // Note that this is `TxFrame` to be sent out in parallel over multiple radio + // radio links in `aRadioTypes, so we need to make sure that it fits in the + // most restricted radio link (with smallest MTU). + + TxFrame *frame = nullptr; + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (aRadioTypes.Contains(kRadioTypeIeee802154)) + { + frame = &mTxFrame802154; + } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (aRadioTypes.Contains(kRadioTypeTrel) && ((frame == nullptr) || (frame->GetMtu() > mTxFrameTrel.GetMtu()))) + { + frame = &mTxFrameTrel; + } +#endif + + mSelectedRadioTypes.Add(aRadioTypes); + + return *frame; +} + +TxFrame &TxFrames::GetBroadcastTxFrame(void) +{ + RadioTypes allRadios; + + allRadios.AddAll(); + return GetTxFrame(allRadios); +} + +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO + +//--------------------------------------------------------------------------------------------------------------------- +// Links + +Links::Links(Instance &aInstance) + : InstanceLocator(aInstance) + , mSubMac(aInstance) +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + , mTrel(aInstance) +#endif + , mTxFrames(aInstance) +#if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + , mShortAddress(kShortAddrInvalid) +#endif +{ +#if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mExtAddress.Clear(); +#endif +} + +#if OPENTHREAD_CONFIG_MULTI_RADIO + +void Links::Send(TxFrame &aFrame, RadioTypes aRadioTypes) +{ +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (aRadioTypes.Contains(kRadioTypeIeee802154) && mTxFrames.mTxFrame802154.IsEmpty()) + { + mTxFrames.mTxFrame802154.CopyFrom(aFrame); + } +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (aRadioTypes.Contains(kRadioTypeTrel) && mTxFrames.mTxFrameTrel.IsEmpty()) + { + mTxFrames.mTxFrameTrel.CopyFrom(aFrame); + } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (aRadioTypes.Contains(kRadioTypeIeee802154)) + { + otError error = mSubMac.Send(); + + OT_ASSERT(error == OT_ERROR_NONE); + OT_UNUSED_VARIABLE(error); + } +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (aRadioTypes.Contains(kRadioTypeTrel)) + { + mTrel.Send(); + } +#endif +} + +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO + +const Key *Links::GetCurrentMacKey(const Frame &aFrame) const +{ + // Gets the security MAC key (for Key Mode 1) based on radio link type of `aFrame`. + + const Key *key = nullptr; +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioType radioType = aFrame.GetRadioType(); +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (radioType == kRadioTypeIeee802154) +#endif + { + ExitNow(key = &Get().GetCurrentMacKey()); + } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (radioType == kRadioTypeTrel) +#endif + { + ExitNow(key = &Get().GetCurrentTrelMacKey()); + } +#endif + + OT_UNUSED_VARIABLE(aFrame); + +exit: + return key; +} + +const Key *Links::GetTemporaryMacKey(const Frame &aFrame, uint32_t aKeySequence) const +{ + // Gets the security MAC key (for Key Mode 1) based on radio link + // type of `aFrame` and given Key Sequence. + + const Key *key = nullptr; +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioType radioType = aFrame.GetRadioType(); +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (radioType == kRadioTypeIeee802154) +#endif + { + if (aKeySequence == Get().GetCurrentKeySequence() - 1) + { + ExitNow(key = &Get().GetPreviousMacKey()); + } + else if (aKeySequence == Get().GetCurrentKeySequence() + 1) + { + ExitNow(key = &Get().GetNextMacKey()); + } + else + { + OT_ASSERT(false); + } + } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (radioType == kRadioTypeTrel) +#endif + { + ExitNow(key = &Get().GetTemporaryTrelMacKey(aKeySequence)); + } +#endif + + OT_UNUSED_VARIABLE(aFrame); + +exit: + return key; +} + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +void Links::SetMacFrameCounter(TxFrame &aFrame) +{ +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioType radioType = aFrame.GetRadioType(); +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (radioType == kRadioTypeTrel) +#endif + { + aFrame.SetFrameCounter(Get().GetTrelMacFrameCounter()); + Get().IncrementTrelMacFrameCounter(); + ExitNow(); + } +#endif + +exit: + return; +} +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +} // namespace Mac +} // namespace ot diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp new file mode 100644 index 000000000..f03374d59 --- /dev/null +++ b/src/core/mac/mac_links.hpp @@ -0,0 +1,691 @@ +/* + * Copyright (c) 2019, 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 MAC radio links. + */ + +#ifndef MAC_LINKS_HPP_ +#define MAC_LINKS_HPP_ + +#include "openthread-core-config.h" + +#include "common/debug.hpp" +#include "common/locator.hpp" +#include "mac/mac_frame.hpp" +#include "mac/mac_types.hpp" +#include "mac/sub_mac.hpp" +#include "radio/trel.hpp" + +namespace ot { +namespace Mac { + +/** + * @addtogroup core-mac + * + * @brief + * This module includes definitions for MAC radio links (multi radio). + * + * @{ + * + */ + +/** + * This class represents tx frames for different radio link types. + * + */ +class TxFrames : InstanceLocator +{ + friend class Links; + +public: +#if OPENTHREAD_CONFIG_MULTI_RADIO + /** + * This method gets the `TxFrame` for a given radio link type. + * + * This method also updates the selected radio types (from `GetSelectedRadioTypes()`) to include the @p aRadioType. + * + * @param[in] aRadioType A radio link type. + * + * @returns A reference to the `TxFrame` for the given radio link type. + * + */ + TxFrame &GetTxFrame(RadioType aRadioType); + + /** + * This method gets the `TxFrame` with the smallest MTU size among a given set of radio types. + * + * This method also updates the selected radio types (from `GetSelectedRadioTypes()`) to include the set + * @p aRadioTypes. + * + * @param[in] aRadioTypes A set of radio link types. + * + * @returns A reference to the `TxFrame` with the smallest MTU size among the set of @p aRadioTypes. + * + */ + TxFrame &GetTxFrame(RadioTypes aRadioTypes); + + /** + * This method gets the `TxFrame` for sending a broadcast frame. + * + * This method also updates the selected radio type (from `GetSelectedRadioTypes()`) to include all radio types + * (supported by device). + * + * The broadcast frame is the `TxFrame` with the smallest MTU size among all radio types. + * + * @returns A reference to a `TxFrame` for broadcast. + * + */ + TxFrame &GetBroadcastTxFrame(void); + + /** + * This method gets the selected radio types. + * + * This set specifies the radio links the frame should be sent over (in parallel). The set starts a empty after + * method `Clear()` is called. It gets updated through calls to methods `GetTxFrame(aType)`, + * `GetTxFrame(aRadioTypes)`, or `GetBroadcastTxFrame()`. + * + * @returns The selected radio types. + * + */ + RadioTypes GetSelectedRadioTypes(void) const { return mSelectedRadioTypes; } + + /** + * This method gets the required radio types. + * + * This set specifies the radio links for which we expect the frame tx to be successful to consider the overall tx + * successful. If the set is empty, successful tx over any radio link is sufficient for overall tx to be considered + * successful. The required radio type set is expected to be a subset of selected radio types. + * + * The set starts as empty after `Clear()` call. It can be updated through `SetRequiredRadioTypes()` method + * + * @returns The required radio types. + * + */ + RadioTypes GetRequiredRadioTypes(void) const { return mRequiredRadioTypes; } + + /** + * This method sets the required types. + * + * Please see `GetRequiredRadioTypes()` for more details on how this set is used during tx. + * + * @param[in] aRadioTypes A set of radio link types. + * + */ + void SetRequiredRadioTypes(RadioTypes aRadioTypes) { mRequiredRadioTypes = aRadioTypes; } + +#else // #if OPENTHREAD_CONFIG_MULTI_RADIO + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + /** + * This method gets the tx frame. + * + * @returns A reference to `TxFrame`. + * + */ + TxFrame &GetTxFrame(void) { return mTxFrame802154; } +#elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + /** + * This method gets the tx frame. + * + * @returns A reference to `TxFrame`. + * + */ + TxFrame &GetTxFrame(void) { return mTxFrameTrel; } +#endif + /** + * This method gets a tx frame for sending a broadcast frame. + * + * @returns A reference to a `TxFrame` for broadcast. + * + */ + TxFrame &GetBroadcastTxFrame(void) { return GetTxFrame(); } + +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO + + /** + * This method clears all supported radio tx frames (sets the PSDU length to zero and clears flags). + * + */ + void Clear(void) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mTxFrame802154.SetLength(0); + mTxFrame802154.SetIsARetransmission(false); + mTxFrame802154.SetIsSecurityProcessed(false); + mTxFrame802154.SetCsmaCaEnabled(true); // Set to true by default, only set to `false` for CSL transmission +#if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + mTxFrame802154.SetTxDelay(0); + mTxFrame802154.SetTxDelayBaseTime(0); +#endif +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTxFrameTrel.SetLength(0); + mTxFrameTrel.SetIsARetransmission(false); + mTxFrameTrel.SetIsSecurityProcessed(false); + mTxFrameTrel.SetCsmaCaEnabled(true); +#endif + +#if OPENTHREAD_CONFIG_MULTI_RADIO + mSelectedRadioTypes.Clear(); + mRequiredRadioTypes.Clear(); +#endif + } + + /** + * This method sets the channel on all supported radio tx frames. + * + * @param[in] aChannel A channel. + * + */ + void SetChannel(uint8_t aChannel) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mTxFrame802154.SetChannel(aChannel); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTxFrameTrel.SetChannel(aChannel); +#endif + } + + /** + * This method sets the Sequence Number value on all supported radio tx frames. + * + * @param[in] aSequence The Sequence Number value. + * + */ + void SetSequence(uint8_t aSequence) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mTxFrame802154.SetSequence(aSequence); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTxFrameTrel.SetSequence(aSequence); +#endif + } + + /** + * This method sets the maximum number of the CSMA-CA backoffs on all supported radio tx + * frames. + * + * @param[in] aMaxCsmaBackoffs The maximum number of CSMA-CA backoffs. + * + */ + void SetMaxCsmaBackoffs(uint8_t aMaxCsmaBackoffs) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mTxFrame802154.SetMaxCsmaBackoffs(aMaxCsmaBackoffs); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTxFrameTrel.SetMaxCsmaBackoffs(aMaxCsmaBackoffs); +#endif + } + + /** + * This method sets the maximum number of retries allowed after a transmission failure on all supported radio tx + * frames. + * + * @param[in] aMaxFrameRetries The maximum number of retries allowed after a transmission failure. + * + */ + void SetMaxFrameRetries(uint8_t aMaxFrameRetries) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mTxFrame802154.SetMaxFrameRetries(aMaxFrameRetries); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTxFrameTrel.SetMaxFrameRetries(aMaxFrameRetries); +#endif + } + +private: + explicit TxFrames(Instance &aInstance); + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + TxFrame &mTxFrame802154; +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + TxFrame &mTxFrameTrel; +#endif + +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioTypes mSelectedRadioTypes; + RadioTypes mRequiredRadioTypes; +#endif +}; + +/** + * This class represents MAC radio links (multi radio). + * + */ +class Links : public InstanceLocator +{ + friend class ot::Instance; + +public: + enum + { + kInvalidRssiValue = SubMac::kInvalidRssiValue, ///< Invalid Received Signal Strength Indicator (RSSI) value. + }; + + /** + * This constructor initializes the `Links` object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit Links(Instance &aInstance); + + /** + * This method sets the PAN ID. + * + * @param[in] aPanId The PAN ID. + * + */ + void SetPanId(PanId aPanId) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.SetPanId(aPanId); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.SetPanId(aPanId); +#endif + } + + /** + * This method gets the MAC Short Address. + * + * @returns The MAC Short Address. + * + */ + ShortAddress GetShortAddress(void) const + { + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.GetShortAddress(); +#else + mShortAddress; +#endif + } + + /** + * This method sets the MAC Short Address. + * + * @param[in] aShortAddress A MAC Short Address. + * + */ + void SetShortAddress(ShortAddress aShortAddress) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.SetShortAddress(aShortAddress); +#else + mShortAddress = aShortAddress; +#endif + } + + /** + * This method gets the MAC Extended Address. + * + * @returns The MAC Extended Address. + * + */ + const ExtAddress &GetExtAddress(void) const + { + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.GetExtAddress(); +#else + mExtAddress; +#endif + } + + /** + * This method sets the MAC Extended Address. + * + * @param[in] aExtAddress A MAC Extended Address. + * + */ + void SetExtAddress(const ExtAddress &aExtAddress) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.SetExtAddress(aExtAddress); +#else + mExtAddress = aExtAddress; +#endif + } + + /** + * This method registers a callback to provide received packet capture for IEEE 802.15.4 frames. + * + * @param[in] aPcapCallback A pointer to a function that is called when receiving an IEEE 802.15.4 link frame + * or nullptr to disable the callback. + * @param[in] aCallbackContext A pointer to application-specific context. + * + */ + void SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.SetPcapCallback(aPcapCallback, aCallbackContext); +#endif + OT_UNUSED_VARIABLE(aPcapCallback); + OT_UNUSED_VARIABLE(aCallbackContext); + } + + /** + * This method indicates whether radio should stay in Receive or Sleep during CSMA backoff. + * + * @param[in] aRxOnWhenBackoff TRUE to keep radio in Receive, FALSE to put to Sleep during CSMA backoff. + * + */ + void SetRxOnWhenBackoff(bool aRxOnWhenBackoff) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.SetRxOnWhenBackoff(aRxOnWhenBackoff); +#endif + OT_UNUSED_VARIABLE(aRxOnWhenBackoff); + } + + /** + * This method enables all radio links. + * + */ + void Enable(void) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + IgnoreError(mSubMac.Enable()); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.Enable(); +#endif + } + + /** + * This method disables all radio links. + * + */ + void Disable(void) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + IgnoreError(mSubMac.Disable()); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.Disable(); +#endif + } + + /** + * This method transitions all radio links to Sleep. + * + */ + void Sleep(void) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + IgnoreError(mSubMac.Sleep()); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.Sleep(); +#endif + } + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * This method transitions all radios link to CSL sample state. + * + * CSL sample state is only applicable and used for 15.4 radio link. Other link are transitioned to sleep state. + * + * @param[in] aPanChannel The current phy channel used by the device. This param will only take effect when CSL + * channel hasn't been explicitly specified. + */ + void CslSample(uint8_t aPanChannel) + { + OT_UNUSED_VARIABLE(aPanChannel); +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + IgnoreError(mSubMac.CslSample(aPanChannel)); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.Sleep(); +#endif + } +#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + + /** + * This method transitions all radio links to Receive. + * + * @param[in] aChannel The channel to use for receiving. + * + */ + void Receive(uint8_t aChannel) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + IgnoreError(mSubMac.Receive(aChannel)); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.Receive(aChannel); +#endif + } + + /** + * This method gets the radio transmit frames. + * + * @returns The transmit frames. + * + */ + TxFrames &GetTxFrames(void) { return mTxFrames; } + +#if !OPENTHREAD_CONFIG_MULTI_RADIO + + /** + * This method sends a prepared frame. + * + * The prepared frame is from `GetTxFrames()`. This method is available only in single radio link mode. + * + */ + void Send(void) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + { + otError error = mSubMac.Send(); + OT_ASSERT(error == OT_ERROR_NONE); + OT_UNUSED_VARIABLE(error); + } +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrel.Send(); +#endif + } + +#else // #if !OPENTHREAD_CONFIG_MULTI_RADIO + + /** + * This method sends prepared frames over a given set of radio links. + * + * The prepared frame must be from `GetTxFrames()`. This method is available only in multi radio link mode. + * + * @param[in] aFrame A reference to a prepared frame. + * @param[in] aRadioTypes A set of radio types to send on. + * + */ + void Send(TxFrame &aFrame, RadioTypes aRadioTypes); + +#endif // !OPENTHREAD_CONFIG_MULTI_RADIO + + /** + * This method gets the number of transmit retries for the last transmitted frame. + * + * @returns Number of transmit retries. + * + */ + uint8_t GetTransmitRetries(void) const + { + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.GetTransmitRetries(); +#else + 0; +#endif + } + + /** + * This method gets the most recent RSSI measurement from radio link. + * + * @returns The RSSI in dBm when it is valid. `kInvalidRssiValue` when RSSI is invalid. + * + */ + int8_t GetRssi(void) const + { + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.GetRssi(); +#else + kInvalidRssiValue; +#endif + } + + /** + * This method begins energy scan. + * + * @param[in] aScanChannel The channel to perform the energy scan on. + * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. + * + * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_INVALID_STATE The radio was disabled or transmitting. + * @retval OT_ERROR_NOT_IMPLEMENTED Energy scan is not supported by radio link. + * + */ + otError EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) + { + OT_UNUSED_VARIABLE(aScanChannel); + OT_UNUSED_VARIABLE(aScanDuration); + + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.EnergyScan(aScanChannel, aScanDuration); +#else + OT_ERROR_NOT_IMPLEMENTED; +#endif + } + + /** + * This method returns the noise floor value (currently use the radio receive sensitivity value). + * + * @returns The noise floor value in dBm. + * + */ + int8_t GetNoiseFloor(void) + { + return +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + mSubMac.GetNoiseFloor(); +#else + kDefaultNoiseFloor; +#endif + } + + /** + * This methods gets a reference to the `SubMac` instance. + * + * @returns A reference to the `SubMac` instance. + * + */ + SubMac &GetSubMac(void) { return mSubMac; } + + /** + * This methods gets a reference to the `SubMac` instance. + * + * @returns A reference to the `SubMac` instance. + * + */ + const SubMac &GetSubMac(void) const { return mSubMac; } + + /** + * This method returns a reference to the current MAC key (for Key Mode 1) for a given Frame. + * + * @param[in] aFrame The frame for which to get the MAC key. + * + * @returns A reference to the current MAC key. + * + */ + const Key *GetCurrentMacKey(const Frame &aFrame) const; + + /** + * This method returns a reference to the temporary MAC key (for Key Mode 1) for a given Frame based on a given + * Key Sequence. + * + * @param[in] aFrame The frame for which to get the MAC key. + * @param[in] aKeySequence The Key Sequence number (MUST be one off (+1 or -1) from current key sequence number). + * + * @returns A reference to the temporary MAC key. + * + */ + const Key *GetTemporaryMacKey(const Frame &aFrame, uint32_t aKeySequence) const; + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + /** + * This method sets the current MAC frame counter value from the value from a `TxFrame`. + * + * @param[in] TxFrame The `TxFrame` from which to get the counter value. + * + * @retval OT_ERROR_NONE If successful. + * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. + * + */ + void SetMacFrameCounter(TxFrame &aFrame); +#endif + +private: + enum + { + kDefaultNoiseFloor = -100, + }; + + SubMac mSubMac; +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + Trel::Link mTrel; +#endif + + // `TxFrames` member definition should be after `mSubMac`, `mTrel` + // definitions to allow it to use their methods from its + // constructor. + TxFrames mTxFrames; + +#if !OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + ShortAddress mShortAddress; + ExtAddress mExtAddress; +#endif +}; + +/** + * @} + * + */ + +} // namespace Mac +} // namespace ot + +#endif // MAC_LINKS_HPP_ diff --git a/src/core/mac/mac_types.cpp b/src/core/mac/mac_types.cpp index df41a7f1d..3ed71506a 100644 --- a/src/core/mac/mac_types.cpp +++ b/src/core/mac/mac_types.cpp @@ -180,5 +180,149 @@ exit: } #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) +#if OPENTHREAD_CONFIG_MULTI_RADIO + +const RadioType RadioTypes::kAllRadioTypes[kNumRadioTypes] = { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + kRadioTypeIeee802154, +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + kRadioTypeTrel, +#endif +}; + +void RadioTypes::AddAll(void) +{ +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + Add(kRadioTypeIeee802154); +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + Add(kRadioTypeTrel); +#endif +} + +RadioTypes::InfoString RadioTypes::ToString(void) const +{ + InfoString string("{"); + bool addComma = false; + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (Contains(kRadioTypeIeee802154)) + { + IgnoreError(string.Append("%s%s", addComma ? ", " : " ", RadioTypeToString(kRadioTypeIeee802154))); + addComma = true; + } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (Contains(kRadioTypeTrel)) + { + IgnoreError(string.Append("%s%s", addComma ? ", " : " ", RadioTypeToString(kRadioTypeTrel))); + addComma = true; + } +#endif + + OT_UNUSED_VARIABLE(addComma); + + IgnoreError(string.Append(" }")); + + return string; +} + +const char *RadioTypeToString(RadioType aRadioType) +{ + const char *str = "unknown"; + + switch (aRadioType) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + case kRadioTypeIeee802154: + str = "15.4"; + break; +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + case kRadioTypeTrel: + str = "trel"; + break; +#endif + } + + return str; +} + +uint32_t LinkFrameCounters::Get(RadioType aRadioType) const +{ + uint32_t counter = 0; + + switch (aRadioType) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + case kRadioTypeIeee802154: + counter = m154Counter; + break; +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + case kRadioTypeTrel: + counter = mTrelCounter; + break; +#endif + } + + return counter; +} + +void LinkFrameCounters::Set(RadioType aRadioType, uint32_t aCounter) +{ + switch (aRadioType) + { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + case kRadioTypeIeee802154: + m154Counter = aCounter; + break; +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + case kRadioTypeTrel: + mTrelCounter = aCounter; + break; +#endif + } +} + +#endif // OPENTHREAD_CONFIG_MULTI_RADIO + +uint32_t LinkFrameCounters::GetMaximum(void) const +{ + uint32_t counter = 0; + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + if (counter < m154Counter) + { + counter = m154Counter; + } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (counter < mTrelCounter) + { + counter = mTrelCounter; + } +#endif + + return counter; +} + +void LinkFrameCounters::SetAll(uint32_t aCounter) +{ +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + m154Counter = aCounter; +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + mTrelCounter = aCounter; +#endif +} + } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac_types.hpp b/src/core/mac/mac_types.hpp index cdb9718b8..5cbdc63d1 100644 --- a/src/core/mac/mac_types.hpp +++ b/src/core/mac/mac_types.hpp @@ -635,6 +635,316 @@ private: }; #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) +#if OPENTHREAD_CONFIG_MULTI_RADIO + +/** + * This enumeration defines the radio link types. + * + */ +enum RadioType +{ +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + kRadioTypeIeee802154, ///< IEEE 802.15.4 (2.4GHz) link type. +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + kRadioTypeTrel, ///< Thread Radio Encapsulation link type. +#endif +}; + +enum +{ + /** + * This constant specifies the number of supported radio link types. + * + */ + kNumRadioTypes = (((OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE) ? 1 : 0) + + ((OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE) ? 1 : 0)), +}; + +/** + * This class represents a set of radio links. + * + */ +class RadioTypes +{ +public: + enum + { + kInfoStringSize = 32, ///< Max chars for the info string (`ToString()`). + }; + + /** + * This type defines the fixed-length `String` object returned from `ToString()`. + * + */ + typedef String InfoString; + + /** + * This static class variable defines an array containing all supported radio link types. + * + */ + static const RadioType kAllRadioTypes[kNumRadioTypes]; + + /** + * This constructor initializes a `RadioTypes` object as empty set + * + */ + RadioTypes(void) + : mBitMask(0) + { + } + + /** + * This constructor initializes a `RadioTypes` object with a given bit-mask. + * + * @param[in] aMask A bit-mask representing the radio types (the first bit corresponds to radio type 0, and so on) + * + */ + RadioTypes(uint8_t aMask) + : mBitMask(aMask) + { + } + + /** + * This method clears the set. + * + */ + void Clear(void) { mBitMask = 0; } + + /** + * This method indicates whether the set is empty or not + * + * @returns TRUE if the set is empty, FALSE otherwise. + * + */ + bool IsEmpty(void) const { return (mBitMask == 0); } + + /** + * This method indicates whether the set contains only a single radio type. + * + * @returns TRUE if the set contains a single radio type, FALSE otherwise. + * + */ + bool ContainsSingleRadio(void) const { return !IsEmpty() && ((mBitMask & (mBitMask - 1)) == 0); } + + /** + * This method indicates whether or not the set contains a given radio type. + * + * @param[in] aType A radio link type. + * + * @returns TRUE if the set contains @p aType, FALSE otherwise. + * + */ + bool Contains(RadioType aType) const { return ((mBitMask & BitFlag(aType)) != 0); } + + /** + * This method adds a radio type to the set. + * + * @param[in] aType A radio link type. + * + */ + void Add(RadioType aType) { mBitMask |= BitFlag(aType); } + + /** + * This method adds another radio types set to the current one. + * + * @param[in] aTypes A radio link type set to add. + * + */ + void Add(RadioTypes aTypes) { mBitMask |= aTypes.mBitMask; } + + /** + * This method adds all radio types supported by device to the set. + * + */ + void AddAll(void); + + /** + * This method removes a given radio type from the set. + * + * @param[in] aType A radio link type. + * + */ + void Remove(RadioType aType) { mBitMask &= ~BitFlag(aType); } + + /** + * This method gets the radio type set as a bitmask. + * + * The first bit in the mask corresponds to first radio type (radio type with value zero), and so on. + * + * @returns A bitmask representing the set of radio types. + * + */ + uint8_t GetAsBitMask(void) const { return mBitMask; } + + /** + * This method overloads operator `-` to return a new set which is the set difference between current set and + * a given set. + * + * @param[in] aOther Another radio type set. + * + * @returns A new set which is set difference between current one and @p aOther. + * + */ + RadioTypes operator-(const RadioTypes &aOther) const { return RadioTypes(mBitMask & ~aOther.mBitMask); } + + /** + * This method converts the radio set to human-readable string. + * + * @return A string representation of the set of radio types. + * + */ + InfoString ToString(void) const; + +private: + static uint8_t BitFlag(RadioType aType) { return static_cast(1U << static_cast(aType)); } + + uint8_t mBitMask; +}; + +/** + * This function converts a link type to a string + * + * @param[in] aRadioType A link type value. + * + * @returns A string representation of the link type. + * + */ +const char *RadioTypeToString(RadioType aRadioType); + +#endif // OPENTHREAD_CONFIG_MULTI_RADIO + +/** + * This class represents Link Frame Counters for all supported radio links. + * + */ +class LinkFrameCounters +{ +public: + /** + * This method resets all counters (set them all to zero). + * + */ + void Reset(void) { SetAll(0); } + +#if OPENTHREAD_CONFIG_MULTI_RADIO + + /** + * This method gets the link Frame Counter for a given radio link. + * + * @param[in] aRadioType A radio link type. + * + * @returns The Link Frame Counter for radio link @p aRadioType. + * + */ + uint32_t Get(RadioType aRadioType) const; + + /** + * This method sets the Link Frame Counter for a given radio link. + * + * @param[in] aRadioType A radio link type. + * @param[in] aCounter The new counter value. + * + */ + void Set(RadioType aRadioType, uint32_t aCounter); + +#else + + /** + * This method gets the Link Frame Counter value. + * + * @return The Link Frame Counter value. + * + */ + uint32_t Get(void) const +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + { + return m154Counter; + } +#elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + { + return mTrelCounter; + } +#endif + + /** + * This method sets the Link Frame Counter for a given radio link. + * + * @param[in] aCounter The new counter value. + * + */ + void Set(uint32_t aCounter) +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + { + m154Counter = aCounter; + } +#elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + { + mTrelCounter = aCounter; + } +#endif + +#endif // OPENTHREAD_CONFIG_MULTI_RADIO + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + /** + * This method gets the Link Frame Counter for 802.15.4 radio link. + * + * @returns The Link Frame Counter for 802.15.4 radio link. + * + */ + uint32_t Get154(void) const { return m154Counter; } + + /** + * This method sets the Link Frame Counter for 802.15.4 radio link. + * + * @param[in] aCounter The new counter value. + * + */ + void Set154(uint32_t aCounter) { m154Counter = aCounter; } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + /** + * This method gets the Link Frame Counter for TREL radio link. + * + * @returns The Link Frame Counter for TREL radio link. + * + */ + uint32_t GetTrel(void) const { return mTrelCounter; } + + /** + * This method increments the Link Frame Counter for TREL radio link. + * + */ + void IncrementTrel(void) { mTrelCounter++; } +#endif + + /** + * This method gets the maximum Link Frame Counter among all supported radio links. + * + * @return The maximum Link frame Counter among all supported radio links. + * + */ + uint32_t GetMaximum(void) const; + + /** + * This method sets the Link Frame Counter value for all radio links. + * + * @param[in] aCounter The Link Frame Counter value. + * + */ + void SetAll(uint32_t aCounter); + +private: +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + uint32_t m154Counter; +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + uint32_t mTrelCounter; +#endif +}; + /** * @} * diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 8ab30ea27..b2c1c8ec0 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -339,7 +339,7 @@ public: otError Send(void); /** - * This method gets the number of transmit retries of last transmit packet. + * This method gets the number of transmit retries of last transmitted frame. * * @returns Number of transmit retries. * diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index 735030cc1..18f82451e 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -74,6 +74,7 @@ #include "config/mle.h" #include "config/parent_search.h" #include "config/platform.h" +#include "config/radio_link.h" #include "config/sntp_client.h" #include "config/time_sync.h" #include "config/tmf.h" diff --git a/src/core/radio/radio.cpp b/src/core/radio/radio.cpp index 33571b6c4..9ac0f96aa 100644 --- a/src/core/radio/radio.cpp +++ b/src/core/radio/radio.cpp @@ -33,9 +33,11 @@ namespace ot { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + void Radio::SetExtendedAddress(const Mac::ExtAddress &aExtAddress) { - otPlatRadioSetExtendedAddress(GetInstance(), &aExtAddress); + otPlatRadioSetExtendedAddress(GetInstancePtr(), &aExtAddress); #if (OPENTHREAD_MTD || OPENTHREAD_FTD) && OPENTHREAD_CONFIG_OTNS_ENABLE Get().EmitExtendedAddress(aExtAddress); @@ -44,7 +46,7 @@ void Radio::SetExtendedAddress(const Mac::ExtAddress &aExtAddress) void Radio::SetShortAddress(Mac::ShortAddress aShortAddress) { - otPlatRadioSetShortAddress(GetInstance(), aShortAddress); + otPlatRadioSetShortAddress(GetInstancePtr(), aShortAddress); #if (OPENTHREAD_MTD || OPENTHREAD_FTD) && OPENTHREAD_CONFIG_OTNS_ENABLE Get().EmitShortAddress(aShortAddress); @@ -57,7 +59,8 @@ otError Radio::Transmit(Mac::TxFrame &aFrame) Get().EmitTransmit(aFrame); #endif - return otPlatRadioTransmit(GetInstance(), &aFrame); + return otPlatRadioTransmit(GetInstancePtr(), &aFrame); } +#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE } // namespace ot diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index 7b7b234ea..417e459cd 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -201,29 +201,13 @@ public: { } - /** - * This method gets the radio capabilities. - * - * @returns The radio capability bit vector (see `OT_RADIO_CAP_*` definitions). - * - */ - otRadioCaps GetCaps(void) { return otPlatRadioGetCaps(GetInstance()); } - /** * This method gets the radio version string. * * @returns A pointer to the OpenThread radio version. * */ - const char *GetVersionString(void) { return otPlatRadioGetVersionString(GetInstance()); } - - /** - * This method gets the radio receive sensitivity value. - * - * @returns The radio receive sensitivity value in dBm. - * - */ - int8_t GetReceiveSensitivity(void) { return otPlatRadioGetReceiveSensitivity(GetInstance()); } + const char *GetVersionString(void); /** * This method gets the factory-assigned IEEE EUI-64 for the device. @@ -231,7 +215,23 @@ public: * @param[out] aIeeeEui64 A reference to `Mac::ExtAddress` to place the factory-assigned IEEE EUI-64. * */ - void GetIeeeEui64(Mac::ExtAddress &aIeeeEui64) { otPlatRadioGetIeeeEui64(GetInstance(), aIeeeEui64.m8); } + void GetIeeeEui64(Mac::ExtAddress &aIeeeEui64); + + /** + * This method gets the radio capabilities. + * + * @returns The radio capability bit vector (see `OT_RADIO_CAP_*` definitions). + * + */ + otRadioCaps GetCaps(void); + + /** + * This method gets the radio receive sensitivity value. + * + * @returns The radio receive sensitivity value in dBm. + * + */ + int8_t GetReceiveSensitivity(void); /** * This method sets the PAN ID for address filtering. @@ -239,7 +239,7 @@ public: * @param[in] aPanId The IEEE 802.15.4 PAN ID. * */ - void SetPanId(Mac::PanId aPanId) { otPlatRadioSetPanId(GetInstance(), aPanId); } + void SetPanId(Mac::PanId aPanId); /** * This method sets the Extended Address for address filtering. @@ -271,10 +271,7 @@ public: uint8_t aKeyId, const Mac::Key &aPrevKey, const Mac::Key &aCurrKey, - const Mac::Key &aNextKey) - { - otPlatRadioSetMacKey(GetInstance(), aKeyIdMode, aKeyId, &aPrevKey, &aCurrKey, &aNextKey); - } + const Mac::Key &aNextKey); /** * This method sets the current MAC Frame Counter value. @@ -284,7 +281,7 @@ public: */ void SetMacFrameCounter(uint32_t aMacFrameCounter) { - otPlatRadioSetMacFrameCounter(GetInstance(), aMacFrameCounter); + otPlatRadioSetMacFrameCounter(GetInstancePtr(), aMacFrameCounter); } /** @@ -296,7 +293,7 @@ public: * @retval OT_ERROR_NOT_IMPLEMENTED Transmit power configuration via dBm is not implemented. * */ - otError GetTransmitPower(int8_t &aPower) { return otPlatRadioGetTransmitPower(GetInstance(), &aPower); } + otError GetTransmitPower(int8_t &aPower); /** * This method sets the radio's transmit power in dBm. @@ -307,7 +304,7 @@ public: * @retval OT_ERROR_NOT_IMPLEMENTED Transmit power configuration via dBm is not implemented. * */ - otError SetTransmitPower(int8_t aPower) { return otPlatRadioSetTransmitPower(GetInstance(), aPower); } + otError SetTransmitPower(int8_t aPower); /** * This method gets the radio's CCA ED threshold in dBm. @@ -318,10 +315,7 @@ public: * @retval OT_ERROR_NOT_IMPLEMENTED CCA ED threshold configuration via dBm is not implemented. * */ - otError GetCcaEnergyDetectThreshold(int8_t &aThreshold) - { - return otPlatRadioGetCcaEnergyDetectThreshold(GetInstance(), &aThreshold); - } + otError GetCcaEnergyDetectThreshold(int8_t &aThreshold); /** * This method sets the radio's CCA ED threshold in dBm. @@ -332,10 +326,7 @@ public: * @retval OT_ERROR_NOT_IMPLEMENTED CCA ED threshold configuration via dBm is not implemented. * */ - otError SetCcaEnergyDetectThreshold(int8_t aThreshold) - { - return otPlatRadioSetCcaEnergyDetectThreshold(GetInstance(), aThreshold); - } + otError SetCcaEnergyDetectThreshold(int8_t aThreshold); /** * This method gets the status of promiscuous mode. @@ -344,7 +335,7 @@ public: * @retval FALSE Promiscuous mode is disabled. * */ - bool GetPromiscuous(void) { return otPlatRadioGetPromiscuous(GetInstance()); } + bool GetPromiscuous(void); /** * This method enables or disables promiscuous mode. @@ -352,7 +343,7 @@ public: * @param[in] aEnable TRUE to enable or FALSE to disable promiscuous mode. * */ - void SetPromiscuous(bool aEnable) { otPlatRadioSetPromiscuous(GetInstance(), aEnable); } + void SetPromiscuous(bool aEnable); /** * This method returns the current state of the radio. @@ -364,7 +355,7 @@ public: * @return Current state of the radio. * */ - otRadioState GetState(void) { return otPlatRadioGetState(GetInstance()); } + otRadioState GetState(void); /** * This method enables the radio. @@ -373,7 +364,7 @@ public: * @retval OT_ERROR_FAILED The radio could not be enabled. * */ - otError Enable(void) { return otPlatRadioEnable(GetInstance()); } + otError Enable(void); /** * This method disables the radio. @@ -382,7 +373,7 @@ public: * @retval OT_ERROR_INVALID_STATE The radio was not in sleep state. * */ - otError Disable(void) { return otPlatRadioDisable(GetInstance()); } + otError Disable(void); /** * This method indicates whether radio is enabled or not. @@ -390,7 +381,7 @@ public: * @returns TRUE if the radio is enabled, FALSE otherwise. * */ - bool IsEnabled(void) { return otPlatRadioIsEnabled(GetInstance()); } + bool IsEnabled(void); /** * This method transitions the radio from Receive to Sleep (turn off the radio). @@ -400,7 +391,7 @@ public: * @retval OT_ERROR_INVALID_STATE The radio was disabled. * */ - otError Sleep(void) { return otPlatRadioSleep(GetInstance()); } + otError Sleep(void); /** * This method transitions the radio from Sleep to Receive (turn on the radio). @@ -411,7 +402,7 @@ public: * @retval OT_ERROR_INVALID_STATE The radio was disabled or transmitting. * */ - otError Receive(uint8_t aChannel) { return otPlatRadioReceive(GetInstance(), aChannel); } + otError Receive(uint8_t aChannel); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** @@ -420,7 +411,7 @@ public: * @param[in] aCslSampleTime The CSL sample time. * */ - void UpdateCslSampleTime(uint32_t aCslSampleTime) { otPlatRadioUpdateCslSampleTime(GetInstance(), aCslSampleTime); } + void UpdateCslSampleTime(uint32_t aCslSampleTime); /** This method enables CSL sampling in radio. * @@ -433,10 +424,7 @@ public: * @retval OT_ERROR_NONE Successfully enabled or disabled CSL. * */ - otError EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr) - { - return otPlatRadioEnableCsl(GetInstance(), aCslPeriod, aExtAddr); - } + otError EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr); #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** @@ -447,10 +435,7 @@ public: * @returns A reference to the transmit frame buffer. * */ - Mac::TxFrame &GetTransmitBuffer(void) - { - return *static_cast(otPlatRadioGetTransmitBuffer(GetInstance())); - } + Mac::TxFrame &GetTransmitBuffer(void); /** * This method starts the transmit sequence on the radio. @@ -472,7 +457,7 @@ public: * @returns The RSSI in dBm when it is valid. 127 when RSSI is invalid. * */ - int8_t GetRssi(void) { return otPlatRadioGetRssi(GetInstance()); } + int8_t GetRssi(void); /** * This method begins the energy scan sequence on the radio. @@ -486,10 +471,7 @@ public: * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * */ - otError EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) - { - return otPlatRadioEnergyScan(GetInstance(), aScanChannel, aScanDuration); - } + otError EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration); /** * This method enables/disables source address match feature. @@ -508,7 +490,7 @@ public: * @param[in] aEnable Enable/disable source address match feature. * */ - void EnableSrcMatch(bool aEnable) { return otPlatRadioEnableSrcMatch(GetInstance(), aEnable); } + void EnableSrcMatch(bool aEnable); /** * This method adds a short address to the source address match table. @@ -519,10 +501,7 @@ public: * @retval OT_ERROR_NO_BUFS No available entry in the source match table. * */ - otError AddSrcMatchShortEntry(Mac::ShortAddress aShortAddress) - { - return otPlatRadioAddSrcMatchShortEntry(GetInstance(), aShortAddress); - } + otError AddSrcMatchShortEntry(Mac::ShortAddress aShortAddress); /** * This method adds an extended address to the source address match table. @@ -533,10 +512,7 @@ public: * @retval OT_ERROR_NO_BUFS No available entry in the source match table. * */ - otError AddSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress) - { - return otPlatRadioAddSrcMatchExtEntry(GetInstance(), &aExtAddress); - } + otError AddSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress); /** * This method removes a short address from the source address match table. @@ -547,10 +523,7 @@ public: * @retval OT_ERROR_NO_ADDRESS The short address is not in source address match table. * */ - otError ClearSrcMatchShortEntry(Mac::ShortAddress aShortAddress) - { - return otPlatRadioClearSrcMatchShortEntry(GetInstance(), aShortAddress); - } + otError ClearSrcMatchShortEntry(Mac::ShortAddress aShortAddress); /** * This method removes an extended address from the source address match table. @@ -561,22 +534,19 @@ public: * @retval OT_ERROR_NO_ADDRESS The extended address is not in source address match table. * */ - otError ClearSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress) - { - return otPlatRadioClearSrcMatchExtEntry(GetInstance(), &aExtAddress); - } + otError ClearSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress); /** * This method clears all short addresses from the source address match table. * */ - void ClearSrcMatchShortEntries(void) { otPlatRadioClearSrcMatchShortEntries(GetInstance()); } + void ClearSrcMatchShortEntries(void); /** * This method clears all the extended/long addresses from source address match table. * */ - void ClearSrcMatchExtEntries(void) { otPlatRadioClearSrcMatchExtEntries(GetInstance()); } + void ClearSrcMatchExtEntries(void); /** * This method gets the radio supported channel mask that the device is allowed to be on. @@ -584,7 +554,7 @@ public: * @returns The radio supported channel mask. * */ - uint32_t GetSupportedChannelMask(void) { return otPlatRadioGetSupportedChannelMask(GetInstance()); } + uint32_t GetSupportedChannelMask(void); /** * This method gets the radio preferred channel mask that the device prefers to form on. @@ -592,7 +562,7 @@ public: * @returns The radio preferred channel mask. * */ - uint32_t GetPreferredChannelMask(void) { return otPlatRadioGetPreferredChannelMask(GetInstance()); } + uint32_t GetPreferredChannelMask(void); /** * This method checks if a given channel is valid as a CSL channel. @@ -607,11 +577,338 @@ public: } private: - otInstance *GetInstance(void) { return reinterpret_cast(&InstanceLocator::GetInstance()); } + otInstance *GetInstancePtr(void) { return reinterpret_cast(&InstanceLocator::GetInstance()); } Callbacks mCallbacks; }; +//--------------------------------------------------------------------------------------------------------------------- +// Radio APIs that are always mapped to the same `otPlatRadio` function (independent of the link type) + +inline const char *Radio::GetVersionString(void) +{ + return otPlatRadioGetVersionString(GetInstancePtr()); +} + +inline void Radio::GetIeeeEui64(Mac::ExtAddress &aIeeeEui64) +{ + otPlatRadioGetIeeeEui64(GetInstancePtr(), aIeeeEui64.m8); +} + +inline uint32_t Radio::GetSupportedChannelMask(void) +{ + return otPlatRadioGetSupportedChannelMask(GetInstancePtr()); +} + +inline uint32_t Radio::GetPreferredChannelMask(void) +{ + return otPlatRadioGetPreferredChannelMask(GetInstancePtr()); +} + +//--------------------------------------------------------------------------------------------------------------------- +// If IEEE 802.15.4 is among supported radio links, provide inline +// mapping of `Radio` method to related `otPlatRadio` functions. + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + +inline otRadioCaps Radio::GetCaps(void) +{ + return otPlatRadioGetCaps(GetInstancePtr()); +} + +inline int8_t Radio::GetReceiveSensitivity(void) +{ + return otPlatRadioGetReceiveSensitivity(GetInstancePtr()); +} + +inline void Radio::SetPanId(Mac::PanId aPanId) +{ + otPlatRadioSetPanId(GetInstancePtr(), aPanId); +} + +inline void Radio::SetMacKey(uint8_t aKeyIdMode, + uint8_t aKeyId, + const Mac::Key &aPrevKey, + const Mac::Key &aCurrKey, + const Mac::Key &aNextKey) +{ + otPlatRadioSetMacKey(GetInstancePtr(), aKeyIdMode, aKeyId, &aPrevKey, &aCurrKey, &aNextKey); +} + +inline otError Radio::GetTransmitPower(int8_t &aPower) +{ + return otPlatRadioGetTransmitPower(GetInstancePtr(), &aPower); +} + +inline otError Radio::SetTransmitPower(int8_t aPower) +{ + return otPlatRadioSetTransmitPower(GetInstancePtr(), aPower); +} + +inline otError Radio::GetCcaEnergyDetectThreshold(int8_t &aThreshold) +{ + return otPlatRadioGetCcaEnergyDetectThreshold(GetInstancePtr(), &aThreshold); +} + +inline otError Radio::SetCcaEnergyDetectThreshold(int8_t aThreshold) +{ + return otPlatRadioSetCcaEnergyDetectThreshold(GetInstancePtr(), aThreshold); +} + +inline bool Radio::GetPromiscuous(void) +{ + return otPlatRadioGetPromiscuous(GetInstancePtr()); +} + +inline void Radio::SetPromiscuous(bool aEnable) +{ + otPlatRadioSetPromiscuous(GetInstancePtr(), aEnable); +} + +inline otRadioState Radio::GetState(void) +{ + return otPlatRadioGetState(GetInstancePtr()); +} + +inline otError Radio::Enable(void) +{ + return otPlatRadioEnable(GetInstancePtr()); +} + +inline otError Radio::Disable(void) +{ + return otPlatRadioDisable(GetInstancePtr()); +} + +inline bool Radio::IsEnabled(void) +{ + return otPlatRadioIsEnabled(GetInstancePtr()); +} + +inline otError Radio::Sleep(void) +{ + return otPlatRadioSleep(GetInstancePtr()); +} + +inline otError 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); +} + +inline otError Radio::EnableCsl(uint32_t aCslPeriod, const otExtAddress *aExtAddr) +{ + return otPlatRadioEnableCsl(GetInstancePtr(), aCslPeriod, aExtAddr); +} +#endif + +inline Mac::TxFrame &Radio::GetTransmitBuffer(void) +{ + return *static_cast(otPlatRadioGetTransmitBuffer(GetInstancePtr())); +} + +inline int8_t Radio::GetRssi(void) +{ + return otPlatRadioGetRssi(GetInstancePtr()); +} + +inline otError Radio::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) +{ + return otPlatRadioEnergyScan(GetInstancePtr(), aScanChannel, aScanDuration); +} + +inline void Radio::EnableSrcMatch(bool aEnable) +{ + otPlatRadioEnableSrcMatch(GetInstancePtr(), aEnable); +} + +inline otError Radio::AddSrcMatchShortEntry(Mac::ShortAddress aShortAddress) +{ + return otPlatRadioAddSrcMatchShortEntry(GetInstancePtr(), aShortAddress); +} + +inline otError Radio::AddSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress) +{ + return otPlatRadioAddSrcMatchExtEntry(GetInstancePtr(), &aExtAddress); +} + +inline otError Radio::ClearSrcMatchShortEntry(Mac::ShortAddress aShortAddress) +{ + return otPlatRadioClearSrcMatchShortEntry(GetInstancePtr(), aShortAddress); +} + +inline otError Radio::ClearSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress) +{ + return otPlatRadioClearSrcMatchExtEntry(GetInstancePtr(), &aExtAddress); +} + +inline void Radio::ClearSrcMatchShortEntries(void) +{ + otPlatRadioClearSrcMatchShortEntries(GetInstancePtr()); +} + +inline void Radio::ClearSrcMatchExtEntries(void) +{ + otPlatRadioClearSrcMatchExtEntries(GetInstancePtr()); +} + +#else //---------------------------------------------------------------------------------------------------------------- + +inline otRadioCaps Radio::GetCaps(void) +{ + return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES; +} + +inline int8_t Radio::GetReceiveSensitivity(void) +{ + return -110; +} + +inline void Radio::SetPanId(Mac::PanId) +{ +} + +inline void Radio::SetExtendedAddress(const Mac::ExtAddress &) +{ +} + +inline void Radio::SetShortAddress(Mac::ShortAddress) +{ +} + +inline void Radio::SetMacKey(uint8_t, uint8_t, const Mac::Key &, const Mac::Key &, const Mac::Key &) +{ +} + +inline otError Radio::GetTransmitPower(int8_t &) +{ + return OT_ERROR_NOT_IMPLEMENTED; +} + +inline otError Radio::SetTransmitPower(int8_t) +{ + return OT_ERROR_NOT_IMPLEMENTED; +} + +inline otError Radio::GetCcaEnergyDetectThreshold(int8_t &) +{ + return OT_ERROR_NOT_IMPLEMENTED; +} + +inline otError Radio::SetCcaEnergyDetectThreshold(int8_t) +{ + return OT_ERROR_NOT_IMPLEMENTED; +} + +inline bool Radio::GetPromiscuous(void) +{ + return false; +} + +inline void Radio::SetPromiscuous(bool) +{ +} + +inline otRadioState Radio::GetState(void) +{ + return OT_RADIO_STATE_DISABLED; +} + +inline otError Radio::Enable(void) +{ + return OT_ERROR_NONE; +} + +inline otError Radio::Disable(void) +{ + return OT_ERROR_INVALID_STATE; +} + +inline bool Radio::IsEnabled(void) +{ + return true; +} + +inline otError Radio::Sleep(void) +{ + return OT_ERROR_NONE; +} + +inline otError Radio::Receive(uint8_t) +{ + return OT_ERROR_NONE; +} + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +inline void Radio::UpdateCslSampleTime(uint32_t) +{ +} + +inline otError Radio::EnableCsl(uint32_t, const otExtAddress *) +{ + return OT_ERROR_NOT_SUPPORTED; +} +#endif + +inline Mac::TxFrame &Radio::GetTransmitBuffer(void) +{ + return *static_cast(otPlatRadioGetTransmitBuffer(GetInstancePtr())); +} + +inline otError Radio::Transmit(Mac::TxFrame &) +{ + return OT_ERROR_ABORT; +} + +inline int8_t Radio::GetRssi(void) +{ + return OT_RADIO_RSSI_INVALID; +} + +inline otError Radio::EnergyScan(uint8_t, uint16_t) +{ + return OT_ERROR_NOT_IMPLEMENTED; +} + +inline void Radio::EnableSrcMatch(bool) +{ +} + +inline otError Radio::AddSrcMatchShortEntry(Mac::ShortAddress) +{ + return OT_ERROR_NONE; +} + +inline otError Radio::AddSrcMatchExtEntry(const Mac::ExtAddress &) +{ + return OT_ERROR_NONE; +} + +inline otError Radio::ClearSrcMatchShortEntry(Mac::ShortAddress) +{ + return OT_ERROR_NONE; +} + +inline otError Radio::ClearSrcMatchExtEntry(const Mac::ExtAddress &) +{ + return OT_ERROR_NONE; +} + +inline void Radio::ClearSrcMatchShortEntries(void) +{ +} + +inline void Radio::ClearSrcMatchExtEntries(void) +{ +} + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + } // namespace ot #endif // RADIO_HPP_ diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index 605ddb781..76d19e038 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -33,6 +33,7 @@ #include #include +#include "common/code_utils.hpp" #include "common/instance.hpp" #include "radio/radio.hpp" @@ -41,63 +42,138 @@ using namespace ot; //--------------------------------------------------------------------------------------------------------------------- // otPlatRadio callbacks +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + extern "C" void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { - Instance *instance = static_cast(aInstance); + Instance & instance = *static_cast(aInstance); + Mac::RxFrame *rxFrame = static_cast(aFrame); - if (instance->IsInitialized()) + VerifyOrExit(instance.IsInitialized()); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (rxFrame != nullptr) { - instance->Get().HandleReceiveDone(static_cast(aFrame), aError); + rxFrame->SetRadioType(Mac::kRadioTypeIeee802154); } +#endif + + instance.Get().HandleReceiveDone(rxFrame, aError); + +exit: + return; } extern "C" void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame) { - Instance *instance = static_cast(aInstance); + Instance & instance = *static_cast(aInstance); + Mac::TxFrame &txFrame = *static_cast(aFrame); - if (instance->IsInitialized()) - { - instance->Get().HandleTransmitStarted(*static_cast(aFrame)); - } + VerifyOrExit(instance.IsInitialized()); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + txFrame.SetRadioType(Mac::kRadioTypeIeee802154); +#endif + + instance.Get().HandleTransmitStarted(txFrame); + +exit: + return; } extern "C" void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) { - Instance *instance = static_cast(aInstance); + Instance & instance = *static_cast(aInstance); + Mac::TxFrame &txFrame = *static_cast(aFrame); + Mac::RxFrame *ackFrame = static_cast(aAckFrame); - if (instance->IsInitialized()) + VerifyOrExit(instance.IsInitialized()); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (ackFrame != nullptr) { - instance->Get().HandleTransmitDone(*static_cast(aFrame), - static_cast(aAckFrame), aError); + ackFrame->SetRadioType(Mac::kRadioTypeIeee802154); } + + txFrame.SetRadioType(Mac::kRadioTypeIeee802154); +#endif + + instance.Get().HandleTransmitDone(txFrame, ackFrame, aError); + +exit: + return; } extern "C" void otPlatRadioEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi) { - Instance *instance = static_cast(aInstance); + Instance &instance = *static_cast(aInstance); - if (instance->IsInitialized()) - { - instance->Get().HandleEnergyScanDone(aEnergyScanMaxRssi); - } + VerifyOrExit(instance.IsInitialized()); + instance.Get().HandleEnergyScanDone(aEnergyScanMaxRssi); + +exit: + return; } #if OPENTHREAD_CONFIG_DIAG_ENABLE extern "C" void otPlatDiagRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { - Instance *instance = static_cast(aInstance); + Instance & instance = *static_cast(aInstance); + Mac::RxFrame *rxFrame = static_cast(aFrame); - instance->Get().HandleDiagsReceiveDone(static_cast(aFrame), aError); +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (rxFrame != nullptr) + { + rxFrame->SetRadioType(Mac::kRadioTypeIeee802154); + } +#endif + + instance.Get().HandleDiagsReceiveDone(rxFrame, aError); } extern "C" void otPlatDiagRadioTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { - Instance *instance = static_cast(aInstance); + Instance & instance = *static_cast(aInstance); + Mac::TxFrame &txFrame = *static_cast(aFrame); - instance->Get().HandleDiagsTransmitDone(*static_cast(aFrame), aError); +#if OPENTHREAD_CONFIG_MULTI_RADIO + txFrame.SetRadioType(Mac::kRadioTypeIeee802154); +#endif + + instance.Get().HandleDiagsTransmitDone(txFrame, aError); } #endif +#else // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + +extern "C" void otPlatRadioReceiveDone(otInstance *, otRadioFrame *, otError) +{ +} + +extern "C" void otPlatRadioTxStarted(otInstance *, otRadioFrame *) +{ +} + +extern "C" void otPlatRadioTxDone(otInstance *, otRadioFrame *, otRadioFrame *, otError) +{ +} + +extern "C" void otPlatRadioEnergyScanDone(otInstance *, int8_t) +{ +} + +#if OPENTHREAD_CONFIG_DIAG_ENABLE +extern "C" void otPlatDiagRadioReceiveDone(otInstance *, otRadioFrame *, otError) +{ +} + +extern "C" void otPlatDiagRadioTransmitDone(otInstance *, otRadioFrame *, otError) +{ +} +#endif + +#endif // // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + //--------------------------------------------------------------------------------------------------------------------- // Default/weak implementation of radio platform APIs diff --git a/src/core/radio/trel.cpp b/src/core/radio/trel.cpp new file mode 100644 index 000000000..a51f933ec --- /dev/null +++ b/src/core/radio/trel.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, 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 Thread Radio Encapsulation Link (TREL). + */ + +#include "trel.hpp" + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +Link::Link(Instance &aInstance) + : InstanceLocator(aInstance) +{ + memset(&mTxFrame, 0, sizeof(mTxFrame)); + mTxFrame.mPsdu = &mFrameBuffer[kHeaderSize]; + mTxFrame.SetLength(0); +#if OPENTHREAD_CONFIG_MULTI_RADIO + mTxFrame.SetRadioType(Mac::kRadioTypeTrel); +#endif +} + +void Link::Send(void) +{ + Get().RecordFrameTransmitStatus(mTxFrame, NULL, OT_ERROR_ABORT, 0, false); + Get().HandleTransmitDone(mTxFrame, NULL, OT_ERROR_ABORT); +} + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/radio/trel.hpp b/src/core/radio/trel.hpp new file mode 100644 index 000000000..025be8275 --- /dev/null +++ b/src/core/radio/trel.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2019, 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 Thread Radio Encapsulation Link (TREL). + */ + +#ifndef TREL_HPP_ +#define TREL_HPP_ + +#include "openthread-core-config.h" + +#include "common/locator.hpp" +#include "mac/mac_frame.hpp" +#include "mac/mac_types.hpp" + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +namespace ot { +namespace Trel { + +/** + * @addtogroup core-trel + * + * @brief + * This module includes definitions for Thread Radio Encapsulation Link (TREL) + * + * @{ + * + */ + +/** + * This class represents a Thread Radio Encapsulation Link (TREL). + * + */ +class Link : public InstanceLocator +{ + friend class Instance; + +public: + enum + { + kMtuSize = 1600, + kFcsSize = 0, + }; + + explicit Link(Instance &aInstance); + + void SetPanId(Mac::PanId) {} + + void Enable(void) {} + void Disable(void) {} + + void Sleep(void) {} + void Receive(uint8_t) {} + void Send(void); + + Mac::TxFrame &GetTransmitFrame(void) { return mTxFrame; } + +private: + enum + { + kHeaderSize = 10, + }; + + Mac::TxFrame mTxFrame; + uint8_t mFrameBuffer[kHeaderSize + kMtuSize]; +}; + +/** + * @} + * + */ + +} // namespace Trel +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +#endif // TREL_HPP_ diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 065ab1994..d81385d68 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -165,16 +165,25 @@ uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild, return static_cast(nextTxWindow - aRadioNow - mCslFrameRequestAheadUs); } -otError CslTxScheduler::HandleFrameRequest(Mac::TxFrame &aFrame) +Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames) { - otError error = OT_ERROR_NONE; + Mac::TxFrame *frame = nullptr; + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE uint32_t txDelay; - VerifyOrExit(mCslTxChild != nullptr, error = OT_ERROR_ABORT); + VerifyOrExit(mCslTxChild != nullptr); - SuccessOrExit(error = mCallbacks.PrepareFrameForChild(aFrame, mFrameContext, *mCslTxChild)); +#if OPENTHREAD_CONFIG_MULTI_RADIO + frame = &aTxFrames.GetTxFrame(kRadioTypeIeee802154); +#else + frame = &aTxFrames.GetTxFrame(); +#endif + + VerifyOrExit(mCallbacks.PrepareFrameForChild(*frame, mFrameContext, *mCslTxChild) == OT_ERROR_NONE, + frame = nullptr); mCslTxMessage = mCslTxChild->GetIndirectMessage(); - VerifyOrExit(mCslTxMessage != nullptr, error = OT_ERROR_ABORT); + VerifyOrExit(mCslTxMessage != nullptr, frame = nullptr); if (mCslTxChild->GetIndirectTxAttempts() > 0 || mCslTxChild->GetCslTxAttempts() > 0) { @@ -182,31 +191,31 @@ otError CslTxScheduler::HandleFrameRequest(Mac::TxFrame &aFrame) // child, we ensure to use the same frame counter, key id, and // data sequence number as the previous attempt. - aFrame.SetIsARetransmission(true); - aFrame.SetSequence(mCslTxChild->GetIndirectDataSequenceNumber()); + frame->SetIsARetransmission(true); + frame->SetSequence(mCslTxChild->GetIndirectDataSequenceNumber()); - if (aFrame.GetSecurityEnabled()) + if (frame->GetSecurityEnabled()) { - aFrame.SetFrameCounter(mCslTxChild->GetIndirectFrameCounter()); - aFrame.SetKeyId(mCslTxChild->GetIndirectKeyId()); + frame->SetFrameCounter(mCslTxChild->GetIndirectFrameCounter()); + frame->SetKeyId(mCslTxChild->GetIndirectKeyId()); } } else { - aFrame.SetIsARetransmission(false); + frame->SetIsARetransmission(false); } - aFrame.SetChannel(mCslTxChild->GetCslChannel() == 0 ? Get().GetPanChannel() + frame->SetChannel(mCslTxChild->GetCslChannel() == 0 ? Get().GetPanChannel() : mCslTxChild->GetCslChannel()); - GetNextCslTransmissionDelay(*mCslTxChild, otPlatRadioGetNow(&GetInstance()), txDelay); - aFrame.SetTxDelay(txDelay); - aFrame.SetTxDelayBaseTime( + frame->SetTxDelay(txDelay); + frame->SetTxDelayBaseTime( static_cast(mCslTxChild->GetLastRxTimestamp())); // Only LSB part of the time is required. - aFrame.SetCsmaCaEnabled(false); + frame->SetCsmaCaEnabled(false); exit: - return error; +#endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + return frame; } void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, otError aError) diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 777f9d9f5..319c59881 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -196,8 +196,8 @@ private: uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint64_t aRadioNow, uint32_t &aDelayFromLastRx) const; // Callbacks from `Mac` - otError HandleFrameRequest(Mac::TxFrame &aFrame); - void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError); + Mac::TxFrame *HandleFrameRequest(Mac::TxFrames &aTxFrames); + void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError); void HandleSentFrame(const Mac::TxFrame &aFrame, otError aError, Child &aChild); diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index f4947f802..3e5720ad4 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -177,9 +177,9 @@ exit: return error; } -otError DiscoverScanner::PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame) +Mac::TxFrame *DiscoverScanner::PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame) { - otError error = OT_ERROR_NONE; + Mac::TxFrame *frame = &aFrame; switch (mState) { @@ -188,16 +188,16 @@ otError DiscoverScanner::PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame) // If scan is finished (no more channels to scan), abort the // Discovery Request frame tx. The handler callback is invoked & // state is cleared from `HandleDiscoveryRequestFrameTxDone()`. - error = OT_ERROR_ABORT; + frame = nullptr; break; case kStateScanning: - aFrame.SetChannel(mScanChannel); + frame->SetChannel(mScanChannel); IgnoreError(Get().SetTemporaryChannel(mScanChannel)); break; } - return error; + return frame; } void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage) diff --git a/src/core/thread/discover_scanner.hpp b/src/core/thread/discover_scanner.hpp index 10a9d7890..e72d8d20b 100644 --- a/src/core/thread/discover_scanner.hpp +++ b/src/core/thread/discover_scanner.hpp @@ -162,9 +162,9 @@ private: }; // Methods used by `MeshForwarder` - otError PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame); - void HandleDiscoveryRequestFrameTxDone(Message &aMessage); - void Stop(void) { HandleDiscoverComplete(); } + Mac::TxFrame *PrepareDiscoveryRequestFrame(Mac::TxFrame &aFrame); + void HandleDiscoveryRequestFrameTxDone(Message &aMessage); + void Stop(void) { HandleDiscoverComplete(); } // Methods used from `Mle` void HandleDiscoveryResponse(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const; diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index bbb5a3633..aa46902a4 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -38,6 +38,7 @@ #include "common/instance.hpp" #include "common/locator-getters.hpp" #include "common/timer.hpp" +#include "crypto/hkdf_sha256.hpp" #include "thread/mle_router.hpp" #include "thread/thread_netif.hpp" @@ -66,6 +67,14 @@ const otMasterKey KeyManager::kDefaultMasterKey = {{ 0xff, }}; +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +const uint8_t KeyManager::kHkdfExtractSaltString[] = {'T', 'h', 'r', 'e', 'a', 'd', 'S', 'e', 'q', 'u', 'e', 'n', + 'c', 'e', 'M', 'a', 's', 't', 'e', 'r', 'K', 'e', 'y'}; + +const uint8_t KeyManager::kTrelInfoString[] = {'T', 'h', 'r', 'e', 'a', 'd', 'O', 'v', 'e', + 'r', 'I', 'n', 'f', 'r', 'a', 'K', 'e', 'y'}; +#endif + KeyManager::KeyManager(Instance &aInstance) : InstanceLocator(aInstance) , mKeySequence(0) @@ -81,6 +90,7 @@ KeyManager::KeyManager(Instance &aInstance) , mSecurityPolicyFlags(kDefaultSecurityPolicyFlags) , mIsPskcSet(false) { + mMacFrameCounters.Reset(); mMasterKey = static_cast(kDefaultMasterKey); mPskc.Clear(); } @@ -117,7 +127,7 @@ otError KeyManager::SetMasterKey(const MasterKey &aKey) // reset parent frame counters parent = &Get().GetParent(); parent->SetKeySequence(0); - parent->SetLinkFrameCounter(0); + parent->GetLinkFrameCounters().Reset(); parent->SetLinkAckFrameCounter(0); parent->SetMleFrameCounter(0); @@ -126,7 +136,7 @@ otError KeyManager::SetMasterKey(const MasterKey &aKey) for (Router &router : Get().Iterate()) { router.SetKeySequence(0); - router.SetLinkFrameCounter(0); + router.GetLinkFrameCounters().Reset(); router.SetLinkAckFrameCounter(0); router.SetMleFrameCounter(0); } @@ -135,7 +145,7 @@ otError KeyManager::SetMasterKey(const MasterKey &aKey) for (Child &child : Get().Iterate(Child::kInStateAnyExceptInvalid)) { child.SetKeySequence(0); - child.SetLinkFrameCounter(0); + child.GetLinkFrameCounters().Reset(); child.SetLinkAckFrameCounter(0); child.SetMleFrameCounter(0); } @@ -159,20 +169,42 @@ void KeyManager::ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys) hmac.Finish(aHashKeys.mHash); } +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +void KeyManager::ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aTrelKey) +{ + Crypto::HkdfSha256 hkdf; + uint8_t salt[sizeof(uint32_t) + sizeof(kHkdfExtractSaltString)]; + + Encoding::BigEndian::WriteUint32(aKeySequence, salt); + memcpy(salt + sizeof(uint32_t), kHkdfExtractSaltString, sizeof(kHkdfExtractSaltString)); + + hkdf.Extract(salt, sizeof(salt), mMasterKey.m8, sizeof(MasterKey)); + hkdf.Expand(kTrelInfoString, sizeof(kTrelInfoString), aTrelKey.m8, sizeof(Mac::Key)); +} +#endif + void KeyManager::UpdateKeyMaterial(void) { - HashKeys prev; HashKeys cur; +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + HashKeys prev; HashKeys next; +#endif - ComputeKeys(mKeySequence - 1, prev); ComputeKeys(mKeySequence, cur); - ComputeKeys(mKeySequence + 1, next); - mMleKey = cur.mKeys.mMleKey; +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + ComputeKeys(mKeySequence - 1, prev); + ComputeKeys(mKeySequence + 1, next); + Get().SetMacKey(Mac::Frame::kKeyIdMode1, (mKeySequence & 0x7f) + 1, prev.mKeys.mMacKey, cur.mKeys.mMacKey, next.mKeys.mMacKey); +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + ComputeTrelKey(mKeySequence, mTrelKey); +#endif } void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence) @@ -194,7 +226,7 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence) mKeySequence = aKeySequence; UpdateKeyMaterial(); - SetMacFrameCounter(0); + mMacFrameCounters.Reset(); mMleFrameCounter = 0; Get().Signal(kEventThreadKeySeqCounterChanged); @@ -213,23 +245,51 @@ const Mle::Key &KeyManager::GetTemporaryMleKey(uint32_t aKeySequence) return mTemporaryMleKey; } -uint32_t KeyManager::GetMacFrameCounter(void) const +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +const Mac::Key &KeyManager::GetTemporaryTrelMacKey(uint32_t aKeySequence) { - return Get().GetFrameCounter(); -} + ComputeTrelKey(aKeySequence, mTemporaryTrelKey); -void KeyManager::SetMacFrameCounter(uint32_t aMacFrameCounter) + return mTemporaryTrelKey; +} +#endif + +void KeyManager::SetAllMacFrameCounters(uint32_t aMacFrameCounter) { + mMacFrameCounters.SetAll(aMacFrameCounter); + +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE Get().SetFrameCounter(aMacFrameCounter); +#endif } +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE void KeyManager::MacFrameCounterUpdated(uint32_t aMacFrameCounter) { - if (aMacFrameCounter >= mStoredMacFrameCounter) + mMacFrameCounters.Set154(aMacFrameCounter); + + if (mMacFrameCounters.Get154() >= mStoredMacFrameCounter) { IgnoreError(Get().Store()); } } +#else +void KeyManager::MacFrameCounterUpdated(uint32_t) +{ +} +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +void KeyManager::IncrementTrelMacFrameCounter(void) +{ + mMacFrameCounters.IncrementTrel(); + + if (mMacFrameCounters.GetTrel() >= mStoredMacFrameCounter) + { + IgnoreError(Get().Store()); + } +} +#endif void KeyManager::IncrementMleFrameCounter(void) { diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index fefebbe11..30ac02750 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -209,39 +209,85 @@ public: */ void SetCurrentKeySequence(uint32_t aKeySequence); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE /** - * This method returns a pointer to the current MLE key. + * This method returns the current MAC key for TREL radio link. * - * @returns A pointer to the current MLE key. + * @returns The current TREL MAC key. + * + */ + const Mac::Key &GetCurrentTrelMacKey(void) const { return mTrelKey; } + + /** + * This method returns a temporary MAC key for TREL radio link computed from the given key sequence. + * + * @param[in] aKeySequence The key sequence value. + * + * @returns The temporary TREL MAC key. + * + */ + const Mac::Key &GetTemporaryTrelMacKey(uint32_t aKeySequence); +#endif + + /** + * This method returns the current MLE key. + * + * @returns The current MLE key. * */ const Mle::Key &GetCurrentMleKey(void) const { return mMleKey; } /** - * This method returns a pointer to a temporary MLE key computed from the given key sequence. + * This method returns a temporary MLE key computed from the given key sequence. * * @param[in] aKeySequence The key sequence value. * - * @returns A pointer to the temporary MLE key. + * @returns The temporary MLE key. * */ const Mle::Key &GetTemporaryMleKey(uint32_t aKeySequence); +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE /** - * This method returns the current MAC Frame Counter value. + * This method returns the current MAC Frame Counter value for 15.4 radio link. * * @returns The current MAC Frame Counter value. * */ - uint32_t GetMacFrameCounter(void) const; + uint32_t Get154MacFrameCounter(void) const { return mMacFrameCounters.Get154(); } +#endif + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + /** + * This method returns the current MAC Frame Counter value for TREL radio link. + * + * @returns The current MAC Frame Counter value for TREL radio link. + * + */ + uint32_t GetTrelMacFrameCounter(void) const { return mMacFrameCounters.GetTrel(); } /** - * This method sets the current MAC Frame Counter value. + * This method increments the current MAC Frame Counter value for TREL radio link. + * + */ + void IncrementTrelMacFrameCounter(void); +#endif + + /** + * This method gets the maximum MAC Frame Counter among all supported radio links. + * + * @return The maximum MAC frame Counter among all supported radio links. + * + */ + uint32_t GetMaximumMacFrameCounter(void) const { return mMacFrameCounters.GetMaximum(); } + + /** + * This method sets the current MAC Frame Counter value for all radio links. * * @param[in] aMacFrameCounter The MAC Frame Counter value. * */ - void SetMacFrameCounter(uint32_t aMacFrameCounter); + void SetAllMacFrameCounters(uint32_t aMacFrameCounter); /** * This method sets the MAC Frame Counter value which is stored in non-volatile memory. @@ -447,9 +493,9 @@ public: void UpdateKeyMaterial(void); /** - * This method handles MAC frame counter change. + * This method handles MAC frame counter change (callback from `SubMac` for 15.4 security frame change) * - * @param[in] aMacFrameCounter The MAC frame counter value. + * @param[in] aMacFrameCounter The 15.4 link MAC frame counter value. * */ void MacFrameCounterUpdated(uint32_t aMacFrameCounter); @@ -477,6 +523,10 @@ private: void ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys); +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + void ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aTrelKey); +#endif + void StartKeyRotationTimer(void); static void HandleKeyRotationTimer(Timer &aTimer); void HandleKeyRotationTimer(void); @@ -484,15 +534,26 @@ private: static const uint8_t kThreadString[]; static const otMasterKey kDefaultMasterKey; +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + static const uint8_t kHkdfExtractSaltString[]; + static const uint8_t kTrelInfoString[]; +#endif + MasterKey mMasterKey; uint32_t mKeySequence; Mle::Key mMleKey; Mle::Key mTemporaryMleKey; - uint32_t mMleFrameCounter; - uint32_t mStoredMacFrameCounter; - uint32_t mStoredMleFrameCounter; +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + Mac::Key mTrelKey; + Mac::Key mTemporaryTrelKey; +#endif + + Mac::LinkFrameCounters mMacFrameCounters; + uint32_t mMleFrameCounter; + uint32_t mStoredMacFrameCounter; + uint32_t mStoredMleFrameCounter; uint32_t mHoursSinceKeyRotation; uint32_t mKeyRotationTime; diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index c0b5a1afe..7586f95d9 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -74,6 +74,9 @@ void ThreadLinkInfo::SetFrom(const Mac::RxFrame &aFrame) mTimeSyncSeq = aFrame.ReadTimeSyncSeq(); } #endif +#if OPENTHREAD_CONFIG_MULTI_RADIO + mRadioType = static_cast(aFrame.GetRadioType()); +#endif } MeshForwarder::MeshForwarder(Instance &aInstance) @@ -462,12 +465,29 @@ exit: return error; } -otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame) +Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames) { - otError error = OT_ERROR_NONE; + Mac::TxFrame *frame = nullptr; + bool addFragHeader = false; - VerifyOrExit(mEnabled, error = OT_ERROR_ABORT); - VerifyOrExit(mSendMessage != nullptr, error = OT_ERROR_ABORT); + VerifyOrExit(mEnabled && (mSendMessage != nullptr)); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + frame = &Get().SelectRadio(*mSendMessage, mMacDest, aTxFrames); + + // If multi-radio link is supported, when sending frame with link + // security enabled, Fragment Header is always included (even if + // the message is small and does not require 6LoWPAN fragmentation). + // This allows the Fragment Header's tag to be used to detect and + // suppress duplicate received frames over different radio links. + + if (mSendMessage->IsLinkSecurityEnabled()) + { + addFragHeader = true; + } +#else + frame = &aTxFrames.GetTxFrame(); +#endif mSendBusy = true; @@ -476,7 +496,8 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame) case Message::kTypeIp6: if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest) { - SuccessOrExit(error = Get().PrepareDiscoveryRequestFrame(aFrame)); + frame = Get().PrepareDiscoveryRequestFrame(*frame); + VerifyOrExit(frame != nullptr); } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled() && mSendMessage->IsSubTypeMle()) @@ -484,18 +505,17 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame) mSendMessage->SetLinkSecurityEnabled(true); } #endif - mMessageNextOffset = - PrepareDataFrame(aFrame, *mSendMessage, mMacSource, mMacDest, mAddMeshHeader, mMeshSource, mMeshDest); + mMessageNextOffset = PrepareDataFrame(*frame, *mSendMessage, mMacSource, mMacDest, mAddMeshHeader, mMeshSource, + mMeshDest, addFragHeader); if ((mSendMessage->GetSubType() == Message::kSubTypeMleChildIdRequest) && mSendMessage->IsLinkSecurityEnabled()) { otLogNoteMac("Child ID Request requires fragmentation, aborting tx"); mMessageNextOffset = mSendMessage->GetLength(); - error = OT_ERROR_ABORT; - ExitNow(); + ExitNow(frame = nullptr); } - OT_ASSERT(aFrame.GetLength() != 7); + OT_ASSERT(frame->GetLength() != 7); break; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -504,7 +524,7 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame) Mac::Address macDestAddr; macDestAddr.SetShort(Get().GetParent().GetRloc16()); - PrepareEmptyFrame(aFrame, macDestAddr, /* aAckRequest */ true); + PrepareEmptyFrame(*frame, macDestAddr, /* aAckRequest */ true); } break; #endif @@ -512,7 +532,7 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame) #if OPENTHREAD_FTD case Message::kType6lowpan: - SendMesh(*mSendMessage, aFrame); + SendMesh(*mSendMessage, *frame); break; case Message::kTypeSupervision: @@ -527,24 +547,25 @@ otError MeshForwarder::HandleFrameRequest(Mac::TxFrame &aFrame) default: mMessageNextOffset = mSendMessage->GetLength(); - error = OT_ERROR_ABORT; - ExitNow(); + ExitNow(frame = nullptr); } - aFrame.SetIsARetransmission(false); + frame->SetIsARetransmission(false); exit: - return error; + return frame; } // This method constructs a MAC data from from a given IPv6 message. // // This method handles generation of MAC header, mesh header (if // requested), lowpan compression of IPv6 header, lowpan fragmentation -// header (if message requires fragmentation). It uses the message -// offset to construct next fragments. This method enables link security -// when message is MLE type and requires fragmentation. It returns the -// next offset into the message after the prepared frame. +// header (if message requires fragmentation or if it is explicitly +// requested by setting `aAddFragHeader` to `true`) It uses the +// message offset to construct next fragments. This method enables +// link security when message is MLE type and requires fragmentation. +// It returns the next offset into the message after the prepared +// frame. // uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame & aFrame, Message & aMessage, @@ -552,7 +573,8 @@ uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame & aFrame, const Mac::Address &aMacDest, bool aAddMeshHeader, uint16_t aMeshSource, - uint16_t aMeshDest) + uint16_t aMeshDest, + bool aAddFragHeader) { uint16_t fcf; uint8_t *payload; @@ -723,7 +745,7 @@ start: payloadLength = aMessage.GetLength() - aMessage.GetOffset(); fragmentLength = aFrame.GetMaxPayloadLength() - headerLength; - if (payloadLength > fragmentLength) + if ((payloadLength > fragmentLength) || aAddFragHeader) { Lowpan::FragmentHeader fragmentHeader; @@ -754,7 +776,13 @@ start: payload += Lowpan::FragmentHeader::kFirstFragmentHeaderSize; headerLength += Lowpan::FragmentHeader::kFirstFragmentHeaderSize; - payloadLength = (aFrame.GetMaxPayloadLength() - headerLength) & ~0x7; + + fragmentLength = aFrame.GetMaxPayloadLength() - headerLength; + + if (payloadLength > fragmentLength) + { + payloadLength = fragmentLength & ~0x7; + } } payload += hcLength; @@ -817,25 +845,70 @@ Neighbor *MeshForwarder::UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, otError VerifyOrExit(aFrame.GetAckRequest()); - if (aError == OT_ERROR_NONE) +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + // TREL radio link uses deferred ack model. We ignore + // `SendDone` event from `Mac` layer with success status and + // wait for deferred ack callback instead. +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (aFrame.GetRadioType() == Mac::kRadioTypeTrel) +#endif { - neighbor->ResetLinkFailures(); + VerifyOrExit(aError != OT_ERROR_NONE); } - else if (aError == OT_ERROR_NO_ACK) - { - neighbor->IncrementLinkFailures(); - VerifyOrExit(Mle::Mle::IsActiveRouter(neighbor->GetRloc16())); +#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - if (neighbor->GetLinkFailures() >= Mle::kFailedRouterTransmissions) - { - Get().RemoveRouterLink(*static_cast(neighbor)); - } - } + UpdateNeighborLinkFailures(*neighbor, aError, /* aAllowNeighborRemove */ true); exit: return neighbor; } +void MeshForwarder::UpdateNeighborLinkFailures(Neighbor &aNeighbor, otError aError, bool aAllowNeighborRemove) +{ + // Update neighbor `LinkFailures` counter on ack error. + + if (aError == OT_ERROR_NONE) + { + aNeighbor.ResetLinkFailures(); + } + else if (aError == OT_ERROR_NO_ACK) + { + aNeighbor.IncrementLinkFailures(); + + if (aAllowNeighborRemove && (Mle::Mle::IsActiveRouter(aNeighbor.GetRloc16())) && + (aNeighbor.GetLinkFailures() >= Mle::kFailedRouterTransmissions)) + { + Get().RemoveRouterLink(static_cast(aNeighbor)); + } + } +} + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +void MeshForwarder::HandleDeferredAck(Neighbor &aNeighbor, otError aError) +{ + bool allowNeighborRemove = true; + + VerifyOrExit(mEnabled); + + if (aError == OT_ERROR_NO_ACK) + { + otLogInfoMac("Deferred ack timeout on trel for neighbor %s rloc16:0x%04x", + aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16()); + } + +#if OPENTHREAD_CONFIG_MULTI_RADIO + // In multi radio mode, `RadioSelector` will update the neighbor's + // link failure counter and removes the neighbor if required. + Get().UpdateOnDeferredAck(aNeighbor, aError, allowNeighborRemove); +#else + UpdateNeighborLinkFailures(aNeighbor, aError, allowNeighborRemove); +#endif + +exit: + return; +} +#endif // #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, otError aError) { Neighbor * neighbor = nullptr; @@ -854,10 +927,19 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, otError aError) neighbor = UpdateNeighborOnSentFrame(aFrame, aError, macDest); } + UpdateSendMessage(aError, macDest, neighbor); + +exit: + return; +} + +void MeshForwarder::UpdateSendMessage(otError aFrameTxError, Mac::Address &aMacDest, Neighbor *aNeighbor) +{ VerifyOrExit(mSendMessage != nullptr); + OT_ASSERT(mSendMessage->GetDirectTransmission()); - if (aError != OT_ERROR_NONE) + if (aFrameTxError != OT_ERROR_NONE) { // If the transmission of any fragment frame fails, // the overall message transmission is considered @@ -880,14 +962,14 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, otError aError) } else { - otError txError = aError; + otError txError = aFrameTxError; mSendMessage->ClearDirectTransmission(); mSendMessage->SetOffset(0); - if (neighbor != nullptr) + if (aNeighbor != nullptr) { - neighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess()); + aNeighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess()); } #if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE @@ -896,7 +978,7 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, otError aError) // disabled, all fragment frames of a larger message are // sent even if the transmission of an earlier fragment fail. // Note that `GetTxSuccess() tracks the tx success of the - // entire message, while `aError` represents the error + // entire message, while `aFrameTxError` represents the error // status of the last fragment frame transmission. if (!mSendMessage->GetTxSuccess() && (txError == OT_ERROR_NONE)) @@ -905,7 +987,7 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, otError aError) } #endif - LogMessage(kMessageTransmit, *mSendMessage, &macDest, txError); + LogMessage(kMessageTransmit, *mSendMessage, &aMacDest, txError); if (mSendMessage->GetType() == Message::kTypeIp6) { @@ -946,11 +1028,7 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, otError aError) } exit: - - if (mEnabled) - { - mScheduleTransmissionTask.Post(); - } + mScheduleTransmissionTask.Post(); } void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) @@ -1032,6 +1110,41 @@ void MeshForwarder::HandleFragment(const uint8_t * aFrame, aFrame += fragmentHeaderLength; aFrameLength -= fragmentHeaderLength; +#if OPENTHREAD_CONFIG_MULTI_RADIO + + if (aLinkInfo.mLinkSecurity) + { + Neighbor *neighbor = Get().FindNeighbor(aMacSource, Neighbor::kInStateAnyExceptInvalid); + + if (neighbor != nullptr) + { + uint16_t tag = fragmentHeader.GetDatagramTag(); + + if (neighbor->IsLastRxFragmentTagSet()) + { + VerifyOrExit(!neighbor->IsLastRxFragmentTagAfter(tag), error = OT_ERROR_DUPLICATED); + + if (neighbor->GetLastRxFragmentTag() == tag) + { + VerifyOrExit(fragmentHeader.GetDatagramOffset() != 0, error = OT_ERROR_DUPLICATED); + + // Duplication suppression for a "next fragment" is handled + // by the code below where the the datagram offset is + // checked against the offset of the corresponding message + // (same datagram tag and size) in Reassembly List. Note + // that if there is no matching message in the Reassembly + // List (e.g., in case the message is already fully + // assembled) the received "next fragment" frame would be + // dropped. + } + } + + neighbor->SetLastRxFragmentTag(tag); + } + } + +#endif // OPENTHREAD_CONFIG_MULTI_RADIO + if (fragmentHeader.GetDatagramOffset() == 0) { uint16_t datagramSize = fragmentHeader.GetDatagramSize(); @@ -1509,19 +1622,27 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, uint16_t sourcePort; uint16_t destPort; bool shouldLogRss; + bool shouldLogRadio = false; + const char *radioString = ""; SuccessOrExit(ParseIp6UdpTcpHeader(aMessage, ip6Header, checksum, sourcePort, destPort)); shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop); - otLogMac(aLogLevel, "%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s%s%s", +#if OPENTHREAD_CONFIG_MULTI_RADIO + shouldLogRadio = true; + radioString = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all"; +#endif + + otLogMac(aLogLevel, "%s IPv6 %s msg, len:%d, chksum:%04x%s%s, sec:%s%s%s, prio:%s%s%s%s%s", MessageActionToString(aAction, aError), Ip6::Ip6::IpProtoToString(ip6Header.GetNextHeader()), aMessage.GetLength(), checksum, (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"), (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), aMessage.IsLinkSecurityEnabled() ? "yes" : "no", (aError == OT_ERROR_NONE) ? "" : ", error:", (aError == OT_ERROR_NONE) ? "" : otThreadErrorToString(aError), MessagePriorityToString(aMessage), - shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : ""); + shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "", + shouldLogRadio ? ", radio:" : "", radioString); if (aAction != kMessagePrepareIndirect) { diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 8ae11d475..884744e56 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -306,6 +306,24 @@ public: */ const PriorityQueue &GetResolvingQueue(void) const { return mResolvingQueue; } #endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + /** + * This method handles a deferred ack. + * + * Some radio links can use deferred ack logic, where a tx request always report `HandleSentFrame()` quickly. The + * link layer would wait for the ack and report it at a later time using this method. + * + * The link layer is expected to call `HandleDeferredAck()` (with success or failure status) for every tx request + * on the radio link. + * + * @param[in] aNeighbor The neighbor for which the deferred ack status is being reported. + * @param[in] aTxError The deferred ack error status: `OT_ERROR_NONE` to indicate a deferred ack was received, + * `OT_ERROR_NO_ACK` to indicate an ack timeout. + * + */ + void HandleDeferredAck(Neighbor &aNeighbor, otError aTxError); +#endif + private: enum : uint8_t { @@ -415,7 +433,8 @@ private: const Mac::Address &aMacDest, bool aAddMeshHeader = false, uint16_t aMeshSource = 0xffff, - uint16_t aMeshDest = 0xffff); + uint16_t aMeshDest = 0xffff, + bool aAddFragHeader = false); void PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aMacDest, bool aAckRequest); void SendMesh(Message &aMessage, Mac::TxFrame &aFrame); @@ -433,10 +452,12 @@ private: void RemoveMessage(Message &aMessage); void HandleDiscoverComplete(void); - void HandleReceivedFrame(Mac::RxFrame &aFrame); - otError HandleFrameRequest(Mac::TxFrame &aFrame); - Neighbor *UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, otError aError, const Mac::Address &aMacDest); - void HandleSentFrame(Mac::TxFrame &aFrame, otError aError); + void HandleReceivedFrame(Mac::RxFrame &aFrame); + Mac::TxFrame *HandleFrameRequest(Mac::TxFrames &aTxFrames); + Neighbor * UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, otError aError, const Mac::Address &aMacDest); + void UpdateNeighborLinkFailures(Neighbor &aNeighbor, otError aError, bool aAllowNeighborRemove); + void HandleSentFrame(Mac::TxFrame &aFrame, otError aError); + void UpdateSendMessage(otError aFrameTxError, Mac::Address &aMacDest, Neighbor *aNeighbor); void HandleTimeTick(void); static void ScheduleTransmissionTask(Tasklet &aTasklet); diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index a9055ac38..4d873165b 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -671,8 +671,22 @@ void MeshForwarder::HandleMesh(uint8_t * aFrame, message->WriteBytes(offset, aFrame, aFrameLength); message->SetLinkInfo(aLinkInfo); +#if OPENTHREAD_CONFIG_MULTI_RADIO + // We set the received radio type on the message in order for it + // to be logged correctly from LogMessage(). + message->SetRadioType(static_cast(aLinkInfo.mRadioType)); +#endif + LogMessage(kMessageReceive, *message, &aMacSource, OT_ERROR_NONE); +#if OPENTHREAD_CONFIG_MULTI_RADIO + // Since the message will be forwarded, we clear the radio + // type on the message to allow the radio type for tx to be + // selected later (based on the radios supported by the next + // hop). + message->ClearRadioType(); +#endif + IgnoreError(SendMessage(*message)); } @@ -990,6 +1004,8 @@ otError MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, Lowpan::MeshHeader meshHeader; Lowpan::FragmentHeader fragmentHeader; uint16_t headerLength; + bool shouldLogRadio = false; + const char * radioString = ""; SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength)); @@ -1006,15 +1022,21 @@ otError MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, shouldLogRss = (aAction == kMessageReceive) || (aAction == kMessageReassemblyDrop); +#if OPENTHREAD_CONFIG_MULTI_RADIO + shouldLogRadio = true; + radioString = aMessage.IsRadioTypeSet() ? RadioTypeToString(aMessage.GetRadioType()) : "all"; +#endif + otLogMac( - aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s", + aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s%s%s", MessageActionToString(aAction, aError), aMessage.GetLength(), (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"), (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), aMeshSource.ToString().AsCString(), aMeshDest.ToString().AsCString(), meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), hasFragmentHeader ? "yes" : "no", aMessage.IsLinkSecurityEnabled() ? "yes" : "no", (aError == OT_ERROR_NONE) ? "" : ", error:", (aError == OT_ERROR_NONE) ? "" : otThreadErrorToString(aError), - shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : ""); + shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "", + shouldLogRadio ? ", radio:" : "", radioString); if (hasFragmentHeader) { diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 83d73775b..84bb9944d 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -330,7 +330,7 @@ otError Mle::Restore(void) Get().SetCurrentKeySequence(networkInfo.GetKeySequence()); Get().SetMleFrameCounter(networkInfo.GetMleFrameCounter()); - Get().SetMacFrameCounter(networkInfo.GetMacFrameCounter()); + Get().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter()); mDeviceMode.Set(networkInfo.GetDeviceMode()); // force re-attach when version mismatch. @@ -449,7 +449,7 @@ otError Mle::Store(void) networkInfo.SetKeySequence(Get().GetCurrentKeySequence()); networkInfo.SetMleFrameCounter(Get().GetMleFrameCounter() + OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD); - networkInfo.SetMacFrameCounter(Get().GetMacFrameCounter() + + networkInfo.SetMacFrameCounter(Get().GetMaximumMacFrameCounter() + OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD); networkInfo.SetDeviceMode(mDeviceMode.Get()); @@ -1080,7 +1080,20 @@ otError Mle::ReadResponse(const Message &aMessage, Challenge &aResponse) otError Mle::AppendLinkFrameCounter(Message &aMessage) { - return Tlv::Append(aMessage, Get().GetMacFrameCounter()); + uint32_t counter; + + // When including Link-layer Frame Counter TLV in an MLE message + // the value is set to the maximum MAC frame counter on all + // supported radio links. All radio links must also start using + // the same counter value as the value included in the TLV. + + counter = Get().GetMaximumMacFrameCounter(); + +#if OPENTHREAD_CONFIG_MULTI_RADIO + Get().SetAllMacFrameCounters(counter); +#endif + + return Tlv::Append(aMessage, counter); } otError Mle::AppendMleFrameCounter(Message &aMessage) @@ -2688,19 +2701,49 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn { if (keySequence == neighbor->GetKeySequence()) { +#if OPENTHREAD_CONFIG_MULTI_RADIO + // Only when counter is exactly one off, we allow it to be + // used for updating radio link info (by `RadioSelector`) + // before message is dropped as a duplicate. This handles + // the common case where a broadcast MLE message (such as + // Link Advertisement) is received over multiple radio + // links. + + if ((frameCounter + 1) == neighbor->GetMleFrameCounter()) + { + OT_ASSERT(aMessage.IsRadioTypeSet()); + Get().UpdateOnReceive(*neighbor, aMessage.GetRadioType(), /* IsDuplicate */ true); + + // We intentionally exit without setting the error to + // skip logging "Failed to process UDP" at the exit + // label. Note that in multi-radio mode, receiving + // duplicate MLE message (with one-off counter) would + // be common and ok for broadcast MLE messages (e.g. + // MLE Link Advertisements). + ExitNow(); + } +#endif VerifyOrExit(frameCounter >= neighbor->GetMleFrameCounter(), error = OT_ERROR_DUPLICATED); } else { VerifyOrExit(keySequence > neighbor->GetKeySequence(), error = OT_ERROR_DUPLICATED); neighbor->SetKeySequence(keySequence); - neighbor->SetLinkFrameCounter(0); + neighbor->GetLinkFrameCounters().Reset(); neighbor->SetLinkAckFrameCounter(0); } neighbor->SetMleFrameCounter(frameCounter + 1); } +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (neighbor != nullptr) + { + OT_ASSERT(aMessage.IsRadioTypeSet()); + Get().UpdateOnReceive(*neighbor, aMessage.GetRadioType(), /* IsDuplicate */ false); + } +#endif + switch (command) { case kCommandAdvertisement: @@ -2801,6 +2844,27 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn ExitNow(error = OT_ERROR_DROP); } +#if OPENTHREAD_CONFIG_MULTI_RADIO + // If we could not find a neighbor matching the MAC address of the + // received MLE messages, or if the neighbor is now invalid, we + // check again after the message is handled with a relaxed neighbor + // state filer. The processing of the received MLE message may + // create a new neighbor or change the neighbor table (e.g., + // receiving a "Parent Request" from a new child, or processing a + // "Link Request" from a previous child which is being promoted to a + // router). + + if ((neighbor == nullptr) || neighbor->IsStateInvalid()) + { + neighbor = Get().FindNeighbor(extAddr, Neighbor::kInStateAnyExceptInvalid); + + if (neighbor != nullptr) + { + Get().UpdateOnReceive(*neighbor, aMessage.GetRadioType(), /* aIsDuplicate */ false); + } + } +#endif + exit: LogProcessError(kTypeGenericUdp, error); } @@ -3326,7 +3390,7 @@ void Mle::HandleParentResponse(const Message &aMessage, const Ip6::MessageInfo & mParentCandidate.SetExtAddress(extAddress); mParentCandidate.SetRloc16(sourceAddress); - mParentCandidate.SetLinkFrameCounter(linkFrameCounter); + mParentCandidate.GetLinkFrameCounters().SetAll(linkFrameCounter); mParentCandidate.SetLinkAckFrameCounter(linkFrameCounter); mParentCandidate.SetMleFrameCounter(mleFrameCounter); mParentCandidate.SetVersion(static_cast(version)); @@ -3559,6 +3623,13 @@ void Mle::HandleChildUpdateRequest(const Message &aMessage, const Ip6::MessageIn ExitNow(error = OT_ERROR_PARSE); } +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (aNeighbor != nullptr) + { + aNeighbor->ClearLastRxFragmentTag(); + } +#endif + SuccessOrExit(error = SendChildUpdateResponse(tlvs, numTlvs, challenge)); exit: @@ -3612,7 +3683,7 @@ void Mle::HandleChildUpdateResponse(const Message & aMessage, case kRoleDetached: SuccessOrExit(error = ReadFrameCounters(aMessage, linkFrameCounter, mleFrameCounter)); - mParent.SetLinkFrameCounter(linkFrameCounter); + mParent.GetLinkFrameCounters().SetAll(linkFrameCounter); mParent.SetLinkAckFrameCounter(linkFrameCounter); mParent.SetMleFrameCounter(mleFrameCounter); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 96839289f..ae1b03a80 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -654,6 +654,13 @@ void MleRouter::HandleLinkRequest(const Message &aMessage, const Ip6::MessageInf } #endif +#if OPENTHREAD_CONFIG_MULTI_RADIO + if (neighbor != nullptr) + { + neighbor->ClearLastRxFragmentTag(); + } +#endif + SuccessOrExit(error = SendLinkAccept(aMessageInfo, neighbor, requestedTlvs, challenge)); exit: @@ -934,7 +941,7 @@ otError MleRouter::HandleLinkAccept(const Message & aMessage, aMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr); router->SetExtAddress(extAddr); router->SetRloc16(sourceAddress); - router->SetLinkFrameCounter(linkFrameCounter); + router->GetLinkFrameCounters().SetAll(linkFrameCounter); router->SetLinkAckFrameCounter(linkFrameCounter); router->SetMleFrameCounter(mleFrameCounter); router->SetLastHeard(TimerMilli::GetNow()); @@ -2248,7 +2255,7 @@ void MleRouter::HandleChildIdRequest(const Message & aMessage, } child->SetLastHeard(TimerMilli::GetNow()); - child->SetLinkFrameCounter(linkFrameCounter); + child->GetLinkFrameCounters().SetAll(linkFrameCounter); child->SetLinkAckFrameCounter(linkFrameCounter); child->SetMleFrameCounter(mleFrameCounter); child->SetKeySequence(aKeySequence); @@ -2256,6 +2263,9 @@ void MleRouter::HandleChildIdRequest(const Message & aMessage, child->SetVersion(static_cast(version)); child->GetLinkInfo().AddRss(aMessageInfo.GetThreadLinkInfo()->GetRss()); child->SetTimeout(timeout); +#if OPENTHREAD_CONFIG_MULTI_RADIO + child->ClearLastRxFragmentTag(); +#endif if (mode.IsFullNetworkData()) { @@ -2497,6 +2507,10 @@ void MleRouter::HandleChildUpdateRequest(const Message & aMessage, } } +#if OPENTHREAD_CONFIG_MULTI_RADIO + child->ClearLastRxFragmentTag(); +#endif + SendChildUpdateResponse(child, aMessageInfo, tlvs, tlvslength, challenge); exit: @@ -2578,7 +2592,7 @@ void MleRouter::HandleChildUpdateResponse(const Message & aMessage, switch (Tlv::Find(aMessage, linkFrameCounter)) { case OT_ERROR_NONE: - child->SetLinkFrameCounter(linkFrameCounter); + child->GetLinkFrameCounters().SetAll(linkFrameCounter); child->SetLinkAckFrameCounter(linkFrameCounter); break; case OT_ERROR_NOT_FOUND: @@ -2857,13 +2871,13 @@ void MleRouter::HandleDiscoveryRequest(const Message &aMessage, const Ip6::Messa } } - error = SendDiscoveryResponse(aMessageInfo.GetPeerAddr(), aMessage.GetPanId()); + error = SendDiscoveryResponse(aMessageInfo.GetPeerAddr(), aMessage); exit: LogProcessError(kTypeDiscoveryRequest, error); } -otError MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_t aPanId) +otError MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const Message &aDiscoverRequestMessage) { otError error = OT_ERROR_NONE; Message * message; @@ -2875,7 +2889,13 @@ otError MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, uint1 VerifyOrExit((message = NewMleMessage()) != nullptr, error = OT_ERROR_NO_BUFS); message->SetSubType(Message::kSubTypeMleDiscoverResponse); - message->SetPanId(aPanId); + message->SetPanId(aDiscoverRequestMessage.GetPanId()); +#if OPENTHREAD_CONFIG_MULTI_RADIO + // Send the MLE Discovery Response message on same radio link + // from which the "MLE Discover Request" message was received. + message->SetRadioType(aDiscoverRequestMessage.GetRadioType()); +#endif + SuccessOrExit(error = AppendHeader(*message, kCommandDiscoveryResponse)); // Discovery TLV diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index 4ce2a1069..01470c6ec 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -623,8 +623,7 @@ private: uint8_t aTlvsLength, uint16_t aDelay, const Message * aRequestMessage = nullptr); - otError SendDiscoveryResponse(const Ip6::Address &aDestination, uint16_t aPanId); - + otError SendDiscoveryResponse(const Ip6::Address &aDestination, const Message &aDiscoverRequestMessage); void SetStateRouter(uint16_t aRloc16); void SetStateLeader(uint16_t aRloc16); void StopLeader(void); diff --git a/src/core/thread/radio_selector.cpp b/src/core/thread/radio_selector.cpp new file mode 100644 index 000000000..d743fdeb3 --- /dev/null +++ b/src/core/thread/radio_selector.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2019, 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 implementation of radio selector (for multi radio links). + */ + +#include "radio_selector.hpp" + +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/locator-getters.hpp" +#include "common/logging.hpp" +#include "common/random.hpp" + +#if OPENTHREAD_CONFIG_MULTI_RADIO + +namespace ot { + +// This array defines the order in which different radio link types are +// selected for message tx (direct message). +const Mac::RadioType RadioSelector::sRadioSelectionOrder[Mac::kNumRadioTypes] = { +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + Mac::kRadioTypeTrel, +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + Mac::kRadioTypeIeee802154, +#endif +}; + +RadioSelector::RadioSelector(Instance &aInstance) + : InstanceLocator(aInstance) +{ +} + +otLogLevel RadioSelector::UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference) +{ + uint8_t old = aNeighbor.GetRadioPreference(aRadioType); + int16_t preferecne = static_cast(old); + + preferecne += aDifference; + + if (preferecne > kMaxPreference) + { + preferecne = kMaxPreference; + } + + if (preferecne < kMinPreference) + { + preferecne = kMinPreference; + } + + aNeighbor.SetRadioPreference(aRadioType, static_cast(preferecne)); + + // We check whether the update to the preference value caused it + // to cross the threshold `kHighPreference`. Based on this we + // return a suggested log level. If there is cross, suggest info + // log level, otherwise debug log level. + + return ((old >= kHighPreference) != (preferecne >= kHighPreference)) ? OT_LOG_LEVEL_INFO : OT_LOG_LEVEL_DEBG; +} + +void RadioSelector::UpdateOnReceive(Neighbor &aNeighbor, Mac::RadioType aRadioType, bool aIsDuplicate) +{ + otLogLevel logLevel = OT_LOG_LEVEL_INFO; + + if (aNeighbor.GetSupportedRadioTypes().Contains(aRadioType)) + { + logLevel = UpdatePreference(aNeighbor, aRadioType, + aIsDuplicate ? kPreferenceChangeOnRxDuplicate : kPreferenceChangeOnRx); + + Log(logLevel, aIsDuplicate ? "UpdateOnDupRx" : "UpdateOnRx", aRadioType, aNeighbor); + } + else + { + aNeighbor.AddSupportedRadioType(aRadioType); + aNeighbor.SetRadioPreference(aRadioType, kInitPreference); + + Log(logLevel, "NewRadio(OnRx)", aRadioType, aNeighbor); + } +} + +void RadioSelector::UpdateOnSendDone(Mac::TxFrame &aFrame, otError aTxError) +{ + otLogLevel logLevel = OT_LOG_LEVEL_INFO; + Mac::RadioType radioType = aFrame.GetRadioType(); + Mac::Address macDest; + Neighbor * neighbor; + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (radioType == Mac::kRadioTypeTrel) + { + // TREL radio link uses deferred ack model. We ignore + // `SendDone` event from `Mac` layer with success status and + // wait for deferred ack callback. + VerifyOrExit(aTxError != OT_ERROR_NONE); + } +#endif + + VerifyOrExit(aFrame.GetAckRequest()); + + IgnoreError(aFrame.GetDstAddr(macDest)); + neighbor = Get().FindNeighbor(macDest, Neighbor::kInStateAnyExceptInvalid); + VerifyOrExit(neighbor != nullptr); + + if (neighbor->GetSupportedRadioTypes().Contains(radioType)) + { + logLevel = + UpdatePreference(*neighbor, radioType, + (aTxError == OT_ERROR_NONE) ? kPreferenceChangeOnTxSuccess : kPreferenceChangeOnTxError); + + Log(logLevel, (aTxError == OT_ERROR_NONE) ? "UpdateOnTxSucc" : "UpdateOnTxErr", radioType, *neighbor); + } + else + { + VerifyOrExit(aTxError == OT_ERROR_NONE); + neighbor->AddSupportedRadioType(radioType); + neighbor->SetRadioPreference(radioType, kInitPreference); + + Log(logLevel, "NewRadio(OnTx)", radioType, *neighbor); + } + +exit: + return; +} + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE +void RadioSelector::UpdateOnDeferredAck(Neighbor &aNeighbor, otError aTxError, bool &aAllowNeighborRemove) +{ + otLogLevel logLevel = OT_LOG_LEVEL_INFO; + + aAllowNeighborRemove = true; + + if (aNeighbor.GetSupportedRadioTypes().Contains(Mac::kRadioTypeTrel)) + { + logLevel = UpdatePreference(aNeighbor, Mac::kRadioTypeTrel, + (aTxError == OT_ERROR_NONE) ? kPreferenceChangeOnDeferredAckSuccess + : kPreferenceChangeOnDeferredAckTimeout); + + Log(logLevel, (aTxError == OT_ERROR_NONE) ? "UpdateOnDefAckSucc" : "UpdateOnDefAckFail", Mac::kRadioTypeTrel, + aNeighbor); + + // In case of deferred ack timeout, we check if the neighbor + // has any other radio link (with high preference) for future + // tx. If it it does, we set `aAllowNeighborRemove` to `false` + // to ensure neighbor is not removed yet. + + VerifyOrExit(aTxError != OT_ERROR_NONE); + + for (Mac::RadioType radio : sRadioSelectionOrder) + { + if ((radio != Mac::kRadioTypeTrel) && aNeighbor.GetSupportedRadioTypes().Contains(radio) && + aNeighbor.GetRadioPreference(radio) >= kHighPreference) + { + aAllowNeighborRemove = false; + ExitNow(); + } + } + } + else + { + VerifyOrExit(aTxError == OT_ERROR_NONE); + aNeighbor.AddSupportedRadioType(Mac::kRadioTypeTrel); + aNeighbor.SetRadioPreference(Mac::kRadioTypeTrel, kInitPreference); + + Log(logLevel, "NewRadio(OnDefAckSucc)", Mac::kRadioTypeTrel, aNeighbor); + } + +exit: + return; +} +#endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + +Mac::RadioType RadioSelector::Select(Mac::RadioTypes aRadioOptions, const Neighbor &aNeighbor) +{ + Mac::RadioType selectedRadio = sRadioSelectionOrder[0]; + uint8_t selectedPreference = 0; + bool found = false; + + // Select the first radio links with preference higher than + // threshold `kHighPreference`. The radio links are checked in the + // order defined by the `sRadioSelectionOrder` array. If no radio + // link has preference higher then threshold, select the one with + // highest preference. + + for (Mac::RadioType radio : sRadioSelectionOrder) + { + if (aRadioOptions.Contains(radio)) + { + uint8_t preference = aNeighbor.GetRadioPreference(radio); + + if (preference >= kHighPreference) + { + selectedRadio = radio; + break; + } + + if (!found || (selectedPreference < preference)) + { + found = true; + selectedRadio = radio; + selectedPreference = preference; + } + } + } + + return selectedRadio; +} + +Mac::TxFrame &RadioSelector::SelectRadio(Message &aMessage, const Mac::Address &aMacDest, Mac::TxFrames &aTxFrames) +{ + Neighbor * neighbor; + Mac::RadioType selectedRadio; + Mac::RadioTypes selections; + + if (aMacDest.IsBroadcast() || aMacDest.IsNone()) + { + aMessage.ClearRadioType(); + ExitNow(selections.AddAll()); + } + + // If the radio type is already set when message was created we + // use the selected radio type. (e.g., MLE Discovery Response + // selects the radio link from which MLE Discovery Request is + // received. + + if (aMessage.IsRadioTypeSet()) + { + ExitNow(selections.Add(aMessage.GetRadioType())); + } + + neighbor = Get().FindNeighbor(aMacDest, Neighbor::kInStateAnyExceptInvalid); + + if ((neighbor == nullptr) || neighbor->GetSupportedRadioTypes().IsEmpty()) + { + // If we do not have a corresponding neighbor or do not yet + // know the supported radio types, we try sending on all radio + // links in parallel. As an example, such a situation can + // happen when recovering a non-sleepy child (sending MLE + // Child Update Request to it) after device itself was reset. + + aMessage.ClearRadioType(); + ExitNow(selections.AddAll()); + } + + selectedRadio = Select(neighbor->GetSupportedRadioTypes(), *neighbor); + selections.Add(selectedRadio); + + Log(OT_LOG_LEVEL_DEBG, "SelectRadio", selectedRadio, *neighbor); + + aMessage.SetRadioType(selectedRadio); + + // We (probabilistically) decide whether to probe on another radio + // link for the current frame tx. When probing we allow the same + // frame to be sent in parallel over multiple radio links but only + // care about the tx outcome (ack status) on the main selected + // radio link. This is done by setting the "required radio types" + // (`SetRequiredRadioTypes()`) to match the main selection on + // `aTxFrames`. We allow probe on TREL radio link if it is not + // currently usable (thus not selected) but is/was supported by + // the neighbor (i.e., we did rx/tx on TREL link from/to this + // neighbor in the past). The probe process helps detect whether + // the TREL link is usable again allowing us to switch over + // faster. + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + if (!selections.Contains(Mac::kRadioTypeTrel) && neighbor->GetSupportedRadioTypes().Contains(Mac::kRadioTypeTrel) && + (Random::NonCrypto::GetUint8InRange(0, 100) < kTrelProbeProbability)) + { + aTxFrames.SetRequiredRadioTypes(selections); + selections.Add(Mac::kRadioTypeTrel); + + Log(OT_LOG_LEVEL_DEBG, "Probe", Mac::kRadioTypeTrel, *neighbor); + } +#endif + +exit: + return aTxFrames.GetTxFrame(selections); +} + +Mac::RadioType RadioSelector::SelectPollFrameRadio(const Neighbor &aParent) +{ + // This array defines the order in which different radio link types + // are selected for data poll frame tx. + static const Mac::RadioType selectionOrder[Mac::kNumRadioTypes] = { +#if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE + Mac::kRadioTypeIeee802154, +#endif +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + Mac::kRadioTypeTrel, +#endif + }; + + Mac::RadioType selection = selectionOrder[0]; + + for (Mac::RadioType radio : selectionOrder) + { + if (aParent.GetSupportedRadioTypes().Contains(radio)) + { + selection = radio; + break; + } + } + + return selection; +} + +// LCOV_EXCL_START + +#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) + +void RadioSelector::Log(otLogLevel aLogLevel, + const char * aActionText, + Mac::RadioType aRadioType, + const Neighbor &aNeighbor) +{ + String preferenceString; + bool isFirstEntry = true; + + VerifyOrExit(otLoggingGetLevel() >= aLogLevel); + + for (Mac::RadioType radio : sRadioSelectionOrder) + { + if (aNeighbor.GetSupportedRadioTypes().Contains(radio)) + { + IgnoreError(preferenceString.Append("%s%s:%d", isFirstEntry ? "" : " ", RadioTypeToString(radio), + aNeighbor.GetRadioPreference(radio))); + isFirstEntry = false; + } + } + + otLogMac(aLogLevel, "RadioSelector: %s %s - neighbor:[%s rloc16:0x%04x radio-pref:{%s} state:%s]", aActionText, + RadioTypeToString(aRadioType), aNeighbor.GetExtAddress().ToString().AsCString(), aNeighbor.GetRloc16(), + preferenceString.AsCString(), Neighbor::StateToString(aNeighbor.GetState())); + +exit: + return; +} + +#else // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) + +void RadioSelector::Log(otLogLevel, const char *, Mac::RadioType, const Neighbor &) +{ +} + +#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) + +// LCOV_EXCL_STOP + +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO diff --git a/src/core/thread/radio_selector.hpp b/src/core/thread/radio_selector.hpp new file mode 100644 index 000000000..d7adf3365 --- /dev/null +++ b/src/core/thread/radio_selector.hpp @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2019, 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 radio selector (for multi radio links). + */ + +#ifndef RADIO_SELECTOR_HPP_ +#define RADIO_SELECTOR_HPP_ + +#include "openthread-core-config.h" + +#include "common/locator.hpp" +#include "common/message.hpp" +#include "mac/mac_frame.hpp" +#include "mac/mac_links.hpp" +#include "mac/mac_types.hpp" + +#if OPENTHREAD_CONFIG_MULTI_RADIO + +namespace ot { + +/** + * @addtogroup core-radio-selector + * + * @brief + * This module includes definition for radio selector (for multi radio links). + * + * @{ + * + */ + +class Neighbor; + +class RadioSelector : InstanceLocator +{ +public: + /** + * This class defines all the neighbor info required for multi radio link and radio selection. + * + * `Neighbor` class publicly inherits from this class. + * + */ + class NeighborInfo + { + friend class RadioSelector; + + /** + * This method returns the supported radio types by the neighbor. + * + * @returns The supported radio types set. + * + */ + Mac::RadioTypes GetSupportedRadioTypes(void) const { return mSupportedRadioTypes; } + + private: + void AddSupportedRadioType(Mac::RadioType aType) { mSupportedRadioTypes.Add(aType); } + void RemoveSupportedRadioType(Mac::RadioType aType) { mSupportedRadioTypes.Remove(aType); } + void ClearSupportedRadioType(void) { mSupportedRadioTypes.Clear(); } + + uint8_t GetRadioPreference(Mac::RadioType aType) const { return mRadioPreference[aType]; } + void SetRadioPreference(Mac::RadioType aType, uint8_t aValue) { mRadioPreference[aType] = aValue; } + + Mac::RadioTypes mSupportedRadioTypes; + uint8_t mRadioPreference[Mac::kNumRadioTypes]; + }; + + /** + * This constructor initializes the RadioSelector object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + RadioSelector(Instance &aInstance); + + /** + * This method updates the neighbor info (for multi radio support) on a received frame event. + * + * This method notifies `RadioSelector` of a received secure frame/message on a radio link type for neighbor. If + * the frame/message happens to be received earlier on another radio link, the `aIsDuplicate` is set to `true`. + * A duplicate frame/message should have passed the security check (i.e., tag/MIC should be valid). + * + * @param[in] aNeighbor The neighbor for which a frame/message was received. + * @param[in] aRadioType The radio link type on which the frame/message was received. + * @param[in] aIsDuplicate Indicates whether the received frame/message is a duplicate or not. + * + */ + void UpdateOnReceive(Neighbor &aNeighbor, Mac::RadioType aRadioType, bool aIsDuplicate); + + /** + * This method updates the neighbor info (for multi radio support) on a send done event. + * + * This method notifies `RadioSelector` the status of frame transmission on a radio link type. The radio link + * type is provided by the `aFrame` itself. + * + * @param[in] aFrame A transmitted frame. + * @param[in] aTxError The transmission error. + * + */ + void UpdateOnSendDone(Mac::TxFrame &aFrame, otError aTxError); + +#if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE + /** + * This method updates the neighbor info (for multi radio support) on a deferred ack event. + * + * The deferred ack model is used by TREL radio link. + * + * @param[in] aNeighbor The neighbor from which ack was expected + * @param[in] aError The deferred ack status (`OT_ERROR_NONE` indicates ack was received). + * @param[out] aAllowNeighborRemove Boolean variable to output whether the neighbor is allowed to be removed. + * + */ + void UpdateOnDeferredAck(Neighbor &aNeighbor, otError aTxError, bool &aAllowNeighborRemove); +#endif + + /** + * This method selects the radio link type for sending a data poll frame to a given parent neighbor. + * + * @param[in] aParent The parent to which the data poll frame will be sent. + * + * @returns The radio type on which the data poll frame should be sent. + * + */ + Mac::RadioType SelectPollFrameRadio(const Neighbor &aParent); + + /** + * This method selects the radio link for sending a given message to a specified MAC destination. + * + * The `aMessage` will be updated to store the selected radio type (please see `Message::GetRadioType()`). + * The `aTxFrames` will also be updated to indicate which radio links are to be used. + * + * @param[inout] aMessage The message to send. + * @param[in] aDest The MAC destination address. + * @param[inout] aTxFrames The set of TxFrames for all radio links. + * + * @returns A reference to `mTxFrame` to use when preparing the frame for tx. + * + */ + Mac::TxFrame &SelectRadio(Message &aMessage, const Mac::Address &aDest, Mac::TxFrames &aTxFrames); + +private: + enum + { + kPreferenceChangeOnTxError = -35, // Preference change on a tx error on a radio link. + kPreferenceChangeOnTxSuccess = 25, // Preference change on tx success on a radio link. + kPreferenceChangeOnDeferredAckSuccess = 25, // Preference change on deferred ack success. + kPreferenceChangeOnDeferredAckTimeout = -100, // Preference change on deferred ack timeout. + kPreferenceChangeOnRx = 15, // Preference change on new (secure) frame/msg rx on a radio link. + kPreferenceChangeOnRxDuplicate = 15, // Preference change on new (secure) duplicate frame/msg rx. + kMinPreference = 0, // Minimum preference value. + kMaxPreference = 255, // Maximum preference value. + kInitPreference = 200, // Initial preference value + kHighPreference = 220, // High preference. + kTrelProbeProbability = 25, // Probability percentage to probe on TREL link + kRadioPreferenceStringSize = 75, + }; + + otLogLevel UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference); + Mac::RadioType Select(Mac::RadioTypes aRadioOptions, const Neighbor &aNeighbor); + void Log(otLogLevel aLogLevel, const char *aActionText, Mac::RadioType aType, const Neighbor &aNeighbor); + + static const Mac::RadioType sRadioSelectionOrder[Mac::kNumRadioTypes]; +}; + +/** + * @} + * + */ + +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_MULTI_RADIO + +#endif // RADIO_SELECTOR_HPP_ diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index f8348bfdd..faf611b9f 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -73,6 +73,9 @@ ThreadNetif::ThreadNetif(Instance &aInstance) , mMeshForwarder(aInstance) , mMleRouter(aInstance) , mDiscoverScanner(aInstance) +#if OPENTHREAD_CONFIG_MULTI_RADIO + , mRadioSelector(aInstance) +#endif #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE , mNetworkDataLocal(aInstance) #endif diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index 3fee6650c..7a6183050 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -92,6 +92,7 @@ #include "thread/network_data_notifier.hpp" #include "thread/network_diagnostic.hpp" #include "thread/panid_query_server.hpp" +#include "thread/radio_selector.hpp" #include "thread/time_sync_service.hpp" #include "utils/child_supervision.hpp" @@ -208,6 +209,9 @@ private: MeshForwarder mMeshForwarder; Mle::MleRouter mMleRouter; Mle::DiscoverScanner mDiscoverScanner; +#if OPENTHREAD_CONFIG_MULTI_RADIO + RadioSelector mRadioSelector; +#endif #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE NetworkData::Local mNetworkDataLocal; #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index 8dfebc21c..3c94c34d6 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -66,10 +66,11 @@ exit: void Neighbor::Info::SetFrom(const Neighbor &aNeighbor) { Clear(); + mExtAddress = aNeighbor.GetExtAddress(); mAge = Time::MsecToSec(TimerMilli::GetNow() - aNeighbor.GetLastHeard()); mRloc16 = aNeighbor.GetRloc16(); - mLinkFrameCounter = aNeighbor.GetLinkFrameCounter(); + mLinkFrameCounter = aNeighbor.GetLinkFrameCounters().GetMaximum(); mMleFrameCounter = aNeighbor.GetMleFrameCounter(); mLinkQualityIn = aNeighbor.GetLinkInfo().GetLinkQuality(); mAverageRssi = aNeighbor.GetLinkInfo().GetAverageRss(); diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index cecf3c9e1..356600ff7 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -52,6 +52,7 @@ #include "thread/link_quality.hpp" #include "thread/mle_tlvs.hpp" #include "thread/mle_types.hpp" +#include "thread/radio_selector.hpp" namespace ot { @@ -64,6 +65,10 @@ class LinkMetricsSeriesInfo; ///< Forward declaration for including each other w * */ class Neighbor : public InstanceLocatorInit +#if OPENTHREAD_CONFIG_MULTI_RADIO + , + public RadioSelector::NeighborInfo +#endif { public: /** @@ -406,20 +411,20 @@ public: void SetLastHeard(TimeMilli aLastHeard) { mLastHeard = aLastHeard; } /** - * This method gets the link frame counter value. + * This method gets the link frame counters. * - * @returns The link frame counter value. + * @returns A reference to `Mac::LinkFrameCounters` containing link frame counter for all supported radio links. * */ - uint32_t GetLinkFrameCounter(void) const { return mValidPending.mValid.mLinkFrameCounter; } + Mac::LinkFrameCounters &GetLinkFrameCounters(void) { return mValidPending.mValid.mLinkFrameCounters; } /** - * This method sets the link frame counter value. + * This method gets the link frame counters. * - * @param[in] aFrameCounter The link frame counter value. + * @returns A reference to `Mac::LinkFrameCounters` containing link frame counter for all supported radio links. * */ - void SetLinkFrameCounter(uint32_t aFrameCounter) { mValidPending.mValid.mLinkFrameCounter = aFrameCounter; } + const Mac::LinkFrameCounters &GetLinkFrameCounters(void) const { return mValidPending.mValid.mLinkFrameCounters; } #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 /** @@ -486,6 +491,60 @@ public: */ void SetRloc16(uint16_t aRloc16) { mRloc16 = aRloc16; } +#if OPENTHREAD_CONFIG_MULTI_RADIO + /** + * This method clears the last received fragment tag. + * + * The last received fragment tag is used for detect duplicate frames (receievd over different radios) when + * multi-radio feature is enabled. + * + */ + void ClearLastRxFragmentTag(void) { mLastRxFragmentTag = 0; } + + /** + * This method gets the last received fragment tag. + * + * This method MUST be used only when the tag is set (and not cleared). Otherwise its behavior is undefined. + * + * @returns The last received fragment tag. + * + */ + uint16_t GetLastRxFragmentTag(void) const { return mLastRxFragmentTag; } + + /** + * This method set the last received fragment tag. + * + * @param[in] aTag The new tag value. + * + */ + void SetLastRxFragmentTag(uint16_t aTag) { mLastRxFragmentTag = (aTag == 0) ? 0xffff : aTag; } + + /** + * This method indicates whether the last received fragment tag is set or not. + * + * @returns TRUE if the last received fragment tag is set, FALSE otherwise. + * + */ + bool IsLastRxFragmentTagSet(void) const { return (mLastRxFragmentTag != 0); } + + /** + * This method indicates whether the last received fragment tag is strictly after a given tag value. + * + * This method MUST be used only when the tag is set (and not cleared). Otherwise its behavior is undefined. + * + * The tag value compassion follows the Serial Number Arithmetic logic from RFC-1982. It is semantically equivalent + * to `LastRxFragementTag > aTag`. + * + * @param[in] aTag A tag value to compare against. + * + * @returns TRUE if the current last rx fragment tag is strictly after @p aTag, FALSE if they are equal or it is + * before @p aTag. + * + */ + bool IsLastRxFragmentTagAfter(uint16_t aTag) const { return ((aTag - mLastRxFragmentTag) & (1U << 15)) != 0; } + +#endif // OPENTHREAD_CONFIG_MULTI_RADIO + /** * This method indicates whether or not it is a valid Thread 1.1 neighbor. * @@ -672,8 +731,8 @@ private: { struct { - uint32_t mLinkFrameCounter; ///< The Link Frame Counter - uint32_t mMleFrameCounter; ///< The MLE Frame Counter + Mac::LinkFrameCounters mLinkFrameCounters; ///< The Link Frame Counters + uint32_t mMleFrameCounter; ///< The MLE Frame Counter #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 uint32_t mLinkAckFrameCounter; ///< The Link Ack Frame Counter #endif @@ -684,6 +743,10 @@ private: } mPending; } mValidPending; +#if OPENTHREAD_CONFIG_MULTI_RADIO + uint16_t mLastRxFragmentTag; ///< Last received fragment tag +#endif + uint32_t mKeySequence; ///< Current key sequence uint16_t mRloc16; ///< The RLOC16 uint8_t mState : 4; ///< The link state diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index b076125cd..ef6102bb0 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -272,11 +272,12 @@ void TestMacHeader(void) for (const auto &test : tests) { - uint8_t psdu[Mac::Frame::kMtu]; + uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; Mac::TxFrame frame; - frame.mPsdu = psdu; - frame.mLength = 0; + frame.mPsdu = psdu; + frame.mLength = 0; + frame.mRadioType = 0; frame.InitMacHeader(test.fcf, test.secCtl); VerifyOrQuit(frame.GetHeaderLength() == test.headerLength, "MacHeader test failed");