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     VerifyOrExit(ShouldHandleTransitionToSleep());
244 
245     error = Get<Radio>().Sleep();
246 
247 exit:
248     if (error != kErrorNone)
249     {
250         LogWarn("RadioSleep() failed, error: %s", ErrorToString(error));
251     }
252     else
253     {
254         SetState(kStateSleep);
255     }
256 
257     return error;
258 }
259 
Receive(uint8_t aChannel)260 Error SubMac::Receive(uint8_t aChannel)
261 {
262     Error error;
263 
264 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
265     if (mRadioFilterEnabled)
266     {
267         error = Get<Radio>().Sleep();
268     }
269     else
270 #endif
271     {
272         error = Get<Radio>().Receive(aChannel);
273     }
274 
275     if (error != kErrorNone)
276     {
277         LogWarn("RadioReceive() failed, error: %s", ErrorToString(error));
278         ExitNow();
279     }
280 
281     SetState(kStateReceive);
282 
283 exit:
284     return error;
285 }
286 
HandleReceiveDone(RxFrame * aFrame,Error aError)287 void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError)
288 {
289     if (mPcapCallback.IsSet() && (aFrame != nullptr) && (aError == kErrorNone))
290     {
291         mPcapCallback.Invoke(aFrame, false);
292     }
293 
294     if (!ShouldHandleTransmitSecurity() && aFrame != nullptr && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
295     {
296         SignalFrameCounterUsed(aFrame->mInfo.mRxInfo.mAckFrameCounter, aFrame->mInfo.mRxInfo.mAckKeyId);
297     }
298 
299 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
300     UpdateCslLastSyncTimestamp(aFrame, aError);
301 #endif
302 
303 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
304     if (!mRadioFilterEnabled)
305 #endif
306     {
307         mCallbacks.ReceiveDone(aFrame, aError);
308     }
309 }
310 
Send(void)311 Error SubMac::Send(void)
312 {
313     Error error = kErrorNone;
314 
315     switch (mState)
316     {
317     case kStateDisabled:
318     case kStateCsmaBackoff:
319 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
320     case kStateCslTransmit:
321 #endif
322     case kStateTransmit:
323 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
324     case kStateDelayBeforeRetx:
325 #endif
326     case kStateSleep:
327     case kStateReceive:
328 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
329     case kStateCslSample:
330 #endif
331         break;
332 
333     case kStateEnergyScan:
334         ExitNow(error = kErrorInvalidState);
335     }
336 
337 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
338     if (mRadioFilterEnabled)
339     {
340         mCallbacks.TransmitDone(mTransmitFrame, nullptr, mTransmitFrame.GetAckRequest() ? kErrorNoAck : kErrorNone);
341         ExitNow();
342     }
343 #endif
344 
345     ProcessTransmitSecurity();
346 
347     mCsmaBackoffs    = 0;
348     mTransmitRetries = 0;
349 
350 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
351     mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
352 #endif
353 
354     StartCsmaBackoff();
355 
356 exit:
357     return error;
358 }
359 
ProcessTransmitSecurity(void)360 void SubMac::ProcessTransmitSecurity(void)
361 {
362     const ExtAddress *extAddress = nullptr;
363     uint8_t           keyIdMode;
364 
365     VerifyOrExit(mTransmitFrame.GetSecurityEnabled());
366     VerifyOrExit(!mTransmitFrame.IsSecurityProcessed());
367 
368     SuccessOrExit(mTransmitFrame.GetKeyIdMode(keyIdMode));
369 
370     if (!mTransmitFrame.IsHeaderUpdated())
371     {
372         mTransmitFrame.SetKeyId(mKeyId);
373     }
374 
375     VerifyOrExit(ShouldHandleTransmitSecurity());
376 
377 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
378     if (mTransmitFrame.GetType() == Frame::kTypeMultipurpose)
379     {
380         VerifyOrExit(keyIdMode == Frame::kKeyIdMode2);
381     }
382     else
383 #endif
384     {
385         VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
386     }
387 
388     mTransmitFrame.SetAesKey(GetCurrentMacKey());
389 
390     if (!mTransmitFrame.IsHeaderUpdated())
391     {
392         uint32_t frameCounter = GetFrameCounter();
393 
394         mTransmitFrame.SetFrameCounter(frameCounter);
395         SignalFrameCounterUsed(frameCounter, mKeyId);
396     }
397 
398     extAddress = &GetExtAddress();
399 
400 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
401     // Transmit security will be processed after time IE content is updated.
402     VerifyOrExit(mTransmitFrame.GetTimeIeOffset() == 0);
403 #endif
404 
405     mTransmitFrame.ProcessTransmitAesCcm(*extAddress);
406 
407 exit:
408     return;
409 }
410 
StartCsmaBackoff(void)411 void SubMac::StartCsmaBackoff(void)
412 {
413     uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
414 
415 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
416     if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0 || mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime != 0)
417     {
418         SetState(kStateCslTransmit);
419 
420         if (ShouldHandleTransmitTargetTime())
421         {
422             static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration;
423             Time                      txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime);
424             Time                      radioNow    = Time(static_cast<uint32_t>(Get<Radio>().GetNow()));
425 
426             txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime);
427 
428             if (radioNow < txStartTime)
429             {
430                 StartTimer(txStartTime - radioNow);
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(mTransmitFrame.GetMaxCsmaBackoffs() > 0 && ShouldHandleCsmaBackOff(), BeginTransmit());
449 
450     backoffExponent = Min(backoffExponent, kCsmaMaxBe);
451 
452     StartTimerForBackoff(backoffExponent);
453 
454 exit:
455     return;
456 }
457 
StartTimerForBackoff(uint8_t aBackoffExponent)458 void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
459 {
460     uint32_t backoff;
461 
462     backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
463     backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);
464 
465     if (mRxOnWhenIdle)
466     {
467         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
468     }
469     else
470     {
471         IgnoreError(Get<Radio>().Sleep());
472     }
473 
474     StartTimer(backoff);
475 
476 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
477     if (mState == kStateDelayBeforeRetx)
478     {
479         LogDebg("Delaying retx for %lu usec (be=%u)", ToUlong(backoff), aBackoffExponent);
480     }
481 #endif
482 }
483 
BeginTransmit(void)484 void SubMac::BeginTransmit(void)
485 {
486     Error error;
487 
488 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
489     VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateCslTransmit);
490 #else
491     VerifyOrExit(mState == kStateCsmaBackoff);
492 #endif
493 
494     if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)
495     {
496         SuccessOrAssert(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
497     }
498 
499     SetState(kStateTransmit);
500 
501     error = Get<Radio>().Transmit(mTransmitFrame);
502 
503     if (error == kErrorInvalidState && mTransmitFrame.mInfo.mTxInfo.mTxDelay > 0)
504     {
505         // Platform `transmit_at` fails and we send the frame directly.
506         mTransmitFrame.mInfo.mTxInfo.mTxDelay         = 0;
507         mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
508 
509         error = Get<Radio>().Transmit(mTransmitFrame);
510     }
511 
512     SuccessOrAssert(error);
513 
514 exit:
515     return;
516 }
517 
HandleTransmitStarted(TxFrame & aFrame)518 void SubMac::HandleTransmitStarted(TxFrame &aFrame)
519 {
520     if (mPcapCallback.IsSet())
521     {
522         mPcapCallback.Invoke(&aFrame, true);
523     }
524 
525     if (ShouldHandleAckTimeout() && aFrame.GetAckRequest())
526     {
527         StartTimer(kAckTimeout);
528     }
529 }
530 
HandleTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)531 void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
532 {
533     bool ccaSuccess = true;
534     bool shouldRetx;
535 
536     // Stop ack timeout timer.
537 
538     mTimer.Stop();
539 
540     // Record CCA success or failure status.
541 
542     switch (aError)
543     {
544     case kErrorAbort:
545         // Do not record CCA status in case of `ABORT` error
546         // since there may be no CCA check performed by radio.
547         break;
548 
549     case kErrorChannelAccessFailure:
550         ccaSuccess = false;
551 
552         OT_FALL_THROUGH;
553 
554     case kErrorNone:
555     case kErrorNoAck:
556         if (aFrame.IsCsmaCaEnabled())
557         {
558             mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel());
559         }
560 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
561         UpdateCslLastSyncTimestamp(aFrame, aAckFrame);
562 #endif
563         break;
564 
565     default:
566         OT_ASSERT(false);
567         OT_UNREACHABLE_CODE(ExitNow());
568     }
569 
570     SignalFrameCounterUsedOnTxDone(aFrame);
571 
572     // Determine whether a CSMA retry is required.
573 
574     if (!ccaSuccess && ShouldHandleCsmaBackOff() && mCsmaBackoffs < aFrame.GetMaxCsmaBackoffs())
575     {
576         mCsmaBackoffs++;
577         StartCsmaBackoff();
578         ExitNow();
579     }
580 
581     mCsmaBackoffs = 0;
582 
583     // Determine whether to re-transmit the frame.
584 
585     shouldRetx = ((aError != kErrorNone) && ShouldHandleRetries() && (mTransmitRetries < aFrame.GetMaxFrameRetries()));
586 
587     mCallbacks.RecordFrameTransmitStatus(aFrame, aError, mTransmitRetries, shouldRetx);
588 
589     if (shouldRetx)
590     {
591         mTransmitRetries++;
592         aFrame.SetIsARetransmission(true);
593 
594 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
595         if (aError == kErrorNoAck)
596         {
597             SetState(kStateDelayBeforeRetx);
598             StartTimerForBackoff(mRetxDelayBackOffExponent);
599             mRetxDelayBackOffExponent =
600                 Min(static_cast<uint8_t>(mRetxDelayBackOffExponent + 1), kRetxDelayMaxBackoffExponent);
601             ExitNow();
602         }
603 #endif
604 
605         StartCsmaBackoff();
606         ExitNow();
607     }
608 
609     SetState(kStateReceive);
610 
611 #if OPENTHREAD_RADIO
612     if (aFrame.GetChannel() != aFrame.GetRxChannelAfterTxDone() && mRxOnWhenIdle)
613     {
614         // On RCP build, we switch immediately to the specified RX
615         // channel if it is different from the channel on which frame
616         // was sent. On FTD or MTD builds we don't need to do
617         // the same as the `Mac` will switch the channel from the
618         // `mCallbacks.TransmitDone()`.
619 
620         IgnoreError(Get<Radio>().Receive(aFrame.GetRxChannelAfterTxDone()));
621     }
622 #endif
623 
624     mCallbacks.TransmitDone(aFrame, aAckFrame, aError);
625 
626 exit:
627     return;
628 }
629 
SignalFrameCounterUsedOnTxDone(const TxFrame & aFrame)630 void SubMac::SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame)
631 {
632     uint8_t  keyIdMode;
633     uint8_t  keyId;
634     uint32_t frameCounter;
635     bool     allowError = false;
636 
637     OT_UNUSED_VARIABLE(allowError);
638 
639     VerifyOrExit(!ShouldHandleTransmitSecurity() && aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated());
640 
641     // In an FTD/MTD build, if/when link-raw is enabled, the `TxFrame`
642     // is prepared and given by user and may not necessarily follow 15.4
643     // frame format (link raw can be used with vendor-specific format),
644     // so we allow failure when parsing the frame (i.e., do not assert
645     // on an error). In other cases (in an RCP build or in an FTD/MTD
646     // build without link-raw) since the `TxFrame` should be prepared by
647     // OpenThread core, we expect no error and therefore assert if
648     // parsing fails.
649 
650 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
651     allowError = Get<LinkRaw>().IsEnabled();
652 #endif
653 
654     VerifyOrExit(aFrame.GetKeyIdMode(keyIdMode) == kErrorNone, OT_ASSERT(allowError));
655     VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
656 
657     VerifyOrExit(aFrame.GetFrameCounter(frameCounter) == kErrorNone, OT_ASSERT(allowError));
658     VerifyOrExit(aFrame.GetKeyId(keyId) == kErrorNone, OT_ASSERT(allowError));
659 
660     SignalFrameCounterUsed(frameCounter, keyId);
661 
662 exit:
663     return;
664 }
665 
GetRssi(void) const666 int8_t SubMac::GetRssi(void) const
667 {
668     int8_t rssi;
669 
670 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
671     if (mRadioFilterEnabled)
672     {
673         rssi = Radio::kInvalidRssi;
674     }
675     else
676 #endif
677     {
678         rssi = Get<Radio>().GetRssi();
679     }
680 
681     return rssi;
682 }
683 
GetNoiseFloor(void) const684 int8_t SubMac::GetNoiseFloor(void) const { return Get<Radio>().GetReceiveSensitivity(); }
685 
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration)686 Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
687 {
688     Error error = kErrorNone;
689 
690     switch (mState)
691     {
692     case kStateDisabled:
693     case kStateCsmaBackoff:
694     case kStateTransmit:
695 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
696     case kStateCslTransmit:
697 #endif
698 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
699     case kStateDelayBeforeRetx:
700 #endif
701     case kStateEnergyScan:
702         ExitNow(error = kErrorInvalidState);
703 
704     case kStateReceive:
705     case kStateSleep:
706 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
707     case kStateCslSample:
708 #endif
709         break;
710     }
711 
712 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
713     VerifyOrExit(!mRadioFilterEnabled, HandleEnergyScanDone(Radio::kInvalidRssi));
714 #endif
715 
716     if (RadioSupportsEnergyScan())
717     {
718         IgnoreError(Get<Radio>().EnergyScan(aScanChannel, aScanDuration));
719         SetState(kStateEnergyScan);
720     }
721     else if (ShouldHandleEnergyScan())
722     {
723         SuccessOrAssert(Get<Radio>().Receive(aScanChannel));
724 
725         SetState(kStateEnergyScan);
726         mEnergyScanMaxRssi = Radio::kInvalidRssi;
727         mEnergyScanEndTime = TimerMilli::GetNow() + static_cast<uint32_t>(aScanDuration);
728         StartTimer(0);
729     }
730     else
731     {
732         error = kErrorNotImplemented;
733     }
734 
735 exit:
736     return error;
737 }
738 
SampleRssi(void)739 void SubMac::SampleRssi(void)
740 {
741     OT_ASSERT(!RadioSupportsEnergyScan());
742 
743     int8_t rssi = GetRssi();
744 
745     if (rssi != Radio::kInvalidRssi)
746     {
747         if ((mEnergyScanMaxRssi == Radio::kInvalidRssi) || (rssi > mEnergyScanMaxRssi))
748         {
749             mEnergyScanMaxRssi = rssi;
750         }
751     }
752 
753     if (TimerMilli::GetNow() < mEnergyScanEndTime)
754     {
755         StartTimerAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval);
756     }
757     else
758     {
759         HandleEnergyScanDone(mEnergyScanMaxRssi);
760     }
761 }
762 
HandleEnergyScanDone(int8_t aMaxRssi)763 void SubMac::HandleEnergyScanDone(int8_t aMaxRssi)
764 {
765     SetState(kStateReceive);
766     mCallbacks.EnergyScanDone(aMaxRssi);
767 }
768 
HandleTimer(void)769 void SubMac::HandleTimer(void)
770 {
771     switch (mState)
772     {
773 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
774     case kStateCslTransmit:
775         BeginTransmit();
776         break;
777 #endif
778     case kStateCsmaBackoff:
779         BeginTransmit();
780         break;
781 
782     case kStateTransmit:
783         LogDebg("Ack timer timed out");
784         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
785         HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck);
786         break;
787 
788 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
789     case kStateDelayBeforeRetx:
790         StartCsmaBackoff();
791         break;
792 #endif
793 
794     case kStateEnergyScan:
795         SampleRssi();
796         break;
797 
798     default:
799         break;
800     }
801 }
802 
ShouldHandleTransmitSecurity(void) const803 bool SubMac::ShouldHandleTransmitSecurity(void) const
804 {
805     bool swTxSecurity = true;
806 
807     VerifyOrExit(!RadioSupportsTransmitSecurity(), swTxSecurity = false);
808 
809 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
810     VerifyOrExit(Get<LinkRaw>().IsEnabled());
811 #endif
812 
813 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
814     swTxSecurity = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE;
815 #endif
816 
817 exit:
818     return swTxSecurity;
819 }
820 
ShouldHandleCsmaBackOff(void) const821 bool SubMac::ShouldHandleCsmaBackOff(void) const
822 {
823     bool swCsma = true;
824 
825     VerifyOrExit(mTransmitFrame.IsCsmaCaEnabled() && !RadioSupportsCsmaBackoff(), swCsma = 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     swCsma = OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE;
833 #endif
834 
835 exit:
836     return swCsma;
837 }
838 
ShouldHandleAckTimeout(void) const839 bool SubMac::ShouldHandleAckTimeout(void) const
840 {
841     bool swAckTimeout = true;
842 
843     VerifyOrExit(!RadioSupportsAckTimeout(), swAckTimeout = 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     swAckTimeout = OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE;
851 #endif
852 
853 exit:
854     return swAckTimeout;
855 }
856 
ShouldHandleRetries(void) const857 bool SubMac::ShouldHandleRetries(void) const
858 {
859     bool swRetries = true;
860 
861     VerifyOrExit(!RadioSupportsRetries(), swRetries = 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     swRetries = OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE;
869 #endif
870 
871 exit:
872     return swRetries;
873 }
874 
ShouldHandleEnergyScan(void) const875 bool SubMac::ShouldHandleEnergyScan(void) const
876 {
877     bool swEnergyScan = true;
878 
879     VerifyOrExit(!RadioSupportsEnergyScan(), swEnergyScan = 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     swEnergyScan = OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE;
887 #endif
888 
889 exit:
890     return swEnergyScan;
891 }
892 
ShouldHandleTransmitTargetTime(void) const893 bool SubMac::ShouldHandleTransmitTargetTime(void) const
894 {
895     bool swTxDelay = true;
896 
897     VerifyOrExit(!RadioSupportsTransmitTiming(), swTxDelay = 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     swTxDelay = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE;
905 #endif
906 
907 exit:
908     return swTxDelay;
909 }
910 
ShouldHandleTransitionToSleep(void) const911 bool SubMac::ShouldHandleTransitionToSleep(void) const { return (mRxOnWhenIdle || !RadioSupportsRxOnWhenIdle()); }
912 
SetState(State aState)913 void SubMac::SetState(State aState)
914 {
915     if (mState != aState)
916     {
917         LogDebg("RadioState: %s -> %s", StateToString(mState), StateToString(aState));
918         mState = aState;
919     }
920 }
921 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const KeyMaterial & aPrevKey,const KeyMaterial & aCurrKey,const KeyMaterial & aNextKey)922 void SubMac::SetMacKey(uint8_t            aKeyIdMode,
923                        uint8_t            aKeyId,
924                        const KeyMaterial &aPrevKey,
925                        const KeyMaterial &aCurrKey,
926                        const KeyMaterial &aNextKey)
927 {
928     switch (aKeyIdMode)
929     {
930     case Frame::kKeyIdMode0:
931     case Frame::kKeyIdMode2:
932         break;
933     case Frame::kKeyIdMode1:
934         mKeyId   = aKeyId;
935         mPrevKey = aPrevKey;
936         mCurrKey = aCurrKey;
937         mNextKey = aNextKey;
938         break;
939 
940     default:
941         OT_ASSERT(false);
942         break;
943     }
944 
945     VerifyOrExit(!ShouldHandleTransmitSecurity());
946 
947     Get<Radio>().SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey);
948 
949 exit:
950     return;
951 }
952 
SignalFrameCounterUsed(uint32_t aFrameCounter,uint8_t aKeyId)953 void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId)
954 {
955     VerifyOrExit(aKeyId == mKeyId);
956 
957     mCallbacks.FrameCounterUsed(aFrameCounter);
958 
959     // It not always guaranteed that this method is invoked in order
960     // for different counter values (i.e., we may get it for a
961     // smaller counter value after a lager one). This may happen due
962     // to a new counter value being used for an enhanced-ack during
963     // tx of a frame. Note that the newer counter used for enhanced-ack
964     // is processed from `HandleReceiveDone()` which can happen before
965     // processing of the older counter value from `HandleTransmitDone()`.
966 
967     VerifyOrExit(mFrameCounter <= aFrameCounter);
968     mFrameCounter = aFrameCounter + 1;
969 
970 exit:
971     return;
972 }
973 
SetFrameCounter(uint32_t aFrameCounter,bool aSetIfLarger)974 void SubMac::SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger)
975 {
976     if (!aSetIfLarger || (aFrameCounter > mFrameCounter))
977     {
978         mFrameCounter = aFrameCounter;
979     }
980 
981     VerifyOrExit(!ShouldHandleTransmitSecurity());
982 
983     if (aSetIfLarger)
984     {
985         Get<Radio>().SetMacFrameCounterIfLarger(aFrameCounter);
986     }
987     else
988     {
989         Get<Radio>().SetMacFrameCounter(aFrameCounter);
990     }
991 
992 exit:
993     return;
994 }
995 
StartTimer(uint32_t aDelayUs)996 void SubMac::StartTimer(uint32_t aDelayUs)
997 {
998 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
999     mTimer.Start(aDelayUs);
1000 #else
1001     mTimer.Start(aDelayUs / Time::kOneMsecInUsec);
1002 #endif
1003 }
1004 
StartTimerAt(Time aStartTime,uint32_t aDelayUs)1005 void SubMac::StartTimerAt(Time aStartTime, uint32_t aDelayUs)
1006 {
1007 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
1008     mTimer.StartAt(aStartTime, aDelayUs);
1009 #else
1010     mTimer.StartAt(aStartTime, aDelayUs / Time::kOneMsecInUsec);
1011 #endif
1012 }
1013 
1014 // LCOV_EXCL_START
1015 
StateToString(State aState)1016 const char *SubMac::StateToString(State aState)
1017 {
1018     static const char *const kStateStrings[] = {
1019         "Disabled",    // (0) kStateDisabled
1020         "Sleep",       // (1) kStateSleep
1021         "Receive",     // (2) kStateReceive
1022         "CsmaBackoff", // (3) kStateCsmaBackoff
1023         "Transmit",    // (4) kStateTransmit
1024         "EnergyScan",  // (5) kStateEnergyScan
1025 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1026         "DelayBeforeRetx", // (6) kStateDelayBeforeRetx
1027 #endif
1028 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1029         "CslTransmit", // (7) kStateCslTransmit
1030 #endif
1031 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1032         "CslSample", // (8) kStateCslSample
1033 #endif
1034     };
1035 
1036     struct StateValueChecker
1037     {
1038         InitEnumValidatorCounter();
1039 
1040         ValidateNextEnum(kStateDisabled);
1041         ValidateNextEnum(kStateSleep);
1042         ValidateNextEnum(kStateReceive);
1043         ValidateNextEnum(kStateCsmaBackoff);
1044         ValidateNextEnum(kStateTransmit);
1045         ValidateNextEnum(kStateEnergyScan);
1046 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1047         ValidateNextEnum(kStateDelayBeforeRetx);
1048 #endif
1049 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1050         ValidateNextEnum(kStateCslTransmit);
1051 #endif
1052 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1053         ValidateNextEnum(kStateCslSample);
1054 #endif
1055     };
1056 
1057     return kStateStrings[aState];
1058 }
1059 
1060 // LCOV_EXCL_STOP
1061 
1062 } // namespace Mac
1063 } // namespace ot
1064