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