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