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