1 /*
2  *  Copyright (c) 2016-2018, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *   This file implements the subset of IEEE 802.15.4 MAC primitives.
32  */
33 
34 #include "sub_mac.hpp"
35 
36 #include <stdio.h>
37 
38 #include <openthread/platform/time.h>
39 
40 #include "common/code_utils.hpp"
41 #include "common/debug.hpp"
42 #include "common/locator_getters.hpp"
43 #include "common/log.hpp"
44 #include "common/num_utils.hpp"
45 #include "common/random.hpp"
46 #include "common/time.hpp"
47 #include "instance/instance.hpp"
48 #include "mac/mac_frame.hpp"
49 
50 namespace ot {
51 namespace Mac {
52 
53 RegisterLogModule("SubMac");
54 
SubMac(Instance & aInstance)55 SubMac::SubMac(Instance &aInstance)
56     : InstanceLocator(aInstance)
57     , mRadioCaps(Get<Radio>().GetCaps())
58     , mTransmitFrame(Get<Radio>().GetTransmitBuffer())
59     , mCallbacks(aInstance)
60     , mTimer(aInstance)
61 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
62     , mCslTimer(aInstance, SubMac::HandleCslTimer)
63 #endif
64 {
65 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
66     mCslParentAccuracy.Init();
67 #endif
68 
69     Init();
70 }
71 
Init(void)72 void SubMac::Init(void)
73 {
74     mState           = kStateDisabled;
75     mCsmaBackoffs    = 0;
76     mTransmitRetries = 0;
77     mShortAddress    = kShortAddrInvalid;
78     mExtAddress.Clear();
79     mRxOnWhenIdle      = true;
80     mEnergyScanMaxRssi = Radio::kInvalidRssi;
81     mEnergyScanEndTime = Time{0};
82 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
83     mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
84 #endif
85 
86 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
87     mRadioFilterEnabled = false;
88 #endif
89 
90     mPrevKey.Clear();
91     mCurrKey.Clear();
92     mNextKey.Clear();
93 
94     mFrameCounter = 0;
95     mKeyId        = 0;
96     mTimer.Stop();
97 
98 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
99     mCslPeriod     = 0;
100     mCslChannel    = 0;
101     mCslPeerShort  = 0;
102     mIsCslSampling = false;
103     mCslSampleTime = TimeMicro{0};
104     mCslLastSync   = TimeMicro{0};
105     mCslTimer.Stop();
106 #endif
107 }
108 
GetCaps(void) const109 otRadioCaps SubMac::GetCaps(void) const
110 {
111     otRadioCaps caps;
112 
113 #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
114     caps = mRadioCaps;
115 
116 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE
117     caps |= OT_RADIO_CAPS_ACK_TIMEOUT;
118 #endif
119 
120 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE
121     caps |= OT_RADIO_CAPS_CSMA_BACKOFF;
122 #endif
123 
124 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE
125     caps |= OT_RADIO_CAPS_TRANSMIT_RETRIES;
126 #endif
127 
128 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE
129     caps |= OT_RADIO_CAPS_ENERGY_SCAN;
130 #endif
131 
132 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
133     caps |= OT_RADIO_CAPS_TRANSMIT_SEC;
134 #endif
135 
136 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
137     caps |= OT_RADIO_CAPS_TRANSMIT_TIMING;
138 #endif
139 
140 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE && (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
141     caps |= OT_RADIO_CAPS_RECEIVE_TIMING;
142 #endif
143 
144 #if OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_ON_WHEN_IDLE_ENABLE
145     caps |= OT_RADIO_CAPS_RX_ON_WHEN_IDLE;
146 #endif
147 
148 #else
149     caps = OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES |
150            OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING |
151            OT_RADIO_CAPS_RECEIVE_TIMING | OT_RADIO_CAPS_RX_ON_WHEN_IDLE;
152 #endif
153 
154     return caps;
155 }
156 
SetPanId(PanId aPanId)157 void SubMac::SetPanId(PanId aPanId)
158 {
159     Get<Radio>().SetPanId(aPanId);
160     LogDebg("RadioPanId: 0x%04x", aPanId);
161 }
162 
SetShortAddress(ShortAddress aShortAddress)163 void SubMac::SetShortAddress(ShortAddress aShortAddress)
164 {
165     mShortAddress = aShortAddress;
166     Get<Radio>().SetShortAddress(mShortAddress);
167     LogDebg("RadioShortAddress: 0x%04x", mShortAddress);
168 }
169 
SetExtAddress(const ExtAddress & aExtAddress)170 void SubMac::SetExtAddress(const ExtAddress &aExtAddress)
171 {
172     ExtAddress address;
173 
174     mExtAddress = aExtAddress;
175 
176     // Reverse the byte order before setting on radio.
177     address.Set(aExtAddress.m8, ExtAddress::kReverseByteOrder);
178     Get<Radio>().SetExtendedAddress(address);
179 
180     LogDebg("RadioExtAddress: %s", mExtAddress.ToString().AsCString());
181 }
182 
SetRxOnWhenIdle(bool aRxOnWhenIdle)183 void SubMac::SetRxOnWhenIdle(bool aRxOnWhenIdle)
184 {
185     mRxOnWhenIdle = aRxOnWhenIdle;
186 
187     if (RadioSupportsRxOnWhenIdle())
188     {
189 #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
190         Get<Radio>().SetRxOnWhenIdle(mRxOnWhenIdle);
191 #endif
192     }
193 
194     LogDebg("RxOnWhenIdle: %d", mRxOnWhenIdle);
195 }
196 
Enable(void)197 Error SubMac::Enable(void)
198 {
199     Error error = kErrorNone;
200 
201     VerifyOrExit(mState == kStateDisabled);
202 
203     SuccessOrExit(error = Get<Radio>().Enable());
204     SuccessOrExit(error = Get<Radio>().Sleep());
205 
206     SetState(kStateSleep);
207 
208 exit:
209     SuccessOrAssert(error);
210     return error;
211 }
212 
Disable(void)213 Error SubMac::Disable(void)
214 {
215     Error error;
216 
217 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
218     mCslTimer.Stop();
219 #endif
220 
221     mTimer.Stop();
222     SuccessOrExit(error = Get<Radio>().Sleep());
223     SuccessOrExit(error = Get<Radio>().Disable());
224     SetState(kStateDisabled);
225 
226 exit:
227     return error;
228 }
229 
Sleep(void)230 Error SubMac::Sleep(void)
231 {
232     Error error = kErrorNone;
233 
234     VerifyOrExit(ShouldHandleTransitionToSleep());
235 
236     error = Get<Radio>().Sleep();
237 
238 exit:
239     if (error != kErrorNone)
240     {
241         LogWarn("RadioSleep() failed, error: %s", ErrorToString(error));
242     }
243     else
244     {
245         SetState(kStateSleep);
246     }
247 
248     return error;
249 }
250 
Receive(uint8_t aChannel)251 Error SubMac::Receive(uint8_t aChannel)
252 {
253     Error error;
254 
255 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
256     if (mRadioFilterEnabled)
257     {
258         error = Get<Radio>().Sleep();
259     }
260     else
261 #endif
262     {
263         error = Get<Radio>().Receive(aChannel);
264     }
265 
266     if (error != kErrorNone)
267     {
268         LogWarn("RadioReceive() failed, error: %s", ErrorToString(error));
269         ExitNow();
270     }
271 
272     SetState(kStateReceive);
273 
274 exit:
275     return error;
276 }
277 
278 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
CslSample(void)279 void SubMac::CslSample(void)
280 {
281 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
282     VerifyOrExit(!mRadioFilterEnabled, IgnoreError(Get<Radio>().Sleep()));
283 #endif
284 
285     SetState(kStateCslSample);
286 
287     if (mIsCslSampling && !RadioSupportsReceiveTiming())
288     {
289         IgnoreError(Get<Radio>().Receive(mCslChannel));
290         ExitNow();
291     }
292 
293 #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
294     IgnoreError(Get<Radio>().Sleep()); // Don't actually sleep for debugging
295 #endif
296 
297 exit:
298     return;
299 }
300 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
301 
302 #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
LogReceived(RxFrame * aFrame)303 void SubMac::LogReceived(RxFrame *aFrame)
304 {
305     static constexpr uint8_t kLogStringSize = 72;
306 
307     String<kLogStringSize> logString;
308     Address                dst;
309     int32_t                deviation;
310     uint32_t               sampleTime, ahead, after;
311 
312     IgnoreError(aFrame->GetDstAddr(dst));
313 
314     VerifyOrExit((dst.GetType() == Address::kTypeShort && dst.GetShort() == GetShortAddress()) ||
315                  (dst.GetType() == Address::kTypeExtended && dst.GetExtended() == GetExtAddress()));
316 
317     LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %lu", StateToString(mState),
318             mIsCslSampling ? "CslSample" : "CslSleep",
319             ToUlong(static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp)));
320 
321     VerifyOrExit(mState == kStateCslSample);
322 
323     GetCslWindowEdges(ahead, after);
324     ahead -= kMinReceiveOnAhead + kCslReceiveTimeAhead;
325 
326     sampleTime = mCslSampleTime.GetValue() - mCslPeriod * kUsPerTenSymbols;
327     deviation  = aFrame->mInfo.mRxInfo.mTimestamp + kRadioHeaderPhrDuration - sampleTime;
328 
329     // This logs three values (all in microseconds):
330     // - Absolute sample time in which the CSL receiver expected the MHR of the received frame.
331     // - Allowed margin around that time accounting for accuracy and uncertainty from both devices.
332     // - Real deviation on the reception of the MHR with regards to expected sample time. This can
333     //   be due to clocks drift and/or CSL Phase rounding error.
334     // This means that a deviation absolute value greater than the margin would result in the frame
335     // not being received out of the debug mode.
336     logString.Append("Expected sample time %lu, margin ±%lu, deviation %ld", ToUlong(sampleTime), ToUlong(ahead),
337                      static_cast<long>(deviation));
338 
339     // Treat as a warning when the deviation is not within the margins. Neither kCslReceiveTimeAhead
340     // or kMinReceiveOnAhead/kMinReceiveOnAfter are considered for the margin since they have no
341     // impact on understanding possible deviation errors between transmitter and receiver. So in this
342     // case only `ahead` is used, as an allowable max deviation in both +/- directions.
343     if ((deviation + ahead > 0) && (deviation < static_cast<int32_t>(ahead)))
344     {
345         LogDebg("%s", logString.AsCString());
346     }
347     else
348     {
349         LogWarn("%s", logString.AsCString());
350     }
351 
352 exit:
353     return;
354 }
355 #endif
356 
HandleReceiveDone(RxFrame * aFrame,Error aError)357 void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError)
358 {
359     if (mPcapCallback.IsSet() && (aFrame != nullptr) && (aError == kErrorNone))
360     {
361         mPcapCallback.Invoke(aFrame, false);
362     }
363 
364     if (!ShouldHandleTransmitSecurity() && aFrame != nullptr && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
365     {
366         SignalFrameCounterUsed(aFrame->mInfo.mRxInfo.mAckFrameCounter, aFrame->mInfo.mRxInfo.mAckKeyId);
367     }
368 
369 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
370     if (aFrame != nullptr && aError == kErrorNone)
371     {
372 #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
373         LogReceived(aFrame);
374 #endif
375         // Assuming the risk of the parent missing the Enh-ACK in favor of smaller CSL receive window
376         if ((mCslPeriod > 0) && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
377         {
378 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
379             mCslLastSync = TimerMicro::GetNow();
380 #else
381             mCslLastSync = TimeMicro(static_cast<uint32_t>(aFrame->mInfo.mRxInfo.mTimestamp));
382 #endif
383         }
384     }
385 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
386 
387 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
388     if (!mRadioFilterEnabled)
389 #endif
390     {
391         mCallbacks.ReceiveDone(aFrame, aError);
392     }
393 }
394 
Send(void)395 Error SubMac::Send(void)
396 {
397     Error error = kErrorNone;
398 
399     switch (mState)
400     {
401     case kStateDisabled:
402     case kStateCsmaBackoff:
403 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
404     case kStateCslTransmit:
405 #endif
406     case kStateTransmit:
407 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
408     case kStateDelayBeforeRetx:
409 #endif
410     case kStateSleep:
411     case kStateReceive:
412 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
413     case kStateCslSample:
414 #endif
415         break;
416 
417     case kStateEnergyScan:
418         ExitNow(error = kErrorInvalidState);
419     }
420 
421 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
422     if (mRadioFilterEnabled)
423     {
424         mCallbacks.TransmitDone(mTransmitFrame, nullptr, mTransmitFrame.GetAckRequest() ? kErrorNoAck : kErrorNone);
425         ExitNow();
426     }
427 #endif
428 
429     ProcessTransmitSecurity();
430 
431     mCsmaBackoffs    = 0;
432     mTransmitRetries = 0;
433 
434 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
435     mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
436 #endif
437 
438     StartCsmaBackoff();
439 
440 exit:
441     return error;
442 }
443 
ProcessTransmitSecurity(void)444 void SubMac::ProcessTransmitSecurity(void)
445 {
446     const ExtAddress *extAddress = nullptr;
447     uint8_t           keyIdMode;
448 
449     VerifyOrExit(mTransmitFrame.GetSecurityEnabled());
450     VerifyOrExit(!mTransmitFrame.IsSecurityProcessed());
451 
452     SuccessOrExit(mTransmitFrame.GetKeyIdMode(keyIdMode));
453 
454     if (!mTransmitFrame.IsHeaderUpdated())
455     {
456         mTransmitFrame.SetKeyId(mKeyId);
457     }
458 
459     VerifyOrExit(ShouldHandleTransmitSecurity());
460     VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
461 
462     mTransmitFrame.SetAesKey(GetCurrentMacKey());
463 
464     if (!mTransmitFrame.IsHeaderUpdated())
465     {
466         uint32_t frameCounter = GetFrameCounter();
467 
468         mTransmitFrame.SetFrameCounter(frameCounter);
469         SignalFrameCounterUsed(frameCounter, mKeyId);
470     }
471 
472     extAddress = &GetExtAddress();
473 
474 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
475     // Transmit security will be processed after time IE content is updated.
476     VerifyOrExit(mTransmitFrame.GetTimeIeOffset() == 0);
477 #endif
478 
479     mTransmitFrame.ProcessTransmitAesCcm(*extAddress);
480 
481 exit:
482     return;
483 }
484 
StartCsmaBackoff(void)485 void SubMac::StartCsmaBackoff(void)
486 {
487     uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
488 
489 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
490     if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)
491     {
492         SetState(kStateCslTransmit);
493 
494         if (ShouldHandleTransmitTargetTime())
495         {
496             if (Time(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance()))) <
497                 Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) + mTransmitFrame.mInfo.mTxInfo.mTxDelay -
498                     kCcaSampleInterval - kCslTransmitTimeAhead - kRadioHeaderShrDuration)
499             {
500                 mTimer.StartAt(Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) - kCcaSampleInterval -
501                                    kCslTransmitTimeAhead - kRadioHeaderShrDuration,
502                                mTransmitFrame.mInfo.mTxInfo.mTxDelay);
503             }
504             else // Transmit without delay
505             {
506                 BeginTransmit();
507             }
508         }
509         else
510         {
511             BeginTransmit();
512         }
513 
514         ExitNow();
515     }
516 #endif // !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
517 
518     SetState(kStateCsmaBackoff);
519 
520     VerifyOrExit(ShouldHandleCsmaBackOff(), BeginTransmit());
521 
522     backoffExponent = Min(backoffExponent, kCsmaMaxBe);
523 
524     StartTimerForBackoff(backoffExponent);
525 
526 exit:
527     return;
528 }
529 
StartTimerForBackoff(uint8_t aBackoffExponent)530 void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
531 {
532     uint32_t backoff;
533 
534     backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
535     backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);
536 
537     if (mRxOnWhenIdle)
538     {
539         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
540     }
541     else
542     {
543         IgnoreError(Get<Radio>().Sleep());
544     }
545 
546 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
547     mTimer.Start(backoff);
548 #else
549     mTimer.Start(backoff / 1000UL);
550 #endif
551 
552 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
553     if (mState == kStateDelayBeforeRetx)
554     {
555         LogDebg("Delaying retx for %lu usec (be=%u)", ToUlong(backoff), aBackoffExponent);
556     }
557 #endif
558 }
559 
BeginTransmit(void)560 void SubMac::BeginTransmit(void)
561 {
562     Error error;
563 
564 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
565     VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateCslTransmit);
566 #else
567     VerifyOrExit(mState == kStateCsmaBackoff);
568 #endif
569 
570     if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)
571     {
572         SuccessOrAssert(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
573     }
574 
575     SetState(kStateTransmit);
576 
577     if (mPcapCallback.IsSet())
578     {
579         mPcapCallback.Invoke(&mTransmitFrame, true);
580     }
581 
582     error = Get<Radio>().Transmit(mTransmitFrame);
583 
584     if (error == kErrorInvalidState && mTransmitFrame.mInfo.mTxInfo.mTxDelay > 0)
585     {
586         // Platform `transmit_at` fails and we send the frame directly.
587         mTransmitFrame.mInfo.mTxInfo.mTxDelay         = 0;
588         mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
589 
590         error = Get<Radio>().Transmit(mTransmitFrame);
591     }
592 
593     SuccessOrAssert(error);
594 
595 exit:
596     return;
597 }
598 
HandleTransmitStarted(TxFrame & aFrame)599 void SubMac::HandleTransmitStarted(TxFrame &aFrame)
600 {
601     if (ShouldHandleAckTimeout() && aFrame.GetAckRequest())
602     {
603 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
604         mTimer.Start(kAckTimeout * 1000UL);
605 #else
606         mTimer.Start(kAckTimeout);
607 #endif
608     }
609 }
610 
HandleTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)611 void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
612 {
613     bool ccaSuccess = true;
614     bool shouldRetx;
615 
616     // Stop ack timeout timer.
617 
618     mTimer.Stop();
619 
620     // Record CCA success or failure status.
621 
622     switch (aError)
623     {
624     case kErrorAbort:
625         // Do not record CCA status in case of `ABORT` error
626         // since there may be no CCA check performed by radio.
627         break;
628 
629     case kErrorChannelAccessFailure:
630         ccaSuccess = false;
631 
632         OT_FALL_THROUGH;
633 
634     case kErrorNone:
635     case kErrorNoAck:
636         if (aFrame.IsCsmaCaEnabled())
637         {
638             mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel());
639         }
640 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
641         // Actual synchronization timestamp should be from the sent frame instead of the current time.
642         // Assuming the error here since it is bounded and has very small effect on the final window duration.
643         if (aAckFrame != nullptr && aFrame.GetHeaderIe(CslIe::kHeaderIeId) != nullptr)
644         {
645 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
646             mCslLastSync = TimerMicro::GetNow();
647 #else
648             mCslLastSync = TimeMicro(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
649 #endif
650         }
651 #endif
652         break;
653 
654     default:
655         OT_ASSERT(false);
656         OT_UNREACHABLE_CODE(ExitNow());
657     }
658 
659     SignalFrameCounterUsedOnTxDone(aFrame);
660 
661     // Determine whether a CSMA retry is required.
662 
663     if (!ccaSuccess && ShouldHandleCsmaBackOff() && mCsmaBackoffs < aFrame.GetMaxCsmaBackoffs())
664     {
665         mCsmaBackoffs++;
666         StartCsmaBackoff();
667         ExitNow();
668     }
669 
670     mCsmaBackoffs = 0;
671 
672     // Determine whether to re-transmit the frame.
673 
674     shouldRetx = ((aError != kErrorNone) && ShouldHandleRetries() && (mTransmitRetries < aFrame.GetMaxFrameRetries()));
675 
676     mCallbacks.RecordFrameTransmitStatus(aFrame, aError, mTransmitRetries, shouldRetx);
677 
678     if (shouldRetx)
679     {
680         mTransmitRetries++;
681         aFrame.SetIsARetransmission(true);
682 
683 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
684         if (aError == kErrorNoAck)
685         {
686             SetState(kStateDelayBeforeRetx);
687             StartTimerForBackoff(mRetxDelayBackOffExponent);
688             mRetxDelayBackOffExponent =
689                 Min(static_cast<uint8_t>(mRetxDelayBackOffExponent + 1), kRetxDelayMaxBackoffExponent);
690             ExitNow();
691         }
692 #endif
693 
694         StartCsmaBackoff();
695         ExitNow();
696     }
697 
698     SetState(kStateReceive);
699 
700 #if OPENTHREAD_RADIO
701     if (aFrame.GetChannel() != aFrame.GetRxChannelAfterTxDone())
702     {
703         // On RCP build, we switch immediately to the specified RX
704         // channel if it is different from the channel on which frame
705         // was sent. On FTD or MTD builds we don't need to do
706         // the same as the `Mac` will switch the channel from the
707         // `mCallbacks.TransmitDone()`.
708 
709         IgnoreError(Get<Radio>().Receive(aFrame.GetRxChannelAfterTxDone()));
710     }
711 #endif
712 
713     mCallbacks.TransmitDone(aFrame, aAckFrame, aError);
714 
715 exit:
716     return;
717 }
718 
SignalFrameCounterUsedOnTxDone(const TxFrame & aFrame)719 void SubMac::SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame)
720 {
721     uint8_t  keyIdMode;
722     uint8_t  keyId;
723     uint32_t frameCounter;
724     bool     allowError = false;
725 
726     OT_UNUSED_VARIABLE(allowError);
727 
728     VerifyOrExit(!ShouldHandleTransmitSecurity() && aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated());
729 
730     // In an FTD/MTD build, if/when link-raw is enabled, the `TxFrame`
731     // is prepared and given by user and may not necessarily follow 15.4
732     // frame format (link raw can be used with vendor-specific format),
733     // so we allow failure when parsing the frame (i.e., do not assert
734     // on an error). In other cases (in an RCP build or in an FTD/MTD
735     // build without link-raw) since the `TxFrame` should be prepared by
736     // OpenThread core, we expect no error and therefore assert if
737     // parsing fails.
738 
739 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
740     allowError = Get<LinkRaw>().IsEnabled();
741 #endif
742 
743     VerifyOrExit(aFrame.GetKeyIdMode(keyIdMode) == kErrorNone, OT_ASSERT(allowError));
744     VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
745 
746     VerifyOrExit(aFrame.GetFrameCounter(frameCounter) == kErrorNone, OT_ASSERT(allowError));
747     VerifyOrExit(aFrame.GetKeyId(keyId) == kErrorNone, OT_ASSERT(allowError));
748 
749     SignalFrameCounterUsed(frameCounter, keyId);
750 
751 exit:
752     return;
753 }
754 
GetRssi(void) const755 int8_t SubMac::GetRssi(void) const
756 {
757     int8_t rssi;
758 
759 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
760     if (mRadioFilterEnabled)
761     {
762         rssi = Radio::kInvalidRssi;
763     }
764     else
765 #endif
766     {
767         rssi = Get<Radio>().GetRssi();
768     }
769 
770     return rssi;
771 }
772 
GetNoiseFloor(void) const773 int8_t SubMac::GetNoiseFloor(void) const { return Get<Radio>().GetReceiveSensitivity(); }
774 
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration)775 Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
776 {
777     Error error = kErrorNone;
778 
779     switch (mState)
780     {
781     case kStateDisabled:
782     case kStateCsmaBackoff:
783     case kStateTransmit:
784 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
785     case kStateCslTransmit:
786 #endif
787 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
788     case kStateDelayBeforeRetx:
789 #endif
790     case kStateEnergyScan:
791         ExitNow(error = kErrorInvalidState);
792 
793     case kStateReceive:
794     case kStateSleep:
795 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
796     case kStateCslSample:
797 #endif
798         break;
799     }
800 
801 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
802     VerifyOrExit(!mRadioFilterEnabled, HandleEnergyScanDone(Radio::kInvalidRssi));
803 #endif
804 
805     if (RadioSupportsEnergyScan())
806     {
807         IgnoreError(Get<Radio>().EnergyScan(aScanChannel, aScanDuration));
808         SetState(kStateEnergyScan);
809     }
810     else if (ShouldHandleEnergyScan())
811     {
812         SuccessOrAssert(Get<Radio>().Receive(aScanChannel));
813 
814         SetState(kStateEnergyScan);
815         mEnergyScanMaxRssi = Radio::kInvalidRssi;
816         mEnergyScanEndTime = TimerMilli::GetNow() + static_cast<uint32_t>(aScanDuration);
817         mTimer.Start(0);
818     }
819     else
820     {
821         error = kErrorNotImplemented;
822     }
823 
824 exit:
825     return error;
826 }
827 
SampleRssi(void)828 void SubMac::SampleRssi(void)
829 {
830     OT_ASSERT(!RadioSupportsEnergyScan());
831 
832     int8_t rssi = GetRssi();
833 
834     if (rssi != Radio::kInvalidRssi)
835     {
836         if ((mEnergyScanMaxRssi == Radio::kInvalidRssi) || (rssi > mEnergyScanMaxRssi))
837         {
838             mEnergyScanMaxRssi = rssi;
839         }
840     }
841 
842     if (TimerMilli::GetNow() < mEnergyScanEndTime)
843     {
844 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
845         mTimer.StartAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval * 1000UL);
846 #else
847         mTimer.StartAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval);
848 #endif
849     }
850     else
851     {
852         HandleEnergyScanDone(mEnergyScanMaxRssi);
853     }
854 }
855 
HandleEnergyScanDone(int8_t aMaxRssi)856 void SubMac::HandleEnergyScanDone(int8_t aMaxRssi)
857 {
858     SetState(kStateReceive);
859     mCallbacks.EnergyScanDone(aMaxRssi);
860 }
861 
HandleTimer(void)862 void SubMac::HandleTimer(void)
863 {
864     switch (mState)
865     {
866 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
867     case kStateCslTransmit:
868         BeginTransmit();
869         break;
870 #endif
871     case kStateCsmaBackoff:
872         BeginTransmit();
873         break;
874 
875     case kStateTransmit:
876         LogDebg("Ack timer timed out");
877         IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
878         HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck);
879         break;
880 
881 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
882     case kStateDelayBeforeRetx:
883         StartCsmaBackoff();
884         break;
885 #endif
886 
887     case kStateEnergyScan:
888         SampleRssi();
889         break;
890 
891     default:
892         break;
893     }
894 }
895 
ShouldHandleTransmitSecurity(void) const896 bool SubMac::ShouldHandleTransmitSecurity(void) const
897 {
898     bool swTxSecurity = true;
899 
900     VerifyOrExit(!RadioSupportsTransmitSecurity(), swTxSecurity = false);
901 
902 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
903     VerifyOrExit(Get<LinkRaw>().IsEnabled());
904 #endif
905 
906 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
907     swTxSecurity = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE;
908 #endif
909 
910 exit:
911     return swTxSecurity;
912 }
913 
ShouldHandleCsmaBackOff(void) const914 bool SubMac::ShouldHandleCsmaBackOff(void) const
915 {
916     bool swCsma = true;
917 
918     VerifyOrExit(!RadioSupportsCsmaBackoff(), swCsma = false);
919 
920 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
921     VerifyOrExit(Get<LinkRaw>().IsEnabled());
922 #endif
923 
924 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
925     swCsma = OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE;
926 #endif
927 
928 exit:
929     return swCsma;
930 }
931 
ShouldHandleAckTimeout(void) const932 bool SubMac::ShouldHandleAckTimeout(void) const
933 {
934     bool swAckTimeout = true;
935 
936     VerifyOrExit(!RadioSupportsAckTimeout(), swAckTimeout = false);
937 
938 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
939     VerifyOrExit(Get<LinkRaw>().IsEnabled());
940 #endif
941 
942 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
943     swAckTimeout = OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE;
944 #endif
945 
946 exit:
947     return swAckTimeout;
948 }
949 
ShouldHandleRetries(void) const950 bool SubMac::ShouldHandleRetries(void) const
951 {
952     bool swRetries = true;
953 
954     VerifyOrExit(!RadioSupportsRetries(), swRetries = false);
955 
956 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
957     VerifyOrExit(Get<LinkRaw>().IsEnabled());
958 #endif
959 
960 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
961     swRetries = OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE;
962 #endif
963 
964 exit:
965     return swRetries;
966 }
967 
ShouldHandleEnergyScan(void) const968 bool SubMac::ShouldHandleEnergyScan(void) const
969 {
970     bool swEnergyScan = true;
971 
972     VerifyOrExit(!RadioSupportsEnergyScan(), swEnergyScan = false);
973 
974 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
975     VerifyOrExit(Get<LinkRaw>().IsEnabled());
976 #endif
977 
978 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
979     swEnergyScan = OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE;
980 #endif
981 
982 exit:
983     return swEnergyScan;
984 }
985 
ShouldHandleTransmitTargetTime(void) const986 bool SubMac::ShouldHandleTransmitTargetTime(void) const
987 {
988     bool swTxDelay = true;
989 
990     VerifyOrExit(!RadioSupportsTransmitTiming(), swTxDelay = false);
991 
992 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
993     VerifyOrExit(Get<LinkRaw>().IsEnabled());
994 #endif
995 
996 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
997     swTxDelay = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE;
998 #endif
999 
1000 exit:
1001     return swTxDelay;
1002 }
1003 
ShouldHandleTransitionToSleep(void) const1004 bool SubMac::ShouldHandleTransitionToSleep(void) const { return (mRxOnWhenIdle || !RadioSupportsRxOnWhenIdle()); }
1005 
SetState(State aState)1006 void SubMac::SetState(State aState)
1007 {
1008     if (mState != aState)
1009     {
1010         LogDebg("RadioState: %s -> %s", StateToString(mState), StateToString(aState));
1011         mState = aState;
1012     }
1013 }
1014 
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const KeyMaterial & aPrevKey,const KeyMaterial & aCurrKey,const KeyMaterial & aNextKey)1015 void SubMac::SetMacKey(uint8_t            aKeyIdMode,
1016                        uint8_t            aKeyId,
1017                        const KeyMaterial &aPrevKey,
1018                        const KeyMaterial &aCurrKey,
1019                        const KeyMaterial &aNextKey)
1020 {
1021     switch (aKeyIdMode)
1022     {
1023     case Frame::kKeyIdMode0:
1024     case Frame::kKeyIdMode2:
1025         break;
1026     case Frame::kKeyIdMode1:
1027         mKeyId   = aKeyId;
1028         mPrevKey = aPrevKey;
1029         mCurrKey = aCurrKey;
1030         mNextKey = aNextKey;
1031         break;
1032 
1033     default:
1034         OT_ASSERT(false);
1035         break;
1036     }
1037 
1038     VerifyOrExit(!ShouldHandleTransmitSecurity());
1039 
1040     Get<Radio>().SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey);
1041 
1042 exit:
1043     return;
1044 }
1045 
SignalFrameCounterUsed(uint32_t aFrameCounter,uint8_t aKeyId)1046 void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId)
1047 {
1048     VerifyOrExit(aKeyId == mKeyId);
1049 
1050     mCallbacks.FrameCounterUsed(aFrameCounter);
1051 
1052     // It not always guaranteed that this method is invoked in order
1053     // for different counter values (i.e., we may get it for a
1054     // smaller counter value after a lager one). This may happen due
1055     // to a new counter value being used for an enhanced-ack during
1056     // tx of a frame. Note that the newer counter used for enhanced-ack
1057     // is processed from `HandleReceiveDone()` which can happen before
1058     // processing of the older counter value from `HandleTransmitDone()`.
1059 
1060     VerifyOrExit(mFrameCounter <= aFrameCounter);
1061     mFrameCounter = aFrameCounter + 1;
1062 
1063 exit:
1064     return;
1065 }
1066 
SetFrameCounter(uint32_t aFrameCounter,bool aSetIfLarger)1067 void SubMac::SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger)
1068 {
1069     if (!aSetIfLarger || (aFrameCounter > mFrameCounter))
1070     {
1071         mFrameCounter = aFrameCounter;
1072     }
1073 
1074     VerifyOrExit(!ShouldHandleTransmitSecurity());
1075 
1076     if (aSetIfLarger)
1077     {
1078         Get<Radio>().SetMacFrameCounterIfLarger(aFrameCounter);
1079     }
1080     else
1081     {
1082         Get<Radio>().SetMacFrameCounter(aFrameCounter);
1083     }
1084 
1085 exit:
1086     return;
1087 }
1088 
1089 // LCOV_EXCL_START
1090 
StateToString(State aState)1091 const char *SubMac::StateToString(State aState)
1092 {
1093     static const char *const kStateStrings[] = {
1094         "Disabled",    // (0) kStateDisabled
1095         "Sleep",       // (1) kStateSleep
1096         "Receive",     // (2) kStateReceive
1097         "CsmaBackoff", // (3) kStateCsmaBackoff
1098         "Transmit",    // (4) kStateTransmit
1099         "EnergyScan",  // (5) kStateEnergyScan
1100 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1101         "DelayBeforeRetx", // (6) kStateDelayBeforeRetx
1102 #endif
1103 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1104         "CslTransmit", // (7) kStateCslTransmit
1105 #endif
1106 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1107         "CslSample", // (8) kStateCslSample
1108 #endif
1109     };
1110 
1111     static_assert(kStateDisabled == 0, "kStateDisabled value is not correct");
1112     static_assert(kStateSleep == 1, "kStateSleep value is not correct");
1113     static_assert(kStateReceive == 2, "kStateReceive value is not correct");
1114     static_assert(kStateCsmaBackoff == 3, "kStateCsmaBackoff value is not correct");
1115     static_assert(kStateTransmit == 4, "kStateTransmit value is not correct");
1116     static_assert(kStateEnergyScan == 5, "kStateEnergyScan value is not correct");
1117 
1118 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1119     static_assert(kStateDelayBeforeRetx == 6, "kStateDelayBeforeRetx value is not correct");
1120 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1121     static_assert(kStateCslTransmit == 7, "kStateCslTransmit value is not correct");
1122 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1123     static_assert(kStateCslSample == 8, "kStateCslSample value is not correct");
1124 #endif
1125 #elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1126     static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
1127 #endif
1128 
1129 #elif !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1130     static_assert(kStateCslTransmit == 6, "kStateCslTransmit value is not correct");
1131 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1132     static_assert(kStateCslSample == 7, "kStateCslSample value is not correct");
1133 #endif
1134 #elif OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1135     static_assert(kStateCslSample == 6, "kStateCslSample value is not correct");
1136 #endif
1137 
1138     return kStateStrings[aState];
1139 }
1140 
1141 // LCOV_EXCL_STOP
1142 
1143 //---------------------------------------------------------------------------------------------------------------------
1144 // CSL Receiver methods
1145 
1146 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
UpdateCsl(uint16_t aPeriod,uint8_t aChannel,otShortAddress aShortAddr,const otExtAddress * aExtAddr)1147 bool SubMac::UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShortAddr, const otExtAddress *aExtAddr)
1148 {
1149     bool diffPeriod  = aPeriod != mCslPeriod;
1150     bool diffChannel = aChannel != mCslChannel;
1151     bool diffPeer    = aShortAddr != mCslPeerShort;
1152     bool retval      = diffPeriod || diffChannel || diffPeer;
1153 
1154     VerifyOrExit(retval);
1155     mCslChannel = aChannel;
1156 
1157     VerifyOrExit(diffPeriod || diffPeer);
1158     mCslPeriod    = aPeriod;
1159     mCslPeerShort = aShortAddr;
1160     IgnoreError(Get<Radio>().EnableCsl(aPeriod, aShortAddr, aExtAddr));
1161 
1162     mCslTimer.Stop();
1163     if (mCslPeriod > 0)
1164     {
1165         mCslSampleTime = TimeMicro(static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance())));
1166         mIsCslSampling = false;
1167         HandleCslTimer();
1168     }
1169 
1170 exit:
1171     return retval;
1172 }
1173 
HandleCslTimer(Timer & aTimer)1174 void SubMac::HandleCslTimer(Timer &aTimer) { aTimer.Get<SubMac>().HandleCslTimer(); }
1175 
HandleCslTimer(void)1176 void SubMac::HandleCslTimer(void)
1177 {
1178     /*
1179      * CSL sample timing diagram
1180      *    |<---------------------------------Sample--------------------------------->|<--------Sleep--------->|
1181      *    |                                                                          |                        |
1182      *    |<--Ahead-->|<--UnCert-->|<--Drift-->|<--Drift-->|<--UnCert-->|<--MinWin-->|                        |
1183      *    |           |            |           |           |            |            |                        |
1184      * ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|---
1185      * -timeAhead                           CslPhase                             +timeAfter             -timeAhead
1186      *
1187      * The handler works in different ways when the radio supports receive-timing and doesn't.
1188      *
1189      * When the radio supports receive-timing:
1190      *   The handler will be called once per CSL period. When the handler is called, it will set the timer to
1191      *   fire at the next CSL sample time and call `Radio::ReceiveAt` to start sampling for the current CSL period.
1192      *   The timer fires some time before the actual sample time. After `Radio::ReceiveAt` is called, the radio will
1193      *   remain in sleep state until the actual sample time.
1194      *   Note that it never call `Radio::Sleep` explicitly. The radio will fall into sleep after `ReceiveAt` ends. This
1195      *   will be done by the platform as part of the `otPlatRadioReceiveAt` API.
1196      *
1197      *   Timer fires                                         Timer fires
1198      *       ^                                                    ^
1199      *       x-|------------|-------------------------------------x-|------------|---------------------------------------|
1200      *            sample                   sleep                        sample                    sleep
1201      *
1202      * When the radio doesn't support receive-timing:
1203      *   The handler will be called twice per CSL period: at the beginning of sample and sleep. When the handler is
1204      *   called, it will explicitly change the radio state due to the current state by calling `Radio::Receive` or
1205      *   `Radio::Sleep`.
1206      *
1207      *   Timer fires  Timer fires                            Timer fires  Timer fires
1208      *       ^            ^                                       ^            ^
1209      *       |------------|---------------------------------------|------------|---------------------------------------|
1210      *          sample                   sleep                        sample                    sleep
1211      *
1212      */
1213     uint32_t periodUs = mCslPeriod * kUsPerTenSymbols;
1214     uint32_t timeAhead, timeAfter, winStart, winDuration;
1215 
1216     GetCslWindowEdges(timeAhead, timeAfter);
1217 
1218     if (mIsCslSampling)
1219     {
1220         mIsCslSampling = false;
1221         mCslTimer.FireAt(mCslSampleTime - timeAhead);
1222         if (mState == kStateCslSample)
1223         {
1224 #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE
1225             IgnoreError(Get<Radio>().Sleep()); // Don't actually sleep for debugging
1226 #endif
1227             LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue()));
1228         }
1229     }
1230     else
1231     {
1232         if (RadioSupportsReceiveTiming())
1233         {
1234             mCslTimer.FireAt(mCslSampleTime - timeAhead + periodUs);
1235             timeAhead -= kCslReceiveTimeAhead;
1236             winStart = mCslSampleTime.GetValue() - timeAhead;
1237         }
1238         else
1239         {
1240             mCslTimer.FireAt(mCslSampleTime + timeAfter);
1241             mIsCslSampling = true;
1242             winStart       = ot::TimerMicro::GetNow().GetValue();
1243         }
1244 
1245         winDuration = timeAhead + timeAfter;
1246         mCslSampleTime += periodUs;
1247 
1248         Get<Radio>().UpdateCslSampleTime(mCslSampleTime.GetValue());
1249 
1250         // Schedule reception window for any state except RX - so that CSL RX Window has lower priority
1251         // than scanning or RX after the data poll.
1252         if (RadioSupportsReceiveTiming() && (mState != kStateDisabled) && (mState != kStateReceive))
1253         {
1254             IgnoreError(Get<Radio>().ReceiveAt(mCslChannel, winStart, winDuration));
1255         }
1256         else if (mState == kStateCslSample)
1257         {
1258             IgnoreError(Get<Radio>().Receive(mCslChannel));
1259         }
1260 
1261         LogDebg("CSL window start %lu, duration %lu", ToUlong(winStart), ToUlong(winDuration));
1262     }
1263 }
1264 
GetCslWindowEdges(uint32_t & aAhead,uint32_t & aAfter)1265 void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter)
1266 {
1267     uint32_t semiPeriod = mCslPeriod * kUsPerTenSymbols / 2;
1268     uint32_t curTime, elapsed, semiWindow;
1269 
1270 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC
1271     curTime = TimerMicro::GetNow().GetValue();
1272 #else
1273     curTime = static_cast<uint32_t>(otPlatRadioGetNow(&GetInstance()));
1274 #endif
1275 
1276     elapsed = curTime - mCslLastSync.GetValue();
1277 
1278     semiWindow =
1279         static_cast<uint32_t>(static_cast<uint64_t>(elapsed) *
1280                               (Get<Radio>().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / 1000000);
1281     semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get<Radio>().GetCslUncertainty() * 10;
1282 
1283     aAhead = Min(semiPeriod, semiWindow + kMinReceiveOnAhead + kCslReceiveTimeAhead);
1284     aAfter = Min(semiPeriod, semiWindow + kMinReceiveOnAfter);
1285 }
1286 #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1287 
1288 } // namespace Mac
1289 } // namespace ot
1290