diff --git a/doc/ot_config_doc.h b/doc/ot_config_doc.h index c61b92e35..96d8f5038 100644 --- a/doc/ot_config_doc.h +++ b/doc/ot_config_doc.h @@ -85,6 +85,7 @@ * @defgroup config-time-sync Time Sync Service * @defgroup config-tmf Thread Management Framework Service * @defgroup config-trel TREL + * @defgroup config-wakeup Wake-up * * @} * diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index f16694652..d85360b1e 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -256,6 +256,8 @@ ot_option(OT_TX_QUEUE_STATS OPENTHREAD_CONFIG_TX_QUEUE_STATISTICS_ENABLE "tx que ot_option(OT_UDP_FORWARD OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE "UDP forward") ot_option(OT_UPTIME OPENTHREAD_CONFIG_UPTIME_ENABLE "uptime") ot_option(OT_VERHOEFF_CHECKSUM OPENTHREAD_CONFIG_VERHOEFF_CHECKSUM_ENABLE "verhoeff checksum") +ot_option(OT_WAKEUP_COORDINATOR OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE "wake-up coordinator") +ot_option(OT_WAKEUP_END_DEVICE OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE "wake-up end device") option(OT_DOC "build OpenThread documentation") message(STATUS "- - - - - - - - - - - - - - - - ") diff --git a/script/test b/script/test index be04e237e..c6f5c63bf 100755 --- a/script/test +++ b/script/test @@ -131,6 +131,8 @@ build_simulation() options+=("-DOT_LINK_METRICS_INITIATOR=ON") options+=("-DOT_LINK_METRICS_SUBJECT=ON") options+=("-DOT_LINK_METRICS_MANAGER=ON") + options+=("-DOT_WAKEUP_COORDINATOR=ON") + options+=("-DOT_WAKEUP_END_DEVICE=ON") fi if [[ ${OT_NODE_TYPE} == cli* ]]; then diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 5d2daef98..ac5f93389 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -842,6 +842,7 @@ source_set("libopenthread_core_config") { "config/time_sync.h", "config/tmf.h", "config/trel.h", + "config/wakeup.h", "openthread-core-config.h", ] public_configs = [ diff --git a/src/core/config/mac.h b/src/core/config/mac.h index ec7e2f9d2..ec0791812 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -46,6 +46,7 @@ */ #include "config/time_sync.h" +#include "config/wakeup.h" /** * @def OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT @@ -435,6 +436,17 @@ #define OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + * + * Define to 1 to enable support for IEEE 802.15.4 MAC Multipurpose frame format. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME +#define OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME \ + (OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE) +#endif + /** * @def OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE * diff --git a/src/core/config/wakeup.h b/src/core/config/wakeup.h new file mode 100644 index 000000000..a473e1e42 --- /dev/null +++ b/src/core/config/wakeup.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file includes compile-time configurations for the Wake-up Coordinator and Wake-up End Device roles. + * + */ + +#ifndef CONFIG_WAKEUP_H_ +#define CONFIG_WAKEUP_H_ + +/** + * @addtogroup config-wakeup + * + * @brief + * This module includes configuration variables for the Wake-up Coordinator and Wake-up End Device roles. + * + * @{ + * + */ + +/** + * @def OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + * + * Define to 1 to enable the Wake-up Coordinator role that is capable of establishing + * a link with one or more Wake-up End Devices by sending a sequence of wake-up frames. + * + */ +#ifndef OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +#define OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + * + * Define to 1 to enable the Wake-up End Device role that periodically listens for wake-up + * frames to establish a link with a Wake-up Coordinator device. + * + */ +#ifndef OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +#define OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE 0 +#endif + +/** + * @} + * + */ + +#endif // CONFIG_WAKEUP_H_ diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 9e76da0cd..1fc2154c6 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -39,6 +39,7 @@ #include "common/debug.hpp" #include "common/frame_builder.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "radio/trel_link.hpp" #if !OPENTHREAD_RADIO || OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE #include "crypto/aes_ccm.hpp" @@ -193,13 +194,13 @@ void Frame::InitMacHeader(Type aType, if (aSuppressSequence) { - fcf |= kFcfSequenceSupression; + fcf |= kFcfSequenceSuppression; } builder.Init(mPsdu, GetMtu()); IgnoreError(builder.AppendLittleEndianUint16(fcf)); - if (!IsSequenceSuppressed(fcf)) + if (IsSequencePresent(fcf)) { IgnoreError(builder.AppendUint8(0)); // Seq number } @@ -238,9 +239,20 @@ void Frame::InitMacHeader(Type aType, mLength += GetFcsSize(); } -uint16_t Frame::GetFrameControlField(void) const { return LittleEndian::ReadUint16(mPsdu); } - -void Frame::SetFrameControlField(uint16_t aFcf) { LittleEndian::WriteUint16(aFcf, mPsdu); } +void Frame::SetFrameControlField(uint16_t aFcf) +{ +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + if (IsShortFcf(aFcf)) + { + OT_ASSERT((aFcf >> 8) == 0); + mPsdu[0] = static_cast(aFcf); + } + else +#endif + { + LittleEndian::WriteUint16(aFcf, mPsdu); + } +} Error Frame::ValidatePsdu(void) const { @@ -254,53 +266,115 @@ exit: return error; } +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +bool Frame::IsWakeupFrame(void) const +{ + const uint16_t fcf = GetFrameControlField(); + bool result = false; + uint8_t keyIdMode; + uint8_t firstIeIndex; + Address srcAddress; + + // Wake-up frame is a Multipurpose frame without Ack Request... + VerifyOrExit((fcf & kFcfFrameTypeMask) == kTypeMultipurpose); + VerifyOrExit((fcf & kMpFcfAckRequest) == 0); + + // ... with extended source address... + SuccessOrExit(GetSrcAddr(srcAddress)); + VerifyOrExit(srcAddress.IsExtended()); + + // ... secured with Key Id Mode 2... + SuccessOrExit(GetKeyIdMode(keyIdMode)); + VerifyOrExit(keyIdMode == kKeyIdMode2); + + // ... that has Rendezvous Time IE and Connection IE... + VerifyOrExit(GetRendezvousTimeIe() != nullptr); + VerifyOrExit(GetConnectionIe() != nullptr); + + // ... but no other IEs nor payload. + firstIeIndex = FindHeaderIeIndex(); + VerifyOrExit(mPsdu + firstIeIndex + sizeof(HeaderIe) + RendezvousTimeIe::kIeContentSize + sizeof(HeaderIe) + + ConnectionIe::kIeContentSize == + GetFooter()); + + result = true; + +exit: + return result; +} +#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + void Frame::SetAckRequest(bool aAckRequest) { + uint16_t fcf = GetFrameControlField(); + uint16_t mask = Select(fcf); + if (aAckRequest) { - mPsdu[0] |= kFcfAckRequest; + fcf |= mask; } else { - mPsdu[0] &= ~kFcfAckRequest; + fcf &= ~mask; } + + SetFrameControlField(fcf); } void Frame::SetFramePending(bool aFramePending) { + uint16_t fcf = GetFrameControlField(); + uint16_t mask = Select(fcf); + if (aFramePending) { - mPsdu[0] |= kFcfFramePending; + fcf |= mask; } else { - mPsdu[0] &= ~kFcfFramePending; + fcf &= ~mask; } + + SetFrameControlField(fcf); } void Frame::SetIePresent(bool aIePresent) { - uint16_t fcf = GetFrameControlField(); + uint16_t fcf = GetFrameControlField(); + uint16_t mask = Select(fcf); if (aIePresent) { - fcf |= kFcfIePresent; + fcf |= mask; } else { - fcf &= ~kFcfIePresent; + fcf &= ~mask; } SetFrameControlField(fcf); } +uint8_t Frame::SkipSequenceIndex(void) const +{ + uint16_t fcf = GetFrameControlField(); + uint8_t index = GetFcfSize(fcf); + + if (IsSequencePresent(fcf)) + { + index += kDsnSize; + } + + return index; +} + uint8_t Frame::FindDstPanIdIndex(void) const { uint8_t index; VerifyOrExit(IsDstPanIdPresent(), index = kInvalidIndex); - index = kFcfSize + GetSeqNumSize(); + index = SkipSequenceIndex(); exit: return index; @@ -308,9 +382,16 @@ exit: bool Frame::IsDstPanIdPresent(uint16_t aFcf) { - bool present = true; + bool present; - if (IsVersion2015(aFcf)) +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + if (IsMultipurpose(aFcf)) + { + present = (aFcf & kMpFcfPanidPresent) != 0; + } + else +#endif + if (IsVersion2015(aFcf)) { // Original table at `InitMacHeader()` // @@ -348,6 +429,7 @@ bool Frame::IsDstPanIdPresent(uint16_t aFcf) present = false; break; default: + present = true; break; } } @@ -382,19 +464,18 @@ void Frame::SetDstPanId(PanId aPanId) uint8_t Frame::GetSequence(void) const { OT_ASSERT(IsSequencePresent()); - return GetPsdu()[kSequenceIndex]; + + return GetPsdu()[GetFcfSize(GetFrameControlField())]; } void Frame::SetSequence(uint8_t aSequence) { OT_ASSERT(IsSequencePresent()); - GetPsdu()[kSequenceIndex] = aSequence; + + GetPsdu()[GetFcfSize(GetFrameControlField())] = aSequence; } -uint8_t Frame::FindDstAddrIndex(void) const -{ - return kFcfSize + GetSeqNumSize() + (IsDstPanIdPresent() ? sizeof(PanId) : 0); -} +uint8_t Frame::FindDstAddrIndex(void) const { return SkipSequenceIndex() + (IsDstPanIdPresent() ? sizeof(PanId) : 0); } Error Frame::GetDstAddr(Address &aAddress) const { @@ -403,13 +484,13 @@ Error Frame::GetDstAddr(Address &aAddress) const VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - switch (GetFrameControlField() & kFcfDstAddrMask) + switch (GetFcfDstAddr(GetFrameControlField())) { - case kFcfDstAddrShort: + case kFcfAddrShort: aAddress.SetShort(LittleEndian::ReadUint16(&mPsdu[index])); break; - case kFcfDstAddrExt: + case kFcfAddrExt: aAddress.SetExtended(&mPsdu[index], ExtAddress::kReverseByteOrder); break; @@ -424,7 +505,7 @@ exit: void Frame::SetDstAddr(ShortAddress aShortAddress) { - OT_ASSERT((GetFrameControlField() & kFcfDstAddrMask) == kFcfDstAddrShort); + OT_ASSERT(GetFcfDstAddr(GetFrameControlField()) == kFcfAddrShort); LittleEndian::WriteUint16(aShortAddress, &mPsdu[FindDstAddrIndex()]); } @@ -432,7 +513,7 @@ void Frame::SetDstAddr(const ExtAddress &aExtAddress) { uint8_t index = FindDstAddrIndex(); - OT_ASSERT((GetFrameControlField() & kFcfDstAddrMask) == kFcfDstAddrExt); + OT_ASSERT(GetFcfDstAddr(GetFrameControlField()) == kFcfAddrExt); OT_ASSERT(index != kInvalidIndex); aExtAddress.CopyTo(&mPsdu[index], ExtAddress::kReverseByteOrder); @@ -458,25 +539,25 @@ void Frame::SetDstAddr(const Address &aAddress) uint8_t Frame::FindSrcPanIdIndex(void) const { - uint8_t index = 0; - uint16_t fcf = GetFrameControlField(); + uint16_t fcf = GetFrameControlField(); + uint8_t index; - VerifyOrExit(IsSrcPanIdPresent(), index = kInvalidIndex); + VerifyOrExit(IsSrcPanIdPresent(fcf), index = kInvalidIndex); - index += kFcfSize + GetSeqNumSize(); + index = SkipSequenceIndex(); if (IsDstPanIdPresent(fcf)) { index += sizeof(PanId); } - switch (fcf & kFcfDstAddrMask) + switch (GetFcfDstAddr(fcf)) { - case kFcfDstAddrShort: + case kFcfAddrShort: index += sizeof(ShortAddress); break; - case kFcfDstAddrExt: + case kFcfAddrExt: index += sizeof(ExtAddress); break; } @@ -487,40 +568,52 @@ exit: bool Frame::IsSrcPanIdPresent(uint16_t aFcf) { - bool present = IsSrcAddrPresent(aFcf) && ((aFcf & kFcfPanidCompression) == 0); + bool present; - // Special case for a IEEE 802.15.4-2015 frame: When both - // addresses are extended, then the source PAN iD is not present - // independent of PAN ID Compression. In this case, if the PAN ID - // compression is set, it indicates that no PAN ID is in the - // frame, while if the PAN ID Compression is zero, it indicates - // the presence of the destination PAN ID in the frame. - // - // +----+--------------+--------------+--------------++--------------+ - // | No | Dest Addr | Src Addr | PAN ID Comp || Src PAN ID | - // +----+--------------+--------------+--------------++--------------+ - // | 1 | Not Present | Not Present | 0 || Not Present | - // | 2 | Not Present | Not Present | 1 || Not Present | - // | 3 | Present | Not Present | 0 || Not Present | - // | 4 | Present | Not Present | 1 || Not Present | - // | 5 | Not Present | Present | 0 || Present | - // | 6 | Not Present | Present | 1 || Not Present | - // +----+--------------+--------------+--------------++--------------+ - // | 7 | Extended | Extended | 0 || Not Present | - // | 8 | Extended | Extended | 1 || Not Present | - // |----+--------------+--------------+--------------++--------------+ - // | 9 | Short | Short | 0 || Present | - // | 10 | Short | Extended | 0 || Present | - // | 11 | Extended | Short | 0 || Present | - // | 12 | Short | Extended | 1 || Not Present | - // | 13 | Extended | Short | 1 || Not Present | - // | 14 | Short | Short | 1 || Not Present | - // +----+--------------+--------------+--------------++--------------+ - - if (IsVersion2015(aFcf) && ((aFcf & (kFcfDstAddrMask | kFcfSrcAddrMask)) == (kFcfDstAddrExt | kFcfSrcAddrExt))) +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + if (IsMultipurpose(aFcf)) { + // Sources PAN ID is implicitly equal to Destination PAN ID in Multipurpose frames present = false; } + else +#endif + if (IsVersion2015(aFcf) && ((aFcf & (kFcfDstAddrMask | kFcfSrcAddrMask)) == (kFcfDstAddrExt | kFcfSrcAddrExt))) + { + // Special case for a IEEE 802.15.4-2015 frame: When both + // addresses are extended, then the source PAN iD is not present + // independent of PAN ID Compression. In this case, if the PAN ID + // compression is set, it indicates that no PAN ID is in the + // frame, while if the PAN ID Compression is zero, it indicates + // the presence of the destination PAN ID in the frame. + // + // +----+--------------+--------------+--------------++--------------+ + // | No | Dest Addr | Src Addr | PAN ID Comp || Src PAN ID | + // +----+--------------+--------------+--------------++--------------+ + // | 1 | Not Present | Not Present | 0 || Not Present | + // | 2 | Not Present | Not Present | 1 || Not Present | + // | 3 | Present | Not Present | 0 || Not Present | + // | 4 | Present | Not Present | 1 || Not Present | + // | 5 | Not Present | Present | 0 || Present | + // | 6 | Not Present | Present | 1 || Not Present | + // +----+--------------+--------------+--------------++--------------+ + // | 7 | Extended | Extended | 0 || Not Present | + // | 8 | Extended | Extended | 1 || Not Present | + // |----+--------------+--------------+--------------++--------------+ + // | 9 | Short | Short | 0 || Present | + // | 10 | Short | Extended | 0 || Present | + // | 11 | Extended | Short | 0 || Present | + // | 12 | Short | Extended | 1 || Not Present | + // | 13 | Extended | Short | 1 || Not Present | + // | 14 | Short | Short | 1 || Not Present | + // +----+--------------+--------------+--------------++--------------+ + + present = false; + } + else + { + present = IsSrcAddrPresent(aFcf) && ((aFcf & kFcfPanidCompression) == 0); + } return present; } @@ -551,23 +644,21 @@ exit: uint8_t Frame::FindSrcAddrIndex(void) const { - uint8_t index = 0; uint16_t fcf = GetFrameControlField(); - - index += kFcfSize + GetSeqNumSize(); + uint8_t index = SkipSequenceIndex(); if (IsDstPanIdPresent(fcf)) { index += sizeof(PanId); } - switch (fcf & kFcfDstAddrMask) + switch (GetFcfDstAddr(fcf)) { - case kFcfDstAddrShort: + case kFcfAddrShort: index += sizeof(ShortAddress); break; - case kFcfDstAddrExt: + case kFcfAddrExt: index += sizeof(ExtAddress); break; } @@ -588,17 +679,17 @@ Error Frame::GetSrcAddr(Address &aAddress) const VerifyOrExit(index != kInvalidIndex, error = kErrorParse); - switch (fcf & kFcfSrcAddrMask) + switch (GetFcfSrcAddr(fcf)) { - case kFcfSrcAddrShort: + case kFcfAddrShort: aAddress.SetShort(LittleEndian::ReadUint16(&mPsdu[index])); break; - case kFcfSrcAddrExt: + case kFcfAddrExt: aAddress.SetExtended(&mPsdu[index], ExtAddress::kReverseByteOrder); break; - case kFcfSrcAddrNone: + case kFcfAddrNone: aAddress.SetNone(); break; @@ -616,7 +707,7 @@ void Frame::SetSrcAddr(ShortAddress aShortAddress) { uint8_t index = FindSrcAddrIndex(); - OT_ASSERT((GetFrameControlField() & kFcfSrcAddrMask) == kFcfSrcAddrShort); + OT_ASSERT(GetFcfSrcAddr(GetFrameControlField()) == kFcfAddrShort); OT_ASSERT(index != kInvalidIndex); LittleEndian::WriteUint16(aShortAddress, &mPsdu[index]); @@ -626,7 +717,7 @@ void Frame::SetSrcAddr(const ExtAddress &aExtAddress) { uint8_t index = FindSrcAddrIndex(); - OT_ASSERT((GetFrameControlField() & kFcfSrcAddrMask) == kFcfSrcAddrExt); + OT_ASSERT(GetFcfSrcAddr(GetFrameControlField()) == kFcfAddrExt); OT_ASSERT(index != kInvalidIndex); aExtAddress.CopyTo(&mPsdu[index], ExtAddress::kReverseByteOrder); @@ -964,8 +1055,6 @@ uint8_t Frame::SkipAddrFieldIndex(void) const VerifyOrExit(kFcfSize + GetFcsSize() <= mLength, index = kInvalidIndex); - VerifyOrExit(!IsSequencePresent() || kFcfSize + kDsnSize + GetFcsSize() <= mLength, index = kInvalidIndex); - index = CalculateAddrFieldSize(GetFrameControlField()); exit: @@ -974,7 +1063,7 @@ exit: uint8_t Frame::CalculateAddrFieldSize(uint16_t aFcf) { - uint8_t size = kFcfSize + GetSeqNumSize(aFcf); + uint8_t size = GetFcfSize(aFcf) + (IsSequencePresent(aFcf) ? kDsnSize : 0); // This static method calculates the size (number of bytes) of // Address header field for a given Frame Control `aFcf` value. @@ -988,16 +1077,16 @@ uint8_t Frame::CalculateAddrFieldSize(uint16_t aFcf) size += sizeof(PanId); } - switch (aFcf & kFcfDstAddrMask) + switch (GetFcfDstAddr(aFcf)) { - case kFcfDstAddrNone: + case kFcfAddrNone: break; - case kFcfDstAddrShort: + case kFcfAddrShort: size += sizeof(ShortAddress); break; - case kFcfDstAddrExt: + case kFcfAddrExt: size += sizeof(ExtAddress); break; @@ -1010,16 +1099,16 @@ uint8_t Frame::CalculateAddrFieldSize(uint16_t aFcf) size += sizeof(PanId); } - switch (aFcf & kFcfSrcAddrMask) + switch (GetFcfSrcAddr(aFcf)) { - case kFcfSrcAddrNone: + case kFcfAddrNone: break; - case kFcfSrcAddrShort: + case kFcfAddrShort: size += sizeof(ShortAddress); break; - case kFcfSrcAddrExt: + case kFcfAddrExt: size += sizeof(ExtAddress); break; @@ -1151,6 +1240,16 @@ template <> void Frame::InitIeContentAt(uint8_t &aIndex) } #endif +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +template <> void Frame::InitIeContentAt(uint8_t &aIndex) { aIndex += sizeof(RendezvousTimeIe); } + +template <> void Frame::InitIeContentAt(uint8_t &aIndex) +{ + reinterpret_cast(mPsdu + aIndex)->Init(); + aIndex += sizeof(ConnectionIe); +} +#endif + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE template <> void Frame::InitIeContentAt(uint8_t &aIndex) { aIndex += sizeof(CslIe); } #endif @@ -1186,7 +1285,8 @@ exit: return header; } -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE || \ + OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE const uint8_t *Frame::GetThreadIe(uint8_t aSubType) const { uint8_t index = FindHeaderIeIndex(); @@ -1218,7 +1318,8 @@ const uint8_t *Frame::GetThreadIe(uint8_t aSubType) const exit: return header; } -#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE || + // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT @@ -1342,6 +1443,10 @@ uint8_t Frame::GetFcsSize(void) const { return Trel::Link::kFcsSize; } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE template Error Frame::AppendHeaderIeAt(uint8_t &aIndex); #endif +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +template Error Frame::AppendHeaderIeAt(uint8_t &aIndex); +template Error Frame::AppendHeaderIeAt(uint8_t &aIndex); +#endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE template Error Frame::AppendHeaderIeAt(uint8_t &aIndex); #endif @@ -1433,7 +1538,7 @@ void TxFrame::GenerateImmAck(const RxFrame &aFrame, bool aIsFramePending) } LittleEndian::WriteUint16(fcf, mPsdu); - mPsdu[kSequenceIndex] = aFrame.GetSequence(); + mPsdu[kFcfSize] = aFrame.GetSequence(); mLength = kImmAckLength; } @@ -1518,6 +1623,62 @@ exit: } #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +Error TxFrame::GenerateWakeupFrame(PanId aPanId, const Address &aDest, const Address &aSource) +{ + Error error = kErrorNone; + uint16_t fcf = kTypeMultipurpose | kMpFcfLongFrame | kMpFcfPanidPresent | kMpFcfSecurityEnabled | + kMpFcfSequenceSuppression | kMpFcfIePresent; + uint8_t index = 0; + + switch (aDest.GetType()) + { + case Address::Type::kTypeShort: + fcf |= kMpFcfDstAddrShort; + break; + case Address::Type::kTypeExtended: + fcf |= kMpFcfDstAddrExt; + break; + default: + ExitNow(error = kErrorInvalidArgs); + break; + } + + switch (aSource.GetType()) + { + case Address::Type::kTypeShort: + fcf |= kMpFcfSrcAddrShort; + break; + case Address::Type::kTypeExtended: + fcf |= kMpFcfSrcAddrExt; + break; + default: + ExitNow(error = kErrorInvalidArgs); + break; + } + + mLength = CalculateAddrFieldSize(fcf); + + OT_ASSERT(mLength != kInvalidSize); + + SetFrameControlField(fcf); + SetDstPanId(aPanId); + SetDstAddr(aDest); + SetSrcAddr(aSource); + + mPsdu[mLength] = kKeyIdMode2 | kSecurityEncMic32; + mLength += CalculateSecurityHeaderSize(kKeyIdMode2 | kSecurityEncMic32); + mLength += CalculateMicSize(kKeyIdMode2 | kSecurityEncMic32); + mLength += GetFcsSize(); + + SuccessOrExit(error = AppendHeaderIeAt(index)); + SuccessOrExit(error = AppendHeaderIeAt(index)); + +exit: + return error; +} +#endif + Error RxFrame::ProcessReceiveAesCcm(const ExtAddress &aExtAddress, const KeyMaterial &aMacKey) { #if OPENTHREAD_RADIO @@ -1575,16 +1736,20 @@ Frame::InfoString Frame::ToInfoString(void) const InfoString string; uint8_t commandId, type; Address src, dst; + uint32_t frameCounter; + bool sequencePresent; - if (IsSequencePresent()) + string.Append("len:%d", mLength); + + sequencePresent = IsSequencePresent(); + + if (sequencePresent) { - string.Append("len:%d, seqnum:%d, type:", mLength, GetSequence()); - } - else - { - string.Append("len:%d, type:", mLength); + string.Append(", seqnum:%d", GetSequence()); } + string.Append(", type:"); + type = GetType(); switch (type) @@ -1624,6 +1789,12 @@ Frame::InfoString Frame::ToInfoString(void) const break; +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + case kTypeMultipurpose: + string.Append("MP"); + break; +#endif + default: string.Append("%d", type); break; @@ -1635,6 +1806,11 @@ Frame::InfoString Frame::ToInfoString(void) const string.Append(", src:%s, dst:%s, sec:%s, ackreq:%s", src.ToString().AsCString(), dst.ToString().AsCString(), ToYesNo(GetSecurityEnabled()), ToYesNo(GetAckRequest())); + if (!sequencePresent && GetFrameCounter(frameCounter) == kErrorNone) + { + string.Append(", fc:%lu", ToUlong(frameCounter)); + } + #if OPENTHREAD_CONFIG_MULTI_RADIO string.Append(", radio:%s", RadioTypeToString(GetRadioType())); #endif diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index d51e45caa..575a0a0e7 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -69,10 +69,11 @@ public: */ enum Type : uint16_t { - kTypeBeacon = 0, ///< Beacon Frame Type. - kTypeData = 1, ///< Data Frame Type. - kTypeAck = 2, ///< Ack Frame Type. - kTypeMacCmd = 3, ///< MAC Command Frame Type. + kTypeBeacon = 0, ///< Beacon Frame Type. + kTypeData = 1, ///< Data Frame Type. + kTypeAck = 2, ///< Ack Frame Type. + kTypeMacCmd = 3, ///< MAC Command Frame Type. + kTypeMultipurpose = 5, ///< Multipurpose Frame Type. }; /** @@ -215,6 +216,59 @@ public: */ bool IsAck(void) const { return GetType() == kTypeAck; } +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** + * This method returns whether the frame is an IEEE 802.15.4 Wake-up frame. + * + * @retval TRUE If this is a Wake-up frame. + * @retval FALSE If this is not a Wake-up frame. + * + */ + bool IsWakeupFrame(void) const; + + /** + * This method returns the Rendezvous Time IE of a wake-up frame. + * + * @returns Pointer to the Rendezvous Time IE. + * + */ + RendezvousTimeIe *GetRendezvousTimeIe(void) { return AsNonConst(AsConst(this)->GetRendezvousTimeIe()); } + + /** + * This method returns the Rendezvous Time IE of a wake-up frame. + * + * @returns Const pointer to the Rendezvous Time IE. + * + */ + const RendezvousTimeIe *GetRendezvousTimeIe(void) const + { + const uint8_t *ie = GetHeaderIe(RendezvousTimeIe::kHeaderIeId); + + return (ie != nullptr) ? reinterpret_cast(ie + sizeof(HeaderIe)) : nullptr; + } + + /** + * This method returns the Connection IE of a wake-up frame. + * + * @returns Pointer to the Connection IE. + * + */ + ConnectionIe *GetConnectionIe(void) { return AsNonConst(AsConst(this)->GetConnectionIe()); } + + /** + * This method returns the Connection IE of a wake-up frame. + * + * @returns Const pointer to the Connection IE. + * + */ + const ConnectionIe *GetConnectionIe(void) const + { + const uint8_t *ie = GetThreadIe(ConnectionIe::kThreadIeSubtype); + + return (ie != nullptr) ? reinterpret_cast(ie + sizeof(HeaderIe)) : nullptr; + } +#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + /** * Returns the IEEE 802.15.4 Frame Version. * @@ -238,7 +292,7 @@ public: * @retval FALSE If security is not enabled. * */ - bool GetSecurityEnabled(void) const { return (GetPsdu()[0] & kFcfSecurityEnabled) != 0; } + bool GetSecurityEnabled(void) const { return IsSecurityEnabled(GetFrameControlField()); } /** * Indicates whether or not the Frame Pending bit is set. @@ -247,11 +301,13 @@ public: * @retval FALSE If the Frame Pending bit is not set. * */ - bool GetFramePending(void) const { return (GetPsdu()[0] & kFcfFramePending) != 0; } + bool GetFramePending(void) const { return IsFramePending(GetFrameControlField()); } /** * Sets the Frame Pending bit. * + * @note This method must not be called on a Multipurpose frame with short Frame Control field. + * * @param[in] aFramePending The Frame Pending bit. * */ @@ -264,11 +320,13 @@ public: * @retval FALSE If the Ack Request bit is not set. * */ - bool GetAckRequest(void) const { return (GetPsdu()[0] & kFcfAckRequest) != 0; } + bool GetAckRequest(void) const { return IsAckRequest(GetFrameControlField()); } /** * Sets the Ack Request bit. * + * @note This method must not be called on a Multipurpose frame with short Frame Control field. + * * @param[in] aAckRequest The Ack Request bit. * */ @@ -277,6 +335,8 @@ public: /** * Indicates whether or not the PanId Compression bit is set. * + * @note This method must not be called on a Multipurpose frame, which lacks this flag. + * * @retval TRUE If the PanId Compression bit is set. * @retval FALSE If the PanId Compression bit is not set. * @@ -290,11 +350,13 @@ public: * @retval FALSE If no IE present. * */ - bool IsIePresent(void) const { return (GetFrameControlField() & kFcfIePresent) != 0; } + bool IsIePresent(void) const { return IsIePresent(GetFrameControlField()); } /** * Sets the IE Present bit. * + * @note This method must not be called on a Multipurpose frame with short Frame Control field. + * * @param[in] aIePresent The IE Present bit. * */ @@ -322,16 +384,7 @@ public: * @returns TRUE if the Sequence Number is present, FALSE otherwise. * */ - uint8_t IsSequencePresent(void) const { return !IsSequenceSuppressed(GetFrameControlField()); } - - /** - * Get the size of the sequence number. - * - * @retval 0 The size of sequence number is 0, indicating it's not present. - * @retval 1 The size of sequence number is 1, indicating it's present. - * - */ - uint8_t GetSeqNumSize(void) const { return GetSeqNumSize(GetFrameControlField()); } + uint8_t IsSequencePresent(void) const { return IsSequencePresent(GetFrameControlField()); } /** * Indicates whether or not the Destination PAN ID is present. @@ -919,30 +972,71 @@ public: * @returns The Frame Control field. * */ - uint16_t GetFrameControlField(void) const; + uint16_t GetFrameControlField(void) const + { + uint16_t fcf = mPsdu[0]; + +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + if (!IsShortFcf(fcf)) +#endif + { + fcf |= (mPsdu[1] << 8); + } + + return fcf; + } protected: + static constexpr uint8_t kShortFcfSize = sizeof(uint8_t); static constexpr uint8_t kSecurityControlSize = sizeof(uint8_t); static constexpr uint8_t kFrameCounterSize = sizeof(uint32_t); static constexpr uint8_t kCommandIdSize = sizeof(uint8_t); static constexpr uint8_t kKeyIndexSize = sizeof(uint8_t); - static constexpr uint16_t kFcfFrameTypeMask = 7 << 0; - static constexpr uint16_t kFcfSecurityEnabled = 1 << 3; - static constexpr uint16_t kFcfFramePending = 1 << 4; - static constexpr uint16_t kFcfAckRequest = 1 << 5; - static constexpr uint16_t kFcfPanidCompression = 1 << 6; - static constexpr uint16_t kFcfSequenceSupression = 1 << 8; - static constexpr uint16_t kFcfIePresent = 1 << 9; - static constexpr uint16_t kFcfDstAddrNone = 0 << 10; - static constexpr uint16_t kFcfDstAddrShort = 2 << 10; - static constexpr uint16_t kFcfDstAddrExt = 3 << 10; - static constexpr uint16_t kFcfDstAddrMask = 3 << 10; - static constexpr uint16_t kFcfFrameVersionMask = 3 << 12; - static constexpr uint16_t kFcfSrcAddrNone = 0 << 14; - static constexpr uint16_t kFcfSrcAddrShort = 2 << 14; - static constexpr uint16_t kFcfSrcAddrExt = 3 << 14; - static constexpr uint16_t kFcfSrcAddrMask = 3 << 14; + static constexpr uint16_t kFcfFrameTypeMask = 7 << 0; + + static constexpr uint16_t kFcfAddrNone = 0; + static constexpr uint16_t kFcfAddrShort = 2; + static constexpr uint16_t kFcfAddrExt = 3; + static constexpr uint16_t kFcfAddrMask = 3; + + // Frame Control field format for general MAC frame + static constexpr uint16_t kFcfSecurityEnabled = 1 << 3; + static constexpr uint16_t kFcfFramePending = 1 << 4; + static constexpr uint16_t kFcfAckRequest = 1 << 5; + static constexpr uint16_t kFcfPanidCompression = 1 << 6; + static constexpr uint16_t kFcfSequenceSuppression = 1 << 8; + static constexpr uint16_t kFcfIePresent = 1 << 9; + static constexpr uint16_t kFcfDstAddrShift = 10; + static constexpr uint16_t kFcfDstAddrNone = kFcfAddrNone << kFcfDstAddrShift; + static constexpr uint16_t kFcfDstAddrShort = kFcfAddrShort << kFcfDstAddrShift; + static constexpr uint16_t kFcfDstAddrExt = kFcfAddrExt << kFcfDstAddrShift; + static constexpr uint16_t kFcfDstAddrMask = kFcfAddrMask << kFcfDstAddrShift; + static constexpr uint16_t kFcfFrameVersionMask = 3 << 12; + static constexpr uint16_t kFcfSrcAddrShift = 14; + static constexpr uint16_t kFcfSrcAddrNone = kFcfAddrNone << kFcfSrcAddrShift; + static constexpr uint16_t kFcfSrcAddrShort = kFcfAddrShort << kFcfSrcAddrShift; + static constexpr uint16_t kFcfSrcAddrExt = kFcfAddrExt << kFcfSrcAddrShift; + static constexpr uint16_t kFcfSrcAddrMask = kFcfAddrMask << kFcfSrcAddrShift; + + // Frame Control field format for MAC Multipurpose frame + static constexpr uint16_t kMpFcfLongFrame = 1 << 3; + static constexpr uint16_t kMpFcfDstAddrShift = 4; + static constexpr uint16_t kMpFcfDstAddrNone = kFcfAddrNone << kMpFcfDstAddrShift; + static constexpr uint16_t kMpFcfDstAddrShort = kFcfAddrShort << kMpFcfDstAddrShift; + static constexpr uint16_t kMpFcfDstAddrExt = kFcfAddrExt << kMpFcfDstAddrShift; + static constexpr uint16_t kMpFcfDstAddrMask = kFcfAddrMask << kMpFcfDstAddrShift; + static constexpr uint16_t kMpFcfSrcAddrShift = 6; + static constexpr uint16_t kMpFcfSrcAddrNone = kFcfAddrNone << kMpFcfSrcAddrShift; + static constexpr uint16_t kMpFcfSrcAddrShort = kFcfAddrShort << kMpFcfSrcAddrShift; + static constexpr uint16_t kMpFcfSrcAddrExt = kFcfAddrExt << kMpFcfSrcAddrShift; + static constexpr uint16_t kMpFcfSrcAddrMask = kFcfAddrMask << kMpFcfSrcAddrShift; + static constexpr uint16_t kMpFcfPanidPresent = 1 << 8; + static constexpr uint16_t kMpFcfSecurityEnabled = 1 << 9; + static constexpr uint16_t kMpFcfSequenceSuppression = 1 << 10; + static constexpr uint16_t kMpFcfFramePending = 1 << 11; + static constexpr uint16_t kMpFcfAckRequest = 1 << 14; + static constexpr uint16_t kMpFcfIePresent = 1 << 15; static constexpr uint8_t kSecLevelMask = 7 << 0; static constexpr uint8_t kKeyIdModeMask = 3 << 3; @@ -958,12 +1052,12 @@ protected: static constexpr uint8_t kKeySourceSizeMode2 = 4; static constexpr uint8_t kKeySourceSizeMode3 = 8; - static constexpr uint8_t kInvalidIndex = 0xff; - static constexpr uint8_t kInvalidSize = kInvalidIndex; - static constexpr uint8_t kMaxPsduSize = kInvalidSize - 1; - static constexpr uint8_t kSequenceIndex = kFcfSize; + static constexpr uint8_t kInvalidIndex = 0xff; + static constexpr uint8_t kInvalidSize = kInvalidIndex; + static constexpr uint8_t kMaxPsduSize = kInvalidSize - 1; void SetFrameControlField(uint16_t aFcf); + uint8_t SkipSequenceIndex(void) const; uint8_t FindDstPanIdIndex(void) const; uint8_t FindDstAddrIndex(void) const; uint8_t FindSrcPanIdIndex(void) const; @@ -981,16 +1075,55 @@ protected: static uint8_t GetKeySourceLength(uint8_t aKeyIdMode); - static bool IsDstAddrPresent(uint16_t aFcf) { return (aFcf & kFcfDstAddrMask) != kFcfDstAddrNone; } - static bool IsDstPanIdPresent(uint16_t aFcf); - static bool IsSequenceSuppressed(uint16_t aFcf) - { - return (aFcf & (kFcfSequenceSupression | kFcfFrameVersionMask)) == (kFcfSequenceSupression | kVersion2015); - } - static uint8_t GetSeqNumSize(uint16_t aFcf) { return !IsSequenceSuppressed(aFcf) ? kDsnSize : 0; } +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + static uint8_t GetFcfSize(uint16_t aFcf) { return IsShortFcf(aFcf) ? kShortFcfSize : kFcfSize; } +#else + // clang-format off + static uint8_t GetFcfSize(uint16_t /* aFcf */) { return kFcfSize; } + // clang-format on +#endif - static bool IsSrcAddrPresent(uint16_t aFcf) { return (aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone; } +#if OPENTHREAD_CONFIG_MAC_MULTIPURPOSE_FRAME + template static uint16_t Select(uint16_t aFcf) + { + return IsMultipurpose(aFcf) ? kMpValue : kValue; + } +#else + template static uint16_t Select(uint16_t /* aFcf */) { return kValue; } +#endif + + template static uint16_t MaskFcf(uint16_t aFcf) + { + return aFcf & Select(aFcf); + } + + static uint16_t GetFcfDstAddr(uint16_t aFcf) + { + return MaskFcf(aFcf) >> Select(aFcf); + } + + static uint16_t GetFcfSrcAddr(uint16_t aFcf) + { + return MaskFcf(aFcf) >> Select(aFcf); + } + + static bool IsMultipurpose(uint16_t aFcf) { return (aFcf & kFcfFrameTypeMask) == kTypeMultipurpose; } + static bool IsShortFcf(uint16_t aFcf) + { + return (aFcf & (kFcfFrameTypeMask | kMpFcfLongFrame)) == (kTypeMultipurpose | 0); + } + static bool IsSequencePresent(uint16_t aFcf) + { + return !MaskFcf(aFcf); + } + static bool IsDstAddrPresent(uint16_t aFcf) { return MaskFcf(aFcf); } + static bool IsDstPanIdPresent(uint16_t aFcf); + static bool IsSrcAddrPresent(uint16_t aFcf) { return MaskFcf(aFcf); } static bool IsSrcPanIdPresent(uint16_t aFcf); + static bool IsSecurityEnabled(uint16_t aFcf) { return MaskFcf(aFcf); } + static bool IsFramePending(uint16_t aFcf) { return MaskFcf(aFcf); } + static bool IsIePresent(uint16_t aFcf) { return MaskFcf(aFcf); } + static bool IsAckRequest(uint16_t aFcf) { return MaskFcf(aFcf); } static bool IsVersion2015(uint16_t aFcf) { return (aFcf & kFcfFrameVersionMask) == kVersion2015; } static uint8_t CalculateAddrFieldSize(uint16_t aFcf); @@ -1375,6 +1508,21 @@ public: */ Error GenerateEnhAck(const RxFrame &aRxFrame, bool aIsFramePending, const uint8_t *aIeData, uint8_t aIeLength); +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + /** + * Generate IEE 802.15.4 Wake-up frame. + * + * @param[in] aPanId A destination PAN identifier + * @param[in] aDest A destination address (short or extended) + * @param[in] aSource A source address (short or extended) + * + * @retval kErrorNone Successfully generated Wake-up frame. + * @retval kErrorInvalidArgs @p aDest or @p aSource have incorrect type. + * + */ + Error GenerateWakeupFrame(PanId aPanId, const Address &aDest, const Address &aSource); +#endif + #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 /** * Set TX delay field for the frame. diff --git a/src/core/mac/mac_header_ie.hpp b/src/core/mac/mac_header_ie.hpp index 5e25dc9cd..41f0845bb 100644 --- a/src/core/mac/mac_header_ie.hpp +++ b/src/core/mac/mac_header_ie.hpp @@ -193,8 +193,6 @@ public: static constexpr uint8_t kIeContentSize = 0; }; -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || \ - OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE /** * Implements vendor specific Header IE generation and parsing. * @@ -307,7 +305,6 @@ private: } OT_TOOL_PACKED_END; #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE class ThreadIe { public: @@ -316,10 +313,118 @@ public: static constexpr uint32_t kVendorOuiThreadCompanyId = 0xeab89b; static constexpr uint8_t kEnhAckProbingIe = 0x00; }; -#endif -#endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || - // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +/** + * This class implements Rendezvous Time IE data structure. + * + * IEEE 802.15.4 Rendezvous Time IE contains two fields, Rendezvous Time and + * Wake-up Interval, but the Wake-up Interval is not used in Thread, so it is + * not included in this class. + * + */ +OT_TOOL_PACKED_BEGIN +class RendezvousTimeIe +{ +public: + static constexpr uint8_t kHeaderIeId = 0x1d; + static constexpr uint8_t kIeContentSize = sizeof(uint16_t); + + /** + * This method returns the Rendezvous Time. + * + * @returns the Rendezvous Time in the units of 10 symbols. + * + */ + uint16_t GetRendezvousTime(void) const { return LittleEndian::HostSwap16(mRendezvousTime); } + + /** + * This method sets the Rendezvous Time. + * + * @param[in] aRendezvousTime The Rendezvous Time in the units of 10 symbols. + * + */ + void SetRendezvousTime(uint16_t aRendezvousTime) { mRendezvousTime = LittleEndian::HostSwap16(aRendezvousTime); } + +private: + uint16_t mRendezvousTime; +} OT_TOOL_PACKED_END; + +/** + * Implements Connection IE data structure. + * + */ +OT_TOOL_PACKED_BEGIN +class ConnectionIe : public VendorIeHeader +{ +public: + static constexpr uint8_t kHeaderIeId = ThreadIe::kHeaderIeId; + static constexpr uint8_t kIeContentSize = ThreadIe::kIeContentSize + sizeof(uint8_t); + static constexpr uint8_t kThreadIeSubtype = 0x01; + + /** + * Initializes the Connection IE. + * + */ + void Init(void) + { + SetVendorOui(ThreadIe::kVendorOuiThreadCompanyId); + SetSubType(kThreadIeSubtype); + mConnectionWindow = 0; + } + + /** + * Returns the Retry Interval. + * + * The Retry Interval defines how frequently the Wake-up End Device is + * supposed to retry sending the Parent Request to the Wake-up Coordinator. + * + * @returns the Retry Interval in the units of Wake-up Intervals (7.5ms by default). + * + */ + uint8_t GetRetryInterval(void) const { return (mConnectionWindow & kRetryIntervalMask) >> kRetryIntervalOffset; } + + /** + * Sets the Retry Interval. + * + * @param[in] aRetryInterval The Retry Interval in the units of Wake-up Intervals (7.5ms by default). + * + */ + void SetRetryInterval(uint8_t aRetryInterval) + { + mConnectionWindow = (aRetryInterval << kRetryIntervalOffset) | (mConnectionWindow & ~kRetryIntervalMask); + } + + /** + * Returns the Retry Count. + * + * The Retry Count defines how many times the Wake-up End Device is supposed + * to retry sending the Parent Request to the Wakeup Coordinator. + * + * @returns the Retry Count. + * + */ + uint8_t GetRetryCount(void) const { return mConnectionWindow & kRetryCountMask; } + + /** + * Sets the Retry Count + * + * @param[in] aRetryCount The Retry Count. + * + */ + void SetRetryCount(uint8_t aRetryCount) + { + mConnectionWindow = aRetryCount | (mConnectionWindow & ~kRetryCountMask); + } + +private: + static constexpr uint8_t kRetryIntervalOffset = 4; + static constexpr uint8_t kRetryIntervalMask = 0x3 << kRetryIntervalOffset; + static constexpr uint8_t kRetryCountMask = 0xf; + + uint8_t mConnectionWindow; +} OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE /** * @} diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index dfbaf72f5..8652c74fb 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -110,6 +110,7 @@ #include "config/time_sync.h" #include "config/tmf.h" #include "config/trel.h" +#include "config/wakeup.h" #undef OPENTHREAD_CORE_CONFIG_H_IN diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index cf7e29603..41bfd8d62 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -787,6 +787,218 @@ void TestMacFrameAckGeneration(void) #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) } +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE +constexpr uint16_t kMpFcfLongFrame = 1 << 3; +constexpr uint16_t kMpFcfDstAddrShift = 4; +constexpr uint16_t kMpFcfDstAddrExt = 3 << kMpFcfDstAddrShift; +constexpr uint16_t kMpFcfSrcAddrShift = 6; +constexpr uint16_t kMpFcfSrcAddrShort = 2 << kMpFcfSrcAddrShift; +constexpr uint16_t kMpFcfSrcAddrExt = 3 << kMpFcfSrcAddrShift; +constexpr uint16_t kMpFcfPanidPresent = 1 << 8; +constexpr uint16_t kMpFcfSecurityEnabled = 1 << 9; +constexpr uint16_t kMpFcfSequenceSuppression = 1 << 10; +constexpr uint16_t kMpFcfAckRequest = 1 << 14; +constexpr uint16_t kMpFcfIePresent = 1 << 15; + +void TestMacWakeupFrameGeneration(void) +{ + constexpr static uint8_t srcExtaddr[] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; + constexpr static uint8_t dstExtaddr[] = {0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD}; + constexpr static uint8_t keySource[] = {0, 0, 0, 0x1C}; + + constexpr static uint8_t wakeupPsdu[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrExt | kMpFcfSrcAddrExt, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent) >> 8, + // PAN ID + 0xCE, 0xFA, + // Destination Address + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + // Source Address + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + // Security Header + Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecurityEncMic32, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x1C, 0x1D, + // Rendezvous Time IE + 0x82, 0x0E, 0xCD, 0xAB, + // Connection IE + 0x05, 0x00, 0x9B, 0xB8, 0xEA, 0x01, 0x1C}; + + uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; + Mac::Address src; + Mac::Address dst; + Mac::Address addr; + Mac::TxFrame txFrame; + Mac::Frame rxFrame; + Mac::ConnectionIe *connectionIe; + + printf("TestMacWakeupFrameGeneration\n"); + + src.SetExtended(srcExtaddr); + dst.SetExtended(dstExtaddr); + txFrame.mPsdu = psdu; + txFrame.mLength = 0; + txFrame.mRadioType = 0; + + SuccessOrQuit(txFrame.GenerateWakeupFrame(0xface, dst, src)); + + // Validate that the frame satisfies the wake-up frame definition + VerifyOrQuit(txFrame.GetType() == Mac::Frame::kTypeMultipurpose); + VerifyOrQuit(!txFrame.GetAckRequest()); + VerifyOrQuit(txFrame.GetRendezvousTimeIe() != nullptr); + VerifyOrQuit(txFrame.GetConnectionIe() != nullptr); + VerifyOrQuit(txFrame.GetPayloadLength() == 0); + SuccessOrQuit(txFrame.GetSrcAddr(addr)); + VerifyOrQuit(CompareAddresses(src, addr)); + SuccessOrQuit(txFrame.GetDstAddr(addr)); + VerifyOrQuit(CompareAddresses(dst, addr)); + + // Initialize remaining fields and check if the frame has the expected contents + txFrame.SetFrameCounter(0xFCFCFCFC); + txFrame.SetKeySource(keySource); + txFrame.SetKeyId(0x1D); + txFrame.GetRendezvousTimeIe()->SetRendezvousTime(0xABCD); + connectionIe = txFrame.GetConnectionIe(); + connectionIe->SetRetryInterval(1); + connectionIe->SetRetryCount(12); + + VerifyOrQuit(txFrame.GetRendezvousTimeIe()->GetRendezvousTime() == 0xABCD); + VerifyOrQuit(connectionIe->GetRetryInterval() == 1); + VerifyOrQuit(connectionIe->GetRetryCount() == 12); + VerifyOrQuit(txFrame.GetLength() == sizeof(wakeupPsdu) + txFrame.GetFooterLength()); + VerifyOrQuit(memcmp(psdu, wakeupPsdu, sizeof(wakeupPsdu)) == 0); + + // Initialize RX Frame with the same PSDU and check if it's recognized as wake-up frame + rxFrame.mPsdu = psdu; + rxFrame.mLength = txFrame.GetLength(); + rxFrame.mRadioType = 0; + + SuccessOrQuit(rxFrame.ValidatePsdu()); + VerifyOrQuit(rxFrame.IsWakeupFrame()); +} + +void TestMacWakeupFrameDetectionNegative(void) +{ + struct TestCase + { + uint8_t *mPsdu; + uint8_t mLength; + }; + + uint8_t ackRequestedPsdu[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrExt | kMpFcfSrcAddrExt, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfAckRequest | kMpFcfIePresent) >> + 8, + // PAN ID + 0xCE, 0xFA, + // Destination Address + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + // Source Address + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + // Security Header + Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecurityEncMic32, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x1C, 0x1D, + // Rendezvous Time IE + 0x82, 0x0E, 0xCD, 0xAB, + // Connection IE + 0x05, 0x00, 0x9B, 0xB8, 0xEA, 0x01, 0x1C, + // Footer + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t shortAddressPsdu[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrExt | kMpFcfSrcAddrShort, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent) >> 8, + // PAN ID + 0xCE, 0xFA, + // Destination Address + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + // Source Address + 0x55, 0x55, + // Security Header + Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecurityEncMic32, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x1C, 0x1D, + // Rendezvous Time IE + 0x82, 0x0E, 0xCD, 0xAB, + // Connection IE + 0x05, 0x00, 0x9B, 0xB8, 0xEA, 0x01, 0x1C, + // Footer + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t noRendezvousIePsdu[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrExt | kMpFcfSrcAddrExt, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent) >> 8, + // PAN ID + 0xCE, 0xFA, + // Destination Address + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + // Source Address + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + // Security Header + Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecurityEncMic32, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x1C, 0x1D, + // Connection IE + 0x05, 0x00, 0x9B, 0xB8, 0xEA, 0x01, 0x1C, + // Footer + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t noConnectionIePsdu[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrExt | kMpFcfSrcAddrExt, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent) >> 8, + // PAN ID + 0xCE, 0xFA, + // Destination Address + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + // Source Address + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + // Security Header + Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecurityEncMic32, 0xFC, 0xFC, 0xFC, 0xFC, 0x00, 0x00, 0x00, 0x1C, 0x1D, + // Rendezvous Time IE + 0x82, 0x0E, 0xCD, 0xAB, + // Connection IE + 0x05, 0x00, 0x9B, 0xB8, 0xEA, 0x02, 0x1C, + // Footer + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t keyIdMode1Psdu[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrExt | kMpFcfSrcAddrExt, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent) >> 8, + // PAN ID + 0xCE, 0xFA, + // Destination Address + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + // Source Address + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + // Security Header + Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecurityEncMic32, 0xFC, 0xFC, 0xFC, 0xFC, 0x1D, + // Rendezvous Time IE + 0x82, 0x0E, 0xCD, 0xAB, + // Connection IE + 0x05, 0x00, 0x9B, 0xB8, 0xEA, 0x01, 0x1C, + // Footer + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + const TestCase testCases[] = { + {ackRequestedPsdu, sizeof(ackRequestedPsdu)}, {shortAddressPsdu, sizeof(shortAddressPsdu)}, + {noRendezvousIePsdu, sizeof(noRendezvousIePsdu)}, {noConnectionIePsdu, sizeof(noConnectionIePsdu)}, + {keyIdMode1Psdu, sizeof(keyIdMode1Psdu)}, + }; + + Mac::Frame rxFrame; + + printf("TestMacWakeupFrameDetectionNegative\n"); + + for (const TestCase &testCase : testCases) + { + rxFrame.mPsdu = testCase.mPsdu; + rxFrame.mLength = testCase.mLength; + rxFrame.mRadioType = 0; + + SuccessOrQuit(rxFrame.ValidatePsdu()); + VerifyOrQuit(!rxFrame.IsWakeupFrame()); + } +} +#endif } // namespace ot int main(void) @@ -796,6 +1008,10 @@ int main(void) ot::TestMacChannelMask(); ot::TestMacFrameApi(); ot::TestMacFrameAckGeneration(); +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE + ot::TestMacWakeupFrameGeneration(); + ot::TestMacWakeupFrameDetectionNegative(); +#endif printf("All tests passed\n"); return 0; }