diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 1cafa255e..05805ae82 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -299,11 +299,12 @@ exit: #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; + const uint16_t fcf = GetFrameControlField(); + bool result = false; + uint8_t keyIdMode; + uint8_t firstIeIndex; + Address srcAddress; + const ConnectionIe *connectionIe; // Wake-up frame is a Multipurpose frame without Ack Request... VerifyOrExit((fcf & kFcfFrameTypeMask) == kTypeMultipurpose); @@ -319,12 +320,12 @@ bool Frame::IsWakeupFrame(void) const // ... that has Rendezvous Time IE and Connection IE... VerifyOrExit(GetRendezvousTimeIe() != nullptr); - VerifyOrExit(GetConnectionIe() != nullptr); + VerifyOrExit((connectionIe = GetConnectionIe()) != nullptr); // ... but no other IEs nor payload. firstIeIndex = FindHeaderIeIndex(); VerifyOrExit(mPsdu + firstIeIndex + sizeof(HeaderIe) + RendezvousTimeIe::kIeContentSize + sizeof(HeaderIe) + - ConnectionIe::kIeContentSize == + connectionIe->GetHeaderIe()->GetLength() == GetFooter()); result = true; @@ -1505,26 +1506,39 @@ 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 TxFrame::GenerateWakeupFrame(PanId aPanId, const WakeupRequest &aWakeupRequest, const Address &aSource) { Error error = kErrorNone; uint16_t fcf; uint8_t secCtl; + uint8_t wakeupIdLength; FrameBuilder builder; + Address dest; fcf = kTypeMultipurpose | kMpFcfLongFrame | kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent; - VerifyOrExit(!aDest.IsNone() && !aSource.IsNone(), error = kErrorInvalidArgs); + VerifyOrExit(!aSource.IsNone(), error = kErrorInvalidArgs); - fcf |= DetermineFcfAddrType(aDest, kMpFcfDstAddrShift); + if (aWakeupRequest.IsWakeupByExtAddress()) + { + wakeupIdLength = 0; + dest.SetExtended(aWakeupRequest.GetExtAddress()); + } + else + { + wakeupIdLength = GetWakeupIdLength(aWakeupRequest.GetWakeupId()); + dest.SetNone(); + } + + fcf |= DetermineFcfAddrType(dest, kMpFcfDstAddrShift); fcf |= DetermineFcfAddrType(aSource, kMpFcfSrcAddrShift); builder.Init(mPsdu, GetMtu()); IgnoreError(builder.AppendLittleEndianUint16(fcf)); IgnoreError(builder.AppendLittleEndianUint16(aPanId)); - IgnoreError(builder.AppendMacAddress(aDest)); + IgnoreError(builder.AppendMacAddress(dest)); IgnoreError(builder.AppendMacAddress(aSource)); secCtl = kKeyIdMode2 | kSecurityEncMic32; @@ -1534,8 +1548,9 @@ Error TxFrame::GenerateWakeupFrame(PanId aPanId, const Address &aDest, const Add builder.Append()->Init(RendezvousTimeIe::kHeaderIeId, sizeof(RendezvousTimeIe)); builder.Append(); - builder.Append()->Init(ConnectionIe::kHeaderIeId, sizeof(ConnectionIe)); + builder.Append()->Init(ConnectionIe::kHeaderIeId, sizeof(ConnectionIe) + wakeupIdLength); builder.Append()->Init(); + builder.AppendLength(wakeupIdLength); builder.AppendLength(CalculateMicSize(secCtl) + GetFcsSize()); diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 830239693..e87e701a6 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -1322,14 +1322,14 @@ public: /** * 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) + * @param[in] aPanId A destination PAN identifier + * @param[in] aWakeupRequest A const reference to the wake-up request. + * @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); + Error GenerateWakeupFrame(PanId aPanId, const WakeupRequest &aWakeupRequest, const Address &aSource); #endif #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 diff --git a/src/core/mac/mac_header_ie.cpp b/src/core/mac/mac_header_ie.cpp index 405a6f099..b6fb821f1 100644 --- a/src/core/mac/mac_header_ie.cpp +++ b/src/core/mac/mac_header_ie.cpp @@ -43,5 +43,41 @@ void HeaderIe::Init(uint16_t aId, uint8_t aLen) SetLength(aLen); } +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +Error ConnectionIe::SetWakeupId(WakeupId aWakeupId) +{ + Error error = kErrorNone; + const HeaderIe *headerIe = GetHeaderIe(); + uint8_t wakeupIdLength = GetWakeupIdLength(aWakeupId); + + VerifyOrExit(headerIe->GetLength() > sizeof(ConnectionIe), error = kErrorParse); + VerifyOrExit(headerIe->GetLength() - sizeof(ConnectionIe) == wakeupIdLength, error = kErrorParse); + aWakeupId = LittleEndian::HostSwap64(aWakeupId); + memcpy(GetWakeupIdData(), reinterpret_cast(&aWakeupId), wakeupIdLength); + +exit: + return error; +} + +Error ConnectionIe::GetWakeupId(WakeupId &aWakeupId) const +{ + Error error = kErrorNone; + const HeaderIe *headerIe = GetHeaderIe(); + uint8_t wakeupIdLength; + + VerifyOrExit(headerIe->GetLength() > sizeof(ConnectionIe), error = kErrorParse); + + wakeupIdLength = headerIe->GetLength() - sizeof(ConnectionIe); + VerifyOrExit(wakeupIdLength <= sizeof(WakeupId), error = kErrorParse); + + aWakeupId = 0; + memcpy(reinterpret_cast(&aWakeupId), GetWakeupIdData(), wakeupIdLength); + aWakeupId = LittleEndian::HostSwap64(aWakeupId); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE + } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac_header_ie.hpp b/src/core/mac/mac_header_ie.hpp index adf37ba0e..ec98201f3 100644 --- a/src/core/mac/mac_header_ie.hpp +++ b/src/core/mac/mac_header_ie.hpp @@ -377,11 +377,44 @@ public: */ void SetRetryCount(uint8_t aRetryCount) { WriteBits(mConnectionWindow, aRetryCount); } + /** + * Sets the Wake-up Identifier. + * + * @param[in] aWakeupId The Wake-up Identifier. + * + * @retval kErrorNone Successfully set the Wake-up Identifier. + * @retval kErrorParse The length of the given Wake-up Identifier didn't match the reserved length. + */ + Error SetWakeupId(WakeupId aWakeupId); + + /** + * Gets the Wake-up Identifier. + * + * @param[out] aWakeupId A reference to the Wake-up Identifier. + * + * @retval kErrorNone Successfully got the Wake-up Identifier. + * @retval kErrorParse Failed to parse the Wake-up Identifier from the Connection IE. + */ + Error GetWakeupId(WakeupId &aWakeupId) const; + + /** + * Gets the pointer to the HeaderIe of this ConnectionIe. + * + * @returns A pointer to the HeaderIe. + */ + const HeaderIe *GetHeaderIe(void) const + { + return reinterpret_cast(reinterpret_cast(this) - sizeof(HeaderIe)); + } + private: static constexpr uint8_t kRetryIntervalOffset = 4; static constexpr uint8_t kRetryIntervalMask = 0x3 << kRetryIntervalOffset; static constexpr uint8_t kRetryCountMask = 0xf; + const uint8_t *GetWakeupIdData(void) const { return reinterpret_cast(this) + sizeof(*this); } + uint8_t *GetWakeupIdData(void) { return reinterpret_cast(this) + sizeof(*this); } + uint8_t mConnectionWindow; } OT_TOOL_PACKED_END; #endif // OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE diff --git a/src/core/mac/mac_types.cpp b/src/core/mac/mac_types.cpp index cef4570c0..f08ac8854 100644 --- a/src/core/mac/mac_types.cpp +++ b/src/core/mac/mac_types.cpp @@ -35,6 +35,7 @@ #include +#include "common/bit_utils.hpp" #include "common/code_utils.hpp" #include "common/random.hpp" #include "common/string.hpp" @@ -404,6 +405,27 @@ bool KeyMaterial::operator==(const KeyMaterial &aOther) const #endif } +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +uint8_t GetWakeupIdLength(WakeupId aWakeupId) +{ + uint8_t zeroBytesCount = 0; + + for (int i = static_cast(sizeof(WakeupId)) - 1; i >= 1; --i) + { + if (((aWakeupId >> (i * kBitsPerByte)) & 0xFF) == 0) + { + zeroBytesCount++; + } + else + { + break; + } + } + + return sizeof(WakeupId) - zeroBytesCount; +} +#endif + #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE void WakeupRequest::SetExtAddress(const ExtAddress &aExtAddress) { diff --git a/src/core/mac/mac_types.hpp b/src/core/mac/mac_types.hpp index 657b5653f..04dbc4da0 100644 --- a/src/core/mac/mac_types.hpp +++ b/src/core/mac/mac_types.hpp @@ -81,6 +81,11 @@ typedef otShortAddress ShortAddress; constexpr ShortAddress kShortAddrBroadcast = OT_RADIO_BROADCAST_SHORT_ADDR; ///< Broadcast Short Address. constexpr ShortAddress kShortAddrInvalid = OT_RADIO_INVALID_SHORT_ADDR; ///< Invalid Short Address. +/** + * Represents the wake-up identifier. + */ +typedef otWakeupId WakeupId; + /** * Generates a random IEEE 802.15.4 PAN ID. * @@ -960,6 +965,19 @@ private: uint8_t mUncertainty; }; +#if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE +/** + * Gets the length of the wake-up identifier. + * + * The length is the number of bytes remaining after removing the most significant zero bytes. + * + * @param[in] aWakeupId The wake-up identifier. + * + * @returns The length of the @p aWakeupId. + */ +uint8_t GetWakeupIdLength(WakeupId aWakeupId); +#endif + #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE /** * Represents a wake-up request. @@ -1004,6 +1022,28 @@ public: */ ExtAddress &GetExtAddress(void); + /** + * Gets the Wake-up Identifier of the wake-up request. + * + * MUST be used only if the wake-up request type is `kTypeWakeupId` or `kTypeGroupWakeupId`. + * + * @returns The Wake-up Identifier. + */ + WakeupId GetWakeupId(void) const { return mShared.mWakeupId; } + + /** + * Sets the wake-up request with the Wake-up Identifier. + * + * The type is also updated to indicate that the wake-up request type is `kTypeWakeupId`. + * + * @param[in] aWakeupId A Wake-up Identifier. + */ + void SetWakeupId(WakeupId aWakeupId) + { + SetType(kTypeWakeupId); + mShared.mWakeupId = aWakeupId; + } + /** * Sets the wake-up request type. * diff --git a/src/core/mac/wakeup_tx_scheduler.cpp b/src/core/mac/wakeup_tx_scheduler.cpp index a0a1a8dde..cb3967b9a 100644 --- a/src/core/mac/wakeup_tx_scheduler.cpp +++ b/src/core/mac/wakeup_tx_scheduler.cpp @@ -80,7 +80,6 @@ void WakeupTxScheduler::RequestWakeupFrameTransmission(void) { Get().R Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames) { Mac::TxFrame *frame = nullptr; - Mac::Address target; Mac::Address source; uint32_t radioTxDelay; uint32_t rendezvousTimeUs; @@ -89,7 +88,6 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames) VerifyOrExit(mIsRunning); - target.SetExtended(mWakeupRequest.GetExtAddress()); source.SetExtended(Get().GetExtAddress()); VerifyOrExit(mTxTimeUs >= nowUs); radioTxDelay = mTxTimeUs - nowUs; @@ -100,7 +98,8 @@ Mac::TxFrame *WakeupTxScheduler::PrepareWakeupFrame(Mac::TxFrames &aTxFrames) frame = &aTxFrames.GetTxFrame(); #endif - VerifyOrExit(frame->GenerateWakeupFrame(Get().GetPanId(), target, source) == kErrorNone, frame = nullptr); + VerifyOrExit(frame->GenerateWakeupFrame(Get().GetPanId(), mWakeupRequest, source) == kErrorNone, + frame = nullptr); frame->SetTxDelayBaseTime(static_cast(Get().GetNow())); frame->SetTxDelay(radioTxDelay); frame->SetCsmaCaEnabled(kWakeupFrameTxCca); diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index 197c5577c..d9bb83caa 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -785,6 +785,7 @@ void TestMacFrameAckGeneration(void) #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE constexpr uint16_t kMpFcfLongFrame = 1 << 3; constexpr uint16_t kMpFcfDstAddrShift = 4; +constexpr uint16_t kMpFcfDstAddrNone = 0 << kMpFcfDstAddrShift; constexpr uint16_t kMpFcfDstAddrExt = 3 << kMpFcfDstAddrShift; constexpr uint16_t kMpFcfSrcAddrShift = 6; constexpr uint16_t kMpFcfSrcAddrShort = 2 << kMpFcfSrcAddrShift; @@ -797,9 +798,10 @@ constexpr uint16_t kMpFcfIePresent = 1 << 15; void TestMacWakeupFrameGeneration(void) { - constexpr static uint8_t kSrcExtaddr[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - constexpr static uint8_t kDstExtaddr[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; - constexpr static uint8_t kKeySource[] = {0, 0, 0, 0x1c}; + constexpr static Mac::WakeupId kWakeupId = 0x1020; + constexpr static uint8_t kSrcExtaddr[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + constexpr static uint8_t kDstExtaddr[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; + constexpr static uint8_t kKeySource[] = {0, 0, 0, 0x1c}; constexpr static uint8_t kWakeupPsdu[] = { // Frame Control @@ -818,10 +820,28 @@ void TestMacWakeupFrameGeneration(void) // Connection IE 0x05, 0x00, 0x9b, 0xb8, 0xea, 0x01, 0x1c}; + constexpr static uint8_t kWakeupPsdu2[] = { + // Frame Control + Mac::Frame::kTypeMultipurpose | kMpFcfLongFrame | kMpFcfDstAddrNone | kMpFcfSrcAddrExt, + (kMpFcfPanidPresent | kMpFcfSecurityEnabled | kMpFcfSequenceSuppression | kMpFcfIePresent) >> 8, + // PAN ID + 0xce, 0xfa, + // No Destination Address + // Source Address + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + // 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 + 0x07, 0x00, 0x9b, 0xb8, 0xea, 0x01, 0x1c, 0x20, 0x10}; + uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; Mac::Address src; Mac::Address dst; Mac::Address addr; + Mac::WakeupId wakeupId; + Mac::WakeupRequest wakeupRequest; Mac::TxFrame txFrame; Mac::Frame rxFrame; Mac::ConnectionIe *connectionIe; @@ -830,11 +850,12 @@ void TestMacWakeupFrameGeneration(void) src.SetExtended(kSrcExtaddr); dst.SetExtended(kDstExtaddr); + wakeupRequest.SetExtAddress(dst.GetExtended()); txFrame.mPsdu = psdu; txFrame.mLength = 0; txFrame.mRadioType = 0; - SuccessOrQuit(txFrame.GenerateWakeupFrame(0xface, dst, src)); + SuccessOrQuit(txFrame.GenerateWakeupFrame(0xface, wakeupRequest, src)); // Validate that the frame satisfies the wake-up frame definition VerifyOrQuit(txFrame.GetType() == Mac::Frame::kTypeMultipurpose); @@ -855,10 +876,12 @@ void TestMacWakeupFrameGeneration(void) connectionIe = txFrame.GetConnectionIe(); connectionIe->SetRetryInterval(1); connectionIe->SetRetryCount(12); + VerifyOrQuit(connectionIe->SetWakeupId(kWakeupId) == kErrorParse); VerifyOrQuit(txFrame.GetRendezvousTimeIe()->GetRendezvousTime() == 0xabcd); VerifyOrQuit(connectionIe->GetRetryInterval() == 1); VerifyOrQuit(connectionIe->GetRetryCount() == 12); + VerifyOrQuit(connectionIe->GetWakeupId(wakeupId) == kErrorParse); VerifyOrQuit(txFrame.GetLength() == sizeof(kWakeupPsdu) + txFrame.GetFooterLength()); VerifyOrQuit(memcmp(psdu, kWakeupPsdu, sizeof(kWakeupPsdu)) == 0); @@ -869,6 +892,53 @@ void TestMacWakeupFrameGeneration(void) SuccessOrQuit(rxFrame.ValidatePsdu()); VerifyOrQuit(rxFrame.IsWakeupFrame()); + + // Validate the wake-up frame using the wake-up identifier. + src.SetExtended(kSrcExtaddr); + wakeupRequest.SetWakeupId(kWakeupId); + txFrame.mPsdu = psdu; + txFrame.mLength = 0; + txFrame.mRadioType = 0; + + SuccessOrQuit(txFrame.GenerateWakeupFrame(0xface, wakeupRequest, 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(addr.IsNone()); + + // Initialize remaining fields and check if the frame has the expected contents + txFrame.SetFrameCounter(0xfcfcfcfc); + txFrame.SetKeySource(kKeySource); + txFrame.SetKeyId(0x1d); + txFrame.GetRendezvousTimeIe()->SetRendezvousTime(0xabcd); + connectionIe = txFrame.GetConnectionIe(); + connectionIe->SetRetryInterval(1); + connectionIe->SetRetryCount(12); + SuccessOrQuit(connectionIe->SetWakeupId(kWakeupId)); + + VerifyOrQuit(txFrame.GetRendezvousTimeIe()->GetRendezvousTime() == 0xabcd); + VerifyOrQuit(connectionIe->GetRetryInterval() == 1); + VerifyOrQuit(connectionIe->GetRetryCount() == 12); + SuccessOrQuit(connectionIe->GetWakeupId(wakeupId)); + VerifyOrQuit(wakeupId == kWakeupId); + VerifyOrQuit(wakeupRequest.GetWakeupId() == kWakeupId); + VerifyOrQuit(txFrame.GetLength() == sizeof(kWakeupPsdu2) + txFrame.GetFooterLength()); + VerifyOrQuit(memcmp(psdu, kWakeupPsdu2, sizeof(kWakeupPsdu2)) == 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)