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