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, };