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