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 VerifyOrExit(ShouldHandleTransitionToSleep());
244
245 error = Get<Radio>().Sleep();
246
247 exit:
248 if (error != kErrorNone)
249 {
250 LogWarn("RadioSleep() failed, error: %s", ErrorToString(error));
251 }
252 else
253 {
254 SetState(kStateSleep);
255 }
256
257 return error;
258 }
259
Receive(uint8_t aChannel)260 Error SubMac::Receive(uint8_t aChannel)
261 {
262 Error error;
263
264 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
265 if (mRadioFilterEnabled)
266 {
267 error = Get<Radio>().Sleep();
268 }
269 else
270 #endif
271 {
272 error = Get<Radio>().Receive(aChannel);
273 }
274
275 if (error != kErrorNone)
276 {
277 LogWarn("RadioReceive() failed, error: %s", ErrorToString(error));
278 ExitNow();
279 }
280
281 SetState(kStateReceive);
282
283 exit:
284 return error;
285 }
286
HandleReceiveDone(RxFrame * aFrame,Error aError)287 void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError)
288 {
289 if (mPcapCallback.IsSet() && (aFrame != nullptr) && (aError == kErrorNone))
290 {
291 mPcapCallback.Invoke(aFrame, false);
292 }
293
294 if (!ShouldHandleTransmitSecurity() && aFrame != nullptr && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck)
295 {
296 SignalFrameCounterUsed(aFrame->mInfo.mRxInfo.mAckFrameCounter, aFrame->mInfo.mRxInfo.mAckKeyId);
297 }
298
299 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
300 UpdateCslLastSyncTimestamp(aFrame, aError);
301 #endif
302
303 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
304 if (!mRadioFilterEnabled)
305 #endif
306 {
307 mCallbacks.ReceiveDone(aFrame, aError);
308 }
309 }
310
Send(void)311 Error SubMac::Send(void)
312 {
313 Error error = kErrorNone;
314
315 switch (mState)
316 {
317 case kStateDisabled:
318 case kStateCsmaBackoff:
319 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
320 case kStateCslTransmit:
321 #endif
322 case kStateTransmit:
323 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
324 case kStateDelayBeforeRetx:
325 #endif
326 case kStateSleep:
327 case kStateReceive:
328 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
329 case kStateCslSample:
330 #endif
331 break;
332
333 case kStateEnergyScan:
334 ExitNow(error = kErrorInvalidState);
335 }
336
337 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
338 if (mRadioFilterEnabled)
339 {
340 mCallbacks.TransmitDone(mTransmitFrame, nullptr, mTransmitFrame.GetAckRequest() ? kErrorNoAck : kErrorNone);
341 ExitNow();
342 }
343 #endif
344
345 ProcessTransmitSecurity();
346
347 mCsmaBackoffs = 0;
348 mTransmitRetries = 0;
349
350 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
351 mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent;
352 #endif
353
354 StartCsmaBackoff();
355
356 exit:
357 return error;
358 }
359
ProcessTransmitSecurity(void)360 void SubMac::ProcessTransmitSecurity(void)
361 {
362 const ExtAddress *extAddress = nullptr;
363 uint8_t keyIdMode;
364
365 VerifyOrExit(mTransmitFrame.GetSecurityEnabled());
366 VerifyOrExit(!mTransmitFrame.IsSecurityProcessed());
367
368 SuccessOrExit(mTransmitFrame.GetKeyIdMode(keyIdMode));
369
370 if (!mTransmitFrame.IsHeaderUpdated())
371 {
372 mTransmitFrame.SetKeyId(mKeyId);
373 }
374
375 VerifyOrExit(ShouldHandleTransmitSecurity());
376
377 #if OPENTHREAD_CONFIG_WAKEUP_COORDINATOR_ENABLE
378 if (mTransmitFrame.GetType() == Frame::kTypeMultipurpose)
379 {
380 VerifyOrExit(keyIdMode == Frame::kKeyIdMode2);
381 }
382 else
383 #endif
384 {
385 VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
386 }
387
388 mTransmitFrame.SetAesKey(GetCurrentMacKey());
389
390 if (!mTransmitFrame.IsHeaderUpdated())
391 {
392 uint32_t frameCounter = GetFrameCounter();
393
394 mTransmitFrame.SetFrameCounter(frameCounter);
395 SignalFrameCounterUsed(frameCounter, mKeyId);
396 }
397
398 extAddress = &GetExtAddress();
399
400 #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
401 // Transmit security will be processed after time IE content is updated.
402 VerifyOrExit(mTransmitFrame.GetTimeIeOffset() == 0);
403 #endif
404
405 mTransmitFrame.ProcessTransmitAesCcm(*extAddress);
406
407 exit:
408 return;
409 }
410
StartCsmaBackoff(void)411 void SubMac::StartCsmaBackoff(void)
412 {
413 uint8_t backoffExponent = kCsmaMinBe + mCsmaBackoffs;
414
415 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
416 if (mTransmitFrame.mInfo.mTxInfo.mTxDelay != 0 || mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime != 0)
417 {
418 SetState(kStateCslTransmit);
419
420 if (ShouldHandleTransmitTargetTime())
421 {
422 static constexpr uint32_t kAheadTime = kCcaSampleInterval + kCslTransmitTimeAhead + kRadioHeaderShrDuration;
423 Time txStartTime = Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime);
424 Time radioNow = Time(static_cast<uint32_t>(Get<Radio>().GetNow()));
425
426 txStartTime += (mTransmitFrame.mInfo.mTxInfo.mTxDelay - kAheadTime);
427
428 if (radioNow < txStartTime)
429 {
430 StartTimer(txStartTime - radioNow);
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(mTransmitFrame.GetMaxCsmaBackoffs() > 0 && ShouldHandleCsmaBackOff(), BeginTransmit());
449
450 backoffExponent = Min(backoffExponent, kCsmaMaxBe);
451
452 StartTimerForBackoff(backoffExponent);
453
454 exit:
455 return;
456 }
457
StartTimerForBackoff(uint8_t aBackoffExponent)458 void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent)
459 {
460 uint32_t backoff;
461
462 backoff = Random::NonCrypto::GetUint32InRange(0, static_cast<uint32_t>(1UL << aBackoffExponent));
463 backoff *= (kUnitBackoffPeriod * Radio::kSymbolTime);
464
465 if (mRxOnWhenIdle)
466 {
467 IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
468 }
469 else
470 {
471 IgnoreError(Get<Radio>().Sleep());
472 }
473
474 StartTimer(backoff);
475
476 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
477 if (mState == kStateDelayBeforeRetx)
478 {
479 LogDebg("Delaying retx for %lu usec (be=%u)", ToUlong(backoff), aBackoffExponent);
480 }
481 #endif
482 }
483
BeginTransmit(void)484 void SubMac::BeginTransmit(void)
485 {
486 Error error;
487
488 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
489 VerifyOrExit(mState == kStateCsmaBackoff || mState == kStateCslTransmit);
490 #else
491 VerifyOrExit(mState == kStateCsmaBackoff);
492 #endif
493
494 if ((mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX) == 0)
495 {
496 SuccessOrAssert(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
497 }
498
499 SetState(kStateTransmit);
500
501 error = Get<Radio>().Transmit(mTransmitFrame);
502
503 if (error == kErrorInvalidState && mTransmitFrame.mInfo.mTxInfo.mTxDelay > 0)
504 {
505 // Platform `transmit_at` fails and we send the frame directly.
506 mTransmitFrame.mInfo.mTxInfo.mTxDelay = 0;
507 mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
508
509 error = Get<Radio>().Transmit(mTransmitFrame);
510 }
511
512 SuccessOrAssert(error);
513
514 exit:
515 return;
516 }
517
HandleTransmitStarted(TxFrame & aFrame)518 void SubMac::HandleTransmitStarted(TxFrame &aFrame)
519 {
520 if (mPcapCallback.IsSet())
521 {
522 mPcapCallback.Invoke(&aFrame, true);
523 }
524
525 if (ShouldHandleAckTimeout() && aFrame.GetAckRequest())
526 {
527 StartTimer(kAckTimeout);
528 }
529 }
530
HandleTransmitDone(TxFrame & aFrame,RxFrame * aAckFrame,Error aError)531 void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError)
532 {
533 bool ccaSuccess = true;
534 bool shouldRetx;
535
536 // Stop ack timeout timer.
537
538 mTimer.Stop();
539
540 // Record CCA success or failure status.
541
542 switch (aError)
543 {
544 case kErrorAbort:
545 // Do not record CCA status in case of `ABORT` error
546 // since there may be no CCA check performed by radio.
547 break;
548
549 case kErrorChannelAccessFailure:
550 ccaSuccess = false;
551
552 OT_FALL_THROUGH;
553
554 case kErrorNone:
555 case kErrorNoAck:
556 if (aFrame.IsCsmaCaEnabled())
557 {
558 mCallbacks.RecordCcaStatus(ccaSuccess, aFrame.GetChannel());
559 }
560 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
561 UpdateCslLastSyncTimestamp(aFrame, aAckFrame);
562 #endif
563 break;
564
565 default:
566 OT_ASSERT(false);
567 OT_UNREACHABLE_CODE(ExitNow());
568 }
569
570 SignalFrameCounterUsedOnTxDone(aFrame);
571
572 // Determine whether a CSMA retry is required.
573
574 if (!ccaSuccess && ShouldHandleCsmaBackOff() && mCsmaBackoffs < aFrame.GetMaxCsmaBackoffs())
575 {
576 mCsmaBackoffs++;
577 StartCsmaBackoff();
578 ExitNow();
579 }
580
581 mCsmaBackoffs = 0;
582
583 // Determine whether to re-transmit the frame.
584
585 shouldRetx = ((aError != kErrorNone) && ShouldHandleRetries() && (mTransmitRetries < aFrame.GetMaxFrameRetries()));
586
587 mCallbacks.RecordFrameTransmitStatus(aFrame, aError, mTransmitRetries, shouldRetx);
588
589 if (shouldRetx)
590 {
591 mTransmitRetries++;
592 aFrame.SetIsARetransmission(true);
593
594 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
595 if (aError == kErrorNoAck)
596 {
597 SetState(kStateDelayBeforeRetx);
598 StartTimerForBackoff(mRetxDelayBackOffExponent);
599 mRetxDelayBackOffExponent =
600 Min(static_cast<uint8_t>(mRetxDelayBackOffExponent + 1), kRetxDelayMaxBackoffExponent);
601 ExitNow();
602 }
603 #endif
604
605 StartCsmaBackoff();
606 ExitNow();
607 }
608
609 SetState(kStateReceive);
610
611 #if OPENTHREAD_RADIO
612 if (aFrame.GetChannel() != aFrame.GetRxChannelAfterTxDone() && mRxOnWhenIdle)
613 {
614 // On RCP build, we switch immediately to the specified RX
615 // channel if it is different from the channel on which frame
616 // was sent. On FTD or MTD builds we don't need to do
617 // the same as the `Mac` will switch the channel from the
618 // `mCallbacks.TransmitDone()`.
619
620 IgnoreError(Get<Radio>().Receive(aFrame.GetRxChannelAfterTxDone()));
621 }
622 #endif
623
624 mCallbacks.TransmitDone(aFrame, aAckFrame, aError);
625
626 exit:
627 return;
628 }
629
SignalFrameCounterUsedOnTxDone(const TxFrame & aFrame)630 void SubMac::SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame)
631 {
632 uint8_t keyIdMode;
633 uint8_t keyId;
634 uint32_t frameCounter;
635 bool allowError = false;
636
637 OT_UNUSED_VARIABLE(allowError);
638
639 VerifyOrExit(!ShouldHandleTransmitSecurity() && aFrame.GetSecurityEnabled() && aFrame.IsHeaderUpdated());
640
641 // In an FTD/MTD build, if/when link-raw is enabled, the `TxFrame`
642 // is prepared and given by user and may not necessarily follow 15.4
643 // frame format (link raw can be used with vendor-specific format),
644 // so we allow failure when parsing the frame (i.e., do not assert
645 // on an error). In other cases (in an RCP build or in an FTD/MTD
646 // build without link-raw) since the `TxFrame` should be prepared by
647 // OpenThread core, we expect no error and therefore assert if
648 // parsing fails.
649
650 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
651 allowError = Get<LinkRaw>().IsEnabled();
652 #endif
653
654 VerifyOrExit(aFrame.GetKeyIdMode(keyIdMode) == kErrorNone, OT_ASSERT(allowError));
655 VerifyOrExit(keyIdMode == Frame::kKeyIdMode1);
656
657 VerifyOrExit(aFrame.GetFrameCounter(frameCounter) == kErrorNone, OT_ASSERT(allowError));
658 VerifyOrExit(aFrame.GetKeyId(keyId) == kErrorNone, OT_ASSERT(allowError));
659
660 SignalFrameCounterUsed(frameCounter, keyId);
661
662 exit:
663 return;
664 }
665
GetRssi(void) const666 int8_t SubMac::GetRssi(void) const
667 {
668 int8_t rssi;
669
670 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
671 if (mRadioFilterEnabled)
672 {
673 rssi = Radio::kInvalidRssi;
674 }
675 else
676 #endif
677 {
678 rssi = Get<Radio>().GetRssi();
679 }
680
681 return rssi;
682 }
683
GetNoiseFloor(void) const684 int8_t SubMac::GetNoiseFloor(void) const { return Get<Radio>().GetReceiveSensitivity(); }
685
EnergyScan(uint8_t aScanChannel,uint16_t aScanDuration)686 Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration)
687 {
688 Error error = kErrorNone;
689
690 switch (mState)
691 {
692 case kStateDisabled:
693 case kStateCsmaBackoff:
694 case kStateTransmit:
695 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
696 case kStateCslTransmit:
697 #endif
698 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
699 case kStateDelayBeforeRetx:
700 #endif
701 case kStateEnergyScan:
702 ExitNow(error = kErrorInvalidState);
703
704 case kStateReceive:
705 case kStateSleep:
706 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
707 case kStateCslSample:
708 #endif
709 break;
710 }
711
712 #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE
713 VerifyOrExit(!mRadioFilterEnabled, HandleEnergyScanDone(Radio::kInvalidRssi));
714 #endif
715
716 if (RadioSupportsEnergyScan())
717 {
718 IgnoreError(Get<Radio>().EnergyScan(aScanChannel, aScanDuration));
719 SetState(kStateEnergyScan);
720 }
721 else if (ShouldHandleEnergyScan())
722 {
723 SuccessOrAssert(Get<Radio>().Receive(aScanChannel));
724
725 SetState(kStateEnergyScan);
726 mEnergyScanMaxRssi = Radio::kInvalidRssi;
727 mEnergyScanEndTime = TimerMilli::GetNow() + static_cast<uint32_t>(aScanDuration);
728 StartTimer(0);
729 }
730 else
731 {
732 error = kErrorNotImplemented;
733 }
734
735 exit:
736 return error;
737 }
738
SampleRssi(void)739 void SubMac::SampleRssi(void)
740 {
741 OT_ASSERT(!RadioSupportsEnergyScan());
742
743 int8_t rssi = GetRssi();
744
745 if (rssi != Radio::kInvalidRssi)
746 {
747 if ((mEnergyScanMaxRssi == Radio::kInvalidRssi) || (rssi > mEnergyScanMaxRssi))
748 {
749 mEnergyScanMaxRssi = rssi;
750 }
751 }
752
753 if (TimerMilli::GetNow() < mEnergyScanEndTime)
754 {
755 StartTimerAt(mTimer.GetFireTime(), kEnergyScanRssiSampleInterval);
756 }
757 else
758 {
759 HandleEnergyScanDone(mEnergyScanMaxRssi);
760 }
761 }
762
HandleEnergyScanDone(int8_t aMaxRssi)763 void SubMac::HandleEnergyScanDone(int8_t aMaxRssi)
764 {
765 SetState(kStateReceive);
766 mCallbacks.EnergyScanDone(aMaxRssi);
767 }
768
HandleTimer(void)769 void SubMac::HandleTimer(void)
770 {
771 switch (mState)
772 {
773 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
774 case kStateCslTransmit:
775 BeginTransmit();
776 break;
777 #endif
778 case kStateCsmaBackoff:
779 BeginTransmit();
780 break;
781
782 case kStateTransmit:
783 LogDebg("Ack timer timed out");
784 IgnoreError(Get<Radio>().Receive(mTransmitFrame.GetChannel()));
785 HandleTransmitDone(mTransmitFrame, nullptr, kErrorNoAck);
786 break;
787
788 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
789 case kStateDelayBeforeRetx:
790 StartCsmaBackoff();
791 break;
792 #endif
793
794 case kStateEnergyScan:
795 SampleRssi();
796 break;
797
798 default:
799 break;
800 }
801 }
802
ShouldHandleTransmitSecurity(void) const803 bool SubMac::ShouldHandleTransmitSecurity(void) const
804 {
805 bool swTxSecurity = true;
806
807 VerifyOrExit(!RadioSupportsTransmitSecurity(), swTxSecurity = false);
808
809 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE
810 VerifyOrExit(Get<LinkRaw>().IsEnabled());
811 #endif
812
813 #if OPENTHREAD_CONFIG_LINK_RAW_ENABLE || OPENTHREAD_RADIO
814 swTxSecurity = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE;
815 #endif
816
817 exit:
818 return swTxSecurity;
819 }
820
ShouldHandleCsmaBackOff(void) const821 bool SubMac::ShouldHandleCsmaBackOff(void) const
822 {
823 bool swCsma = true;
824
825 VerifyOrExit(mTransmitFrame.IsCsmaCaEnabled() && !RadioSupportsCsmaBackoff(), swCsma = 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 swCsma = OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE;
833 #endif
834
835 exit:
836 return swCsma;
837 }
838
ShouldHandleAckTimeout(void) const839 bool SubMac::ShouldHandleAckTimeout(void) const
840 {
841 bool swAckTimeout = true;
842
843 VerifyOrExit(!RadioSupportsAckTimeout(), swAckTimeout = 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 swAckTimeout = OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE;
851 #endif
852
853 exit:
854 return swAckTimeout;
855 }
856
ShouldHandleRetries(void) const857 bool SubMac::ShouldHandleRetries(void) const
858 {
859 bool swRetries = true;
860
861 VerifyOrExit(!RadioSupportsRetries(), swRetries = 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 swRetries = OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE;
869 #endif
870
871 exit:
872 return swRetries;
873 }
874
ShouldHandleEnergyScan(void) const875 bool SubMac::ShouldHandleEnergyScan(void) const
876 {
877 bool swEnergyScan = true;
878
879 VerifyOrExit(!RadioSupportsEnergyScan(), swEnergyScan = 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 swEnergyScan = OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE;
887 #endif
888
889 exit:
890 return swEnergyScan;
891 }
892
ShouldHandleTransmitTargetTime(void) const893 bool SubMac::ShouldHandleTransmitTargetTime(void) const
894 {
895 bool swTxDelay = true;
896
897 VerifyOrExit(!RadioSupportsTransmitTiming(), swTxDelay = 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 swTxDelay = OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE;
905 #endif
906
907 exit:
908 return swTxDelay;
909 }
910
ShouldHandleTransitionToSleep(void) const911 bool SubMac::ShouldHandleTransitionToSleep(void) const { return (mRxOnWhenIdle || !RadioSupportsRxOnWhenIdle()); }
912
SetState(State aState)913 void SubMac::SetState(State aState)
914 {
915 if (mState != aState)
916 {
917 LogDebg("RadioState: %s -> %s", StateToString(mState), StateToString(aState));
918 mState = aState;
919 }
920 }
921
SetMacKey(uint8_t aKeyIdMode,uint8_t aKeyId,const KeyMaterial & aPrevKey,const KeyMaterial & aCurrKey,const KeyMaterial & aNextKey)922 void SubMac::SetMacKey(uint8_t aKeyIdMode,
923 uint8_t aKeyId,
924 const KeyMaterial &aPrevKey,
925 const KeyMaterial &aCurrKey,
926 const KeyMaterial &aNextKey)
927 {
928 switch (aKeyIdMode)
929 {
930 case Frame::kKeyIdMode0:
931 case Frame::kKeyIdMode2:
932 break;
933 case Frame::kKeyIdMode1:
934 mKeyId = aKeyId;
935 mPrevKey = aPrevKey;
936 mCurrKey = aCurrKey;
937 mNextKey = aNextKey;
938 break;
939
940 default:
941 OT_ASSERT(false);
942 break;
943 }
944
945 VerifyOrExit(!ShouldHandleTransmitSecurity());
946
947 Get<Radio>().SetMacKey(aKeyIdMode, aKeyId, aPrevKey, aCurrKey, aNextKey);
948
949 exit:
950 return;
951 }
952
SignalFrameCounterUsed(uint32_t aFrameCounter,uint8_t aKeyId)953 void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId)
954 {
955 VerifyOrExit(aKeyId == mKeyId);
956
957 mCallbacks.FrameCounterUsed(aFrameCounter);
958
959 // It not always guaranteed that this method is invoked in order
960 // for different counter values (i.e., we may get it for a
961 // smaller counter value after a lager one). This may happen due
962 // to a new counter value being used for an enhanced-ack during
963 // tx of a frame. Note that the newer counter used for enhanced-ack
964 // is processed from `HandleReceiveDone()` which can happen before
965 // processing of the older counter value from `HandleTransmitDone()`.
966
967 VerifyOrExit(mFrameCounter <= aFrameCounter);
968 mFrameCounter = aFrameCounter + 1;
969
970 exit:
971 return;
972 }
973
SetFrameCounter(uint32_t aFrameCounter,bool aSetIfLarger)974 void SubMac::SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger)
975 {
976 if (!aSetIfLarger || (aFrameCounter > mFrameCounter))
977 {
978 mFrameCounter = aFrameCounter;
979 }
980
981 VerifyOrExit(!ShouldHandleTransmitSecurity());
982
983 if (aSetIfLarger)
984 {
985 Get<Radio>().SetMacFrameCounterIfLarger(aFrameCounter);
986 }
987 else
988 {
989 Get<Radio>().SetMacFrameCounter(aFrameCounter);
990 }
991
992 exit:
993 return;
994 }
995
StartTimer(uint32_t aDelayUs)996 void SubMac::StartTimer(uint32_t aDelayUs)
997 {
998 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
999 mTimer.Start(aDelayUs);
1000 #else
1001 mTimer.Start(aDelayUs / Time::kOneMsecInUsec);
1002 #endif
1003 }
1004
StartTimerAt(Time aStartTime,uint32_t aDelayUs)1005 void SubMac::StartTimerAt(Time aStartTime, uint32_t aDelayUs)
1006 {
1007 #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE
1008 mTimer.StartAt(aStartTime, aDelayUs);
1009 #else
1010 mTimer.StartAt(aStartTime, aDelayUs / Time::kOneMsecInUsec);
1011 #endif
1012 }
1013
1014 // LCOV_EXCL_START
1015
StateToString(State aState)1016 const char *SubMac::StateToString(State aState)
1017 {
1018 static const char *const kStateStrings[] = {
1019 "Disabled", // (0) kStateDisabled
1020 "Sleep", // (1) kStateSleep
1021 "Receive", // (2) kStateReceive
1022 "CsmaBackoff", // (3) kStateCsmaBackoff
1023 "Transmit", // (4) kStateTransmit
1024 "EnergyScan", // (5) kStateEnergyScan
1025 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1026 "DelayBeforeRetx", // (6) kStateDelayBeforeRetx
1027 #endif
1028 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1029 "CslTransmit", // (7) kStateCslTransmit
1030 #endif
1031 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1032 "CslSample", // (8) kStateCslSample
1033 #endif
1034 };
1035
1036 struct StateValueChecker
1037 {
1038 InitEnumValidatorCounter();
1039
1040 ValidateNextEnum(kStateDisabled);
1041 ValidateNextEnum(kStateSleep);
1042 ValidateNextEnum(kStateReceive);
1043 ValidateNextEnum(kStateCsmaBackoff);
1044 ValidateNextEnum(kStateTransmit);
1045 ValidateNextEnum(kStateEnergyScan);
1046 #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY
1047 ValidateNextEnum(kStateDelayBeforeRetx);
1048 #endif
1049 #if !OPENTHREAD_MTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
1050 ValidateNextEnum(kStateCslTransmit);
1051 #endif
1052 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
1053 ValidateNextEnum(kStateCslSample);
1054 #endif
1055 };
1056
1057 return kStateStrings[aState];
1058 }
1059
1060 // LCOV_EXCL_STOP
1061
1062 } // namespace Mac
1063 } // namespace ot
1064