From 10194c0bfcfaaee22dd5c78656a94e81c5ac5dcf Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Sun, 2 Apr 2017 22:54:59 -0700 Subject: [PATCH] Adding `DataPollManager` to manage the (periodic) data poll transmission (#1508) This commit introduces a new class `DataPollManager`. This class contains all the logic related to sending of the data poll messages and managing the data poll transmission period. The data poll interval is derived based on combination of value from user (from `SetExternalPollPeriod()`) and the internal state of the OpenThread. Data poll manager may send data polls more frequently during attach process or when expecting a control-message from parent or in case of data poll transmission failures or data poll timeouts. --- etc/visual-studio/libopenthread.vcxproj | 2 + .../libopenthread.vcxproj.filters | 6 + etc/visual-studio/libopenthread_k.vcxproj | 2 + .../libopenthread_k.vcxproj.filters | 6 + include/openthread/openthread.h | 1 + src/core/Makefile.am | 2 + src/core/api/link_api.cpp | 6 +- src/core/thread/data_poll_manager.cpp | 381 ++++++++++++++++++ src/core/thread/data_poll_manager.hpp | 232 +++++++++++ src/core/thread/mesh_forwarder.cpp | 183 +-------- src/core/thread/mesh_forwarder.hpp | 96 +---- src/core/thread/mle.cpp | 10 +- src/core/thread/mle.hpp | 1 - 13 files changed, 675 insertions(+), 253 deletions(-) create mode 100644 src/core/thread/data_poll_manager.cpp create mode 100644 src/core/thread/data_poll_manager.hpp diff --git a/etc/visual-studio/libopenthread.vcxproj b/etc/visual-studio/libopenthread.vcxproj index 0f13f09b2..d55f272a6 100644 --- a/etc/visual-studio/libopenthread.vcxproj +++ b/etc/visual-studio/libopenthread.vcxproj @@ -120,6 +120,7 @@ + @@ -198,6 +199,7 @@ + diff --git a/etc/visual-studio/libopenthread.vcxproj.filters b/etc/visual-studio/libopenthread.vcxproj.filters index dd4c8d7f3..0547019bf 100644 --- a/etc/visual-studio/libopenthread.vcxproj.filters +++ b/etc/visual-studio/libopenthread.vcxproj.filters @@ -186,6 +186,9 @@ Source Files\thread + + Source Files\thread + Source Files\thread @@ -413,6 +416,9 @@ Header Files\thread + + Header Files\thread + Header Files\thread diff --git a/etc/visual-studio/libopenthread_k.vcxproj b/etc/visual-studio/libopenthread_k.vcxproj index 5140a0d0c..d488c7aee 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj +++ b/etc/visual-studio/libopenthread_k.vcxproj @@ -124,6 +124,7 @@ + @@ -226,6 +227,7 @@ + diff --git a/etc/visual-studio/libopenthread_k.vcxproj.filters b/etc/visual-studio/libopenthread_k.vcxproj.filters index eec183de7..2c901adf5 100644 --- a/etc/visual-studio/libopenthread_k.vcxproj.filters +++ b/etc/visual-studio/libopenthread_k.vcxproj.filters @@ -186,6 +186,9 @@ Source Files\thread + + Source Files\thread + Source Files\thread @@ -410,6 +413,9 @@ Header Files\thread + + Header Files\thread + Header Files\thread diff --git a/include/openthread/openthread.h b/include/openthread/openthread.h index 6bb98cbf3..56b77324f 100644 --- a/include/openthread/openthread.h +++ b/include/openthread/openthread.h @@ -94,6 +94,7 @@ extern "C" { * @defgroup core-ipv6 IPv6 * @defgroup core-mac MAC * @defgroup core-mesh-forwarding Mesh Forwarding + * @defgroup core-data-poll-manager Data Poll Manager * @defgroup core-message Message * @defgroup core-mle MLE * @defgroup core-netdata Network Data diff --git a/src/core/Makefile.am b/src/core/Makefile.am index c7cf162b8..01b0996f7 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -81,6 +81,7 @@ SOURCES_COMMON = \ net/netif.cpp \ net/udp6.cpp \ thread/announce_begin_server.cpp \ + thread/data_poll_manager.cpp \ thread/energy_scan_server.cpp \ thread/key_manager.cpp \ thread/link_quality.cpp \ @@ -311,6 +312,7 @@ noinst_HEADERS = \ thread/address_resolver_ftd.hpp \ thread/address_resolver_mtd.hpp \ thread/announce_begin_server.hpp \ + thread/data_poll_manager.hpp \ thread/energy_scan_server.hpp \ thread/key_manager.hpp \ thread/link_quality.hpp \ diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index 9e05d5cbf..42945f47c 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -116,17 +116,17 @@ exit: uint32_t otLinkGetPollPeriod(otInstance *aInstance) { - return aInstance->mThreadNetif.GetMeshForwarder().GetAssignPollPeriod(); + return aInstance->mThreadNetif.GetMeshForwarder().GetDataPollManager().GetExternalPollPeriod(); } void otLinkSetPollPeriod(otInstance *aInstance, uint32_t aPollPeriod) { - aInstance->mThreadNetif.GetMeshForwarder().SetAssignPollPeriod(aPollPeriod); + aInstance->mThreadNetif.GetMeshForwarder().GetDataPollManager().SetExternalPollPeriod(aPollPeriod); } ThreadError otLinkSendDataRequest(otInstance *aInstance) { - return aInstance->mThreadNetif.GetMeshForwarder().SendMacDataRequest(); + return aInstance->mThreadNetif.GetMeshForwarder().GetDataPollManager().SendDataPoll(); } otShortAddress otLinkGetShortAddress(otInstance *aInstance) diff --git a/src/core/thread/data_poll_manager.cpp b/src/core/thread/data_poll_manager.cpp new file mode 100644 index 000000000..201a5a26f --- /dev/null +++ b/src/core/thread/data_poll_manager.cpp @@ -0,0 +1,381 @@ +/* + * 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/platform/random.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Thread { + +DataPollManager::DataPollManager(MeshForwarder &aMeshForwarder): + mMeshForwarder(aMeshForwarder), + mTimer(aMeshForwarder.GetNetif().GetIp6().mTimerScheduler, &DataPollManager::HandlePollTimer, this), + mExternalPollPeriod(0), + mPollPeriod(0), + mEnabled(false), + mAttachMode(false), + mRetxMode(false), + mNoBufferRetxMode(false), + mPollTimeoutCounter(0), + mPollTxFailureCounter(0), + mResponseExpectedPolls(0) +{ +} + +otInstance *DataPollManager::GetInstance(void) +{ + return mMeshForwarder.GetInstance(); +} + +ThreadError DataPollManager::StartPolling(void) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(!mEnabled, error = kThreadError_Already); + VerifyOrExit((mMeshForwarder.GetNetif().GetMle().GetDeviceMode() & Mle::ModeTlv::kModeFFD) == 0, + error = kThreadError_InvalidState); + + mEnabled = true; + ScheduleNextPoll(kRecalculatePollPeriod); + +exit: + return error; +} + +void DataPollManager::StopPolling(void) +{ + mTimer.Stop(); + mAttachMode = false; + mRetxMode = false; + mNoBufferRetxMode = false; + mPollTimeoutCounter = 0; + mPollTxFailureCounter = 0; + mResponseExpectedPolls = 0; + mEnabled = false; +} + +ThreadError DataPollManager::SendDataPoll(void) +{ + ThreadError error; + Message *message; + + VerifyOrExit(mEnabled, error = kThreadError_InvalidState); + VerifyOrExit(!mMeshForwarder.GetNetif().GetMac().GetRxOnWhenIdle(), error = kThreadError_InvalidState); + + mTimer.Stop(); + + for (message = mMeshForwarder.GetSendQueue().GetHead(); message; message = message->GetNext()) + { + VerifyOrExit(message->GetType() != Message::kTypeMacDataPoll, error = kThreadError_Already); + } + + message = mMeshForwarder.GetNetif().GetIp6().mMessagePool.New(Message::kTypeMacDataPoll, 0); + VerifyOrExit(message != NULL, error = kThreadError_NoBufs); + + error = mMeshForwarder.SendMessage(*message); + + if (error != kThreadError_None) + { + message->Free(); + } + +exit: + + switch (error) + { + case kThreadError_None: + otLogDebgMac(GetInstance(), "Sent poll"); + ScheduleNextPoll(kUsePreviousPollPeriod); + break; + + case kThreadError_InvalidState: + otLogWarnMac(GetInstance(), "Data poll tx requested while data polling was not enabled!"); + StopPolling(); + break; + + case kThreadError_Already: + otLogDebgMac(GetInstance(), "Data poll tx requested when a previous data request still in send queue."); + ScheduleNextPoll(kUsePreviousPollPeriod); + break; + + case kThreadError_NoBufs: + default: + mNoBufferRetxMode = true; + ScheduleNextPoll(kRecalculatePollPeriod); + break; + } + + return error; +} + +void DataPollManager::SetExternalPollPeriod(uint32_t aPeriod) +{ + if (mExternalPollPeriod != aPeriod) + { + mExternalPollPeriod = aPeriod; + + if (mEnabled) + { + ScheduleNextPoll(kRecalculatePollPeriod); + } + } +} + +void DataPollManager::HandlePollSent(ThreadError aError) +{ + bool shouldRecalculatePollPeriod = false; + + VerifyOrExit(mEnabled, ;); + + switch (aError) + { + case kThreadError_None: + + if (mResponseExpectedPolls != 0) + { + mResponseExpectedPolls--; + shouldRecalculatePollPeriod = (mResponseExpectedPolls == 0); + } + + if (mRetxMode == true) + { + mRetxMode = false; + mPollTxFailureCounter = 0; + shouldRecalculatePollPeriod = true; + } + + break; + + default: + mPollTxFailureCounter++; + + if (mPollTxFailureCounter < kMaxPollRetxAttempts) + { + if (mRetxMode == false) + { + mRetxMode = true; + shouldRecalculatePollPeriod = true; + } + } + else + { + otLogWarnMac(GetInstance(), "Data poll tx failed in %d back-to-back attempts.", mPollTxFailureCounter); + + 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++; + + if (mPollTimeoutCounter <= kQuickPollsAfterTimeout) + { + SendDataPoll(); + } + else + { + otLogInfoMac(GetInstance(), "Data poll timeout happened %d times back-to-back.", mPollTimeoutCounter); + mPollTimeoutCounter = 0; + } + +exit: + return; +} + +void DataPollManager::HandleReceivedFrame(Mac::Frame &aFrame) +{ + VerifyOrExit(mEnabled, ;); + + mPollTimeoutCounter = 0; + + if (aFrame.GetFramePending() == true) + { + SendDataPoll(); + } + +exit: + return; +} + +void DataPollManager::HandleTimeoutChanged(void) +{ + if (mEnabled) + { + ScheduleNextPoll(kRecalculatePollPeriod); + } +} + +void DataPollManager::SetAttachMode(bool aMode) +{ + if (mAttachMode != aMode) + { + mAttachMode = aMode; + + if (mEnabled) + { + ScheduleNextPoll(kRecalculatePollPeriod); + } + } +} + +void DataPollManager::HandleResponseExpected(void) +{ + bool shouldRecalculatePollPeriod = (mResponseExpectedPolls == 0); + + mResponseExpectedPolls = kQuickPollsForResponse; + + if (mEnabled && shouldRecalculatePollPeriod) + { + ScheduleNextPoll(kRecalculatePollPeriod); + } +} + +void DataPollManager::ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector) +{ + if (aPollPeriodSelector == kRecalculatePollPeriod) + { + mPollPeriod = CalculatePollPeriod(); + } + + if (mTimer.IsRunning()) + { + uint32_t elapsedTime = Timer::GetNow() - mTimer.Gett0(); + + if (elapsedTime >= mPollPeriod) + { + SendDataPoll(); + } + else + { + mTimer.Start(mPollPeriod - elapsedTime); + } + } + else + { + mTimer.Start(mPollPeriod); + } +} + +uint32_t DataPollManager::CalculatePollPeriod(void) const +{ + uint32_t period = 0; + + if (mAttachMode == true) + { + if ((period == 0) || (period > kAttachDataPollPeriod)) + { + period = kAttachDataPollPeriod; + } + } + + if (mRetxMode == true) + { + if ((period == 0) || (period > kRetxPollPeriod)) + { + period = kRetxPollPeriod; + } + } + + if (mNoBufferRetxMode == true) + { + if ((period == 0) || (period > kNoBufferRetxPollPeriod)) + { + period = kNoBufferRetxPollPeriod; + } + } + + if (mResponseExpectedPolls != 0) + { + if ((period == 0) || (period > kResponseExpectedPeriod)) + { + period = kResponseExpectedPeriod; + } + } + + if (mExternalPollPeriod != 0) + { + if ((period == 0) || (period > mExternalPollPeriod)) + { + period = mExternalPollPeriod; + } + } + + if (period == 0) + { + period = Timer::SecToMsec(mMeshForwarder.GetNetif().GetMle().GetTimeout() / Mle::kMaxChildKeepAliveAttempts); + + if (period == 0) + { + period = kMinPollPeriod; + } + } + + return period; +} + +void DataPollManager::HandlePollTimer(void *aContext) +{ + static_cast(aContext)->SendDataPoll(); +} + +} // namespace Thread diff --git a/src/core/thread/data_poll_manager.hpp b/src/core/thread/data_poll_manager.hpp new file mode 100644 index 000000000..86443d3ea --- /dev/null +++ b/src/core/thread/data_poll_manager.hpp @@ -0,0 +1,232 @@ +/* + * 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 includes definitions for data poll (mac data request command) manager. + */ + +#ifndef DATA_POLL_MANAGER_HPP_ +#define DATA_POLL_MANAGER_HPP_ + +#include + +#include "openthread/types.h" +#include + +namespace Thread { + +class MeshForwarder; + +/** + * @addtogroup core-data-poll-manager + * + * @brief + * This module includes definitions for data poll manager. + * + * @{ + */ + +/** + * This class implements the data poll (mac data request command) manager. + * + */ + +class DataPollManager +{ +public: + /** + * This constructor initializes the data poll manager object. + * + * @param[in] aMeshForwarder A reference to the Mesh Forwarder. + * + */ + explicit DataPollManager(MeshForwarder &aMeshForwarder); + + /** + * This method returns the pointer to the parent otInstance structure. + * + * @returns The pointer to the parent otInstance structure. + * + */ + otInstance *GetInstance(void); + + /** + * This method instructs the data poll manager to start sending periodic data polls. + * + * @retval kThreadError_None Successfully started sending periodic data polls. + * @retval kThreadError_Already Periodic data poll transmission is already started/enabled. + * @retval kThreadError_InvalidState Device is not in rx-off-when-idle mode. + * + */ + ThreadError StartPolling(void); + + /** + * This method instructs the data poll manager to stop sending periodic data polls. + * + */ + void StopPolling(void); + + /** + * This method enqueues a data poll (an IEEE 802.15.4 Data Request) message. + * + * @retval kThreadError_None Successfully enqueued a data poll message + * @retval kThreadError_Already A data poll message is already enqueued. + * @retval kThreadError_InvalidState Device is not in rx-off-when-idle mode. + * @retval kThreadError_NoBufs Insufficient message buffers available. + * + */ + ThreadError SendDataPoll(void); + + /** + * This method sets a user-specified/external data poll period. + * + * If the user provides a non-zero poll period, the user value specifies the maximum period between data + * request transmissions. Note that OpenThread may send data request transmissions more frequently when expecting + * a control-message from a parent or in case of data poll transmission failures or timeouts. + * + * Default value for the external poll period is zero (i.e., no user-specified poll period). + * + * @param[in] aPeriod The data poll period in milliseconds, or zero to mean no user-specified poll period. + * + */ + void SetExternalPollPeriod(uint32_t aPeriod); + + /** + * This method gets the current user-specified/external data poll period. + * + * @returns The data poll period in milliseconds. + * + */ + uint32_t GetExternalPollPeriod(void) const { return mExternalPollPeriod; } + + /** + * This method informs the data poll manager of success/error status of a previously requested poll message + * transmission. + * + * In case of transmit failure, the data poll manager may choose to send the next data poll more quickly (up to + * some fixed number of attempts). + * + * @param[in] aError Error status of a data poll message transmission. + * + */ + void HandlePollSent(ThreadError aError); + + /** + * This method informs the data poll manager that a data poll timeout happened, i.e., when the ack in response to + * a data request command indicated that a frame was pending, but no frame was received after timeout interval. + * + * Data poll manager may choose to transmit another data poll immediately (up to some fixed number of attempts). + * + */ + void HandlePollTimeout(void); + + /** + * This method informs the data poll manager that a mac frame has been received. + * + * Data poll manager will check the "pending frame" in the received frame header and if it is set, it will send + * an immediate data poll to retrieve the pending frame. + * + */ + void HandleReceivedFrame(Mac::Frame &aFrame); + + /** + * This method informs the data poll manager that the device's timeout value is changed. + * + * The timeout is used to determine the default data poll period. + * + */ + void HandleTimeoutChanged(void); + + /** + * This method sets/clears the attach mode on data poll period manager. + * + * When attach mode is enabled, the data poll manager will send data polls at a faster period determined by + * configuration option `OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD`. + * + * @param[in] aMode The mode value. + * + */ + void SetAttachMode(bool aMode); + + /** + * This method informs the data poll manager that a response is expected from the parent/sender. + * + * Data poll manager will transmit data polls more frequently (up to some fixed number of polls). + */ + void HandleResponseExpected(void); + +private: + enum // Poll period under different conditions (in milliseconds). + { + kAttachDataPollPeriod = OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD, + kRetxPollPeriod = 500, + kNoBufferRetxPollPeriod = 200, + kResponseExpectedPeriod = 250, + kMinPollPeriod = 10, + }; + + enum + { + kQuickPollsAfterTimeout = 5, ///< Maximum number of quick data poll tx in case of back-to-back poll timeouts. + kMaxPollRetxAttempts = 5, ///< Maximum number of retransmit attempts of data poll (mac data request). + kQuickPollsForResponse = 8, ///< Number of quick data poll transmissions when a response is expected. + }; + + enum PollPeriodSelector + { + kUsePreviousPollPeriod, + kRecalculatePollPeriod, + }; + + void ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector); + uint32_t CalculatePollPeriod(void) const; + static void HandlePollTimer(void *aContext); + + MeshForwarder &mMeshForwarder; + Timer mTimer; + uint32_t mExternalPollPeriod; + uint32_t mPollPeriod; + + bool mEnabled: 1; //< Indicates if data polling is enabled/started. + bool mAttachMode: 1; //< Indicates if in attach mode (to use attach poll period). + bool mRetxMode: 1; //< Indicates if last poll tx failed at mac/radio layer(poll retx mode). + bool mNoBufferRetxMode: 1; //< Indicates if last poll tx failed due to insufficient buffer. + uint8_t mPollTimeoutCounter: 4; //< Poll timeouts counter (0 to `kQuickPollsAfterTimout`). + uint8_t mPollTxFailureCounter: 4; //< Poll tx failure counter (0 to `kMaxPollRetxAttempts`). + uint8_t mResponseExpectedPolls: 4; //< # of remaining quick polls when "response expected". +}; + +/** + * @} + * + */ + +} // namespace Thread + +#endif // DATA_POLL_MANAGER_HPP_ diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index 377f7857b..4026b0a54 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -55,14 +55,12 @@ using Thread::Encoding::BigEndian::HostSwap16; namespace Thread { MeshForwarder::MeshForwarder(ThreadNetif &aThreadNetif): + mNetif(aThreadNetif), mMacReceiver(&MeshForwarder::HandleReceivedFrame, &MeshForwarder::HandleDataPollTimeout, this), mMacSender(&MeshForwarder::HandleFrameRequest, &MeshForwarder::HandleSentFrame, this), mDiscoverTimer(aThreadNetif.GetIp6().mTimerScheduler, &MeshForwarder::HandleDiscoverTimer, this), - mPollTimer(aThreadNetif.GetIp6().mTimerScheduler, &MeshForwarder::HandlePollTimer, this), mReassemblyTimer(aThreadNetif.GetIp6().mTimerScheduler, &MeshForwarder::HandleReassemblyTimer, this), mMessageNextOffset(0), - mPollPeriod(0), - mAssignPollPeriod(0), mSendMessageFrameCounter(0), mSendMessage(NULL), mSendMessageIsARetransmission(false), @@ -81,8 +79,7 @@ MeshForwarder::MeshForwarder(ThreadNetif &aThreadNetif): mRestoreChannel(0), mRestorePanId(Mac::kPanIdBroadcast), mScanning(false), - mBacktoBackPollTimeoutCounter(0), - mNetif(aThreadNetif), + mDataPollManager(*this), mSrcMatchEnabled(false) { mFragTag = static_cast(otPlatRandomGet()); @@ -116,7 +113,7 @@ ThreadError MeshForwarder::Stop(void) VerifyOrExit(mEnabled == true,); - mPollTimer.Stop(); + mDataPollManager.StopPolling(); mReassemblyTimer.Stop(); if (mScanning) @@ -889,11 +886,7 @@ exit: void MeshForwarder::SetRxOff(void) { mNetif.GetMac().SetRxOnWhenIdle(false); - - if (mPollTimer.IsRunning()) - { - mPollTimer.Stop(); - } + mDataPollManager.StopPolling(); } bool MeshForwarder::GetRxOnWhenIdle() @@ -907,149 +900,14 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle) if (aRxOnWhenIdle) { - if (mPollTimer.IsRunning()) - { - mPollTimer.Stop(); - } + mDataPollManager.StopPolling(); } else { - ScheduleNextPoll(mPollPeriod); + mDataPollManager.StartPolling(); } } -void MeshForwarder::SetAssignPollPeriod(uint32_t aPeriod) -{ - mAssignPollPeriod = aPeriod; - - if (mPollTimer.IsRunning() && ((mNetif.GetMle().GetDeviceMode() & Mle::ModeTlv::kModeFFD) == 0)) - { - SetPollPeriod(mAssignPollPeriod); - } -} - -uint32_t MeshForwarder::GetAssignPollPeriod() -{ - return mAssignPollPeriod; -} - -void MeshForwarder::SetPollPeriod(uint32_t aPeriod) -{ - if (mPollPeriod != aPeriod) - { - if (mAssignPollPeriod != 0 && aPeriod != (OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD)) - { - mPollPeriod = mAssignPollPeriod; - } - else - { - mPollPeriod = aPeriod; - } - - if (mPollTimer.IsRunning() && ((mNetif.GetMle().GetDeviceMode() & Mle::ModeTlv::kModeFFD) == 0)) - { - ScheduleNextPoll(mPollPeriod); - } - } -} - -uint32_t MeshForwarder::GetPollPeriod() -{ - return mPollPeriod; -} - -void MeshForwarder::ScheduleNextPoll(uint32_t aDelay) -{ - if (aDelay) - { - mPollTimer.Start(aDelay); - } - else - { - otLogWarnMac(GetInstance(), "Cannot start poll timer with uninitialized value of poll period."); - } -} - -void MeshForwarder::HandlePollTimer(void *aContext) -{ - static_cast(aContext)->HandlePollTimer(); -} - -void MeshForwarder::HandlePollTimer() -{ - ThreadError error; - - error = SendMacDataRequest(); - - switch (error) - { - case kThreadError_None: - break; - - case kThreadError_InvalidState: - // The poll timer should have been stopped. Hitting - // this might indicate a logic error. - otLogWarnMac(GetInstance(), "Poll timer fired while RxOnWhenIdle set!"); - break; - - case kThreadError_NoBufs: - // Failed to send DataRequest due to a lack of buffers. - // Try again following a brief pause to free buffers. - ScheduleNextPoll(kDataRequestRetryDelay); - break; - - case kThreadError_Already: - // This is perhaps a sign of - // bad behavior, as it suggests that mPollPeriod was not long - // enough for the previously scheduled DataRequest to get out of - // the sendQueue. - otLogDebgMac(GetInstance(), "Poll timer fired with DataRequest in SendQueue."); - - // Intentional fall-thru - default: - // Restart for any other error which might originate from SendMessage(). - ScheduleNextPoll(mPollPeriod); - break; - } -} - -ThreadError MeshForwarder::SendMacDataRequest(void) -{ - ThreadError error; - Message *message; - - // only send MAC Data Requests in rx-off-when-idle mode - VerifyOrExit(!mNetif.GetMac().GetRxOnWhenIdle(), error = kThreadError_InvalidState); - - // only enqueue one MAC Data Request at a time - for (message = mSendQueue.GetHead(); message; message = message->GetNext()) - { - VerifyOrExit(message->GetType() != Message::kTypeMacDataPoll, error = kThreadError_Already); - } - - // enqueue a MAC Data Request message - message = mNetif.GetIp6().mMessagePool.New(Message::kTypeMacDataPoll, 0); - VerifyOrExit(message != NULL, error = kThreadError_NoBufs); - - error = SendMessage(*message); - - if (error == kThreadError_None) - { - otLogDebgMac(GetInstance(), "Sent poll"); - - // restart the polling timer - ScheduleNextPoll(mPollPeriod); - } - else - { - message->Free(); - message = NULL; - } - -exit: - return error; -} - ThreadError MeshForwarder::GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr) { aMacAddr.mLength = sizeof(aMacAddr.mExtAddress); @@ -1773,9 +1631,13 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, ThreadError aError) if (neighbor->mState == Neighbor::kStateInvalid) { - mPollTimer.Stop(); + mDataPollManager.StopPolling(); mNetif.GetMle().BecomeDetached(); } + else + { + mDataPollManager.HandlePollSent(aError); + } } if (mMessageNextOffset >= mSendMessage->GetLength()) @@ -1859,8 +1721,6 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame) ExitNow(error = kThreadError_InvalidState); } - mBacktoBackPollTimeoutCounter = 0; - SuccessOrExit(error = aFrame.GetSrcAddr(macSource)); SuccessOrExit(aFrame.GetDstAddr(macDest)); @@ -1873,10 +1733,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame) payload = aFrame.GetPayload(); payloadLength = aFrame.GetPayloadLength(); - if (mPollTimer.IsRunning() && aFrame.GetFramePending()) - { - HandlePollTimer(); - } + mDataPollManager.HandleReceivedFrame(aFrame); switch (aFrame.GetType()) { @@ -2261,21 +2118,7 @@ exit: void MeshForwarder::HandleDataPollTimeout(void *aContext) { - static_cast(aContext)->HandleDataPollTimeout(); -} - -void MeshForwarder::HandleDataPollTimeout(void) -{ - mBacktoBackPollTimeoutCounter++; - - if (mBacktoBackPollTimeoutCounter <= kQuickPollsAfterTimout) - { - SendMacDataRequest(); - } - else - { - mBacktoBackPollTimeoutCounter = 0; - } + static_cast(aContext)->GetDataPollManager().HandlePollTimeout(); } #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OPENTHREAD_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_MAC == 1) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index 4f31d8bdf..3b71f9ece 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -86,7 +87,7 @@ public: * @returns The pointer to the parent otInstance structure. * */ - otInstance *GetInstance(); + otInstance *GetInstance(void); /** * This method enables mesh forwarding and the IEEE 802.15.4 MAC layer. @@ -146,62 +147,6 @@ public: */ void SetRxOnWhenIdle(bool aRxOnWhenIdle); - /** - * This method sets a user-specified Data Poll period. - * - * If the value is set to zero, then the poll interval is managed by the OpenThread stack. - * If the user has provided a non-zero poll period, the user value specifies the maximum period between data - * request transmissions. Note that OpenThread may send data request transmissions more frequently when expecting - * a control-message from a parent. - * - * Initial/Default value for "assign poll period" is zero. - * - * @param[in] aPeriod The Data Poll period in milliseconds, or zero to mean no user-specified poll period. - * - */ - void SetAssignPollPeriod(uint32_t aPeriod); - - /** - * This method gets the current user-specified Data Poll period. - * - * @returns The Data Poll period in milliseconds. - * - */ - uint32_t GetAssignPollPeriod(void); - - /** - * - * This method sets the maximum period between data request command transmissions. Note that OpenThread may send - * data request transmissions more frequently when expecting a control-message from a parent. - * - * If the user has provided a non-zero assign poll period (@sa SetAssignPollPeriod), the user value specifies the - * maximum period between data request command transmissions and is used in place of @p aPeriod. - * - * - * @param[in] aPeriod The Data Poll period in milliseconds. - * - */ - void SetPollPeriod(uint32_t aPeriod); - - /** - * This method enqueues an IEEE 802.15.4 Data Request message. - * - * @retval kThreadError_None Successfully enqueued an IEEE 802.15.4 Data Request message. - * @retval kThreadError_Already An IEEE 802.15.4 Data Request message is already enqueued. - * @retval kThreadError_InvalidState Device is not in rx-off-when-idle mode. - * @retval kThreadError_NoBufs Insufficient message buffers available. - * - */ - ThreadError SendMacDataRequest(void); - - /** - * This method gets the Data Poll period. - * - * @returns The Data Poll period in milliseconds. - * - */ - uint32_t GetPollPeriod(void); - /** * This method sets the scan parameters for MLE Discovery Request messages. * @@ -234,6 +179,13 @@ public: */ void SetSrcMatchAsShort(Child &aChild, bool aMatchShort); + /** + * This method returns a reference to the thread network interface instance. + * + * @ returns A reference to the thread network interface instance. + */ + ThreadNetif &GetNetif(void) { return mNetif; } + /** * This method returns a reference to the send queue. * @@ -258,12 +210,18 @@ public: */ const MessageQueue &GetResolvingQueue(void) const { return mResolvingQueue; } + /** + * This method returns a reference to the data poll manager. + * + * @returns A reference to the data poll manager. + * + */ + DataPollManager &GetDataPollManager(void) { return mDataPollManager; } + private: enum { kStateUpdatePeriod = 1000, ///< State update period in milliseconds. - kDataRequestRetryDelay = 200, ///< Retry delay in milliseconds (for sending data request if no buffer). - kQuickPollsAfterTimout = 5, ///< Maximum number of quick data poll tx in case of back-to-back poll timeouts. }; enum @@ -286,7 +244,6 @@ private: ThreadError CheckReachability(uint8_t *aFrame, uint8_t aFrameLength, const Mac::Address &aMeshSource, const Mac::Address &aMeshDest); - void ScheduleNextPoll(uint32_t aDelay); ThreadError GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); ThreadError GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *GetDirectTransmission(void); @@ -313,25 +270,17 @@ private: static void HandleReceivedFrame(void *aContext, Mac::Frame &aFrame); void HandleReceivedFrame(Mac::Frame &aFrame); - - static void HandleDataPollTimeout(void *aContext); - void HandleDataPollTimeout(void); - static ThreadError HandleFrameRequest(void *aContext, Mac::Frame &aFrame); ThreadError HandleFrameRequest(Mac::Frame &aFrame); - static void HandleSentFrame(void *aContext, Mac::Frame &aFrame, ThreadError aError); void HandleSentFrame(Mac::Frame &aFrame, ThreadError aError); - static void HandleDiscoverTimer(void *aContext); void HandleDiscoverTimer(void); static void HandleReassemblyTimer(void *aContext); void HandleReassemblyTimer(void); - static void HandlePollTimer(void *aContext); - void HandlePollTimer(void); - static void ScheduleTransmissionTask(void *aContext); void ScheduleTransmissionTask(void); + static void HandleDataPollTimeout(void *aContext); ThreadError AddPendingSrcMatchEntries(void); ThreadError AddSrcMatchEntry(Child &aChild); @@ -340,10 +289,11 @@ private: void LogIp6Message(MessageAction aAction, const Message &aMessage, const Mac::Address &aMacAddress, ThreadError aError); + ThreadNetif &mNetif; + Mac::Receiver mMacReceiver; Mac::Sender mMacSender; Timer mDiscoverTimer; - Timer mPollTimer; Timer mReassemblyTimer; PriorityQueue mSendQueue; @@ -351,8 +301,6 @@ private: MessageQueue mResolvingQueue; uint16_t mFragTag; uint16_t mMessageNextOffset; - uint32_t mPollPeriod; - uint32_t mAssignPollPeriod; uint32_t mSendMessageFrameCounter; Message *mSendMessage; @@ -379,9 +327,7 @@ private: uint16_t mRestorePanId; bool mScanning; - uint8_t mBacktoBackPollTimeoutCounter; - - ThreadNetif &mNetif; + DataPollManager mDataPollManager; bool mSrcMatchEnabled; }; diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index c0f6b74ce..4a22c5ca1 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -611,6 +611,8 @@ ThreadError Mle::SetTimeout(uint32_t aTimeout) mTimeout = aTimeout; + mNetif.GetMeshForwarder().GetDataPollManager().HandleTimeoutChanged(); + if (mDeviceState == kDeviceStateChild) { SendChildUpdateRequest(); @@ -1590,7 +1592,7 @@ ThreadError Mle::SendChildIdRequest(void) if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { - mNetif.GetMeshForwarder().SetPollPeriod(kAttachDataPollPeriod); + mNetif.GetMeshForwarder().GetDataPollManager().SetAttachMode(true); mNetif.GetMeshForwarder().SetRxOnWhenIdle(false); } @@ -1715,7 +1717,7 @@ ThreadError Mle::SendChildUpdateRequest(void) if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { - mNetif.GetMeshForwarder().SetPollPeriod(kAttachDataPollPeriod); + mNetif.GetMeshForwarder().GetDataPollManager().SetAttachMode(true); mNetif.GetMeshForwarder().SetRxOnWhenIdle(false); } else @@ -2764,7 +2766,7 @@ ThreadError Mle::HandleChildIdResponse(const Message &aMessage, const Ip6::Messa if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { - mNetif.GetMeshForwarder().SetPollPeriod(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); + mNetif.GetMeshForwarder().GetDataPollManager().SetAttachMode(false); mNetif.GetMeshForwarder().SetRxOnWhenIdle(false); } else @@ -2958,7 +2960,7 @@ ThreadError Mle::HandleChildUpdateResponse(const Message &aMessage, const Ip6::M if ((mDeviceMode & ModeTlv::kModeRxOnWhenIdle) == 0) { - mNetif.GetMeshForwarder().SetPollPeriod(Timer::SecToMsec(mTimeout / kMaxChildKeepAliveAttempts)); + mNetif.GetMeshForwarder().GetDataPollManager().SetAttachMode(false); mNetif.GetMeshForwarder().SetRxOnWhenIdle(false); mParentRequestTimer.Stop(); } diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index d8d8fbc87..2e804259c 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -1362,7 +1362,6 @@ protected: private: enum { - kAttachDataPollPeriod = OPENTHREAD_CONFIG_ATTACH_DATA_POLL_PERIOD, kMleMessagePriority = Message::kPriorityHigh, };