| /* |
| * 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) manager class. |
| */ |
| |
| #define WPP_NAME "data_poll_manager.tmh" |
| #include <openthread/config.h> |
| |
| #include "data_poll_manager.hpp" |
| #include <openthread/platform/random.h> |
| |
| #include "openthread-instance.h" |
| #include "common/code_utils.hpp" |
| #include "common/logging.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 { |
| |
| DataPollManager::DataPollManager(MeshForwarder &aMeshForwarder): |
| MeshForwarderLocator(aMeshForwarder), |
| mTimer(aMeshForwarder.GetNetif().GetIp6().mTimerScheduler, &DataPollManager::HandlePollTimer, this), |
| mTimerStartTime(0), |
| mExternalPollPeriod(0), |
| mPollPeriod(0), |
| mEnabled(false), |
| mAttachMode(false), |
| mRetxMode(false), |
| mNoBufferRetxMode(false), |
| mPollTimeoutCounter(0), |
| mPollTxFailureCounter(0), |
| mRemainingFastPolls(0) |
| { |
| } |
| |
| otError DataPollManager::StartPolling(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(!mEnabled, error = OT_ERROR_ALREADY); |
| VerifyOrExit((GetMeshForwarder().GetNetif().GetMle().GetDeviceMode() & Mle::ModeTlv::kModeFFD) == 0, |
| error = OT_ERROR_INVALID_STATE); |
| |
| mEnabled = true; |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| |
| exit: |
| return error; |
| } |
| |
| void DataPollManager::StopPolling(void) |
| { |
| mTimer.Stop(); |
| mAttachMode = false; |
| mRetxMode = false; |
| mNoBufferRetxMode = false; |
| mPollTimeoutCounter = 0; |
| mPollTxFailureCounter = 0; |
| mRemainingFastPolls = 0; |
| mEnabled = false; |
| } |
| |
| otError DataPollManager::SendDataPoll(void) |
| { |
| MeshForwarder &meshForwarder = GetMeshForwarder(); |
| otError error; |
| Message *message; |
| |
| VerifyOrExit(mEnabled, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(!meshForwarder.GetNetif().GetMac().GetRxOnWhenIdle(), error = OT_ERROR_INVALID_STATE); |
| |
| mTimer.Stop(); |
| |
| for (message = meshForwarder.GetSendQueue().GetHead(); message; message = message->GetNext()) |
| { |
| VerifyOrExit(message->GetType() != Message::kTypeMacDataPoll, error = OT_ERROR_ALREADY); |
| } |
| |
| message = meshForwarder.GetNetif().GetIp6().mMessagePool.New(Message::kTypeMacDataPoll, 0); |
| VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); |
| |
| error = meshForwarder.SendMessage(*message); |
| |
| if (error != OT_ERROR_NONE) |
| { |
| message->Free(); |
| } |
| |
| exit: |
| |
| switch (error) |
| { |
| case OT_ERROR_NONE: |
| otLogDebgMac(GetInstance(), "Sending data poll"); |
| |
| if (mNoBufferRetxMode == true) |
| { |
| mNoBufferRetxMode = false; |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| } |
| else |
| { |
| ScheduleNextPoll(kUsePreviousPollPeriod); |
| } |
| |
| break; |
| |
| case OT_ERROR_INVALID_STATE: |
| otLogWarnMac(GetInstance(), "Data poll tx requested while data polling was not enabled!"); |
| StopPolling(); |
| break; |
| |
| case OT_ERROR_ALREADY: |
| otLogDebgMac(GetInstance(), "Data poll tx requested when a previous data request still in send queue."); |
| ScheduleNextPoll(kUsePreviousPollPeriod); |
| break; |
| |
| case OT_ERROR_NO_BUFS: |
| default: |
| mNoBufferRetxMode = true; |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| break; |
| } |
| |
| return error; |
| } |
| |
| void DataPollManager::SetExternalPollPeriod(uint32_t aPeriod) |
| { |
| if (mExternalPollPeriod != aPeriod) |
| { |
| mExternalPollPeriod = aPeriod; |
| |
| if (mEnabled) |
| { |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| } |
| } |
| } |
| |
| uint32_t DataPollManager::GetKeepAlivePollPeriod(void) const |
| { |
| uint32_t period = 0; |
| |
| if (mExternalPollPeriod != 0) |
| { |
| period = mExternalPollPeriod; |
| } |
| else |
| { |
| period = GetDefaultPollPeriod(); |
| } |
| |
| return period; |
| } |
| |
| void DataPollManager::HandlePollSent(otError aError) |
| { |
| bool shouldRecalculatePollPeriod = false; |
| |
| VerifyOrExit(mEnabled); |
| |
| switch (aError) |
| { |
| case OT_ERROR_NONE: |
| |
| if (mRemainingFastPolls != 0) |
| { |
| mRemainingFastPolls--; |
| shouldRecalculatePollPeriod = (mRemainingFastPolls == 0); |
| } |
| |
| if (mRetxMode == true) |
| { |
| mRetxMode = false; |
| mPollTxFailureCounter = 0; |
| shouldRecalculatePollPeriod = true; |
| } |
| |
| otLogInfoMac(GetInstance(), "Sent data poll"); |
| |
| break; |
| |
| default: |
| mPollTxFailureCounter++; |
| |
| otLogInfoMac(GetInstance(), "Failed to send data poll, error:%s, retx:%d/%d", |
| otThreadErrorToString(aError), mPollTxFailureCounter, kMaxPollRetxAttempts); |
| |
| if (mPollTxFailureCounter < kMaxPollRetxAttempts) |
| { |
| if (mRetxMode == false) |
| { |
| mRetxMode = true; |
| shouldRecalculatePollPeriod = true; |
| } |
| } |
| else |
| { |
| mRetxMode = false; |
| mPollTxFailureCounter = 0; |
| shouldRecalculatePollPeriod = true; |
| } |
| |
| break; |
| } |
| |
| if (shouldRecalculatePollPeriod) |
| { |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void DataPollManager::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++; |
| |
| otLogInfoMac(GetInstance(), "Data poll timeout, retry:%d/%d", mPollTimeoutCounter, kQuickPollsAfterTimeout); |
| |
| if (mPollTimeoutCounter < kQuickPollsAfterTimeout) |
| { |
| SendDataPoll(); |
| } |
| else |
| { |
| mPollTimeoutCounter = 0; |
| } |
| |
| exit: |
| return; |
| } |
| |
| void DataPollManager::CheckFramePending(Mac::Frame &aFrame) |
| { |
| VerifyOrExit(mEnabled); |
| |
| mPollTimeoutCounter = 0; |
| |
| if (aFrame.GetFramePending() == true) |
| { |
| SendDataPoll(); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void DataPollManager::RecalculatePollPeriod(void) |
| { |
| if (mEnabled) |
| { |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| } |
| } |
| |
| void DataPollManager::SetAttachMode(bool aMode) |
| { |
| if (mAttachMode != aMode) |
| { |
| mAttachMode = aMode; |
| |
| if (mEnabled) |
| { |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| } |
| } |
| } |
| |
| void DataPollManager::SendFastPolls(uint8_t aNumFastPolls) |
| { |
| bool shouldRecalculatePollPeriod = (mRemainingFastPolls == 0); |
| |
| if (aNumFastPolls == 0) |
| { |
| aNumFastPolls = kDefaultFastPolls; |
| } |
| |
| if (aNumFastPolls > kMaxFastPolls) |
| { |
| aNumFastPolls = kMaxFastPolls; |
| } |
| |
| if (mRemainingFastPolls < aNumFastPolls) |
| { |
| mRemainingFastPolls = aNumFastPolls; |
| } |
| |
| if (mEnabled && shouldRecalculatePollPeriod) |
| { |
| ScheduleNextPoll(kRecalculatePollPeriod); |
| } |
| } |
| |
| void DataPollManager::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector) |
| { |
| if (aPollPeriodSelector == kRecalculatePollPeriod) |
| { |
| mPollPeriod = CalculatePollPeriod(); |
| } |
| |
| if (mTimer.IsRunning()) |
| { |
| mTimer.StartAt(mTimerStartTime, mPollPeriod); |
| } |
| else |
| { |
| mTimerStartTime = Timer::GetNow(); |
| mTimer.StartAt(mTimerStartTime, mPollPeriod); |
| } |
| } |
| |
| uint32_t DataPollManager::CalculatePollPeriod(void) const |
| { |
| uint32_t period = 0; |
| |
| if (mAttachMode == true) |
| { |
| period = kAttachDataPollPeriod; |
| } |
| |
| if (mRetxMode == true) |
| { |
| if ((period == 0) || (period > kRetxPollPeriod)) |
| { |
| period = kRetxPollPeriod; |
| } |
| } |
| |
| if (mNoBufferRetxMode == true) |
| { |
| if ((period == 0) || (period > kNoBufferRetxPollPeriod)) |
| { |
| period = kNoBufferRetxPollPeriod; |
| } |
| } |
| |
| if (mRemainingFastPolls != 0) |
| { |
| if ((period == 0) || (period > kFastPollPeriod)) |
| { |
| period = kFastPollPeriod; |
| } |
| } |
| |
| if (mExternalPollPeriod != 0) |
| { |
| if ((period == 0) || (period > mExternalPollPeriod)) |
| { |
| period = mExternalPollPeriod; |
| } |
| } |
| |
| if (period == 0) |
| { |
| period = GetDefaultPollPeriod(); |
| |
| if (period == 0) |
| { |
| period = kMinPollPeriod; |
| } |
| } |
| |
| return period; |
| } |
| |
| void DataPollManager::HandlePollTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).SendDataPoll(); |
| } |
| |
| DataPollManager &DataPollManager::GetOwner(Context &aContext) |
| { |
| #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES |
| DataPollManager &manager = *static_cast<DataPollManager *>(aContext.GetContext()); |
| #else |
| DataPollManager &manager = otGetMeshForwarder().GetDataPollManager(); |
| OT_UNUSED_VARIABLE(aContext); |
| #endif |
| return manager; |
| } |
| |
| uint32_t DataPollManager::GetDefaultPollPeriod(void) const |
| { |
| return Timer::SecToMsec(GetMeshForwarder().GetNetif().GetMle().GetTimeout()) - |
| static_cast<uint32_t>(kRetxPollPeriod) * kMaxPollRetxAttempts; |
| } |
| |
| } // namespace ot |