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