632 lines
16 KiB
C++
632 lines
16 KiB
C++
/*
|
|
* Copyright (c) 2017, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements data poll (mac data request command) sender class.
|
|
*/
|
|
|
|
#include "data_poll_sender.hpp"
|
|
|
|
#include "common/code_utils.hpp"
|
|
#include "common/instance.hpp"
|
|
#include "common/locator_getters.hpp"
|
|
#include "common/log.hpp"
|
|
#include "common/message.hpp"
|
|
#include "net/ip6.hpp"
|
|
#include "net/netif.hpp"
|
|
#include "thread/mesh_forwarder.hpp"
|
|
#include "thread/mle.hpp"
|
|
#include "thread/thread_netif.hpp"
|
|
|
|
namespace ot {
|
|
|
|
RegisterLogModule("DataPollSender");
|
|
|
|
DataPollSender::DataPollSender(Instance &aInstance)
|
|
: InstanceLocator(aInstance)
|
|
, mTimerStartTime(0)
|
|
, mPollPeriod(0)
|
|
, mExternalPollPeriod(0)
|
|
, mFastPollsUsers(0)
|
|
, mTimer(aInstance, DataPollSender::HandlePollTimer)
|
|
, mEnabled(false)
|
|
, mAttachMode(false)
|
|
, mRetxMode(false)
|
|
, mPollTimeoutCounter(0)
|
|
, mPollTxFailureCounter(0)
|
|
, mRemainingFastPolls(0)
|
|
{
|
|
}
|
|
|
|
const Neighbor &DataPollSender::GetParent(void) const
|
|
{
|
|
const Neighbor &parentCandidate = Get<Mle::MleRouter>().GetParentCandidate();
|
|
|
|
return parentCandidate.IsStateValid() ? parentCandidate : Get<Mle::MleRouter>().GetParent();
|
|
}
|
|
|
|
void DataPollSender::StartPolling(void)
|
|
{
|
|
VerifyOrExit(!mEnabled);
|
|
|
|
OT_ASSERT(!Get<Mle::MleRouter>().IsRxOnWhenIdle());
|
|
|
|
mEnabled = true;
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void DataPollSender::StopPolling(void)
|
|
{
|
|
mTimer.Stop();
|
|
mAttachMode = false;
|
|
mRetxMode = false;
|
|
mPollTimeoutCounter = 0;
|
|
mPollTxFailureCounter = 0;
|
|
mRemainingFastPolls = 0;
|
|
mFastPollsUsers = 0;
|
|
mEnabled = false;
|
|
}
|
|
|
|
Error DataPollSender::SendDataPoll(void)
|
|
{
|
|
Error error;
|
|
|
|
VerifyOrExit(mEnabled, error = kErrorInvalidState);
|
|
VerifyOrExit(!Get<Mac::Mac>().GetRxOnWhenIdle(), error = kErrorInvalidState);
|
|
|
|
VerifyOrExit(GetParent().IsStateValidOrRestoring(), error = kErrorInvalidState);
|
|
|
|
mTimer.Stop();
|
|
|
|
SuccessOrExit(error = Get<Mac::Mac>().RequestDataPollTransmission());
|
|
|
|
exit:
|
|
|
|
switch (error)
|
|
{
|
|
case kErrorNone:
|
|
LogDebg("Sending data poll");
|
|
ScheduleNextPoll(kUsePreviousPollPeriod);
|
|
break;
|
|
|
|
case kErrorInvalidState:
|
|
LogWarn("Data poll tx requested while data polling was not enabled!");
|
|
StopPolling();
|
|
break;
|
|
|
|
default:
|
|
LogWarn("Unexpected error %s requesting data poll", ErrorToString(error));
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
break;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_MULTI_RADIO
|
|
Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const
|
|
#else
|
|
Error DataPollSender::GetPollDestinationAddress(Mac::Address &aDest) const
|
|
#endif
|
|
{
|
|
Error error = kErrorNone;
|
|
const Neighbor &parent = GetParent();
|
|
|
|
VerifyOrExit(parent.IsStateValidOrRestoring(), error = kErrorAbort);
|
|
|
|
// Use extended address attaching to a new parent (i.e. parent is the parent candidate).
|
|
if ((Get<Mac::Mac>().GetShortAddress() == Mac::kShortAddrInvalid) ||
|
|
(&parent == &Get<Mle::MleRouter>().GetParentCandidate()))
|
|
{
|
|
aDest.SetExtended(parent.GetExtAddress());
|
|
}
|
|
else
|
|
{
|
|
aDest.SetShort(parent.GetRloc16());
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_MULTI_RADIO
|
|
aRadioType = Get<RadioSelector>().SelectPollFrameRadio(parent);
|
|
#endif
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
Error DataPollSender::SetExternalPollPeriod(uint32_t aPeriod)
|
|
{
|
|
Error error = kErrorNone;
|
|
|
|
if (aPeriod != 0)
|
|
{
|
|
VerifyOrExit(aPeriod >= OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD, error = kErrorInvalidArgs);
|
|
|
|
// Clipped by the maximal value.
|
|
if (aPeriod > kMaxExternalPeriod)
|
|
{
|
|
aPeriod = kMaxExternalPeriod;
|
|
}
|
|
}
|
|
|
|
if (mExternalPollPeriod != aPeriod)
|
|
{
|
|
mExternalPollPeriod = aPeriod;
|
|
|
|
if (mEnabled)
|
|
{
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const
|
|
{
|
|
uint32_t period = GetDefaultPollPeriod();
|
|
|
|
if (mExternalPollPeriod != 0)
|
|
{
|
|
period = OT_MIN(period, mExternalPollPeriod);
|
|
}
|
|
|
|
return period;
|
|
}
|
|
|
|
void DataPollSender::HandlePollSent(Mac::TxFrame &aFrame, Error aError)
|
|
{
|
|
Mac::Address macDest;
|
|
bool shouldRecalculatePollPeriod = false;
|
|
|
|
VerifyOrExit(mEnabled);
|
|
|
|
if (!aFrame.IsEmpty())
|
|
{
|
|
IgnoreError(aFrame.GetDstAddr(macDest));
|
|
Get<MeshForwarder>().UpdateNeighborOnSentFrame(aFrame, aError, macDest, /* aIsDataPoll */ true);
|
|
}
|
|
|
|
if (GetParent().IsStateInvalid())
|
|
{
|
|
StopPolling();
|
|
IgnoreError(Get<Mle::MleRouter>().BecomeDetached());
|
|
ExitNow();
|
|
}
|
|
|
|
switch (aError)
|
|
{
|
|
case kErrorNone:
|
|
|
|
if (mRemainingFastPolls != 0)
|
|
{
|
|
mRemainingFastPolls--;
|
|
|
|
if (mRemainingFastPolls == 0)
|
|
{
|
|
shouldRecalculatePollPeriod = true;
|
|
mFastPollsUsers = 0;
|
|
}
|
|
}
|
|
|
|
if (mRetxMode)
|
|
{
|
|
mRetxMode = false;
|
|
mPollTxFailureCounter = 0;
|
|
shouldRecalculatePollPeriod = true;
|
|
}
|
|
|
|
break;
|
|
|
|
case kErrorChannelAccessFailure:
|
|
case kErrorAbort:
|
|
mRetxMode = true;
|
|
shouldRecalculatePollPeriod = true;
|
|
break;
|
|
|
|
default:
|
|
mPollTxFailureCounter++;
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
|
|
(aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts
|
|
: kMaxPollRetxAttempts);
|
|
#else
|
|
LogInfo("Failed to send data poll, error:%s, retx:%d/%d", ErrorToString(aError), mPollTxFailureCounter,
|
|
kMaxPollRetxAttempts);
|
|
#endif
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
if (mPollTxFailureCounter <
|
|
((aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) ? kMaxCslPollRetxAttempts : kMaxPollRetxAttempts))
|
|
#else
|
|
if (mPollTxFailureCounter < kMaxPollRetxAttempts)
|
|
#endif
|
|
{
|
|
if (!mRetxMode)
|
|
{
|
|
mRetxMode = true;
|
|
shouldRecalculatePollPeriod = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mRetxMode = false;
|
|
mPollTxFailureCounter = 0;
|
|
shouldRecalculatePollPeriod = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (shouldRecalculatePollPeriod)
|
|
{
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void DataPollSender::HandlePollTimeout(void)
|
|
{
|
|
// A data poll timeout happened, i.e., the ack in response to
|
|
// a data poll indicated that a frame was pending, but no frame
|
|
// was received after timeout interval.
|
|
|
|
VerifyOrExit(mEnabled);
|
|
|
|
mPollTimeoutCounter++;
|
|
|
|
LogInfo("Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout);
|
|
|
|
if (mPollTimeoutCounter < kQuickPollsAfterTimeout)
|
|
{
|
|
IgnoreError(SendDataPoll());
|
|
}
|
|
else
|
|
{
|
|
mPollTimeoutCounter = 0;
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void DataPollSender::ProcessRxFrame(const Mac::RxFrame &aFrame)
|
|
{
|
|
VerifyOrExit(mEnabled);
|
|
|
|
mPollTimeoutCounter = 0;
|
|
|
|
if (aFrame.GetFramePending())
|
|
{
|
|
IgnoreError(SendDataPoll());
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
|
void DataPollSender::ProcessTxDone(const Mac::TxFrame &aFrame, const Mac::RxFrame *aAckFrame, Error aError)
|
|
{
|
|
bool sendDataPoll = false;
|
|
|
|
VerifyOrExit(mEnabled);
|
|
VerifyOrExit(Get<Mle::MleRouter>().GetParent().IsEnhancedKeepAliveSupported());
|
|
VerifyOrExit(aFrame.GetSecurityEnabled());
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
if (aFrame.mInfo.mTxInfo.mIsARetx && (aFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr))
|
|
{
|
|
// For retransmission frame, use a data poll to resync its parent with correct CSL phase
|
|
sendDataPoll = true;
|
|
}
|
|
#endif
|
|
|
|
if (aError == kErrorNone && aAckFrame != nullptr)
|
|
{
|
|
mPollTimeoutCounter = 0;
|
|
|
|
if (aAckFrame->GetFramePending())
|
|
{
|
|
sendDataPoll = true;
|
|
}
|
|
else
|
|
{
|
|
ResetKeepAliveTimer();
|
|
}
|
|
}
|
|
|
|
if (sendDataPoll)
|
|
{
|
|
IgnoreError(SendDataPoll());
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
|
|
|
|
void DataPollSender::RecalculatePollPeriod(void)
|
|
{
|
|
if (mEnabled)
|
|
{
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
}
|
|
}
|
|
|
|
void DataPollSender::SetAttachMode(bool aMode)
|
|
{
|
|
if (mAttachMode != aMode)
|
|
{
|
|
mAttachMode = aMode;
|
|
|
|
if (mEnabled)
|
|
{
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DataPollSender::SendFastPolls(uint8_t aNumFastPolls)
|
|
{
|
|
bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0);
|
|
|
|
if (mFastPollsUsers < kMaxFastPollsUsers)
|
|
{
|
|
mFastPollsUsers++;
|
|
}
|
|
|
|
if (aNumFastPolls == 0)
|
|
{
|
|
aNumFastPolls = kDefaultFastPolls;
|
|
}
|
|
|
|
if (aNumFastPolls > kMaxFastPolls)
|
|
{
|
|
aNumFastPolls = kMaxFastPolls;
|
|
}
|
|
|
|
if (mRemainingFastPolls < aNumFastPolls)
|
|
{
|
|
mRemainingFastPolls = aNumFastPolls;
|
|
}
|
|
|
|
if (mEnabled && shouldRecalculatePollPeriod)
|
|
{
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
}
|
|
}
|
|
|
|
void DataPollSender::StopFastPolls(void)
|
|
{
|
|
VerifyOrExit(mFastPollsUsers != 0);
|
|
|
|
// If `mFastPollsUsers` hits the max, let it be cleared
|
|
// from `HandlePollSent()` (after all fast polls are sent).
|
|
VerifyOrExit(mFastPollsUsers < kMaxFastPollsUsers);
|
|
|
|
mFastPollsUsers--;
|
|
|
|
VerifyOrExit(mFastPollsUsers == 0);
|
|
|
|
mRemainingFastPolls = 0;
|
|
ScheduleNextPoll(kRecalculatePollPeriod);
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
void DataPollSender::ResetKeepAliveTimer(void)
|
|
{
|
|
if (mTimer.IsRunning() && mPollPeriod == GetDefaultPollPeriod())
|
|
{
|
|
mTimerStartTime = TimerMilli::GetNow();
|
|
mTimer.StartAt(mTimerStartTime, mPollPeriod);
|
|
}
|
|
}
|
|
|
|
void DataPollSender::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector)
|
|
{
|
|
TimeMilli now;
|
|
uint32_t oldPeriod = mPollPeriod;
|
|
|
|
if (aPollPeriodSelector == kRecalculatePollPeriod)
|
|
{
|
|
mPollPeriod = CalculatePollPeriod();
|
|
}
|
|
|
|
now = TimerMilli::GetNow();
|
|
|
|
if (mTimer.IsRunning())
|
|
{
|
|
if (oldPeriod != mPollPeriod)
|
|
{
|
|
// If poll interval did change and re-starting the timer from
|
|
// last start time with new poll interval would fire quickly
|
|
// (i.e., fires within window `[now, now + kMinPollPeriod]`)
|
|
// add an extra minimum delay of `kMinPollPeriod`. This
|
|
// ensures that when an internal or external request triggers
|
|
// a switch to a shorter poll interval, the first data poll
|
|
// will not be sent too quickly (and possibly before the
|
|
// response is available/prepared on the parent node).
|
|
|
|
if (mTimerStartTime + mPollPeriod < now + kMinPollPeriod)
|
|
{
|
|
mTimer.StartAt(now, kMinPollPeriod);
|
|
}
|
|
else
|
|
{
|
|
mTimer.StartAt(mTimerStartTime, mPollPeriod);
|
|
}
|
|
}
|
|
// Do nothing on the running poll timer if the poll interval doesn't change
|
|
}
|
|
else
|
|
{
|
|
mTimerStartTime = now;
|
|
mTimer.StartAt(mTimerStartTime, mPollPeriod);
|
|
}
|
|
}
|
|
|
|
uint32_t DataPollSender::CalculatePollPeriod(void) const
|
|
{
|
|
uint32_t period = GetDefaultPollPeriod();
|
|
|
|
if (mAttachMode)
|
|
{
|
|
period = OT_MIN(period, kAttachDataPollPeriod);
|
|
}
|
|
|
|
if (mRetxMode)
|
|
{
|
|
period = OT_MIN(period, kRetxPollPeriod);
|
|
}
|
|
|
|
if (mRemainingFastPolls != 0)
|
|
{
|
|
period = OT_MIN(period, kFastPollPeriod);
|
|
}
|
|
|
|
if (mExternalPollPeriod != 0)
|
|
{
|
|
period = OT_MIN(period, mExternalPollPeriod);
|
|
}
|
|
|
|
if (period == 0)
|
|
{
|
|
period = kMinPollPeriod;
|
|
}
|
|
|
|
return period;
|
|
}
|
|
|
|
void DataPollSender::HandlePollTimer(Timer &aTimer)
|
|
{
|
|
IgnoreError(aTimer.Get<DataPollSender>().SendDataPoll());
|
|
}
|
|
|
|
uint32_t DataPollSender::GetDefaultPollPeriod(void) const
|
|
{
|
|
uint32_t period = Time::SecToMsec(Get<Mle::MleRouter>().GetTimeout());
|
|
uint32_t pollAhead = static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts;
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE
|
|
if (Get<Mac::Mac>().IsCslEnabled())
|
|
{
|
|
period = OT_MIN(period, Time::SecToMsec(Get<Mle::MleRouter>().GetCslTimeout()));
|
|
pollAhead = static_cast<uint32_t>(kRetxPollPeriod);
|
|
}
|
|
#endif
|
|
|
|
if (period > pollAhead)
|
|
{
|
|
period -= pollAhead;
|
|
}
|
|
|
|
return period;
|
|
}
|
|
|
|
Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames)
|
|
{
|
|
Mac::TxFrame *frame = nullptr;
|
|
Mac::Address src, dst;
|
|
uint16_t fcf;
|
|
bool iePresent;
|
|
|
|
#if OPENTHREAD_CONFIG_MULTI_RADIO
|
|
Mac::RadioType radio;
|
|
|
|
SuccessOrExit(GetPollDestinationAddress(dst, radio));
|
|
frame = &aTxFrames.GetTxFrame(radio);
|
|
#else
|
|
SuccessOrExit(GetPollDestinationAddress(dst));
|
|
frame = &aTxFrames.GetTxFrame();
|
|
#endif
|
|
|
|
fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfAckRequest |
|
|
Mac::Frame::kFcfSecurityEnabled;
|
|
|
|
iePresent = Get<MeshForwarder>().CalcIePresent(nullptr);
|
|
|
|
if (iePresent)
|
|
{
|
|
fcf |= Mac::Frame::kFcfIePresent;
|
|
}
|
|
|
|
fcf |= Get<MeshForwarder>().CalcFrameVersion(Get<NeighborTable>().FindNeighbor(dst), iePresent);
|
|
|
|
if (dst.IsExtended())
|
|
{
|
|
fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt;
|
|
src.SetExtended(Get<Mac::Mac>().GetExtAddress());
|
|
}
|
|
else
|
|
{
|
|
fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort;
|
|
src.SetShort(Get<Mac::Mac>().GetShortAddress());
|
|
}
|
|
|
|
frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
|
|
|
|
if (frame->IsDstPanIdPresent())
|
|
{
|
|
frame->SetDstPanId(Get<Mac::Mac>().GetPanId());
|
|
}
|
|
|
|
frame->SetSrcAddr(src);
|
|
frame->SetDstAddr(dst);
|
|
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
|
|
if (iePresent)
|
|
{
|
|
Get<MeshForwarder>().AppendHeaderIe(nullptr, *frame);
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
|
|
if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr)
|
|
{
|
|
// Disable frame retransmission when the data poll has CSL IE included
|
|
aTxFrames.SetMaxFrameRetries(0);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest));
|
|
|
|
exit:
|
|
return frame;
|
|
}
|
|
|
|
} // namespace ot
|