1 /*
2 * Copyright (c) 2017, 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 data poll (mac data request command) sender class.
32 */
33
34 #include "data_poll_sender.hpp"
35
36 #include "instance/instance.hpp"
37
38 namespace ot {
39
40 RegisterLogModule("DataPollSender");
41
DataPollSender(Instance & aInstance)42 DataPollSender::DataPollSender(Instance &aInstance)
43 : InstanceLocator(aInstance)
44 , mTimerStartTime(0)
45 , mPollPeriod(0)
46 , mExternalPollPeriod(0)
47 , mFastPollsUsers(0)
48 , mTimer(aInstance)
49 , mEnabled(false)
50 , mAttachMode(false)
51 , mRetxMode(false)
52 , mPollTimeoutCounter(0)
53 , mPollTxFailureCounter(0)
54 , mRemainingFastPolls(0)
55 {
56 }
57
GetParent(void) const58 const Neighbor &DataPollSender::GetParent(void) const
59 {
60 const Neighbor &parentCandidate = Get<Mle::MleRouter>().GetParentCandidate();
61
62 return parentCandidate.IsStateValid() ? parentCandidate : Get<Mle::MleRouter>().GetParent();
63 }
64
StartPolling(void)65 void DataPollSender::StartPolling(void)
66 {
67 VerifyOrExit(!mEnabled);
68
69 OT_ASSERT(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
70
71 mEnabled = true;
72 ScheduleNextPoll(kRecalculatePollPeriod);
73
74 exit:
75 return;
76 }
77
StopPolling(void)78 void DataPollSender::StopPolling(void)
79 {
80 mTimer.Stop();
81 mAttachMode = false;
82 mRetxMode = false;
83 mPollTimeoutCounter = 0;
84 mPollTxFailureCounter = 0;
85 mRemainingFastPolls = 0;
86 mFastPollsUsers = 0;
87 mEnabled = false;
88 }
89
SendDataPoll(void)90 Error DataPollSender::SendDataPoll(void)
91 {
92 Error error;
93
94 VerifyOrExit(mEnabled, error = kErrorInvalidState);
95 VerifyOrExit(!Get<Mac::Mac>().GetRxOnWhenIdle(), error = kErrorInvalidState);
96
97 VerifyOrExit(GetParent().IsStateValidOrRestoring(), error = kErrorInvalidState);
98
99 mTimer.Stop();
100
101 SuccessOrExit(error = Get<Mac::Mac>().RequestDataPollTransmission());
102
103 exit:
104
105 switch (error)
106 {
107 case kErrorNone:
108 LogDebg("Sending data poll");
109 ScheduleNextPoll(kUsePreviousPollPeriod);
110 break;
111
112 case kErrorInvalidState:
113 LogWarn("Data poll tx requested while data polling was not enabled!");
114 StopPolling();
115 break;
116
117 default:
118 LogWarn("Unexpected error %s requesting data poll", ErrorToString(error));
119 ScheduleNextPoll(kRecalculatePollPeriod);
120 break;
121 }
122
123 return error;
124 }
125
126 #if OPENTHREAD_CONFIG_MULTI_RADIO
GetPollDestinationAddress(Mac::Address & aDest,Mac::RadioType & aRadioType) const127 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const
128 #else
129 Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const
130 #endif
131 {
132 Error error = kErrorNone;
133 const Neighbor &parent = GetParent();
134
135 VerifyOrExit(parent.IsStateValidOrRestoring(), error = kErrorAbort);
136
137 // Use extended address attaching to a new parent (i.e. parent is the parent candidate).
138 if ((Get<Mac::Mac>().GetShortAddress() == Mac::kShortAddrInvalid) ||
139 (&parent == &Get<Mle::MleRouter>().GetParentCandidate()))
140 {
141 aDest.SetExtended(parent.GetExtAddress());
142 }
143 else
144 {
145 aDest.SetShort(parent.GetRloc16());
146 }
147
148 #if OPENTHREAD_CONFIG_MULTI_RADIO
149 aRadioType = Get<RadioSelector>().SelectPollFrameRadio(parent);
150 #endif
151
152 exit:
153 return error;
154 }
155
SetExternalPollPeriod(uint32_t aPeriod)156 Error DataPollSender::SetExternalPollPeriod(uint32_t aPeriod)
157 {
158 Error error = kErrorNone;
159
160 if (aPeriod != 0)
161 {
162 VerifyOrExit(aPeriod >= OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD, error = kErrorInvalidArgs);
163
164 aPeriod = Min(aPeriod, kMaxExternalPeriod);
165 }
166
167 if (mExternalPollPeriod != aPeriod)
168 {
169 mExternalPollPeriod = aPeriod;
170
171 if (mEnabled)
172 {
173 ScheduleNextPoll(kRecalculatePollPeriod);
174 }
175 }
176
177 exit:
178 return error;
179 }
180
GetKeepAlivePollPeriod(void) const181 uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const
182 {
183 uint32_t period = GetDefaultPollPeriod();
184
185 if (mExternalPollPeriod != 0)
186 {
187 period = Min(period, mExternalPollPeriod);
188 }
189
190 return period;
191 }
192
HandlePollSent(Mac::TxFrame & aFrame,Error aError)193 void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError)
194 {
195 Mac::Address macDest;
196 bool shouldRecalculatePollPeriod = false;
197
198 VerifyOrExit(mEnabled);
199
200 if (!aFrame.IsEmpty())
201 {
202 IgnoreError(aFrame.GetDstAddr(macDest));
203 Get<MeshForwarder>().UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ true);
204 }
205
206 if (GetParent().IsStateInvalid())
207 {
208 StopPolling();
209 IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
210 ExitNow();
211 }
212
213 switch (aError)
214 {
215 case kErrorNone:
216
217 if (mRemainingFastPolls != 0)
218 {
219 mRemainingFastPolls--;
220
221 if (mRemainingFastPolls == 0)
222 {
223 shouldRecalculatePollPeriod = true;
224 mFastPollsUsers = 0;
225 }
226 }
227
228 if (mRetxMode)
229 {
230 mRetxMode = false;
231 mPollTxFailureCounter = 0;
232 shouldRecalculatePollPeriod = true;
233 }
234
235 break;
236
237 case kErrorChannelAccessFailure:
238 case kErrorAbort:
239 mRetxMode = true;
240 shouldRecalculatePollPeriod = true;
241 break;
242
243 default:
244 mPollTxFailureCounter++;
245
246 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
247 LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
248 aFrame.HasCslIe() ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts);
249 #else
250 LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
251 kMaxPollRetxAttempts);
252 #endif
253
254 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
255 if (mPollTxFailureCounter < (aFrame.HasCslIe() ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts))
256 #else
257 if (mPollTxFailureCounter < kMaxPollRetxAttempts)
258 #endif
259 {
260 if (!mRetxMode)
261 {
262 mRetxMode = true;
263 shouldRecalculatePollPeriod = true;
264 }
265 }
266 else
267 {
268 mRetxMode = false;
269 mPollTxFailureCounter = 0;
270 shouldRecalculatePollPeriod = true;
271 }
272
273 break;
274 }
275
276 if (shouldRecalculatePollPeriod)
277 {
278 ScheduleNextPoll(kRecalculatePollPeriod);
279 }
280
281 exit:
282 return;
283 }
284
HandlePollTimeout(void)285 void DataPollSender::HandlePollTimeout(void)
286 {
287 // A data poll timeout happened, i.e., the ack in response to
288 // a data poll indicated that a frame was pending, but no frame
289 // was received after timeout interval.
290
291 VerifyOrExit(mEnabled);
292
293 mPollTimeoutCounter++;
294
295 LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
296
297 if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
298 {
299 IgnoreError(SendDataPoll());
300 }
301 else
302 {
303 mPollTimeoutCounter = 0;
304 }
305
306 exit:
307 return;
308 }
309
ProcessRxFrame(const Mac::RxFrame & aFrame)310 void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame)
311 {
312 VerifyOrExit(mEnabled);
313
314 mPollTimeoutCounter = 0;
315
316 if (aFrame.GetFramePending())
317 {
318 IgnoreError(SendDataPoll());
319 }
320
321 exit:
322 return;
323 }
324
325 #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
ProcessTxDone(const Mac::TxFrame & aFrame,const Mac::RxFrame * aAckFrame,Error aError)326 void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError)
327 {
328 bool sendDataPoll = false;
329
330 VerifyOrExit(mEnabled);
331 VerifyOrExit(Get<Mle::MleRouter>().GetParent().IsEnhancedKeepAliveSupported());
332 VerifyOrExit(aFrame.GetSecurityEnabled());
333
334 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
335 if (aFrame.mInfo.mTxInfo.mIsARetx && aFrame.HasCslIe())
336 {
337 // For retransmission frame, use a data poll to resync its parent with correct CSL phase
338 sendDataPoll = true;
339 }
340 #endif
341
342 if (aError == kErrorNone && aAckFrame != nullptr)
343 {
344 mPollTimeoutCounter = 0;
345
346 if (aAckFrame->GetFramePending())
347 {
348 sendDataPoll = true;
349 }
350 else
351 {
352 ResetKeepAliveTimer();
353 }
354 }
355
356 if (sendDataPoll)
357 {
358 IgnoreError(SendDataPoll());
359 }
360
361 exit:
362 return;
363 }
364 #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
365
RecalculatePollPeriod(void)366 void DataPollSender::RecalculatePollPeriod(void)
367 {
368 if (mEnabled)
369 {
370 ScheduleNextPoll(kRecalculatePollPeriod);
371 }
372 }
373
SetAttachMode(bool aMode)374 void DataPollSender::SetAttachMode(bool aMode)
375 {
376 if (mAttachMode != aMode)
377 {
378 mAttachMode = aMode;
379
380 if (mEnabled)
381 {
382 ScheduleNextPoll(kRecalculatePollPeriod);
383 }
384 }
385 }
386
SendFastPolls(uint8_t aNumFastPolls)387 void DataPollSender::SendFastPolls(uint8_t aNumFastPolls)
388 {
389 bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0);
390
391 if (mFastPollsUsers < kMaxFastPollsUsers)
392 {
393 mFastPollsUsers++;
394 }
395
396 if (aNumFastPolls == 0)
397 {
398 aNumFastPolls = kDefaultFastPolls;
399 }
400
401 aNumFastPolls = Min(aNumFastPolls, kMaxFastPolls);
402 mRemainingFastPolls = Max(mRemainingFastPolls, aNumFastPolls);
403
404 if (mEnabled && shouldRecalculatePollPeriod)
405 {
406 ScheduleNextPoll(kRecalculatePollPeriod);
407 }
408 }
409
StopFastPolls(void)410 void DataPollSender::StopFastPolls(void)
411 {
412 VerifyOrExit(mFastPollsUsers != 0);
413
414 // If `mFastPollsUsers` hits the max, let it be cleared
415 // from `HandlePollSent()` (after all fast polls are sent).
416 VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
417
418 mFastPollsUsers--;
419
420 VerifyOrExit(mFastPollsUsers == 0);
421
422 mRemainingFastPolls = 0;
423 ScheduleNextPoll(kRecalculatePollPeriod);
424
425 exit:
426 return;
427 }
428
ResetKeepAliveTimer(void)429 void DataPollSender::ResetKeepAliveTimer(void)
430 {
431 if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
432 {
433 mTimerStartTime = TimerMilli::GetNow();
434 mTimer.StartAt(mTimerStartTime, mPollPeriod);
435 }
436 }
437
ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)438 void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
439 {
440 TimeMilli now;
441 uint32_t oldPeriod = mPollPeriod;
442
443 if (aPollPeriodSelector == kRecalculatePollPeriod)
444 {
445 mPollPeriod = CalculatePollPeriod();
446 }
447
448 now = TimerMilli::GetNow();
449
450 if (mTimer.IsRunning())
451 {
452 if (oldPeriod != mPollPeriod)
453 {
454 // If poll interval did change and re-starting the timer from
455 // last start time with new poll interval would fire quickly
456 // (i.e., fires within window `[now, now + kMinPollPeriod]`)
457 // add an extra minimum delay of `kMinPollPeriod`. This
458 // ensures that when an internal or external request triggers
459 // a switch to a shorter poll interval, the first data poll
460 // will not be sent too quickly (and possibly before the
461 // response is available/prepared on the parent node).
462
463 if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
464 {
465 mTimer.StartAt(now, kMinPollPeriod);
466 }
467 else
468 {
469 mTimer.StartAt(mTimerStartTime, mPollPeriod);
470 }
471 }
472 // Do nothing on the running poll timer if the poll interval doesn't change
473 }
474 else
475 {
476 mTimerStartTime = now;
477 mTimer.StartAt(mTimerStartTime, mPollPeriod);
478 }
479 }
480
CalculatePollPeriod(void) const481 uint32_t DataPollSender::CalculatePollPeriod(void) const
482 {
483 uint32_t period = GetDefaultPollPeriod();
484
485 if (mAttachMode)
486 {
487 period = Min(period, kAttachDataPollPeriod);
488 }
489
490 if (mRetxMode)
491 {
492 period = Min(period, kRetxPollPeriod);
493
494 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
495 if (Get<Mac::Mac>().GetCslPeriodInMsec() > 0)
496 {
497 period = Min(period, Get<Mac::Mac>().GetCslPeriodInMsec());
498 }
499 #endif
500 }
501
502 if (mRemainingFastPolls != 0)
503 {
504 period = Min(period, kFastPollPeriod);
505 }
506
507 if (mExternalPollPeriod != 0)
508 {
509 period = Min(period, mExternalPollPeriod);
510 }
511
512 if (period == 0)
513 {
514 period = kMinPollPeriod;
515 }
516
517 return period;
518 }
519
GetDefaultPollPeriod(void) const520 uint32_t DataPollSender::GetDefaultPollPeriod(void) const
521 {
522 uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
523 uint32_t period;
524
525 period = Time::SecToMsec(Min(Get<Mle::MleRouter>().GetTimeout(), Time::MsecToSec(TimerMilli::kMaxDelay)));
526
527 #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
528 if (Get<Mac::Mac>().IsCslEnabled())
529 {
530 period = Min(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
531 pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
532 }
533 #endif
534
535 if (period > pollAhead)
536 {
537 period -= pollAhead;
538 }
539
540 return period;
541 }
542
PrepareDataRequest(Mac::TxFrames & aTxFrames)543 Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
544 {
545 Mac::TxFrame *frame = nullptr;
546 Mac::TxFrame::Info frameInfo;
547
548 #if OPENTHREAD_CONFIG_MULTI_RADIO
549 Mac::RadioType radio;
550
551 SuccessOrExit(GetPollDestinationAddress(frameInfo.mAddrs.mDestination, radio));
552 frame = &aTxFrames.GetTxFrame(radio);
553 #else
554 SuccessOrExit(GetPollDestinationAddress(frameInfo.mAddrs.mDestination));
555 frame = &aTxFrames.GetTxFrame();
556 #endif
557
558 if (frameInfo.mAddrs.mDestination.IsExtended())
559 {
560 frameInfo.mAddrs.mSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
561 }
562 else
563 {
564 frameInfo.mAddrs.mSource.SetShort(Get<Mac::Mac>().GetShortAddress());
565 }
566
567 frameInfo.mPanIds.SetBothSourceDestination(Get<Mac::Mac>().GetPanId());
568
569 frameInfo.mType = Mac::Frame::kTypeMacCmd;
570 frameInfo.mCommandId = Mac::Frame::kMacCmdDataRequest;
571 frameInfo.mSecurityLevel = Mac::Frame::kSecurityEncMic32;
572 frameInfo.mKeyIdMode = Mac::Frame::kKeyIdMode1;
573
574 Get<MeshForwarder>().PrepareMacHeaders(*frame, frameInfo, nullptr);
575
576 #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
577 if (frame->HasCslIe())
578 {
579 // Disable frame retransmission when the data poll has CSL IE included
580 aTxFrames.SetMaxFrameRetries(0);
581 }
582 #endif
583
584 exit:
585 return frame;
586 }
587
588 } // namespace ot
589