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