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