• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2016, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the subset of IEEE 802.15.4 primitives required for Thread.
32  */
33 
34 #include "mac.hpp"
35 
36 #include <stdio.h>
37 
38 #include "crypto/aes_ccm.hpp"
39 #include "crypto/sha256.hpp"
40 #include "instance/instance.hpp"
41 #include "utils/static_counter.hpp"
42 
43 namespace ot {
44 namespace Mac {
45 
46 RegisterLogModule("Mac");
47 
48 const otExtAddress Mac::sMode2ExtAddress = {
49     {0x35, 0x06, 0xfe, 0xb8, 0x23, 0xd4, 0x87, 0x12},
50 };
51 
Mac(Instance & aInstance)52 Mac::Mac(Instance &aInstance)
53     : InstanceLocator(aInstance)
54     , mEnabled(false)
55     , mShouldTxPollBeforeData(false)
56     , mRxOnWhenIdle(false)
57     , mPromiscuous(false)
58     , mBeaconsEnabled(false)
59     , mUsingTemporaryChannel(false)
60 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
61     , mShouldDelaySleep(false)
62     , mDelayingSleep(false)
63 #endif
64 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
65     , mWakeupListenEnabled(false)
66 #endif
67     , mOperation(kOperationIdle)
68     , mPendingOperations(0)
69     , mBeaconSequence(Random::NonCrypto::GetUint8())
70     , mDataSequence(Random::NonCrypto::GetUint8())
71     , mBroadcastTransmitCount(0)
72     , mPanId(kPanIdBroadcast)
73     , mPanChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
74     , mRadioChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL)
75     , mSupportedChannelMask(Get<Radio>().GetSupportedChannelMask())
76     , mScanChannel(Radio::kChannelMin)
77     , mScanDuration(0)
78     , mMaxFrameRetriesDirect(kDefaultMaxFrameRetriesDirect)
79 #if OPENTHREAD_FTD
80     , mMaxFrameRetriesIndirect(kDefaultMaxFrameRetriesIndirect)
81 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
82     , mCslTxFireTime(TimeMilli::kMaxDuration)
83 #endif
84 #endif
85 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
86     , mCslChannel(0)
87     , mCslPeriod(0)
88 #endif
89     , mWakeupChannel(OPENTHREAD_CONFIG_DEFAULT_WAKEUP_CHANNEL)
90 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
91     , mWakeupListenInterval(kDefaultWedListenInterval)
92     , mWakeupListenDuration(kDefaultWedListenDuration)
93 #endif
94     , mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union
95     , mScanHandlerContext(nullptr)
96     , mLinks(aInstance)
97     , mOperationTask(aInstance)
98     , mTimer(aInstance)
99     , mKeyIdMode2FrameCounter(0)
100     , mCcaSampleCount(0)
101 #if OPENTHREAD_CONFIG_MULTI_RADIO
102     , mTxError(kErrorNone)
103 #endif
104 {
105     ExtAddress randomExtAddress;
106 
107     static const otMacKey sMode2Key = {
108         {0x78, 0x58, 0x16, 0x86, 0xfd, 0xb4, 0x58, 0x0f, 0xb0, 0x92, 0x54, 0x6a, 0xec, 0xbd, 0x15, 0x66}};
109 
110     randomExtAddress.GenerateRandom();
111 
112     mCcaSuccessRateTracker.Clear();
113     ResetCounters();
114 
115     SetEnabled(true);
116 
117     Get<KeyManager>().UpdateKeyMaterial();
118     SetPanId(mPanId);
119     SetExtAddress(randomExtAddress);
120     SetShortAddress(GetShortAddress());
121 #if OPENTHREAD_FTD
122     SetAlternateShortAddress(kShortAddrInvalid);
123 #endif
124 
125     mMode2KeyMaterial.SetFrom(AsCoreType(&sMode2Key));
126 }
127 
SetEnabled(bool aEnable)128 void Mac::SetEnabled(bool aEnable)
129 {
130     mEnabled = aEnable;
131 
132     if (aEnable)
133     {
134         mLinks.Enable();
135     }
136     else
137     {
138         mLinks.Disable();
139     }
140 }
141 
ActiveScan(uint32_t aScanChannels,uint16_t aScanDuration,ActiveScanHandler aHandler,void * aContext)142 Error Mac::ActiveScan(uint32_t aScanChannels, uint16_t aScanDuration, ActiveScanHandler aHandler, void *aContext)
143 {
144     Error error = kErrorNone;
145 
146     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
147     VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = kErrorBusy);
148 
149     mActiveScanHandler  = aHandler;
150     mScanHandlerContext = aContext;
151 
152     if (aScanDuration == 0)
153     {
154         aScanDuration = kScanDurationDefault;
155     }
156 
157     Scan(kOperationActiveScan, aScanChannels, aScanDuration);
158 
159 exit:
160     return error;
161 }
162 
EnergyScan(uint32_t aScanChannels,uint16_t aScanDuration,EnergyScanHandler aHandler,void * aContext)163 Error Mac::EnergyScan(uint32_t aScanChannels, uint16_t aScanDuration, EnergyScanHandler aHandler, void *aContext)
164 {
165     Error error = kErrorNone;
166 
167     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
168     VerifyOrExit(!IsActiveScanInProgress() && !IsEnergyScanInProgress(), error = kErrorBusy);
169 
170     mEnergyScanHandler  = aHandler;
171     mScanHandlerContext = aContext;
172 
173     Scan(kOperationEnergyScan, aScanChannels, aScanDuration);
174 
175 exit:
176     return error;
177 }
178 
Scan(Operation aScanOperation,uint32_t aScanChannels,uint16_t aScanDuration)179 void Mac::Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanDuration)
180 {
181     mScanDuration = aScanDuration;
182     mScanChannel  = ChannelMask::kChannelIteratorFirst;
183 
184     if (aScanChannels == 0)
185     {
186         aScanChannels = mSupportedChannelMask.GetMask();
187     }
188 
189     mScanChannelMask.SetMask(aScanChannels);
190     mScanChannelMask.Intersect(mSupportedChannelMask);
191     StartOperation(aScanOperation);
192 }
193 
IsInTransmitState(void) const194 bool Mac::IsInTransmitState(void) const
195 {
196     bool retval = false;
197 
198     switch (mOperation)
199     {
200     case kOperationTransmitDataDirect:
201 #if OPENTHREAD_FTD
202     case kOperationTransmitDataIndirect:
203 #endif
204 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
205     case kOperationTransmitDataCsl:
206 #endif
207     case kOperationTransmitBeacon:
208     case kOperationTransmitPoll:
209 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
210     case kOperationTransmitWakeup:
211 #endif
212         retval = true;
213         break;
214 
215     case kOperationIdle:
216     case kOperationActiveScan:
217     case kOperationEnergyScan:
218     case kOperationWaitingForData:
219         retval = false;
220         break;
221     }
222 
223     return retval;
224 }
225 
ConvertBeaconToActiveScanResult(const RxFrame * aBeaconFrame,ActiveScanResult & aResult)226 Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveScanResult &aResult)
227 {
228     Error   error = kErrorNone;
229     Address address;
230 #if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE
231     const BeaconPayload *beaconPayload = nullptr;
232     const Beacon        *beacon        = nullptr;
233     uint16_t             payloadLength;
234 #endif
235 
236     ClearAllBytes(aResult);
237 
238     VerifyOrExit(aBeaconFrame != nullptr, error = kErrorInvalidArgs);
239 
240     VerifyOrExit(aBeaconFrame->GetType() == Frame::kTypeBeacon, error = kErrorParse);
241     SuccessOrExit(error = aBeaconFrame->GetSrcAddr(address));
242     VerifyOrExit(address.IsExtended(), error = kErrorParse);
243     aResult.mExtAddress = address.GetExtended();
244 
245     if (kErrorNone != aBeaconFrame->GetSrcPanId(aResult.mPanId))
246     {
247         IgnoreError(aBeaconFrame->GetDstPanId(aResult.mPanId));
248     }
249 
250     aResult.mChannel = aBeaconFrame->GetChannel();
251     aResult.mRssi    = aBeaconFrame->GetRssi();
252     aResult.mLqi     = aBeaconFrame->GetLqi();
253 
254 #if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE
255     payloadLength = aBeaconFrame->GetPayloadLength();
256 
257     beacon        = reinterpret_cast<const Beacon *>(aBeaconFrame->GetPayload());
258     beaconPayload = reinterpret_cast<const BeaconPayload *>(beacon->GetPayload());
259 
260     if ((payloadLength >= (sizeof(*beacon) + sizeof(*beaconPayload))) && beacon->IsValid() && beaconPayload->IsValid())
261     {
262         aResult.mVersion    = beaconPayload->GetProtocolVersion();
263         aResult.mIsJoinable = beaconPayload->IsJoiningPermitted();
264         aResult.mIsNative   = beaconPayload->IsNative();
265         IgnoreError(AsCoreType(&aResult.mNetworkName).Set(beaconPayload->GetNetworkName()));
266         VerifyOrExit(IsValidUtf8String(aResult.mNetworkName.m8), error = kErrorParse);
267         aResult.mExtendedPanId = beaconPayload->GetExtendedPanId();
268     }
269 #endif
270 
271     LogBeacon("Received");
272 
273 exit:
274     return error;
275 }
276 
UpdateScanChannel(void)277 Error Mac::UpdateScanChannel(void)
278 {
279     Error error;
280 
281     VerifyOrExit(IsEnabled(), error = kErrorAbort);
282 
283     error = mScanChannelMask.GetNextChannel(mScanChannel);
284 
285 exit:
286     return error;
287 }
288 
PerformActiveScan(void)289 void Mac::PerformActiveScan(void)
290 {
291     if (UpdateScanChannel() == kErrorNone)
292     {
293         // If there are more channels to scan, send the beacon request.
294         BeginTransmit();
295     }
296     else
297     {
298         mLinks.SetPanId(mPanId);
299         FinishOperation();
300         ReportActiveScanResult(nullptr);
301         PerformNextOperation();
302     }
303 }
304 
ReportActiveScanResult(const RxFrame * aBeaconFrame)305 void Mac::ReportActiveScanResult(const RxFrame *aBeaconFrame)
306 {
307     VerifyOrExit(mActiveScanHandler != nullptr);
308 
309     if (aBeaconFrame == nullptr)
310     {
311         mActiveScanHandler(nullptr, mScanHandlerContext);
312     }
313     else
314     {
315         ActiveScanResult result;
316 
317         SuccessOrExit(ConvertBeaconToActiveScanResult(aBeaconFrame, result));
318         mActiveScanHandler(&result, mScanHandlerContext);
319     }
320 
321 exit:
322     return;
323 }
324 
PerformEnergyScan(void)325 void Mac::PerformEnergyScan(void)
326 {
327     Error error = kErrorNone;
328 
329     SuccessOrExit(error = UpdateScanChannel());
330 
331     if (mScanDuration == 0)
332     {
333         while (true)
334         {
335             mLinks.Receive(mScanChannel);
336             ReportEnergyScanResult(mLinks.GetRssi());
337             SuccessOrExit(error = UpdateScanChannel());
338         }
339     }
340     else
341     {
342         if (!mRxOnWhenIdle)
343         {
344             mLinks.Receive(mScanChannel);
345         }
346         error = mLinks.EnergyScan(mScanChannel, mScanDuration);
347     }
348 
349 exit:
350 
351     if (error != kErrorNone)
352     {
353         FinishOperation();
354 
355         if (mEnergyScanHandler != nullptr)
356         {
357             mEnergyScanHandler(nullptr, mScanHandlerContext);
358         }
359 
360         PerformNextOperation();
361     }
362 }
363 
ReportEnergyScanResult(int8_t aRssi)364 void Mac::ReportEnergyScanResult(int8_t aRssi)
365 {
366     EnergyScanResult result;
367 
368     VerifyOrExit((mEnergyScanHandler != nullptr) && (aRssi != Radio::kInvalidRssi));
369 
370     result.mChannel = mScanChannel;
371     result.mMaxRssi = aRssi;
372 
373     mEnergyScanHandler(&result, mScanHandlerContext);
374 
375 exit:
376     return;
377 }
378 
EnergyScanDone(int8_t aEnergyScanMaxRssi)379 void Mac::EnergyScanDone(int8_t aEnergyScanMaxRssi)
380 {
381     ReportEnergyScanResult(aEnergyScanMaxRssi);
382     PerformEnergyScan();
383 }
384 
SetRxOnWhenIdle(bool aRxOnWhenIdle)385 void Mac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
386 {
387     VerifyOrExit(mRxOnWhenIdle != aRxOnWhenIdle);
388 
389     mRxOnWhenIdle = aRxOnWhenIdle;
390 
391     // If the new value for `mRxOnWhenIdle` is `true` (i.e., radio should
392     // remain in Rx while idle) we stop any ongoing or pending `WaitingForData`
393     // operation (since this operation only applies to sleepy devices).
394 
395     if (mRxOnWhenIdle)
396     {
397         if (IsPending(kOperationWaitingForData))
398         {
399             mTimer.Stop();
400             ClearPending(kOperationWaitingForData);
401         }
402 
403         if (mOperation == kOperationWaitingForData)
404         {
405             mTimer.Stop();
406             FinishOperation();
407             mOperationTask.Post();
408         }
409 
410 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
411         mDelayingSleep    = false;
412         mShouldDelaySleep = false;
413 #endif
414     }
415 
416     mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
417     UpdateIdleMode();
418 
419 exit:
420     return;
421 }
422 
SetPanChannel(uint8_t aChannel)423 Error Mac::SetPanChannel(uint8_t aChannel)
424 {
425     Error error = kErrorNone;
426 
427     VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs);
428 
429     SuccessOrExit(Get<Notifier>().Update(mPanChannel, aChannel, kEventThreadChannelChanged));
430 
431     mCcaSuccessRateTracker.Clear();
432 
433     VerifyOrExit(!mUsingTemporaryChannel);
434 
435     mRadioChannel = mPanChannel;
436 
437 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
438     UpdateCsl();
439 #endif
440 
441     UpdateIdleMode();
442 
443 exit:
444     return error;
445 }
446 
SetTemporaryChannel(uint8_t aChannel)447 Error Mac::SetTemporaryChannel(uint8_t aChannel)
448 {
449     Error error = kErrorNone;
450 
451     VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs);
452 
453     mUsingTemporaryChannel = true;
454     mRadioChannel          = aChannel;
455 
456     UpdateIdleMode();
457 
458 exit:
459     return error;
460 }
461 
ClearTemporaryChannel(void)462 void Mac::ClearTemporaryChannel(void)
463 {
464     if (mUsingTemporaryChannel)
465     {
466         mUsingTemporaryChannel = false;
467         mRadioChannel          = mPanChannel;
468         UpdateIdleMode();
469     }
470 }
471 
SetSupportedChannelMask(const ChannelMask & aMask)472 void Mac::SetSupportedChannelMask(const ChannelMask &aMask)
473 {
474     ChannelMask newMask = aMask;
475 
476     newMask.Intersect(mSupportedChannelMask);
477     IgnoreError(Get<Notifier>().Update(mSupportedChannelMask, newMask, kEventSupportedChannelMaskChanged));
478 }
479 
SetPanId(PanId aPanId)480 void Mac::SetPanId(PanId aPanId)
481 {
482     SuccessOrExit(Get<Notifier>().Update(mPanId, aPanId, kEventThreadPanIdChanged));
483     mLinks.SetPanId(mPanId);
484 
485 exit:
486     return;
487 }
488 
RequestDirectFrameTransmission(void)489 void Mac::RequestDirectFrameTransmission(void)
490 {
491     VerifyOrExit(IsEnabled());
492     VerifyOrExit(!IsActiveOrPending(kOperationTransmitDataDirect));
493 
494     StartOperation(kOperationTransmitDataDirect);
495 
496 exit:
497     return;
498 }
499 
500 #if OPENTHREAD_FTD
RequestIndirectFrameTransmission(void)501 void Mac::RequestIndirectFrameTransmission(void)
502 {
503     VerifyOrExit(IsEnabled());
504     VerifyOrExit(!IsActiveOrPending(kOperationTransmitDataIndirect));
505 
506     StartOperation(kOperationTransmitDataIndirect);
507 
508 exit:
509     return;
510 }
511 #endif
512 
513 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
RequestCslFrameTransmission(uint32_t aDelay)514 void Mac::RequestCslFrameTransmission(uint32_t aDelay)
515 {
516     VerifyOrExit(mEnabled);
517 
518     mCslTxFireTime = TimerMilli::GetNow() + aDelay;
519 
520     StartOperation(kOperationTransmitDataCsl);
521 
522 exit:
523     return;
524 }
525 #endif
526 
527 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
RequestWakeupFrameTransmission(void)528 void Mac::RequestWakeupFrameTransmission(void)
529 {
530     VerifyOrExit(IsEnabled());
531     StartOperation(kOperationTransmitWakeup);
532 
533 exit:
534     return;
535 }
536 #endif
537 
RequestDataPollTransmission(void)538 Error Mac::RequestDataPollTransmission(void)
539 {
540     Error error = kErrorNone;
541 
542     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
543     VerifyOrExit(!IsActiveOrPending(kOperationTransmitPoll));
544 
545     // We ensure data frame and data poll tx requests are handled in the
546     // order they are requested. So if we have a pending direct data frame
547     // tx request, it should be sent before the poll frame.
548 
549     mShouldTxPollBeforeData = !IsPending(kOperationTransmitDataDirect);
550 
551     StartOperation(kOperationTransmitPoll);
552 
553 exit:
554     return error;
555 }
556 
UpdateIdleMode(void)557 void Mac::UpdateIdleMode(void)
558 {
559     bool shouldSleep = !mRxOnWhenIdle && !mPromiscuous;
560 
561     VerifyOrExit(mOperation == kOperationIdle);
562 
563     if (!mRxOnWhenIdle)
564     {
565 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
566         if (mShouldDelaySleep)
567         {
568             mTimer.Start(kSleepDelay);
569             mShouldDelaySleep = false;
570             mDelayingSleep    = true;
571             LogDebg("Idle mode: Sleep delayed");
572         }
573 
574         if (mDelayingSleep)
575         {
576             shouldSleep = false;
577         }
578 #endif
579     }
580 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
581     else if (IsPending(kOperationTransmitDataCsl))
582     {
583         mTimer.FireAt(mCslTxFireTime);
584     }
585 #endif
586 
587     if (shouldSleep)
588     {
589         mLinks.Sleep();
590         LogDebg("Idle mode: Radio sleeping");
591     }
592     else
593     {
594         mLinks.Receive(mRadioChannel);
595         LogDebg("Idle mode: Radio receiving on channel %u", mRadioChannel);
596     }
597 
598 exit:
599     return;
600 }
601 
IsActiveOrPending(Operation aOperation) const602 bool Mac::IsActiveOrPending(Operation aOperation) const { return (mOperation == aOperation) || IsPending(aOperation); }
603 
StartOperation(Operation aOperation)604 void Mac::StartOperation(Operation aOperation)
605 {
606     if (aOperation != kOperationIdle)
607     {
608         SetPending(aOperation);
609 
610         LogDebg("Request to start operation \"%s\"", OperationToString(aOperation));
611 
612 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
613         if (mDelayingSleep)
614         {
615             LogDebg("Canceling sleep delay");
616             mTimer.Stop();
617             mDelayingSleep    = false;
618             mShouldDelaySleep = false;
619         }
620 #endif
621     }
622 
623     if (mOperation == kOperationIdle)
624     {
625         mOperationTask.Post();
626     }
627 }
628 
PerformNextOperation(void)629 void Mac::PerformNextOperation(void)
630 {
631     VerifyOrExit(mOperation == kOperationIdle);
632 
633     if (!IsEnabled())
634     {
635         mPendingOperations = 0;
636         mTimer.Stop();
637 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
638         mDelayingSleep    = false;
639         mShouldDelaySleep = false;
640 #endif
641         ExitNow();
642     }
643 
644     // `WaitingForData` should be checked before any other pending
645     // operations since radio should remain in receive mode after
646     // a data poll ack indicating a pending frame from parent.
647     if (IsPending(kOperationWaitingForData))
648     {
649         mOperation = kOperationWaitingForData;
650     }
651 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
652     else if (IsPending(kOperationTransmitWakeup))
653     {
654         mOperation = kOperationTransmitWakeup;
655     }
656 #endif
657 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
658     else if (IsPending(kOperationTransmitDataCsl) && TimerMilli::GetNow() >= mCslTxFireTime)
659     {
660         mOperation = kOperationTransmitDataCsl;
661     }
662 #endif
663     else if (IsPending(kOperationActiveScan))
664     {
665         mOperation = kOperationActiveScan;
666     }
667     else if (IsPending(kOperationEnergyScan))
668     {
669         mOperation = kOperationEnergyScan;
670     }
671     else if (IsPending(kOperationTransmitBeacon))
672     {
673         mOperation = kOperationTransmitBeacon;
674     }
675 #if OPENTHREAD_FTD
676     else if (IsPending(kOperationTransmitDataIndirect))
677     {
678         mOperation = kOperationTransmitDataIndirect;
679     }
680 #endif // OPENTHREAD_FTD
681     else if (IsPending(kOperationTransmitPoll) && (!IsPending(kOperationTransmitDataDirect) || mShouldTxPollBeforeData))
682     {
683         mOperation = kOperationTransmitPoll;
684     }
685     else if (IsPending(kOperationTransmitDataDirect))
686     {
687         mOperation = kOperationTransmitDataDirect;
688 
689         if (IsPending(kOperationTransmitPoll))
690         {
691             // Ensure that a pending "transmit poll" operation request
692             // is prioritized over any future "transmit data" requests.
693             mShouldTxPollBeforeData = true;
694         }
695     }
696 
697     if (mOperation != kOperationIdle)
698     {
699         ClearPending(mOperation);
700         LogDebg("Starting operation \"%s\"", OperationToString(mOperation));
701         mTimer.Stop(); // Stop the timer before any non-idle operation, have the operation itself be responsible to
702                        // start the timer (if it wants to).
703     }
704 
705     switch (mOperation)
706     {
707     case kOperationIdle:
708         UpdateIdleMode();
709         break;
710 
711     case kOperationActiveScan:
712         PerformActiveScan();
713         break;
714 
715     case kOperationEnergyScan:
716         PerformEnergyScan();
717         break;
718 
719     case kOperationTransmitBeacon:
720     case kOperationTransmitDataDirect:
721 #if OPENTHREAD_FTD
722     case kOperationTransmitDataIndirect:
723 #endif
724 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
725     case kOperationTransmitDataCsl:
726 #endif
727     case kOperationTransmitPoll:
728 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
729     case kOperationTransmitWakeup:
730 #endif
731         BeginTransmit();
732         break;
733 
734     case kOperationWaitingForData:
735         mLinks.Receive(mRadioChannel);
736         mTimer.Start(kDataPollTimeout);
737         break;
738     }
739 
740 exit:
741     return;
742 }
743 
FinishOperation(void)744 void Mac::FinishOperation(void)
745 {
746     LogDebg("Finishing operation \"%s\"", OperationToString(mOperation));
747     mOperation = kOperationIdle;
748 }
749 
PrepareBeaconRequest(void)750 TxFrame *Mac::PrepareBeaconRequest(void)
751 {
752     TxFrame      &frame = mLinks.GetTxFrames().GetBroadcastTxFrame();
753     TxFrame::Info frameInfo;
754 
755     frameInfo.mAddrs.mSource.SetNone();
756     frameInfo.mAddrs.mDestination.SetShort(kShortAddrBroadcast);
757     frameInfo.mPanIds.SetDestination(kShortAddrBroadcast);
758 
759     frameInfo.mType      = Frame::kTypeMacCmd;
760     frameInfo.mCommandId = Frame::kMacCmdBeaconRequest;
761     frameInfo.mVersion   = Frame::kVersion2003;
762 
763     frameInfo.PrepareHeadersIn(frame);
764 
765     LogInfo("Sending Beacon Request");
766 
767     return &frame;
768 }
769 
PrepareBeacon(void)770 TxFrame *Mac::PrepareBeacon(void)
771 {
772     TxFrame      *frame;
773     TxFrame::Info frameInfo;
774     Beacon       *beacon = nullptr;
775 #if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE
776     uint8_t        beaconLength;
777     BeaconPayload *beaconPayload = nullptr;
778 #endif
779 
780 #if OPENTHREAD_CONFIG_MULTI_RADIO
781     OT_ASSERT(!mTxBeaconRadioLinks.IsEmpty());
782     frame = &mLinks.GetTxFrames().GetTxFrame(mTxBeaconRadioLinks);
783     mTxBeaconRadioLinks.Clear();
784 #else
785     frame = &mLinks.GetTxFrames().GetBroadcastTxFrame();
786 #endif
787 
788     frameInfo.mAddrs.mSource.SetExtended(GetExtAddress());
789     frameInfo.mPanIds.SetSource(mPanId);
790     frameInfo.mAddrs.mDestination.SetNone();
791 
792     frameInfo.mType    = Frame::kTypeBeacon;
793     frameInfo.mVersion = Frame::kVersion2003;
794 
795     frameInfo.PrepareHeadersIn(*frame);
796 
797     beacon = reinterpret_cast<Beacon *>(frame->GetPayload());
798     beacon->Init();
799 
800 #if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE
801     beaconLength = sizeof(*beacon);
802 
803     beaconPayload = reinterpret_cast<BeaconPayload *>(beacon->GetPayload());
804 
805     beaconPayload->Init();
806 
807     if (IsJoinable())
808     {
809         beaconPayload->SetJoiningPermitted();
810     }
811     else
812     {
813         beaconPayload->ClearJoiningPermitted();
814     }
815 
816     beaconPayload->SetNetworkName(Get<MeshCoP::NetworkNameManager>().GetNetworkName().GetAsData());
817     beaconPayload->SetExtendedPanId(Get<MeshCoP::ExtendedPanIdManager>().GetExtPanId());
818 
819     beaconLength += sizeof(*beaconPayload);
820 
821     frame->SetPayloadLength(beaconLength);
822 #endif
823 
824     LogBeacon("Sending");
825 
826     return frame;
827 }
828 
ShouldSendBeacon(void) const829 bool Mac::ShouldSendBeacon(void) const
830 {
831     bool shouldSend = false;
832 
833     VerifyOrExit(IsEnabled());
834 
835     shouldSend = IsBeaconEnabled();
836 
837 #if OPENTHREAD_CONFIG_MAC_BEACON_RSP_WHEN_JOINABLE_ENABLE
838     if (!shouldSend)
839     {
840         // When `ENABLE_BEACON_RSP_WHEN_JOINABLE` feature is enabled,
841         // the device should transmit IEEE 802.15.4 Beacons in response
842         // to IEEE 802.15.4 Beacon Requests even while the device is not
843         // router capable and detached (i.e., `IsBeaconEnabled()` is
844         // false) but only if it is in joinable state (unsecure port
845         // list is not empty).
846 
847         shouldSend = IsJoinable();
848     }
849 #endif
850 
851 exit:
852     return shouldSend;
853 }
854 
IsJoinable(void) const855 bool Mac::IsJoinable(void) const
856 {
857     uint8_t numUnsecurePorts;
858 
859     Get<Ip6::Filter>().GetUnsecurePorts(numUnsecurePorts);
860 
861     return (numUnsecurePorts != 0);
862 }
863 
ProcessTransmitSecurity(TxFrame & aFrame)864 void Mac::ProcessTransmitSecurity(TxFrame &aFrame)
865 {
866     KeyManager       &keyManager = Get<KeyManager>();
867     uint8_t           keyIdMode;
868     const ExtAddress *extAddress = nullptr;
869 
870     VerifyOrExit(aFrame.GetSecurityEnabled());
871 
872     IgnoreError(aFrame.GetKeyIdMode(keyIdMode));
873 
874     switch (keyIdMode)
875     {
876     case Frame::kKeyIdMode0:
877         aFrame.SetAesKey(keyManager.GetKek());
878         extAddress = &GetExtAddress();
879 
880         if (!aFrame.IsHeaderUpdated())
881         {
882             aFrame.SetFrameCounter(keyManager.GetKekFrameCounter());
883             keyManager.IncrementKekFrameCounter();
884         }
885 
886         break;
887 
888     case Frame::kKeyIdMode1:
889 
890         // For 15.4 radio link, the AES CCM* and frame security counter (under MAC
891         // key ID mode 1) are managed by `SubMac` or `Radio` modules.
892 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
893 #if !OPENTHREAD_CONFIG_MULTI_RADIO
894         ExitNow();
895 #else
896         VerifyOrExit(aFrame.GetRadioType() != kRadioTypeIeee802154);
897 #endif
898 #endif
899 
900 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
901         aFrame.SetAesKey(*mLinks.GetCurrentMacKey(aFrame));
902         extAddress = &GetExtAddress();
903 
904         // If the frame header is marked as updated, `MeshForwarder` which
905         // prepared the frame should set the frame counter and key id to the
906         // same values used in the earlier transmit attempt. For a new frame (header
907         // not updated), we get a new frame counter and key id from the key
908         // manager.
909 
910         if (!aFrame.IsHeaderUpdated())
911         {
912             mLinks.SetMacFrameCounter(aFrame);
913             aFrame.SetKeyId((keyManager.GetCurrentKeySequence() & 0x7f) + 1);
914         }
915 #endif
916         break;
917 
918     case Frame::kKeyIdMode2:
919     {
920         uint8_t keySource[] = {0xff, 0xff, 0xff, 0xff};
921 
922 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
923         if (aFrame.IsWakeupFrame())
924         {
925             // Just set the key source here, further security processing will happen in SubMac
926             BigEndian::WriteUint32(keyManager.GetCurrentKeySequence(), keySource);
927             aFrame.SetKeySource(keySource);
928             ExitNow();
929         }
930 #endif
931         aFrame.SetAesKey(mMode2KeyMaterial);
932 
933         mKeyIdMode2FrameCounter++;
934         aFrame.SetFrameCounter(mKeyIdMode2FrameCounter);
935         aFrame.SetKeySource(keySource);
936         aFrame.SetKeyId(0xff);
937         extAddress = &AsCoreType(&sMode2ExtAddress);
938         break;
939     }
940 
941     default:
942         OT_ASSERT(false);
943     }
944 
945 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
946     // Transmit security will be processed after time IE content is updated.
947     VerifyOrExit(aFrame.GetTimeIeOffset() == 0);
948 #endif
949 
950 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
951     // Transmit security will be processed after time IE content is updated.
952     VerifyOrExit(!aFrame.IsCslIePresent());
953 #endif
954 
955     aFrame.ProcessTransmitAesCcm(*extAddress);
956 
957 exit:
958     return;
959 }
960 
BeginTransmit(void)961 void Mac::BeginTransmit(void)
962 {
963     TxFrame  *frame    = nullptr;
964     TxFrames &txFrames = mLinks.GetTxFrames();
965     Address   dstAddr;
966 
967     txFrames.Clear();
968 
969 #if OPENTHREAD_CONFIG_MULTI_RADIO
970     mTxPendingRadioLinks.Clear();
971     mTxError = kErrorAbort;
972 #endif
973 
974     VerifyOrExit(IsEnabled());
975 
976     switch (mOperation)
977     {
978     case kOperationActiveScan:
979         mLinks.SetPanId(kPanIdBroadcast);
980         frame = PrepareBeaconRequest();
981         VerifyOrExit(frame != nullptr);
982         frame->SetChannel(mScanChannel);
983         frame->SetSequence(0);
984         frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
985         frame->SetMaxFrameRetries(mMaxFrameRetriesDirect);
986         break;
987 
988     case kOperationTransmitBeacon:
989         frame = PrepareBeacon();
990         VerifyOrExit(frame != nullptr);
991         frame->SetChannel(mRadioChannel);
992         frame->SetSequence(mBeaconSequence++);
993         frame->SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
994         frame->SetMaxFrameRetries(mMaxFrameRetriesDirect);
995         break;
996 
997     case kOperationTransmitPoll:
998         txFrames.SetChannel(mRadioChannel);
999         txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
1000         txFrames.SetMaxFrameRetries(mMaxFrameRetriesDirect);
1001         frame = Get<DataPollSender>().PrepareDataRequest(txFrames);
1002         VerifyOrExit(frame != nullptr);
1003         frame->SetSequence(mDataSequence++);
1004         break;
1005 
1006     case kOperationTransmitDataDirect:
1007         // Set channel and retry counts on all TxFrames before asking
1008         // the next layer (`MeshForwarder`) to prepare the frame. This
1009         // allows next layer to possibility change these parameters.
1010         txFrames.SetChannel(mRadioChannel);
1011         txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsDirect);
1012         txFrames.SetMaxFrameRetries(mMaxFrameRetriesDirect);
1013         frame = Get<MeshForwarder>().HandleFrameRequest(txFrames);
1014         VerifyOrExit(frame != nullptr);
1015         frame->SetSequence(mDataSequence++);
1016         break;
1017 
1018 #if OPENTHREAD_FTD
1019     case kOperationTransmitDataIndirect:
1020         txFrames.SetChannel(mRadioChannel);
1021         txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsIndirect);
1022         txFrames.SetMaxFrameRetries(mMaxFrameRetriesIndirect);
1023         frame = Get<DataPollHandler>().HandleFrameRequest(txFrames);
1024         VerifyOrExit(frame != nullptr);
1025 
1026         // If the frame is marked as retransmission, then data sequence number is already set.
1027         if (!frame->IsARetransmission())
1028         {
1029             frame->SetSequence(mDataSequence++);
1030         }
1031         break;
1032 #endif
1033 
1034 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1035     case kOperationTransmitDataCsl:
1036         txFrames.SetMaxCsmaBackoffs(kMaxCsmaBackoffsCsl);
1037         txFrames.SetMaxFrameRetries(kMaxFrameRetriesCsl);
1038         frame = Get<CslTxScheduler>().HandleFrameRequest(txFrames);
1039         VerifyOrExit(frame != nullptr);
1040 
1041         // If the frame is marked as retransmission, then data sequence number is already set.
1042         if (!frame->IsARetransmission())
1043         {
1044             frame->SetSequence(mDataSequence++);
1045         }
1046 
1047         break;
1048 
1049 #endif
1050 
1051 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
1052     case kOperationTransmitWakeup:
1053         frame = Get<WakeupTxScheduler>().PrepareWakeupFrame(txFrames);
1054         VerifyOrExit(frame != nullptr);
1055         frame->SetChannel(mWakeupChannel);
1056         frame->SetRxChannelAfterTxDone(mRadioChannel);
1057         break;
1058 #endif
1059 
1060     default:
1061         OT_ASSERT(false);
1062     }
1063 
1064 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
1065     {
1066         uint8_t timeIeOffset = GetTimeIeOffset(*frame);
1067 
1068         frame->SetTimeIeOffset(timeIeOffset);
1069 
1070         if (timeIeOffset != 0)
1071         {
1072             frame->SetTimeSyncSeq(Get<TimeSync>().GetTimeSyncSeq());
1073             frame->SetNetworkTimeOffset(Get<TimeSync>().GetNetworkTimeOffset());
1074         }
1075     }
1076 #endif
1077 
1078     if (!frame->IsSecurityProcessed())
1079     {
1080 #if OPENTHREAD_CONFIG_MULTI_RADIO
1081         // Go through all selected radio link types for this tx and
1082         // copy the frame into correct `TxFrame` for each radio type
1083         // (if it is not already prepared).
1084 
1085         for (RadioType radio : RadioTypes::kAllRadioTypes)
1086         {
1087             if (txFrames.GetSelectedRadioTypes().Contains(radio))
1088             {
1089                 TxFrame &txFrame = txFrames.GetTxFrame(radio);
1090 
1091                 if (txFrame.IsEmpty())
1092                 {
1093                     txFrame.CopyFrom(*frame);
1094                 }
1095             }
1096         }
1097 
1098         // Go through all selected radio link types for this tx and
1099         // process security for each radio type separately. This
1100         // allows radio links to handle security differently, e.g.,
1101         // with different keys or link frame counters.
1102         for (RadioType radio : RadioTypes::kAllRadioTypes)
1103         {
1104             if (txFrames.GetSelectedRadioTypes().Contains(radio))
1105             {
1106                 ProcessTransmitSecurity(txFrames.GetTxFrame(radio));
1107             }
1108         }
1109 #else
1110         ProcessTransmitSecurity(*frame);
1111 #endif
1112     }
1113 
1114     mBroadcastTransmitCount = 0;
1115 
1116 #if OPENTHREAD_CONFIG_MULTI_RADIO
1117     mTxPendingRadioLinks = txFrames.GetSelectedRadioTypes();
1118 
1119     // If the "required radio type set" is empty,`mTxError` starts as
1120     // `kErrorAbort`. In this case, successful tx over any radio
1121     // link is sufficient for overall tx to be considered successful.
1122     // When the "required radio type set" is not empty, `mTxError`
1123     // starts as `kErrorNone` and we update it if tx over any link
1124     // in the required set fails.
1125 
1126     if (!txFrames.GetRequiredRadioTypes().IsEmpty())
1127     {
1128         mTxError = kErrorNone;
1129     }
1130 #endif
1131 
1132 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
1133     if (!mRxOnWhenIdle && !mPromiscuous)
1134     {
1135         mShouldDelaySleep = frame->GetFramePending();
1136         LogDebg("Delay sleep for pending tx");
1137     }
1138 #endif
1139 
1140 #if OPENTHREAD_CONFIG_MULTI_RADIO
1141     mLinks.Send(*frame, mTxPendingRadioLinks);
1142 #else
1143     mLinks.Send();
1144 #endif
1145 
1146 exit:
1147 
1148     if (frame == nullptr)
1149     {
1150         // If the frame could not be prepared and the tx is being
1151         // aborted, we set the frame length to zero to mark it as empty.
1152         // The empty frame helps differentiate between an aborted tx due
1153         // to OpenThread itself not being able to prepare the frame, versus
1154         // the radio platform aborting the tx operation.
1155 
1156         frame = &txFrames.GetBroadcastTxFrame();
1157         frame->SetLength(0);
1158         HandleTransmitDone(*frame, nullptr, kErrorAbort);
1159     }
1160 }
1161 
RecordCcaStatus(bool aCcaSuccess,uint8_t aChannel)1162 void Mac::RecordCcaStatus(bool aCcaSuccess, uint8_t aChannel)
1163 {
1164     if (!aCcaSuccess)
1165     {
1166         mCounters.mTxErrCca++;
1167     }
1168 
1169     // Only track the CCA success rate for frame transmissions
1170     // on the PAN channel or the CSL channel.
1171 
1172 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1173     if ((aChannel == mPanChannel) || (IsCslEnabled() && (aChannel == mCslChannel)))
1174 #else
1175     if (aChannel == mPanChannel)
1176 #endif
1177     {
1178         if (mCcaSampleCount < kMaxCcaSampleCount)
1179         {
1180             mCcaSampleCount++;
1181         }
1182 
1183         mCcaSuccessRateTracker.AddSample(aCcaSuccess, mCcaSampleCount);
1184     }
1185 }
1186 
RecordFrameTransmitStatus(const TxFrame & aFrame,Error aError,uint8_t aRetryCount,bool aWillRetx)1187 void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame, Error aError, uint8_t aRetryCount, bool aWillRetx)
1188 {
1189     bool      ackRequested = aFrame.GetAckRequest();
1190     Address   dstAddr;
1191     Neighbor *neighbor;
1192 
1193     VerifyOrExit(!aFrame.IsEmpty());
1194 
1195     IgnoreError(aFrame.GetDstAddr(dstAddr));
1196     neighbor = Get<NeighborTable>().FindNeighbor(dstAddr);
1197 
1198     // Record frame transmission success/failure state (for a neighbor).
1199 
1200     if ((neighbor != nullptr) && ackRequested)
1201     {
1202         bool frameTxSuccess = true;
1203 
1204         // CCA or abort errors are excluded from frame tx error
1205         // rate tracking, since when they occur, the frame is
1206         // not actually sent over the air.
1207 
1208         switch (aError)
1209         {
1210         case kErrorNoAck:
1211             frameTxSuccess = false;
1212 
1213             OT_FALL_THROUGH;
1214 
1215         case kErrorNone:
1216             neighbor->GetLinkInfo().AddFrameTxStatus(frameTxSuccess);
1217             break;
1218 
1219         default:
1220             break;
1221         }
1222     }
1223 
1224     // Log frame transmission failure.
1225 
1226     if (aError != kErrorNone)
1227     {
1228         LogFrameTxFailure(aFrame, aError, aRetryCount, aWillRetx);
1229         DumpDebg("TX ERR", aFrame.GetHeader(), 16);
1230 
1231         if (aWillRetx)
1232         {
1233             mCounters.mTxRetry++;
1234 
1235             // Since this failed transmission will be retried by `SubMac` layer
1236             // there is no need to update any other MAC counter. MAC counters
1237             // are updated on the final transmission attempt.
1238 
1239             ExitNow();
1240         }
1241     }
1242 
1243     // Update MAC counters.
1244 
1245     mCounters.mTxTotal++;
1246 
1247     if (aError == kErrorAbort)
1248     {
1249         mCounters.mTxErrAbort++;
1250     }
1251 
1252     if (aError == kErrorChannelAccessFailure)
1253     {
1254         mCounters.mTxErrBusyChannel++;
1255     }
1256 
1257     if (ackRequested)
1258     {
1259         mCounters.mTxAckRequested++;
1260 
1261         if (aError == kErrorNone)
1262         {
1263             mCounters.mTxAcked++;
1264         }
1265     }
1266     else
1267     {
1268         mCounters.mTxNoAckRequested++;
1269     }
1270 
1271     if (dstAddr.IsBroadcast())
1272     {
1273         mCounters.mTxBroadcast++;
1274     }
1275     else
1276     {
1277         mCounters.mTxUnicast++;
1278     }
1279 
1280 exit:
1281     return;
1282 }
1283 
HandleTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)1284 void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
1285 {
1286     bool ackRequested = aFrame.GetAckRequest();
1287 
1288 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
1289     if (!aFrame.IsEmpty()
1290 #if OPENTHREAD_CONFIG_MULTI_RADIO
1291         && (aFrame.GetRadioType() == kRadioTypeIeee802154)
1292 #endif
1293     )
1294     {
1295         Address dstAddr;
1296 
1297         IgnoreError(aFrame.GetDstAddr(dstAddr));
1298 
1299         // Determine whether to re-transmit a broadcast frame.
1300         if (dstAddr.IsBroadcast())
1301         {
1302             mBroadcastTransmitCount++;
1303 
1304             if (mBroadcastTransmitCount < kTxNumBcast)
1305             {
1306 #if OPENTHREAD_CONFIG_MULTI_RADIO
1307                 {
1308                     RadioTypes radioTypes;
1309                     radioTypes.Add(kRadioTypeIeee802154);
1310                     mLinks.Send(aFrame, radioTypes);
1311                 }
1312 #else
1313                 mLinks.Send();
1314 #endif
1315                 ExitNow();
1316             }
1317 
1318             mBroadcastTransmitCount = 0;
1319         }
1320 
1321         if (ackRequested && (aAckFrame != nullptr))
1322         {
1323             Neighbor *neighbor = Get<NeighborTable>().FindNeighbor(dstAddr);
1324 
1325 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
1326             if ((aError == kErrorNone) && (neighbor != nullptr) &&
1327                 (mFilter.ApplyToRxFrame(*aAckFrame, neighbor->GetExtAddress(), neighbor) != kErrorNone))
1328             {
1329                 aError = kErrorNoAck;
1330             }
1331 #endif
1332 
1333 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1334             // Verify Enh-ACK integrity by checking its MIC
1335             if ((aError == kErrorNone) && (ProcessEnhAckSecurity(aFrame, *aAckFrame) != kErrorNone))
1336             {
1337                 aError = kErrorNoAck;
1338             }
1339 #endif
1340 
1341             if ((aError == kErrorNone) && (neighbor != nullptr))
1342             {
1343                 UpdateNeighborLinkInfo(*neighbor, *aAckFrame);
1344 
1345 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
1346                 ProcessEnhAckProbing(*aAckFrame, *neighbor);
1347 #endif
1348 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1349                 ProcessCsl(*aAckFrame, dstAddr);
1350 #endif
1351 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1352                 if (!mRxOnWhenIdle && aFrame.HasCslIe())
1353                 {
1354                     Get<DataPollSender>().ResetKeepAliveTimer();
1355                 }
1356 #endif
1357             }
1358         }
1359     }
1360 #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
1361 
1362 #if OPENTHREAD_CONFIG_MULTI_RADIO
1363     if (!aFrame.IsEmpty())
1364     {
1365         RadioType  radio          = aFrame.GetRadioType();
1366         RadioTypes requiredRadios = mLinks.GetTxFrames().GetRequiredRadioTypes();
1367 
1368         Get<RadioSelector>().UpdateOnSendDone(aFrame, aError);
1369 
1370         if (requiredRadios.IsEmpty())
1371         {
1372             // If the "required radio type set" is empty, successful
1373             // tx over any radio link is sufficient for overall tx to
1374             // be considered successful. In this case `mTxError`
1375             // starts as `kErrorAbort` and we update it only when
1376             // it is not already `kErrorNone`.
1377 
1378             if (mTxError != kErrorNone)
1379             {
1380                 mTxError = aError;
1381             }
1382         }
1383         else
1384         {
1385             // When the "required radio type set" is not empty we
1386             // expect the successful frame tx on all links in this set
1387             // to consider the overall tx successful. In this case,
1388             // `mTxError` starts as `kErrorNone` and we update it
1389             // if tx over any link in the set fails.
1390 
1391             if (requiredRadios.Contains(radio) && (aError != kErrorNone))
1392             {
1393                 LogDebg("Frame tx failed on required radio link %s with error %s", RadioTypeToString(radio),
1394                         ErrorToString(aError));
1395 
1396                 mTxError = aError;
1397             }
1398         }
1399 
1400         // Keep track of radio links on which the frame is sent
1401         // and wait for all radio links to finish.
1402         mTxPendingRadioLinks.Remove(radio);
1403 
1404         VerifyOrExit(mTxPendingRadioLinks.IsEmpty());
1405 
1406         aError = mTxError;
1407     }
1408 #endif // OPENTHREAD_CONFIG_MULTI_RADIO
1409 
1410     // Determine next action based on current operation.
1411 
1412     switch (mOperation)
1413     {
1414     case kOperationActiveScan:
1415         mCounters.mTxBeaconRequest++;
1416         mTimer.Start(mScanDuration);
1417         break;
1418 
1419     case kOperationTransmitBeacon:
1420         mCounters.mTxBeacon++;
1421         FinishOperation();
1422         PerformNextOperation();
1423         break;
1424 
1425     case kOperationTransmitPoll:
1426         OT_ASSERT(aFrame.IsEmpty() || ackRequested);
1427 
1428         if ((aError == kErrorNone) && (aAckFrame != nullptr))
1429         {
1430             bool framePending = aAckFrame->GetFramePending();
1431 
1432             if (IsEnabled() && framePending)
1433             {
1434                 StartOperation(kOperationWaitingForData);
1435             }
1436 
1437             LogInfo("Sent data poll, fp:%s", ToYesNo(framePending));
1438         }
1439 
1440         mCounters.mTxDataPoll++;
1441         FinishOperation();
1442         Get<DataPollSender>().HandlePollSent(aFrame, aError);
1443         PerformNextOperation();
1444         break;
1445 
1446     case kOperationTransmitDataDirect:
1447         mCounters.mTxData++;
1448 
1449         if (aError != kErrorNone)
1450         {
1451             mCounters.mTxDirectMaxRetryExpiry++;
1452         }
1453 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
1454         else if (mLinks.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT)
1455         {
1456             mRetryHistogram.mTxDirectRetrySuccess[mLinks.GetTransmitRetries()]++;
1457         }
1458 #endif
1459 
1460         DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
1461         FinishOperation();
1462         Get<MeshForwarder>().HandleSentFrame(aFrame, aError);
1463 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1464         Get<DataPollSender>().ProcessTxDone(aFrame, aAckFrame, aError);
1465 #endif
1466         PerformNextOperation();
1467         break;
1468 
1469 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1470     case kOperationTransmitDataCsl:
1471         mCounters.mTxData++;
1472 
1473         DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
1474         FinishOperation();
1475         Get<CslTxScheduler>().HandleSentFrame(aFrame, aError);
1476         PerformNextOperation();
1477 
1478         break;
1479 #endif
1480 
1481 #if OPENTHREAD_FTD
1482     case kOperationTransmitDataIndirect:
1483         mCounters.mTxData++;
1484 
1485         if (aError != kErrorNone)
1486         {
1487             mCounters.mTxIndirectMaxRetryExpiry++;
1488         }
1489 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
1490         else if (mLinks.GetTransmitRetries() < OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT)
1491         {
1492             mRetryHistogram.mTxIndirectRetrySuccess[mLinks.GetTransmitRetries()]++;
1493         }
1494 #endif
1495 
1496         DumpDebg("TX", aFrame.GetHeader(), aFrame.GetLength());
1497         FinishOperation();
1498         Get<DataPollHandler>().HandleSentFrame(aFrame, aError);
1499         PerformNextOperation();
1500         break;
1501 #endif // OPENTHREAD_FTD
1502 
1503 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
1504     case kOperationTransmitWakeup:
1505         FinishOperation();
1506         PerformNextOperation();
1507         break;
1508 #endif
1509 
1510     default:
1511         OT_ASSERT(false);
1512     }
1513 
1514     ExitNow(); // Added to suppress "unused label exit" warning (in TREL radio only).
1515 
1516 exit:
1517     return;
1518 }
1519 
HandleTimer(void)1520 void Mac::HandleTimer(void)
1521 {
1522     switch (mOperation)
1523     {
1524     case kOperationActiveScan:
1525         PerformActiveScan();
1526         break;
1527 
1528     case kOperationWaitingForData:
1529         LogDebg("Data poll timeout");
1530         FinishOperation();
1531         Get<DataPollSender>().HandlePollTimeout();
1532         PerformNextOperation();
1533         break;
1534 
1535     case kOperationIdle:
1536         if (!mRxOnWhenIdle)
1537         {
1538 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
1539             if (mDelayingSleep)
1540             {
1541                 LogDebg("Sleep delay timeout expired");
1542                 mDelayingSleep = false;
1543                 UpdateIdleMode();
1544             }
1545 #endif
1546         }
1547 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1548         else if (IsPending(kOperationTransmitDataCsl))
1549         {
1550             PerformNextOperation();
1551         }
1552 #endif
1553         break;
1554 
1555     default:
1556         OT_ASSERT(false);
1557     }
1558 }
1559 
ProcessReceiveSecurity(RxFrame & aFrame,const Address & aSrcAddr,Neighbor * aNeighbor)1560 Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor)
1561 {
1562     KeyManager        &keyManager = Get<KeyManager>();
1563     Error              error      = kErrorSecurity;
1564     uint8_t            securityLevel;
1565     uint8_t            keyIdMode;
1566     uint32_t           frameCounter;
1567     uint8_t            keyid;
1568     uint32_t           keySequence = 0;
1569     const KeyMaterial *macKey;
1570     const ExtAddress  *extAddress;
1571 
1572     VerifyOrExit(aFrame.GetSecurityEnabled(), error = kErrorNone);
1573 
1574     IgnoreError(aFrame.GetSecurityLevel(securityLevel));
1575     VerifyOrExit(securityLevel == Frame::kSecurityEncMic32);
1576 
1577     IgnoreError(aFrame.GetFrameCounter(frameCounter));
1578     LogDebg("Rx security - frame counter %lu", ToUlong(frameCounter));
1579 
1580     IgnoreError(aFrame.GetKeyIdMode(keyIdMode));
1581 
1582     switch (keyIdMode)
1583     {
1584     case Frame::kKeyIdMode0:
1585         macKey     = &keyManager.GetKek();
1586         extAddress = &aSrcAddr.GetExtended();
1587         break;
1588 
1589     case Frame::kKeyIdMode1:
1590         VerifyOrExit(aNeighbor != nullptr);
1591 
1592         IgnoreError(aFrame.GetKeyId(keyid));
1593         keyid--;
1594 
1595         if (keyid == (keyManager.GetCurrentKeySequence() & 0x7f))
1596         {
1597             keySequence = keyManager.GetCurrentKeySequence();
1598             macKey      = mLinks.GetCurrentMacKey(aFrame);
1599         }
1600         else if (keyid == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f))
1601         {
1602             keySequence = keyManager.GetCurrentKeySequence() - 1;
1603             macKey      = mLinks.GetTemporaryMacKey(aFrame, keySequence);
1604         }
1605         else if (keyid == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f))
1606         {
1607             keySequence = keyManager.GetCurrentKeySequence() + 1;
1608             macKey      = mLinks.GetTemporaryMacKey(aFrame, keySequence);
1609         }
1610         else
1611         {
1612             ExitNow();
1613         }
1614 
1615         // If the frame is from a neighbor not in valid state (e.g., it is from a child being
1616         // restored), skip the key sequence and frame counter checks but continue to verify
1617         // the tag/MIC. Such a frame is later filtered in `RxDoneTask` which only allows MAC
1618         // Data Request frames from a child being restored.
1619 
1620         if (aNeighbor->IsStateValid())
1621         {
1622             VerifyOrExit(keySequence >= aNeighbor->GetKeySequence());
1623 
1624             if (keySequence == aNeighbor->GetKeySequence())
1625             {
1626                 uint32_t neighborFrameCounter;
1627 
1628 #if OPENTHREAD_CONFIG_MULTI_RADIO
1629                 neighborFrameCounter = aNeighbor->GetLinkFrameCounters().Get(aFrame.GetRadioType());
1630 #else
1631                 neighborFrameCounter = aNeighbor->GetLinkFrameCounters().Get();
1632 #endif
1633 
1634                 // If frame counter is one off, then frame is a duplicate.
1635                 VerifyOrExit((frameCounter + 1) != neighborFrameCounter, error = kErrorDuplicated);
1636 
1637                 VerifyOrExit(frameCounter >= neighborFrameCounter);
1638             }
1639         }
1640 
1641         extAddress = &aSrcAddr.GetExtended();
1642 
1643         break;
1644 
1645     case Frame::kKeyIdMode2:
1646 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
1647         if (aFrame.IsWakeupFrame())
1648         {
1649             uint32_t sequence;
1650 
1651             // TODO: Avoid generating a new key if a wake-up frame was recently received already
1652 
1653             IgnoreError(aFrame.GetKeyId(keyid));
1654             sequence = BigEndian::ReadUint32(aFrame.GetKeySource());
1655             VerifyOrExit(((sequence & 0x7f) + 1) == keyid, error = kErrorSecurity);
1656 
1657             macKey     = (sequence == keyManager.GetCurrentKeySequence()) ? mLinks.GetCurrentMacKey(aFrame)
1658                                                                           : &keyManager.GetTemporaryMacKey(sequence);
1659             extAddress = &aSrcAddr.GetExtended();
1660         }
1661         else
1662 #endif
1663         {
1664             macKey     = &mMode2KeyMaterial;
1665             extAddress = &AsCoreType(&sMode2ExtAddress);
1666         }
1667         break;
1668 
1669     default:
1670         ExitNow();
1671     }
1672 
1673     SuccessOrExit(aFrame.ProcessReceiveAesCcm(*extAddress, *macKey));
1674 
1675     if ((keyIdMode == Frame::kKeyIdMode1) && aNeighbor->IsStateValid())
1676     {
1677         if (aNeighbor->GetKeySequence() != keySequence)
1678         {
1679             aNeighbor->SetKeySequence(keySequence);
1680             aNeighbor->SetMleFrameCounter(0);
1681             aNeighbor->GetLinkFrameCounters().Reset();
1682         }
1683 
1684 #if OPENTHREAD_CONFIG_MULTI_RADIO
1685         aNeighbor->GetLinkFrameCounters().Set(aFrame.GetRadioType(), frameCounter + 1);
1686 #else
1687         aNeighbor->GetLinkFrameCounters().Set(frameCounter + 1);
1688 #endif
1689 
1690 #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
1691 #if OPENTHREAD_CONFIG_MULTI_RADIO
1692         if (aFrame.GetRadioType() == kRadioTypeIeee802154)
1693 #endif
1694         {
1695             if ((frameCounter + 1) > aNeighbor->GetLinkAckFrameCounter())
1696             {
1697                 aNeighbor->SetLinkAckFrameCounter(frameCounter + 1);
1698             }
1699         }
1700 #endif
1701 
1702         if (keySequence > keyManager.GetCurrentKeySequence())
1703         {
1704             keyManager.SetCurrentKeySequence(keySequence, KeyManager::kApplySwitchGuard | KeyManager::kResetGuardTimer);
1705         }
1706     }
1707 
1708     error = kErrorNone;
1709 
1710 exit:
1711     return error;
1712 }
1713 
1714 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
ProcessEnhAckSecurity(TxFrame & aTxFrame,RxFrame & aAckFrame)1715 Error Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame)
1716 {
1717     Error              error = kErrorSecurity;
1718     uint8_t            securityLevel;
1719     uint8_t            txKeyId;
1720     uint8_t            ackKeyId;
1721     uint8_t            keyIdMode;
1722     uint32_t           frameCounter;
1723     Address            srcAddr;
1724     Address            dstAddr;
1725     Neighbor          *neighbor   = nullptr;
1726     KeyManager        &keyManager = Get<KeyManager>();
1727     const KeyMaterial *macKey;
1728 
1729     VerifyOrExit(aAckFrame.GetSecurityEnabled(), error = kErrorNone);
1730     VerifyOrExit(aAckFrame.IsVersion2015());
1731 
1732     SuccessOrExit(aAckFrame.ValidatePsdu());
1733 
1734     IgnoreError(aAckFrame.GetSecurityLevel(securityLevel));
1735     VerifyOrExit(securityLevel == Frame::kSecurityEncMic32);
1736 
1737     IgnoreError(aAckFrame.GetKeyIdMode(keyIdMode));
1738     VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
1739 
1740     IgnoreError(aTxFrame.GetKeyId(txKeyId));
1741     IgnoreError(aAckFrame.GetKeyId(ackKeyId));
1742 
1743     VerifyOrExit(txKeyId == ackKeyId);
1744 
1745     IgnoreError(aAckFrame.GetFrameCounter(frameCounter));
1746     LogDebg("Rx security - Ack frame counter %lu", ToUlong(frameCounter));
1747 
1748     IgnoreError(aAckFrame.GetSrcAddr(srcAddr));
1749 
1750     if (!srcAddr.IsNone())
1751     {
1752         neighbor = Get<NeighborTable>().FindNeighbor(srcAddr);
1753     }
1754     else
1755     {
1756         IgnoreError(aTxFrame.GetDstAddr(dstAddr));
1757 
1758         if (!dstAddr.IsNone())
1759         {
1760             // Get neighbor from destination address of transmitted frame
1761             neighbor = Get<NeighborTable>().FindNeighbor(dstAddr);
1762         }
1763     }
1764 
1765     if (!srcAddr.IsExtended() && neighbor != nullptr)
1766     {
1767         srcAddr.SetExtended(neighbor->GetExtAddress());
1768     }
1769 
1770     VerifyOrExit(srcAddr.IsExtended() && neighbor != nullptr);
1771 
1772     ackKeyId--;
1773 
1774     if (ackKeyId == (keyManager.GetCurrentKeySequence() & 0x7f))
1775     {
1776         macKey = &mLinks.GetSubMac().GetCurrentMacKey();
1777     }
1778     else if (ackKeyId == ((keyManager.GetCurrentKeySequence() - 1) & 0x7f))
1779     {
1780         macKey = &mLinks.GetSubMac().GetPreviousMacKey();
1781     }
1782     else if (ackKeyId == ((keyManager.GetCurrentKeySequence() + 1) & 0x7f))
1783     {
1784         macKey = &mLinks.GetSubMac().GetNextMacKey();
1785     }
1786     else
1787     {
1788         ExitNow();
1789     }
1790 
1791     if (neighbor->IsStateValid())
1792     {
1793         VerifyOrExit(frameCounter >= neighbor->GetLinkAckFrameCounter());
1794     }
1795 
1796     error = aAckFrame.ProcessReceiveAesCcm(srcAddr.GetExtended(), *macKey);
1797     SuccessOrExit(error);
1798 
1799     if (neighbor->IsStateValid())
1800     {
1801         neighbor->SetLinkAckFrameCounter(frameCounter + 1);
1802     }
1803 
1804 exit:
1805     if (error != kErrorNone)
1806     {
1807         LogInfo("Frame tx attempt failed, error: Enh-ACK security check fail");
1808     }
1809 
1810     return error;
1811 }
1812 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
1813 
FilterDestShortAddress(ShortAddress aDestAddress) const1814 Error Mac::FilterDestShortAddress(ShortAddress aDestAddress) const
1815 {
1816     Error error = kErrorNone;
1817 
1818     if (aDestAddress == GetShortAddress())
1819     {
1820         ExitNow();
1821     }
1822 
1823 #if OPENTHREAD_FTD
1824     if ((GetAlternateShortAddress() != kShortAddrInvalid) && (aDestAddress == GetAlternateShortAddress()))
1825     {
1826         ExitNow();
1827     }
1828 #endif
1829 
1830     if (mRxOnWhenIdle && (aDestAddress == kShortAddrBroadcast))
1831     {
1832         ExitNow();
1833     }
1834 
1835     error = kErrorDestinationAddressFiltered;
1836 
1837 exit:
1838     return error;
1839 }
1840 
HandleReceivedFrame(RxFrame * aFrame,Error aError)1841 void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError)
1842 {
1843     Address   srcaddr;
1844     Address   dstaddr;
1845     PanId     panid;
1846     Neighbor *neighbor;
1847     Error     error = aError;
1848 
1849     mCounters.mRxTotal++;
1850 
1851     SuccessOrExit(error);
1852     VerifyOrExit(aFrame != nullptr, error = kErrorNoFrameReceived);
1853     VerifyOrExit(IsEnabled(), error = kErrorInvalidState);
1854 
1855     // Ensure we have a valid frame before attempting to read any contents of
1856     // the buffer received from the radio.
1857     SuccessOrExit(error = aFrame->ValidatePsdu());
1858 
1859     IgnoreError(aFrame->GetSrcAddr(srcaddr));
1860     IgnoreError(aFrame->GetDstAddr(dstaddr));
1861     neighbor = !srcaddr.IsNone() ? Get<NeighborTable>().FindNeighbor(srcaddr) : nullptr;
1862 
1863     // Destination Address Filtering
1864     switch (dstaddr.GetType())
1865     {
1866     case Address::kTypeNone:
1867         break;
1868 
1869     case Address::kTypeShort:
1870         SuccessOrExit(error = FilterDestShortAddress(dstaddr.GetShort()));
1871 
1872 #if OPENTHREAD_FTD
1873         // Allow multicasts from neighbor routers if FTD
1874         if (neighbor == nullptr && dstaddr.IsBroadcast() && Get<Mle::MleRouter>().IsFullThreadDevice())
1875         {
1876             neighbor = Get<NeighborTable>().FindRxOnlyNeighborRouter(srcaddr);
1877         }
1878 #endif
1879 
1880         break;
1881 
1882     case Address::kTypeExtended:
1883         VerifyOrExit(dstaddr.GetExtended() == GetExtAddress(), error = kErrorDestinationAddressFiltered);
1884         break;
1885     }
1886 
1887     // Verify destination PAN ID if present
1888     if (kErrorNone == aFrame->GetDstPanId(panid))
1889     {
1890         VerifyOrExit(panid == kShortAddrBroadcast || panid == mPanId, error = kErrorDestinationAddressFiltered);
1891     }
1892 
1893     // Source Address Filtering
1894     switch (srcaddr.GetType())
1895     {
1896     case Address::kTypeNone:
1897         break;
1898 
1899     case Address::kTypeShort:
1900         LogDebg("Received frame from short address 0x%04x", srcaddr.GetShort());
1901 
1902         VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor);
1903 
1904         srcaddr.SetExtended(neighbor->GetExtAddress());
1905 
1906         OT_FALL_THROUGH;
1907 
1908     case Address::kTypeExtended:
1909 
1910         // Duplicate Address Protection
1911         VerifyOrExit(srcaddr.GetExtended() != GetExtAddress(), error = kErrorInvalidSourceAddress);
1912 
1913 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
1914         SuccessOrExit(error = mFilter.ApplyToRxFrame(*aFrame, srcaddr.GetExtended(), neighbor));
1915 #endif
1916 
1917         break;
1918     }
1919 
1920     if (dstaddr.IsBroadcast())
1921     {
1922         mCounters.mRxBroadcast++;
1923     }
1924     else
1925     {
1926         mCounters.mRxUnicast++;
1927     }
1928 
1929     error = ProcessReceiveSecurity(*aFrame, srcaddr, neighbor);
1930 
1931     switch (error)
1932     {
1933     case kErrorDuplicated:
1934 
1935         // Allow a duplicate received frame pass, only if the
1936         // current operation is `kOperationWaitingForData` (i.e.,
1937         // the sleepy device is waiting to receive a frame after
1938         // a data poll ack from parent indicating there is a
1939         // pending frame for it). This ensures that the sleepy
1940         // device goes to sleep faster and avoids a data poll
1941         // timeout.
1942         //
1943         // Note that `error` is checked again later after the
1944         // operation `kOperationWaitingForData` is processed
1945         // so the duplicate frame will not be passed to next
1946         // layer (`MeshForwarder`).
1947 
1948         VerifyOrExit(mOperation == kOperationWaitingForData);
1949 
1950         OT_FALL_THROUGH;
1951 
1952     case kErrorNone:
1953         break;
1954 
1955     default:
1956         ExitNow();
1957     }
1958 
1959 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1960     ProcessCsl(*aFrame, srcaddr);
1961 #endif
1962 
1963     Get<DataPollSender>().ProcessRxFrame(*aFrame);
1964 
1965     if (neighbor != nullptr)
1966     {
1967         UpdateNeighborLinkInfo(*neighbor, *aFrame);
1968 
1969         if (aFrame->GetSecurityEnabled())
1970         {
1971             uint8_t keyIdMode;
1972 
1973             IgnoreError(aFrame->GetKeyIdMode(keyIdMode));
1974 
1975             if (keyIdMode == Frame::kKeyIdMode1)
1976             {
1977                 switch (neighbor->GetState())
1978                 {
1979                 case Neighbor::kStateValid:
1980                     break;
1981 
1982                 case Neighbor::kStateRestored:
1983                 case Neighbor::kStateChildUpdateRequest:
1984 
1985                     // Only accept a "MAC Data Request" frame from a child being restored.
1986                     VerifyOrExit(aFrame->IsDataRequestCommand(), error = kErrorDrop);
1987                     break;
1988 
1989                 default:
1990                     ExitNow(error = kErrorUnknownNeighbor);
1991                 }
1992 
1993 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 && OPENTHREAD_FTD
1994                 // From Thread 1.2, MAC Data Frame can also act as keep-alive message if child supports
1995                 if (aFrame->GetType() == Frame::kTypeData && !neighbor->IsRxOnWhenIdle() &&
1996                     neighbor->IsEnhancedKeepAliveSupported())
1997                 {
1998                     neighbor->SetLastHeard(TimerMilli::GetNow());
1999                 }
2000 #endif
2001             }
2002 
2003 #if OPENTHREAD_CONFIG_MULTI_RADIO
2004             Get<RadioSelector>().UpdateOnReceive(*neighbor, aFrame->GetRadioType(), /* aIsDuplicate */ false);
2005 #endif
2006         }
2007     }
2008 
2009     switch (mOperation)
2010     {
2011     case kOperationActiveScan:
2012 
2013         if (aFrame->GetType() == Frame::kTypeBeacon)
2014         {
2015             mCounters.mRxBeacon++;
2016             ReportActiveScanResult(aFrame);
2017             ExitNow();
2018         }
2019 
2020         OT_FALL_THROUGH;
2021 
2022     case kOperationEnergyScan:
2023 
2024         // We can possibly receive a data frame while either active or
2025         // energy scan is ongoing. We continue to process the frame only
2026         // if the current scan channel matches `mPanChannel`.
2027 
2028         VerifyOrExit(mScanChannel == mPanChannel, mCounters.mRxOther++);
2029         break;
2030 
2031     case kOperationWaitingForData:
2032 
2033         if (!dstaddr.IsNone())
2034         {
2035             mTimer.Stop();
2036 
2037 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
2038             if (!mRxOnWhenIdle && !mPromiscuous && aFrame->GetFramePending())
2039             {
2040                 mShouldDelaySleep = true;
2041                 LogDebg("Delay sleep for pending rx");
2042             }
2043 #endif
2044             FinishOperation();
2045             PerformNextOperation();
2046         }
2047 
2048         SuccessOrExit(error);
2049 
2050         break;
2051 
2052     default:
2053         break;
2054     }
2055 
2056     switch (aFrame->GetType())
2057     {
2058     case Frame::kTypeMacCmd:
2059         if (HandleMacCommand(*aFrame)) // returns `true` when handled
2060         {
2061             ExitNow(error = kErrorNone);
2062         }
2063 
2064         break;
2065 
2066     case Frame::kTypeBeacon:
2067         mCounters.mRxBeacon++;
2068         break;
2069 
2070     case Frame::kTypeData:
2071         mCounters.mRxData++;
2072         break;
2073 
2074 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2075     case Frame::kTypeMultipurpose:
2076         SuccessOrExit(error = HandleWakeupFrame(*aFrame));
2077         OT_FALL_THROUGH;
2078 #endif
2079 
2080     default:
2081         mCounters.mRxOther++;
2082         ExitNow();
2083     }
2084 
2085     DumpDebg("RX", aFrame->GetHeader(), aFrame->GetLength());
2086     Get<MeshForwarder>().HandleReceivedFrame(*aFrame);
2087 
2088     UpdateIdleMode();
2089 
2090 exit:
2091 
2092     if (error != kErrorNone)
2093     {
2094         LogFrameRxFailure(aFrame, error);
2095 
2096         switch (error)
2097         {
2098         case kErrorSecurity:
2099             mCounters.mRxErrSec++;
2100             break;
2101 
2102         case kErrorFcs:
2103             mCounters.mRxErrFcs++;
2104             break;
2105 
2106         case kErrorNoFrameReceived:
2107             mCounters.mRxErrNoFrame++;
2108             break;
2109 
2110         case kErrorUnknownNeighbor:
2111             mCounters.mRxErrUnknownNeighbor++;
2112             break;
2113 
2114         case kErrorInvalidSourceAddress:
2115             mCounters.mRxErrInvalidSrcAddr++;
2116             break;
2117 
2118         case kErrorAddressFiltered:
2119             mCounters.mRxAddressFiltered++;
2120             break;
2121 
2122         case kErrorDestinationAddressFiltered:
2123             mCounters.mRxDestAddrFiltered++;
2124             break;
2125 
2126         case kErrorDuplicated:
2127             mCounters.mRxDuplicated++;
2128             break;
2129 
2130         default:
2131             mCounters.mRxErrOther++;
2132             break;
2133         }
2134     }
2135 
2136 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2137 #if OPENTHREAD_CONFIG_MULTI_RADIO
2138     if (aFrame->GetRadioType() == kRadioTypeTrel)
2139 #endif
2140     {
2141         if (error == kErrorNone)
2142         {
2143             // If the received frame is using TREL and is successfully
2144             // processed, check for any discrepancy between the socket
2145             // address of the received TREL packet and the information
2146             // saved in the corresponding TREL peer, and signal this to
2147             // the platform layer.
2148             //
2149             // If the frame used link security and was successfully
2150             // processed, we allow the `Peer` entry socket information
2151             // to be updated directly.
2152 
2153             Get<Trel::Link>().CheckPeerAddrOnRxSuccess(aFrame->GetSecurityEnabled()
2154                                                            ? Trel::Link::kAllowPeerSockAddrUpdate
2155                                                            : Trel::Link::kDisallowPeerSockAddrUpdate);
2156         }
2157     }
2158 #endif // OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2159 }
2160 
UpdateNeighborLinkInfo(Neighbor & aNeighbor,const RxFrame & aRxFrame)2161 void Mac::UpdateNeighborLinkInfo(Neighbor &aNeighbor, const RxFrame &aRxFrame)
2162 {
2163     LinkQuality oldLinkQuality = aNeighbor.GetLinkInfo().GetLinkQualityIn();
2164 
2165     aNeighbor.GetLinkInfo().AddRss(aRxFrame.GetRssi());
2166 
2167 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
2168     aNeighbor.AggregateLinkMetrics(/* aSeriesId */ 0, aRxFrame.GetType(), aRxFrame.GetLqi(), aRxFrame.GetRssi());
2169 #endif
2170 
2171     // Signal when `aNeighbor` is the current parent and its link
2172     // quality gets changed.
2173 
2174     VerifyOrExit(Get<Mle::Mle>().IsChild() && (&aNeighbor == &Get<Mle::Mle>().GetParent()));
2175     VerifyOrExit(aNeighbor.GetLinkInfo().GetLinkQualityIn() != oldLinkQuality);
2176     Get<Notifier>().Signal(kEventParentLinkQualityChanged);
2177 
2178 exit:
2179     return;
2180 }
2181 
HandleMacCommand(RxFrame & aFrame)2182 bool Mac::HandleMacCommand(RxFrame &aFrame)
2183 {
2184     bool    didHandle = false;
2185     uint8_t commandId;
2186 
2187     IgnoreError(aFrame.GetCommandId(commandId));
2188 
2189     switch (commandId)
2190     {
2191     case Frame::kMacCmdBeaconRequest:
2192         mCounters.mRxBeaconRequest++;
2193         LogInfo("Received Beacon Request");
2194 
2195         if (ShouldSendBeacon())
2196         {
2197 #if OPENTHREAD_CONFIG_MULTI_RADIO
2198             mTxBeaconRadioLinks.Add(aFrame.GetRadioType());
2199 #endif
2200             StartOperation(kOperationTransmitBeacon);
2201         }
2202 
2203         didHandle = true;
2204         break;
2205 
2206     case Frame::kMacCmdDataRequest:
2207         mCounters.mRxDataPoll++;
2208 #if OPENTHREAD_FTD
2209         Get<DataPollHandler>().HandleDataPoll(aFrame);
2210         didHandle = true;
2211 #endif
2212         break;
2213 
2214     default:
2215         mCounters.mRxOther++;
2216         break;
2217     }
2218 
2219     return didHandle;
2220 }
2221 
SetPromiscuous(bool aPromiscuous)2222 void Mac::SetPromiscuous(bool aPromiscuous)
2223 {
2224     mPromiscuous = aPromiscuous;
2225     Get<Radio>().SetPromiscuous(aPromiscuous);
2226 
2227 #if OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS
2228     mDelayingSleep    = false;
2229     mShouldDelaySleep = false;
2230 #endif
2231 
2232     mLinks.SetRxOnWhenIdle(mRxOnWhenIdle || mPromiscuous);
2233     UpdateIdleMode();
2234 }
2235 
SetRegion(uint16_t aRegionCode)2236 Error Mac::SetRegion(uint16_t aRegionCode)
2237 {
2238     Error       error;
2239     ChannelMask oldMask = mSupportedChannelMask;
2240 
2241     SuccessOrExit(error = Get<Radio>().SetRegion(aRegionCode));
2242     mSupportedChannelMask.SetMask(Get<Radio>().GetSupportedChannelMask());
2243     IgnoreError(Get<Notifier>().Update(oldMask, mSupportedChannelMask, kEventSupportedChannelMaskChanged));
2244 
2245 exit:
2246     return error;
2247 }
2248 
GetRegion(uint16_t & aRegionCode) const2249 Error Mac::GetRegion(uint16_t &aRegionCode) const { return Get<Radio>().GetRegion(aRegionCode); }
2250 
2251 #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
GetDirectRetrySuccessHistogram(uint8_t & aNumberOfEntries)2252 const uint32_t *Mac::GetDirectRetrySuccessHistogram(uint8_t &aNumberOfEntries)
2253 {
2254     if (mMaxFrameRetriesDirect >= OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT)
2255     {
2256         aNumberOfEntries = OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_DIRECT;
2257     }
2258     else
2259     {
2260         aNumberOfEntries = mMaxFrameRetriesDirect + 1;
2261     }
2262 
2263     return mRetryHistogram.mTxDirectRetrySuccess;
2264 }
2265 
2266 #if OPENTHREAD_FTD
GetIndirectRetrySuccessHistogram(uint8_t & aNumberOfEntries)2267 const uint32_t *Mac::GetIndirectRetrySuccessHistogram(uint8_t &aNumberOfEntries)
2268 {
2269     if (mMaxFrameRetriesIndirect >= OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT)
2270     {
2271         aNumberOfEntries = OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_MAX_SIZE_COUNT_INDIRECT;
2272     }
2273     else
2274     {
2275         aNumberOfEntries = mMaxFrameRetriesIndirect + 1;
2276     }
2277 
2278     return mRetryHistogram.mTxIndirectRetrySuccess;
2279 }
2280 #endif
2281 
ResetRetrySuccessHistogram()2282 void Mac::ResetRetrySuccessHistogram() { ClearAllBytes(mRetryHistogram); }
2283 #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE
2284 
ComputeLinkMargin(int8_t aRss) const2285 uint8_t Mac::ComputeLinkMargin(int8_t aRss) const { return ot::ComputeLinkMargin(GetNoiseFloor(), aRss); }
2286 
2287 // LCOV_EXCL_START
2288 
2289 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2290 
OperationToString(Operation aOperation)2291 const char *Mac::OperationToString(Operation aOperation)
2292 {
2293     static const char *const kOperationStrings[] = {
2294         "Idle",               // (0) kOperationIdle
2295         "ActiveScan",         // (1) kOperationActiveScan
2296         "EnergyScan",         // (2) kOperationEnergyScan
2297         "TransmitBeacon",     // (3) kOperationTransmitBeacon
2298         "TransmitDataDirect", // (4) kOperationTransmitDataDirect
2299         "TransmitPoll",       // (5) kOperationTransmitPoll
2300         "WaitingForData",     // (6) kOperationWaitingForData
2301 #if OPENTHREAD_FTD
2302         "TransmitDataIndirect", // (7) kOperationTransmitDataIndirect
2303 #endif
2304 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2305         "TransmitDataCsl", // (8) kOperationTransmitDataCsl
2306 #endif
2307 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
2308         "TransmitWakeup", // kOperationTransmitWakeup
2309 #endif
2310     };
2311 
2312     struct OperationChecker
2313     {
2314         InitEnumValidatorCounter();
2315 
2316         ValidateNextEnum(kOperationIdle);
2317         ValidateNextEnum(kOperationActiveScan);
2318         ValidateNextEnum(kOperationEnergyScan);
2319         ValidateNextEnum(kOperationTransmitBeacon);
2320         ValidateNextEnum(kOperationTransmitDataDirect);
2321         ValidateNextEnum(kOperationTransmitPoll);
2322         ValidateNextEnum(kOperationWaitingForData);
2323 #if OPENTHREAD_FTD
2324         ValidateNextEnum(kOperationTransmitDataIndirect);
2325 #endif
2326 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2327         ValidateNextEnum(kOperationTransmitDataCsl);
2328 #endif
2329     };
2330 
2331     return kOperationStrings[aOperation];
2332 }
2333 
LogFrameRxFailure(const RxFrame * aFrame,Error aError) const2334 void Mac::LogFrameRxFailure(const RxFrame *aFrame, Error aError) const
2335 {
2336     LogLevel logLevel;
2337 
2338     switch (aError)
2339     {
2340     case kErrorAbort:
2341     case kErrorNoFrameReceived:
2342     case kErrorAddressFiltered:
2343     case kErrorDestinationAddressFiltered:
2344         logLevel = kLogLevelDebg;
2345         break;
2346 
2347     default:
2348         logLevel = kLogLevelInfo;
2349         break;
2350     }
2351 
2352     if (aFrame == nullptr)
2353     {
2354         LogAt(logLevel, "Frame rx failed, error:%s", ErrorToString(aError));
2355     }
2356     else
2357     {
2358         LogAt(logLevel, "Frame rx failed, error:%s, %s", ErrorToString(aError), aFrame->ToInfoString().AsCString());
2359     }
2360 }
2361 
LogFrameTxFailure(const TxFrame & aFrame,Error aError,uint8_t aRetryCount,bool aWillRetx) const2362 void Mac::LogFrameTxFailure(const TxFrame &aFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) const
2363 {
2364 #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
2365 #if OPENTHREAD_CONFIG_MULTI_RADIO
2366     if (aFrame.GetRadioType() == kRadioTypeIeee802154)
2367 #endif
2368     {
2369         uint8_t maxAttempts = aFrame.GetMaxFrameRetries() + 1;
2370         uint8_t curAttempt  = aWillRetx ? (aRetryCount + 1) : maxAttempts;
2371 
2372         LogInfo("Frame tx attempt %u/%u failed, error:%s, %s", curAttempt, maxAttempts, ErrorToString(aError),
2373                 aFrame.ToInfoString().AsCString());
2374     }
2375 #else
2376     OT_UNUSED_VARIABLE(aRetryCount);
2377     OT_UNUSED_VARIABLE(aWillRetx);
2378 #endif
2379 
2380 #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE
2381 #if OPENTHREAD_CONFIG_MULTI_RADIO
2382     if (aFrame.GetRadioType() == kRadioTypeTrel)
2383 #endif
2384     {
2385         if (Get<Trel::Interface>().IsEnabled())
2386         {
2387             LogInfo("Frame tx failed, error:%s, %s", ErrorToString(aError), aFrame.ToInfoString().AsCString());
2388         }
2389     }
2390 #endif
2391 }
2392 
LogBeacon(const char * aActionText) const2393 void Mac::LogBeacon(const char *aActionText) const { LogInfo("%s Beacon", aActionText); }
2394 
2395 #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2396 
LogFrameRxFailure(const RxFrame *,Error) const2397 void Mac::LogFrameRxFailure(const RxFrame *, Error) const {}
2398 
LogBeacon(const char *) const2399 void Mac::LogBeacon(const char *) const {}
2400 
LogFrameTxFailure(const TxFrame &,Error,uint8_t,bool) const2401 void Mac::LogFrameTxFailure(const TxFrame &, Error, uint8_t, bool) const {}
2402 
2403 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2404 
2405 // LCOV_EXCL_STOP
2406 
2407 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
GetTimeIeOffset(const Frame & aFrame)2408 uint8_t Mac::GetTimeIeOffset(const Frame &aFrame)
2409 {
2410     uint8_t        offset = 0;
2411     const uint8_t *base   = aFrame.GetPsdu();
2412     const uint8_t *cur    = nullptr;
2413 
2414     cur = reinterpret_cast<const uint8_t *>(aFrame.GetTimeIe());
2415     VerifyOrExit(cur != nullptr);
2416 
2417     cur += sizeof(VendorIeHeader);
2418     offset = static_cast<uint8_t>(cur - base);
2419 
2420 exit:
2421     return offset;
2422 }
2423 #endif
2424 
2425 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
UpdateCsl(void)2426 void Mac::UpdateCsl(void)
2427 {
2428     uint16_t period  = IsCslEnabled() ? GetCslPeriod() : 0;
2429     uint8_t  channel = GetCslChannel() ? GetCslChannel() : mRadioChannel;
2430 
2431     if (mLinks.UpdateCsl(period, channel, Get<Mle::Mle>().GetParent().GetRloc16(),
2432                          Get<Mle::Mle>().GetParent().GetExtAddress()))
2433     {
2434         if (Get<Mle::Mle>().IsChild())
2435         {
2436             Get<DataPollSender>().RecalculatePollPeriod();
2437 
2438             if (period != 0)
2439             {
2440                 Get<Mle::Mle>().ScheduleChildUpdateRequest();
2441             }
2442         }
2443 
2444         UpdateIdleMode();
2445     }
2446 }
2447 
SetCslChannel(uint8_t aChannel)2448 void Mac::SetCslChannel(uint8_t aChannel)
2449 {
2450     mCslChannel = aChannel;
2451     UpdateCsl();
2452 }
2453 
SetCslPeriod(uint16_t aPeriod)2454 void Mac::SetCslPeriod(uint16_t aPeriod)
2455 {
2456 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2457     if (IsWakeupListenEnabled() && aPeriod != 0)
2458     {
2459         IgnoreError(SetWakeupListenEnabled(false));
2460         LogWarn("Disabling wake-up frame listening due to CSL period change");
2461     }
2462 #endif
2463 
2464     mCslPeriod = aPeriod;
2465     UpdateCsl();
2466 }
2467 
GetCslPeriodInMsec(void) const2468 uint32_t Mac::GetCslPeriodInMsec(void) const
2469 {
2470     return DivideAndRoundToClosest<uint32_t>(CslPeriodToUsec(GetCslPeriod()), 1000u);
2471 }
2472 
CslPeriodToUsec(uint16_t aPeriodInTenSymbols)2473 uint32_t Mac::CslPeriodToUsec(uint16_t aPeriodInTenSymbols)
2474 {
2475     return static_cast<uint32_t>(aPeriodInTenSymbols) * kUsPerTenSymbols;
2476 }
2477 
IsCslEnabled(void) const2478 bool Mac::IsCslEnabled(void) const { return !Get<Mle::Mle>().IsRxOnWhenIdle() && IsCslCapable(); }
2479 
IsCslCapable(void) const2480 bool Mac::IsCslCapable(void) const { return (GetCslPeriod() > 0) && IsCslSupported(); }
2481 
IsCslSupported(void) const2482 bool Mac::IsCslSupported(void) const
2483 {
2484     return Get<Mle::MleRouter>().IsChild() && Get<Mle::Mle>().GetParent().IsEnhancedKeepAliveSupported();
2485 }
2486 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2487 
2488 #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2489 
ProcessCsl(const RxFrame & aFrame,const Address & aSrcAddr)2490 void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr)
2491 {
2492     CslNeighbor *neighbor = nullptr;
2493     const CslIe *csl;
2494 
2495     VerifyOrExit(aFrame.IsVersion2015() && aFrame.GetSecurityEnabled());
2496 
2497     csl = aFrame.GetCslIe();
2498     VerifyOrExit(csl != nullptr);
2499 
2500 #if OPENTHREAD_FTD
2501     neighbor = Get<ChildTable>().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid);
2502 #else
2503     OT_UNUSED_VARIABLE(aSrcAddr);
2504 #endif
2505 
2506     VerifyOrExit(neighbor != nullptr);
2507 
2508     VerifyOrExit(csl->GetPeriod() >= kMinCslIePeriod);
2509 
2510     neighbor->SetCslPeriod(csl->GetPeriod());
2511     neighbor->SetCslPhase(csl->GetPhase());
2512     neighbor->SetCslSynchronized(true);
2513     neighbor->SetCslLastHeard(TimerMilli::GetNow());
2514     neighbor->SetLastRxTimestamp(aFrame.GetTimestamp());
2515     LogDebg("Timestamp=%lu Sequence=%u CslPeriod=%u CslPhase=%u TransmitPhase=%u",
2516             ToUlong(static_cast<uint32_t>(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(),
2517             csl->GetPhase(), neighbor->GetCslPhase());
2518 
2519 #if OPENTHREAD_FTD
2520     Get<CslTxScheduler>().Update();
2521 #endif
2522 
2523 exit:
2524     return;
2525 }
2526 #endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
2527 
2528 #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
ProcessEnhAckProbing(const RxFrame & aFrame,const Neighbor & aNeighbor)2529 void Mac::ProcessEnhAckProbing(const RxFrame &aFrame, const Neighbor &aNeighbor)
2530 {
2531     constexpr uint8_t kEnhAckProbingIeMaxLen = 2;
2532 
2533     const HeaderIe *enhAckProbingIe =
2534         reinterpret_cast<const HeaderIe *>(aFrame.GetThreadIe(ThreadIe::kEnhAckProbingIe));
2535     const uint8_t *data =
2536         reinterpret_cast<const uint8_t *>(enhAckProbingIe) + sizeof(HeaderIe) + sizeof(VendorIeHeader);
2537     uint8_t dataLen = 0;
2538 
2539     VerifyOrExit(enhAckProbingIe != nullptr);
2540 
2541     dataLen = enhAckProbingIe->GetLength() - sizeof(VendorIeHeader);
2542     VerifyOrExit(dataLen <= kEnhAckProbingIeMaxLen);
2543 
2544     Get<LinkMetrics::Initiator>().ProcessEnhAckIeData(data, dataLen, aNeighbor);
2545 exit:
2546     return;
2547 }
2548 #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE
2549 
2550 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE
SetRadioFilterEnabled(bool aFilterEnabled)2551 void Mac::SetRadioFilterEnabled(bool aFilterEnabled)
2552 {
2553     mLinks.GetSubMac().SetRadioFilterEnabled(aFilterEnabled);
2554     UpdateIdleMode();
2555 }
2556 #endif
2557 
2558 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE || OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
SetWakeupChannel(uint8_t aChannel)2559 Error Mac::SetWakeupChannel(uint8_t aChannel)
2560 {
2561     Error error = kErrorNone;
2562 
2563     if (aChannel == 0)
2564     {
2565         mWakeupChannel = GetPanChannel();
2566         ExitNow();
2567     }
2568 
2569     VerifyOrExit(mSupportedChannelMask.ContainsChannel(aChannel), error = kErrorInvalidArgs);
2570     mWakeupChannel = aChannel;
2571 
2572 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2573     UpdateWakeupListening();
2574 #endif
2575 
2576 exit:
2577     return error;
2578 }
2579 #endif
2580 
2581 #if OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
GetWakeupListenParameters(uint32_t & aInterval,uint32_t & aDuration) const2582 void Mac::GetWakeupListenParameters(uint32_t &aInterval, uint32_t &aDuration) const
2583 {
2584     aInterval = mWakeupListenInterval;
2585     aDuration = mWakeupListenDuration;
2586 }
2587 
SetWakeupListenParameters(uint32_t aInterval,uint32_t aDuration)2588 Error Mac::SetWakeupListenParameters(uint32_t aInterval, uint32_t aDuration)
2589 {
2590     Error error = kErrorNone;
2591 
2592     VerifyOrExit(aDuration >= kMinWakeupListenDuration, error = kErrorInvalidArgs);
2593     VerifyOrExit(aInterval > aDuration, error = kErrorInvalidArgs);
2594 
2595     mWakeupListenInterval = aInterval;
2596     mWakeupListenDuration = aDuration;
2597     UpdateWakeupListening();
2598 
2599 exit:
2600     return error;
2601 }
2602 
SetWakeupListenEnabled(bool aEnable)2603 Error Mac::SetWakeupListenEnabled(bool aEnable)
2604 {
2605     Error error = kErrorNone;
2606 
2607 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
2608     if (aEnable && GetCslPeriod() > 0)
2609     {
2610         LogWarn("Cannot enable wake-up frame listening while CSL is enabled");
2611         ExitNow(error = kErrorInvalidState);
2612     }
2613 #endif
2614 
2615     if (aEnable == mWakeupListenEnabled)
2616     {
2617         LogInfo("Listening for wake up frames was already %s", aEnable ? "started" : "stopped");
2618         ExitNow();
2619     }
2620 
2621     mWakeupListenEnabled = aEnable;
2622     UpdateWakeupListening();
2623 
2624     LogInfo("Listening for wake up frames %s: chan:%u, addr:%s", aEnable ? "started" : "stopped", mWakeupChannel,
2625             GetExtAddress().ToString().AsCString());
2626 
2627 exit:
2628     return error;
2629 }
2630 
UpdateWakeupListening(void)2631 void Mac::UpdateWakeupListening(void)
2632 {
2633     uint8_t channel = mWakeupChannel ? mWakeupChannel : mPanChannel;
2634 
2635     mLinks.UpdateWakeupListening(mWakeupListenEnabled, mWakeupListenInterval, mWakeupListenDuration, channel);
2636 }
2637 
HandleWakeupFrame(const RxFrame & aFrame)2638 Error Mac::HandleWakeupFrame(const RxFrame &aFrame)
2639 {
2640     Error               error = kErrorNone;
2641     const ConnectionIe *connectionIe;
2642     uint32_t            rvTimeUs;
2643     uint64_t            rvTimestampUs;
2644     uint32_t            attachDelayMs;
2645     uint64_t            radioNowUs;
2646     uint8_t             retryInterval;
2647     uint8_t             retryCount;
2648 
2649     VerifyOrExit(mWakeupListenEnabled && aFrame.IsWakeupFrame());
2650     connectionIe  = aFrame.GetConnectionIe();
2651     retryInterval = connectionIe->GetRetryInterval();
2652     retryCount    = connectionIe->GetRetryCount();
2653     VerifyOrExit(retryInterval > 0 && retryCount > 0, error = kErrorInvalidArgs);
2654 
2655     radioNowUs    = otPlatRadioGetNow(&GetInstance());
2656     rvTimeUs      = aFrame.GetRendezvousTimeIe()->GetRendezvousTime() * kUsPerTenSymbols;
2657     rvTimestampUs = aFrame.GetTimestamp() + kRadioHeaderPhrDuration + aFrame.GetLength() * kOctetDuration + rvTimeUs;
2658 
2659     if (rvTimestampUs > radioNowUs + kCslRequestAhead)
2660     {
2661         attachDelayMs = static_cast<uint32_t>(rvTimestampUs - radioNowUs - kCslRequestAhead);
2662         attachDelayMs = attachDelayMs / 1000;
2663     }
2664     else
2665     {
2666         attachDelayMs = 0;
2667     }
2668 
2669 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
2670     {
2671         uint32_t frameCounter;
2672 
2673         IgnoreError(aFrame.GetFrameCounter(frameCounter));
2674         LogInfo("Received wake-up frame, fc:%lu, rendezvous:%luus, retries:%u/%u", ToUlong(frameCounter),
2675                 ToUlong(rvTimeUs), retryCount, retryInterval);
2676     }
2677 #endif
2678 
2679     // Stop receiving more wake up frames
2680     IgnoreError(SetWakeupListenEnabled(false));
2681 
2682     // TODO: start MLE attach process with the WC
2683     OT_UNUSED_VARIABLE(attachDelayMs);
2684 
2685 exit:
2686     return error;
2687 }
2688 #endif // OPENTHREAD_CONFIG_WAKEUP_END_DEVICE_ENABLE
2689 
CalculateRadioBusTransferTime(uint16_t aFrameSize) const2690 uint32_t Mac::CalculateRadioBusTransferTime(uint16_t aFrameSize) const
2691 {
2692     uint32_t busSpeed     = Get<Radio>().GetBusSpeed();
2693     uint32_t trasnferTime = 0;
2694 
2695     if (busSpeed != 0)
2696     {
2697         trasnferTime = DivideAndRoundUp<uint32_t>(aFrameSize * kBitsPerByte * Time::kOneSecondInUsec, busSpeed);
2698     }
2699 
2700     trasnferTime += Get<Radio>().GetBusLatency();
2701 
2702     return trasnferTime;
2703 }
2704 
2705 } // namespace Mac
2706 } // namespace ot
2707