1 /*
2  *  Copyright (c) 2016-2018, 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 MAC primitives.
32  */
33 
34 #include "sub_mac.hpp"
35 
36 #include <stdio.h>
37 
38 #include <openthread/platform/time.h>
39 
40 #include "common/code_utils.hpp"
41 #include "common/debug.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/log.hpp"
44 #include "common/num_utils.hpp"
45 #include "common/random.hpp"
46 #include "common/time.hpp"
47 #include "instance/instance.hpp"
48 #include "mac/mac_frame.hpp"
49 
50 namespace ot {
51 namespace Mac {
52 
53 RegisterLogModule("SubMac");
54 
SubMac(Instance & aInstance)55 SubMac::SubMac(Instance &aInstance)
56     : InstanceLocator(aInstance)
57     , mRadioCaps(Get<Radio>().GetCaps())
58     , mTransmitFrame(Get<Radio>().GetTransmitBuffer())
59     , mCallbacks(aInstance)
60     , mTimer(aInstance)
61 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
62     , mCslTimer(aInstance, SubMac::HandleCslTimer)
63 #endif
64 {
65 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
66     mCslParentAccuracy.Init();
67 #endif
68 
69     Init();
70 }
71 
Init(void)72 void SubMac::Init(void)
73 {
74     mState           = kStateDisabled;
75     mCsmaBackoffs    = 0;
76     mTransmitRetries = 0;
77     mShortAddress    = kShortAddrInvalid;
78     mExtAddress.Clear();
79     mRxOnWhenIdle      = true;
80     mEnergyScanMaxRssi = Radio::kInvalidRssi;
81     mEnergyScanEndTime = Time{0};
82 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
83     mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
84 #endif
85 
86 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
87     mRadioFilterEnabled = false;
88 #endif
89 
90     mPrevKey.Clear();
91     mCurrKey.Clear();
92     mNextKey.Clear();
93 
94     mFrameCounter = 0;
95     mKeyId        = 0;
96     mTimer.Stop();
97 
98 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
99     CslInit();
100 #endif
101 }
102 
GetCaps(void) const103 otRadioCaps SubMac::GetCaps(void) const
104 {
105     otRadioCaps caps;
106 
107 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
108     caps = mRadioCaps;
109 
110 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE
111     caps |= OT_RADIO_CAPS_ACK_TIMEOUT;
112 #endif
113 
114 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE
115     caps |= OT_RADIO_CAPS_CSMA_BACKOFF;
116 #endif
117 
118 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE
119     caps |= OT_RADIO_CAPS_TRANSMIT_RETRIES;
120 #endif
121 
122 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE
123     caps |= OT_RADIO_CAPS_ENERGY_SCAN;
124 #endif
125 
126 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
127     caps |= OT_RADIO_CAPS_TRANSMIT_SEC;
128 #endif
129 
130 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
131     caps |= OT_RADIO_CAPS_TRANSMIT_TIMING;
132 #endif
133 
134 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
135     caps |= OT_RADIO_CAPS_RECEIVE_TIMING;
136 #endif
137 
138 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE
139     caps |= OT_RADIO_CAPS_RX_ON_WHEN_IDLE;
140 #endif
141 
142 #else
143     caps = OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES |
144            OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
145            OT_RADIO_CAPS_RECEIVE_TIMING | OT_RADIO_CAPS_RX_ON_WHEN_IDLE;
146 #endif
147 
148     return caps;
149 }
150 
SetPanId(PanId aPanId)151 void SubMac::SetPanId(PanId aPanId)
152 {
153     Get<Radio>().SetPanId(aPanId);
154     LogDebg("RadioPanId: 0x%04x", aPanId);
155 }
156 
SetShortAddress(ShortAddress aShortAddress)157 void SubMac::SetShortAddress(ShortAddress aShortAddress)
158 {
159     mShortAddress = aShortAddress;
160     Get<Radio>().SetShortAddress(mShortAddress);
161     LogDebg("RadioShortAddress: 0x%04x", mShortAddress);
162 }
163 
SetExtAddress(const ExtAddress & aExtAddress)164 void SubMac::SetExtAddress(const ExtAddress &aExtAddress)
165 {
166     ExtAddress address;
167 
168     mExtAddress = aExtAddress;
169 
170     // Reverse the byte order before setting on radio.
171     address.Set(aExtAddress.m8, ExtAddress::kReverseByteOrder);
172     Get<Radio>().SetExtendedAddress(address);
173 
174     LogDebg("RadioExtAddress: %s", mExtAddress.ToString().AsCString());
175 }
176 
SetRxOnWhenIdle(bool aRxOnWhenIdle)177 void SubMac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
178 {
179     mRxOnWhenIdle = aRxOnWhenIdle;
180 
181     if (RadioSupportsRxOnWhenIdle())
182     {
183 #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
184         Get<Radio>().SetRxOnWhenIdle(mRxOnWhenIdle);
185 #endif
186     }
187 
188     LogDebg("RxOnWhenIdle: %d", mRxOnWhenIdle);
189 }
190 
Enable(void)191 Error SubMac::Enable(void)
192 {
193     Error error = kErrorNone;
194 
195     VerifyOrExit(mState == kStateDisabled);
196 
197     SuccessOrExit(error = Get<Radio>().Enable());
198     SuccessOrExit(error = Get<Radio>().Sleep());
199 
200     SetState(kStateSleep);
201 
202 exit:
203     SuccessOrAssert(error);
204     return error;
205 }
206 
Disable(void)207 Error SubMac::Disable(void)
208 {
209     Error error;
210 
211 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
212     mCslTimer.Stop();
213 #endif
214 
215     mTimer.Stop();
216     SuccessOrExit(error = Get<Radio>().Sleep());
217     SuccessOrExit(error = Get<Radio>().Disable());
218     SetState(kStateDisabled);
219 
220 exit:
221     return error;
222 }
223 
Sleep(void)224 Error SubMac::Sleep(void)
225 {
226     Error error = kErrorNone;
227 
228     VerifyOrExit(ShouldHandleTransitionToSleep());
229 
230     error = Get<Radio>().Sleep();
231 
232 exit:
233     if (error != kErrorNone)
234     {
235         LogWarn("RadioSleep() failed, error: %s", ErrorToString(error));
236     }
237     else
238     {
239         SetState(kStateSleep);
240     }
241 
242     return error;
243 }
244 
Receive(uint8_t aChannel)245 Error SubMac::Receive(uint8_t aChannel)
246 {
247     Error error;
248 
249 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
250     if (mRadioFilterEnabled)
251     {
252         error = Get<Radio>().Sleep();
253     }
254     else
255 #endif
256     {
257         error = Get<Radio>().Receive(aChannel);
258     }
259 
260     if (error != kErrorNone)
261     {
262         LogWarn("RadioReceive() failed, error: %s", ErrorToString(error));
263         ExitNow();
264     }
265 
266     SetState(kStateReceive);
267 
268 exit:
269     return error;
270 }
271 
HandleReceiveDone(RxFrame * aFrame,Error aError)272 void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError)
273 {
274     if (mPcapCallback.IsSet() && (aFrame != nullptr) && (aError == kErrorNone))
275     {
276         mPcapCallback.Invoke(aFrame, false);
277     }
278 
279     if (!ShouldHandleTransmitSecurity() && aFrame != nullptr && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
280     {
281         SignalFrameCounterUsed(aFrame->mInfo.mRxInfo.mAckFrameCounter, aFrame->mInfo.mRxInfo.mAckKeyId);
282     }
283 
284 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
285     UpdateCslLastSyncTimestamp(aFrame, aError);
286 #endif
287 
288 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
289     if (!mRadioFilterEnabled)
290 #endif
291     {
292         mCallbacks.ReceiveDone(aFrame, aError);
293     }
294 }
295 
Send(void)296 Error SubMac::Send(void)
297 {
298     Error error = kErrorNone;
299 
300     switch (mState)
301     {
302     case kStateDisabled:
303     case kStateCsmaBackoff:
304 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
305     case kStateCslTransmit:
306 #endif
307     case kStateTransmit:
308 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
309     case kStateDelayBeforeRetx:
310 #endif
311     case kStateSleep:
312     case kStateReceive:
313 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
314     case kStateCslSample:
315 #endif
316         break;
317 
318     case kStateEnergyScan:
319         ExitNow(error = kErrorInvalidState);
320     }
321 
322 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
323     if (mRadioFilterEnabled)
324     {
325         mCallbacks.TransmitDone(mTransmitFrame, nullptr, mTransmitFrame.GetAckRequest() ? kErrorNoAck : kErrorNone);
326         ExitNow();
327     }
328 #endif
329 
330     ProcessTransmitSecurity();
331 
332     mCsmaBackoffs    = 0;
333     mTransmitRetries = 0;
334 
335 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
336     mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
337 #endif
338 
339     StartCsmaBackoff();
340 
341 exit:
342     return error;
343 }
344 
ProcessTransmitSecurity(void)345 void SubMac::ProcessTransmitSecurity(void)
346 {
347     const ExtAddress *extAddress = nullptr;
348     uint8_t           keyIdMode;
349 
350     VerifyOrExit(mTransmitFrame.GetSecurityEnabled());
351     VerifyOrExit(!mTransmitFrame.IsSecurityProcessed());
352 
353     SuccessOrExit(mTransmitFrame.GetKeyIdMode(keyIdMode));
354 
355     if (!mTransmitFrame.IsHeaderUpdated())
356     {
357         mTransmitFrame.SetKeyId(mKeyId);
358     }
359 
360     VerifyOrExit(ShouldHandleTransmitSecurity());
361     VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
362 
363     mTransmitFrame.SetAesKey(GetCurrentMacKey());
364 
365     if (!mTransmitFrame.IsHeaderUpdated())
366     {
367         uint32_t frameCounter = GetFrameCounter();
368 
369         mTransmitFrame.SetFrameCounter(frameCounter);
370         SignalFrameCounterUsed(frameCounter, mKeyId);
371     }
372 
373     extAddress = &GetExtAddress();
374 
375 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
376     // Transmit security will be processed after time IE content is updated.
377     VerifyOrExit(mTransmitFrame.GetTimeIeOffset() == 0);
378 #endif
379 
380     mTransmitFrame.ProcessTransmitAesCcm(*extAddress);
381 
382 exit:
383     return;
384 }
385 
StartCsmaBackoff(void)386 void SubMac::StartCsmaBackoff(void)
387 {
388     uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
389 
390 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
391     if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)
392     {
393         SetState(kStateCslTransmit);
394 
395         if (ShouldHandleTransmitTargetTime())
396         {
397             static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration;
398             Time                      txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime);
399             Time                      radioNow    = Time(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
400 
401             txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime);
402 
403             if (radioNow < txStartTime)
404             {
405                 StartTimer(txStartTime - radioNow);
406             }
407             else // Transmit without delay
408             {
409                 BeginTransmit();
410             }
411         }
412         else
413         {
414             BeginTransmit();
415         }
416 
417         ExitNow();
418     }
419 #endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
420 
421     SetState(kStateCsmaBackoff);
422 
423     VerifyOrExit(ShouldHandleCsmaBackOff(), BeginTransmit());
424 
425     backoffExponent = Min(backoffExponent, kCsmaMaxBe);
426 
427     StartTimerForBackoff(backoffExponent);
428 
429 exit:
430     return;
431 }
432 
StartTimerForBackoff(uint8_t aBackoffExponent)433 void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
434 {
435     uint32_t backoff;
436 
437     backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
438     backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);
439 
440     if (mRxOnWhenIdle)
441     {
442         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
443     }
444     else
445     {
446         IgnoreError(Get<Radio>().Sleep());
447     }
448 
449     StartTimer(backoff);
450 
451 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
452     if (mState == kStateDelayBeforeRetx)
453     {
454         LogDebg("Delaying retx for %lu usec (be=%u)", ToUlong(backoff), aBackoffExponent);
455     }
456 #endif
457 }
458 
BeginTransmit(void)459 void SubMac::BeginTransmit(void)
460 {
461     Error error;
462 
463 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
464     VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateCslTransmit);
465 #else
466     VerifyOrExit(mState == kStateCsmaBackoff);
467 #endif
468 
469     if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)
470     {
471         SuccessOrAssert(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
472     }
473 
474     SetState(kStateTransmit);
475 
476     error = Get<Radio>().Transmit(mTransmitFrame);
477 
478     if (error == kErrorInvalidState && mTransmitFrame.mInfo.mTxInfo.mTxDelay > 0)
479     {
480         // Platform `transmit_at` fails and we send the frame directly.
481         mTransmitFrame.mInfo.mTxInfo.mTxDelay         = 0;
482         mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
483 
484         error = Get<Radio>().Transmit(mTransmitFrame);
485     }
486 
487     SuccessOrAssert(error);
488 
489 exit:
490     return;
491 }
492 
HandleTransmitStarted(TxFrame & aFrame)493 void SubMac::HandleTransmitStarted(TxFrame &aFrame)
494 {
495     if (mPcapCallback.IsSet())
496     {
497         mPcapCallback.Invoke(&aFrame, true);
498     }
499 
500     if (ShouldHandleAckTimeout() && aFrame.GetAckRequest())
501     {
502         StartTimer(kAckTimeout);
503     }
504 }
505 
HandleTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)506 void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
507 {
508     bool ccaSuccess = true;
509     bool shouldRetx;
510 
511     // Stop ack timeout timer.
512 
513     mTimer.Stop();
514 
515     // Record CCA success or failure status.
516 
517     switch (aError)
518     {
519     case kErrorAbort:
520         // Do not record CCA status in case of `ABORT` error
521         // since there may be no CCA check performed by radio.
522         break;
523 
524     case kErrorChannelAccessFailure:
525         ccaSuccess = false;
526 
527         OT_FALL_THROUGH;
528 
529     case kErrorNone:
530     case kErrorNoAck:
531         if (aFrame.IsCsmaCaEnabled())
532         {
533             mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel());
534         }
535 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
536         UpdateCslLastSyncTimestamp(aFrame, aAckFrame);
537 #endif
538         break;
539 
540     default:
541         OT_ASSERT(false);
542         OT_UNREACHABLE_CODE(ExitNow());
543     }
544 
545     SignalFrameCounterUsedOnTxDone(aFrame);
546 
547     // Determine whether a CSMA retry is required.
548 
549     if (!ccaSuccess && ShouldHandleCsmaBackOff() && mCsmaBackoffs < aFrame.GetMaxCsmaBackoffs())
550     {
551         mCsmaBackoffs++;
552         StartCsmaBackoff();
553         ExitNow();
554     }
555 
556     mCsmaBackoffs = 0;
557 
558     // Determine whether to re-transmit the frame.
559 
560     shouldRetx = ((aError != kErrorNone) && ShouldHandleRetries() && (mTransmitRetries < aFrame.GetMaxFrameRetries()));
561 
562     mCallbacks.RecordFrameTransmitStatus(aFrame, aError, mTransmitRetries, shouldRetx);
563 
564     if (shouldRetx)
565     {
566         mTransmitRetries++;
567         aFrame.SetIsARetransmission(true);
568 
569 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
570         if (aError == kErrorNoAck)
571         {
572             SetState(kStateDelayBeforeRetx);
573             StartTimerForBackoff(mRetxDelayBackOffExponent);
574             mRetxDelayBackOffExponent =
575                 Min(static_cast<uint8_t>(mRetxDelayBackOffExponent + 1), kRetxDelayMaxBackoffExponent);
576             ExitNow();
577         }
578 #endif
579 
580         StartCsmaBackoff();
581         ExitNow();
582     }
583 
584     SetState(kStateReceive);
585 
586 #if OPENTHREAD_RADIO
587     if (aFrame.GetChannel() != aFrame.GetRxChannelAfterTxDone())
588     {
589         // On RCP build, we switch immediately to the specified RX
590         // channel if it is different from the channel on which frame
591         // was sent. On FTD or MTD builds we don't need to do
592         // the same as the `Mac` will switch the channel from the
593         // `mCallbacks.TransmitDone()`.
594 
595         IgnoreError(Get<Radio>().Receive(aFrame.GetRxChannelAfterTxDone()));
596     }
597 #endif
598 
599     mCallbacks.TransmitDone(aFrame, aAckFrame, aError);
600 
601 exit:
602     return;
603 }
604 
SignalFrameCounterUsedOnTxDone(const TxFrame & aFrame)605 void SubMac::SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame)
606 {
607     uint8_t  keyIdMode;
608     uint8_t  keyId;
609     uint32_t frameCounter;
610     bool     allowError = false;
611 
612     OT_UNUSED_VARIABLE(allowError);
613 
614     VerifyOrExit(!ShouldHandleTransmitSecurity() && aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated());
615 
616     // In an FTD/MTD build, if/when link-raw is enabled, the `TxFrame`
617     // is prepared and given by user and may not necessarily follow 15.4
618     // frame format (link raw can be used with vendor-specific format),
619     // so we allow failure when parsing the frame (i.e., do not assert
620     // on an error). In other cases (in an RCP build or in an FTD/MTD
621     // build without link-raw) since the `TxFrame` should be prepared by
622     // OpenThread core, we expect no error and therefore assert if
623     // parsing fails.
624 
625 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
626     allowError = Get<LinkRaw>().IsEnabled();
627 #endif
628 
629     VerifyOrExit(aFrame.GetKeyIdMode(keyIdMode) == kErrorNone, OT_ASSERT(allowError));
630     VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
631 
632     VerifyOrExit(aFrame.GetFrameCounter(frameCounter) == kErrorNone, OT_ASSERT(allowError));
633     VerifyOrExit(aFrame.GetKeyId(keyId) == kErrorNone, OT_ASSERT(allowError));
634 
635     SignalFrameCounterUsed(frameCounter, keyId);
636 
637 exit:
638     return;
639 }
640 
GetRssi(void) const641 int8_t SubMac::GetRssi(void) const
642 {
643     int8_t rssi;
644 
645 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
646     if (mRadioFilterEnabled)
647     {
648         rssi = Radio::kInvalidRssi;
649     }
650     else
651 #endif
652     {
653         rssi = Get<Radio>().GetRssi();
654     }
655 
656     return rssi;
657 }
658 
GetNoiseFloor(void) const659 int8_t SubMac::GetNoiseFloor(void) const { return Get<Radio>().GetReceiveSensitivity(); }
660 
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration)661 Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
662 {
663     Error error = kErrorNone;
664 
665     switch (mState)
666     {
667     case kStateDisabled:
668     case kStateCsmaBackoff:
669     case kStateTransmit:
670 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
671     case kStateCslTransmit:
672 #endif
673 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
674     case kStateDelayBeforeRetx:
675 #endif
676     case kStateEnergyScan:
677         ExitNow(error = kErrorInvalidState);
678 
679     case kStateReceive:
680     case kStateSleep:
681 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
682     case kStateCslSample:
683 #endif
684         break;
685     }
686 
687 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
688     VerifyOrExit(!mRadioFilterEnabled, HandleEnergyScanDone(Radio::kInvalidRssi));
689 #endif
690 
691     if (RadioSupportsEnergyScan())
692     {
693         IgnoreError(Get<Radio>().EnergyScan(aScanChannel, aScanDuration));
694         SetState(kStateEnergyScan);
695     }
696     else if (ShouldHandleEnergyScan())
697     {
698         SuccessOrAssert(Get<Radio>().Receive(aScanChannel));
699 
700         SetState(kStateEnergyScan);
701         mEnergyScanMaxRssi = Radio::kInvalidRssi;
702         mEnergyScanEndTime = TimerMilli::GetNow() + static_cast<uint32_t>(aScanDuration);
703         StartTimer(0);
704     }
705     else
706     {
707         error = kErrorNotImplemented;
708     }
709 
710 exit:
711     return error;
712 }
713 
SampleRssi(void)714 void SubMac::SampleRssi(void)
715 {
716     OT_ASSERT(!RadioSupportsEnergyScan());
717 
718     int8_t rssi = GetRssi();
719 
720     if (rssi != Radio::kInvalidRssi)
721     {
722         if ((mEnergyScanMaxRssi == Radio::kInvalidRssi) || (rssi > mEnergyScanMaxRssi))
723         {
724             mEnergyScanMaxRssi = rssi;
725         }
726     }
727 
728     if (TimerMilli::GetNow() < mEnergyScanEndTime)
729     {
730         StartTimerAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval);
731     }
732     else
733     {
734         HandleEnergyScanDone(mEnergyScanMaxRssi);
735     }
736 }
737 
HandleEnergyScanDone(int8_t aMaxRssi)738 void SubMac::HandleEnergyScanDone(int8_t aMaxRssi)
739 {
740     SetState(kStateReceive);
741     mCallbacks.EnergyScanDone(aMaxRssi);
742 }
743 
HandleTimer(void)744 void SubMac::HandleTimer(void)
745 {
746     switch (mState)
747     {
748 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
749     case kStateCslTransmit:
750         BeginTransmit();
751         break;
752 #endif
753     case kStateCsmaBackoff:
754         BeginTransmit();
755         break;
756 
757     case kStateTransmit:
758         LogDebg("Ack timer timed out");
759         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
760         HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck);
761         break;
762 
763 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
764     case kStateDelayBeforeRetx:
765         StartCsmaBackoff();
766         break;
767 #endif
768 
769     case kStateEnergyScan:
770         SampleRssi();
771         break;
772 
773     default:
774         break;
775     }
776 }
777 
ShouldHandleTransmitSecurity(void) const778 bool SubMac::ShouldHandleTransmitSecurity(void) const
779 {
780     bool swTxSecurity = true;
781 
782     VerifyOrExit(!RadioSupportsTransmitSecurity(), swTxSecurity = false);
783 
784 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
785     VerifyOrExit(Get<LinkRaw>().IsEnabled());
786 #endif
787 
788 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
789     swTxSecurity = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE;
790 #endif
791 
792 exit:
793     return swTxSecurity;
794 }
795 
ShouldHandleCsmaBackOff(void) const796 bool SubMac::ShouldHandleCsmaBackOff(void) const
797 {
798     bool swCsma = true;
799 
800     VerifyOrExit(!RadioSupportsCsmaBackoff(), swCsma = false);
801 
802 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
803     VerifyOrExit(Get<LinkRaw>().IsEnabled());
804 #endif
805 
806 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
807     swCsma = OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE;
808 #endif
809 
810 exit:
811     return swCsma;
812 }
813 
ShouldHandleAckTimeout(void) const814 bool SubMac::ShouldHandleAckTimeout(void) const
815 {
816     bool swAckTimeout = true;
817 
818     VerifyOrExit(!RadioSupportsAckTimeout(), swAckTimeout = false);
819 
820 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
821     VerifyOrExit(Get<LinkRaw>().IsEnabled());
822 #endif
823 
824 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
825     swAckTimeout = OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE;
826 #endif
827 
828 exit:
829     return swAckTimeout;
830 }
831 
ShouldHandleRetries(void) const832 bool SubMac::ShouldHandleRetries(void) const
833 {
834     bool swRetries = true;
835 
836     VerifyOrExit(!RadioSupportsRetries(), swRetries = false);
837 
838 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
839     VerifyOrExit(Get<LinkRaw>().IsEnabled());
840 #endif
841 
842 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
843     swRetries = OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE;
844 #endif
845 
846 exit:
847     return swRetries;
848 }
849 
ShouldHandleEnergyScan(void) const850 bool SubMac::ShouldHandleEnergyScan(void) const
851 {
852     bool swEnergyScan = true;
853 
854     VerifyOrExit(!RadioSupportsEnergyScan(), swEnergyScan = false);
855 
856 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
857     VerifyOrExit(Get<LinkRaw>().IsEnabled());
858 #endif
859 
860 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
861     swEnergyScan = OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE;
862 #endif
863 
864 exit:
865     return swEnergyScan;
866 }
867 
ShouldHandleTransmitTargetTime(void) const868 bool SubMac::ShouldHandleTransmitTargetTime(void) const
869 {
870     bool swTxDelay = true;
871 
872     VerifyOrExit(!RadioSupportsTransmitTiming(), swTxDelay = false);
873 
874 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
875     VerifyOrExit(Get<LinkRaw>().IsEnabled());
876 #endif
877 
878 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
879     swTxDelay = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE;
880 #endif
881 
882 exit:
883     return swTxDelay;
884 }
885 
ShouldHandleTransitionToSleep(void) const886 bool SubMac::ShouldHandleTransitionToSleep(void) const { return (mRxOnWhenIdle || !RadioSupportsRxOnWhenIdle()); }
887 
SetState(State aState)888 void SubMac::SetState(State aState)
889 {
890     if (mState != aState)
891     {
892         LogDebg("RadioState: %s -> %s", StateToString(mState), StateToString(aState));
893         mState = aState;
894     }
895 }
896 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const KeyMaterial & aPrevKey,const KeyMaterial & aCurrKey,const KeyMaterial & aNextKey)897 void SubMac::SetMacKey(uint8_t            aKeyIdMode,
898                        uint8_t            aKeyId,
899                        const KeyMaterial &aPrevKey,
900                        const KeyMaterial &aCurrKey,
901                        const KeyMaterial &aNextKey)
902 {
903     switch (aKeyIdMode)
904     {
905     case Frame::kKeyIdMode0:
906     case Frame::kKeyIdMode2:
907         break;
908     case Frame::kKeyIdMode1:
909         mKeyId   = aKeyId;
910         mPrevKey = aPrevKey;
911         mCurrKey = aCurrKey;
912         mNextKey = aNextKey;
913         break;
914 
915     default:
916         OT_ASSERT(false);
917         break;
918     }
919 
920     VerifyOrExit(!ShouldHandleTransmitSecurity());
921 
922     Get<Radio>().SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey);
923 
924 exit:
925     return;
926 }
927 
SignalFrameCounterUsed(uint32_t aFrameCounter,uint8_t aKeyId)928 void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId)
929 {
930     VerifyOrExit(aKeyId == mKeyId);
931 
932     mCallbacks.FrameCounterUsed(aFrameCounter);
933 
934     // It not always guaranteed that this method is invoked in order
935     // for different counter values (i.e., we may get it for a
936     // smaller counter value after a lager one). This may happen due
937     // to a new counter value being used for an enhanced-ack during
938     // tx of a frame. Note that the newer counter used for enhanced-ack
939     // is processed from `HandleReceiveDone()` which can happen before
940     // processing of the older counter value from `HandleTransmitDone()`.
941 
942     VerifyOrExit(mFrameCounter <= aFrameCounter);
943     mFrameCounter = aFrameCounter + 1;
944 
945 exit:
946     return;
947 }
948 
SetFrameCounter(uint32_t aFrameCounter,bool aSetIfLarger)949 void SubMac::SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger)
950 {
951     if (!aSetIfLarger || (aFrameCounter > mFrameCounter))
952     {
953         mFrameCounter = aFrameCounter;
954     }
955 
956     VerifyOrExit(!ShouldHandleTransmitSecurity());
957 
958     if (aSetIfLarger)
959     {
960         Get<Radio>().SetMacFrameCounterIfLarger(aFrameCounter);
961     }
962     else
963     {
964         Get<Radio>().SetMacFrameCounter(aFrameCounter);
965     }
966 
967 exit:
968     return;
969 }
970 
StartTimer(uint32_t aDelayUs)971 void SubMac::StartTimer(uint32_t aDelayUs)
972 {
973 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
974     mTimer.Start(aDelayUs);
975 #else
976     mTimer.Start(aDelayUs / Time::kOneMsecInUsec);
977 #endif
978 }
979 
StartTimerAt(Time aStartTime,uint32_t aDelayUs)980 void SubMac::StartTimerAt(Time aStartTime, uint32_t aDelayUs)
981 {
982 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
983     mTimer.StartAt(aStartTime, aDelayUs);
984 #else
985     mTimer.StartAt(aStartTime, aDelayUs / Time::kOneMsecInUsec);
986 #endif
987 }
988 
989 // LCOV_EXCL_START
990 
StateToString(State aState)991 const char *SubMac::StateToString(State aState)
992 {
993     static const char *const kStateStrings[] = {
994         "Disabled",    // (0) kStateDisabled
995         "Sleep",       // (1) kStateSleep
996         "Receive",     // (2) kStateReceive
997         "CsmaBackoff", // (3) kStateCsmaBackoff
998         "Transmit",    // (4) kStateTransmit
999         "EnergyScan",  // (5) kStateEnergyScan
1000 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1001         "DelayBeforeRetx", // (6) kStateDelayBeforeRetx
1002 #endif
1003 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1004         "CslTransmit", // (7) kStateCslTransmit
1005 #endif
1006 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1007         "CslSample", // (8) kStateCslSample
1008 #endif
1009     };
1010 
1011     static_assert(kStateDisabled == 0, "kStateDisabled value is not correct");
1012     static_assert(kStateSleep == 1, "kStateSleep value is not correct");
1013     static_assert(kStateReceive == 2, "kStateReceive value is not correct");
1014     static_assert(kStateCsmaBackoff == 3, "kStateCsmaBackoff value is not correct");
1015     static_assert(kStateTransmit == 4, "kStateTransmit value is not correct");
1016     static_assert(kStateEnergyScan == 5, "kStateEnergyScan value is not correct");
1017 
1018 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1019     static_assert(kStateDelayBeforeRetx == 6, "kStateDelayBeforeRetx value is not correct");
1020 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1021     static_assert(kStateCslTransmit == 7, "kStateCslTransmit value is not correct");
1022 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1023     static_assert(kStateCslSample == 8, "kStateCslSample value is not correct");
1024 #endif
1025 #elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1026     static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
1027 #endif
1028 
1029 #elif !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1030     static_assert(kStateCslTransmit == 6, "kStateCslTransmit value is not correct");
1031 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1032     static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
1033 #endif
1034 #elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1035     static_assert(kStateCslSample == 6, "kStateCslSample value is not correct");
1036 #endif
1037 
1038     return kStateStrings[aState];
1039 }
1040 
1041 // LCOV_EXCL_STOP
1042 
1043 } // namespace Mac
1044 } // namespace ot
1045