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