[data-poll-handler] adding DataPollHandler class (#3952)

This commit adds a new class `DataPollHandler` which sits between
`Mac` layer and `IndirectSender`. It interfaces to `Mac` to handle any
received data poll and perform indirect frame transmission including
the re-transmission logic of frames (per poll). All state info (per
child) for handling of indirect frame retransmission is now defined
and managed by the `DataPollHanlder` class itself. This commit updates
the `IndiretSender` to interface with the `DataPollHandler` class and
handle preparation of frames from `Message` for indirect transmission.
This commit is contained in:
Abtin Keshavarzian
2019-07-16 08:38:29 -07:00
committed by Jonathan Hui
parent a222a82e04
commit d4ce582f95
15 changed files with 1019 additions and 512 deletions
+1
View File
@@ -152,6 +152,7 @@ LOCAL_SRC_FILES := \
src/core/crypto/pbkdf2_cmac.cpp \
src/core/crypto/sha256.cpp \
src/core/mac/channel_mask.cpp \
src/core/mac/data_poll_handler.cpp \
src/core/mac/data_poll_sender.cpp \
src/core/mac/mac.cpp \
src/core/mac/mac_filter.cpp \
+2
View File
@@ -156,6 +156,7 @@ SOURCES_COMMON = \
crypto/pbkdf2_cmac.cpp \
crypto/sha256.cpp \
mac/channel_mask.cpp \
mac/data_poll_handler.cpp \
mac/data_poll_sender.cpp \
mac/link_raw.cpp \
mac/mac.cpp \
@@ -322,6 +323,7 @@ HEADERS_COMMON = \
crypto/pbkdf2_cmac.h \
crypto/sha256.hpp \
mac/channel_mask.hpp \
mac/data_poll_handler.hpp \
mac/data_poll_sender.hpp \
mac/link_raw.hpp \
mac/mac.hpp \
+1
View File
@@ -35,6 +35,7 @@
#include <string.h>
#include <openthread/diag.h>
#include <openthread/thread.h>
#include <openthread/platform/diag.h>
#include "common/debug.hpp"
+6
View File
@@ -458,6 +458,7 @@ template <> inline Ip6::Filter &Instance::Get(void)
}
#if OPENTHREAD_FTD
template <> inline IndirectSender &Instance::Get(void)
{
return mThreadNetif.mMeshForwarder.mIndirectSender;
@@ -468,6 +469,11 @@ template <> inline SourceMatchController &Instance::Get(void)
return mThreadNetif.mMeshForwarder.mIndirectSender.mSourceMatchController;
}
template <> inline DataPollHandler &Instance::Get(void)
{
return mThreadNetif.mMeshForwarder.mIndirectSender.mDataPollHandler;
}
template <> inline AddressResolver &Instance::Get(void)
{
return mThreadNetif.mAddressResolver;
+315
View File
@@ -0,0 +1,315 @@
/*
* Copyright (c) 2019, 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 the implementation for handling of data polls and indirect frame transmission.
*/
#if OPENTHREAD_FTD
#include "data_poll_handler.hpp"
#include "common/code_utils.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
namespace ot {
DataPollHandler::Callbacks::Callbacks(Instance &aInstance)
: InstanceLocator(aInstance)
{
}
inline otError DataPollHandler::Callbacks::PrepareFrameForChild(Mac::Frame &aFrame, Child &aChild)
{
return Get<IndirectSender>().PrepareFrameForChild(aFrame, aChild);
}
inline void DataPollHandler::Callbacks::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, Child &aChild)
{
Get<IndirectSender>().HandleSentFrameToChild(aFrame, aError, aChild);
}
inline void DataPollHandler::Callbacks::HandleFrameChangeDone(Child &aChild)
{
Get<IndirectSender>().HandleFrameChangeDone(aChild);
}
//---------------------------------------------------------
DataPollHandler::DataPollHandler(Instance &aInstance)
: InstanceLocator(aInstance)
, mIndirectTxChild(NULL)
, mCallbacks(aInstance)
{
}
void DataPollHandler::Clear(void)
{
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter++)
{
Child &child = *iter.GetChild();
child.SetDataPollPending(false);
child.SetFrameReplacePending(false);
child.SetFramePurgePending(false);
child.ResetIndirectTxAttempts();
}
mIndirectTxChild = NULL;
}
void DataPollHandler::HandleNewFrame(Child &aChild)
{
OT_UNUSED_VARIABLE(aChild);
// There is no need to take any action with current data poll
// handler implementation, since the preparation of the frame
// happens after receiving of a data poll from the child. This
// method is included for use by other data poll handler models
// (e.g., in RCP/host model if the handling of data polls is
// delegated to RCP).
}
void DataPollHandler::RequestFrameChange(FrameChange aChange, Child &aChild)
{
if ((mIndirectTxChild == &aChild) && Get<Mac::Mac>().IsPerformingIndirectTransmit())
{
switch (aChange)
{
case kReplaceFrame:
aChild.SetFrameReplacePending(true);
break;
case kPurgeFrame:
aChild.SetFramePurgePending(true);
break;
}
}
else
{
mCallbacks.HandleFrameChangeDone(aChild);
}
}
void DataPollHandler::HandleDataPoll(Mac::Frame &aFrame)
{
Mac::Address macSource;
Child * child;
uint16_t indirectMsgCount;
VerifyOrExit(aFrame.GetSecurityEnabled());
VerifyOrExit(Get<Mle::MleRouter>().GetRole() != OT_DEVICE_ROLE_DETACHED);
SuccessOrExit(aFrame.GetSrcAddr(macSource));
child = Get<ChildTable>().FindChild(macSource, ChildTable::kInStateValidOrRestoring);
VerifyOrExit(child != NULL);
child->SetLastHeard(TimerMilli::GetNow());
child->ResetLinkFailures();
indirectMsgCount = child->GetIndirectMessageCount();
otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
aFrame.GetRssi(), aFrame.IsAckedWithFramePending());
if (!aFrame.IsAckedWithFramePending())
{
if ((indirectMsgCount > 0) && macSource.IsShort())
{
Get<SourceMatchController>().SetSrcMatchAsShort(*child, true);
}
ExitNow();
}
VerifyOrExit(!Get<SourceMatchController>().IsEnabled() || (indirectMsgCount > 0));
if (mIndirectTxChild == NULL)
{
mIndirectTxChild = child;
Get<Mac::Mac>().RequestIndirectFrameTransmission();
}
else
{
child->SetDataPollPending(true);
}
exit:
return;
}
otError DataPollHandler::HandleFrameRequest(Mac::Frame &aFrame)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(mIndirectTxChild != NULL, error = OT_ERROR_ABORT);
SuccessOrExit(error = mCallbacks.PrepareFrameForChild(aFrame, *mIndirectTxChild));
if (mIndirectTxChild->GetIndirectTxAttempts() > 0)
{
// For a re-transmission of an indirect frame to a sleepy
// child, we ensure to use the same frame counter, key id, and
// data sequence number as the previous attempt.
aFrame.SetIsARetransmission(true);
aFrame.SetSequence(mIndirectTxChild->GetIndirectDataSequenceNumber());
if (aFrame.GetSecurityEnabled())
{
aFrame.SetFrameCounter(mIndirectTxChild->GetIndirectFrameCounter());
aFrame.SetKeyId(mIndirectTxChild->GetIndirectKeyId());
}
}
else
{
aFrame.SetIsARetransmission(false);
}
exit:
return error;
}
void DataPollHandler::HandleSentFrame(const Mac::Frame &aFrame, otError aError)
{
Child *child = mIndirectTxChild;
VerifyOrExit(child != NULL);
mIndirectTxChild = NULL;
HandleSentFrame(aFrame, aError, *child);
exit:
ProcessPendingPolls();
}
void DataPollHandler::HandleSentFrame(const Mac::Frame &aFrame, otError aError, Child &aChild)
{
if (aChild.IsFramePurgePending())
{
aChild.SetFramePurgePending(false);
aChild.SetFrameReplacePending(false);
aChild.ResetIndirectTxAttempts();
mCallbacks.HandleFrameChangeDone(aChild);
ExitNow();
}
switch (aError)
{
case OT_ERROR_NONE:
aChild.ResetIndirectTxAttempts();
aChild.SetFrameReplacePending(false);
break;
case OT_ERROR_NO_ACK:
aChild.IncrementIndirectTxAttempts();
// Fall through
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
case OT_ERROR_ABORT:
if (aChild.IsFrameReplacePending())
{
aChild.SetFrameReplacePending(false);
aChild.ResetIndirectTxAttempts();
mCallbacks.HandleFrameChangeDone(aChild);
ExitNow();
}
otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d, error:%s", aChild.GetRloc16(),
aChild.GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts, otThreadErrorToString(aError));
if (aChild.GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts)
{
// We save the frame counter, key id, and data sequence number of
// current frame so we use the same values for the retransmission
// of the frame following the receipt of the next data poll.
aChild.SetIndirectDataSequenceNumber(aFrame.GetSequence());
if (aFrame.GetSecurityEnabled())
{
uint32_t frameCounter;
uint8_t keyId;
aFrame.GetFrameCounter(frameCounter);
aChild.SetIndirectFrameCounter(frameCounter);
aFrame.GetKeyId(keyId);
aChild.SetIndirectKeyId(keyId);
}
ExitNow();
}
aChild.ResetIndirectTxAttempts();
break;
default:
assert(false);
break;
}
mCallbacks.HandleSentFrameToChild(aFrame, aError, aChild);
exit:
return;
}
void DataPollHandler::ProcessPendingPolls(void)
{
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter++)
{
Child *child = iter.GetChild();
if (!child->IsDataPollPending())
{
continue;
}
// Find the child with earliest poll receive time.
if ((mIndirectTxChild == NULL) ||
TimerScheduler::IsStrictlyBefore(child->GetLastHeard(), mIndirectTxChild->GetLastHeard()))
{
mIndirectTxChild = child;
}
}
if (mIndirectTxChild != NULL)
{
mIndirectTxChild->SetDataPollPending(false);
Get<Mac::Mac>().RequestIndirectFrameTransmission();
}
}
} // namespace ot
#endif // #if OPENTHREAD_FTD
+263
View File
@@ -0,0 +1,263 @@
/*
* Copyright (c) 2019, 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 handling of data polls and indirect frame transmission.
*/
#ifndef DATA_POLL_HANDLER_HPP_
#define DATA_POLL_HANDLER_HPP_
#include "openthread-core-config.h"
#include "common/code_utils.hpp"
#include "common/locator.hpp"
#include "common/timer.hpp"
#include "mac/mac.hpp"
#include "mac/mac_frame.hpp"
namespace ot {
/**
* @addtogroup core-data-poll-handler
*
* @brief
* This module includes definitions for data poll handler.
*
* @{
*/
class Child;
/**
* This class implements the data poll (mac data request command) handler.
*
*/
class DataPollHandler : public InstanceLocator
{
friend class Mac::Mac;
public:
enum
{
kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS,
};
/**
* This enumeration defines frame change request types used as input to `RequestFrameChange()`.
*
*/
enum FrameChange
{
kPurgeFrame, ///< Indicates that previous frame should be purged. Any ongoing indirect tx should be aborted.
kReplaceFrame, ///< Indicates that previous frame needs to be replaced with a new higher priority one.
};
/**
* This class defines all the child info required for handling of data polls and indirect frame transmissions.
*
* `Child` class publicly inherits from this class.
*
*/
class ChildInfo
{
friend class DataPollHandler;
private:
bool IsDataPollPending(void) const { return mDataPollPending; }
void SetDataPollPending(bool aPending) { mDataPollPending = aPending; }
uint32_t GetIndirectFrameCounter(void) const { return mIndirectFrameCounter; }
void SetIndirectFrameCounter(uint32_t aFrameCounter) { mIndirectFrameCounter = aFrameCounter; }
uint8_t GetIndirectKeyId(void) const { return mIndirectKeyId; }
void SetIndirectKeyId(uint8_t aKeyId) { mIndirectKeyId = aKeyId; }
uint8_t GetIndirectTxAttempts(void) const { return mIndirectTxAttempts; }
void SetIndirectTxAttemptsToMax(void) { mIndirectTxAttempts = kMaxPollTriggeredTxAttempts; }
void ResetIndirectTxAttempts(void) { mIndirectTxAttempts = 0; }
void IncrementIndirectTxAttempts(void) { mIndirectTxAttempts++; }
uint8_t GetIndirectDataSequenceNumber(void) const { return mIndirectDsn; }
void SetIndirectDataSequenceNumber(uint8_t aDsn) { mIndirectDsn = aDsn; }
bool IsFramePurgePending(void) const { return mFramePurgePending; }
void SetFramePurgePending(bool aPurgePending) { mFramePurgePending = aPurgePending; }
bool IsFrameReplacePending(void) const { return mFrameReplacePending; }
void SetFrameReplacePending(bool aReplacePending) { mFrameReplacePending = aReplacePending; }
uint32_t mIndirectFrameCounter; // Frame counter for current indirect frame (used for retx).
uint8_t mIndirectKeyId; // Key Id for current indirect frame (used for retx).
uint8_t mIndirectDsn; // MAC level Data Sequence Number (DSN) for retx attempts.
uint8_t mIndirectTxAttempts : 5; // Number of data poll triggered tx attempts.
bool mDataPollPending : 1; // Indicates whether or not a Data Poll was received.
bool mFramePurgePending : 1; // Indicates a pending purge request for the current indirect frame.
bool mFrameReplacePending : 1; // Indicates a pending replace request for the current indirect frame.
OT_STATIC_ASSERT(kMaxPollTriggeredTxAttempts < (1 << 5), "mIndirectTxAttempts cannot fit max!");
};
/**
* This class defines the callbacks used by the `DataPollHandler`.
*
*/
class Callbacks : public InstanceLocator
{
friend class DataPollHandler;
private:
/**
* This constructor initializes the data poll handler object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit Callbacks(Instance &aInstance);
/**
* This callback method requests a frame to be prepared for indirect transmission to a given sleepy child.
*
* @param[in] aFrame A reference to a MAC frame where the new frame would be placed.
* @param[in] aChild The child for which to prepare the frame.
*
* @retval OT_ERROR_NONE Frame was prepared successfully
* @retval OT_ERROR_ABORT Indirect transmission to child should be aborted (no frame for the child).
*
*/
otError PrepareFrameForChild(Mac::Frame &aFrame, Child &aChild);
/**
* This callback method notifies the end of indirect frame transmission to a child.
*
* @param[in] aFrame The transmitted frame.
* @param[in] aError OT_ERROR_NONE when the frame was transmitted successfully,
* OT_ERROR_NO_ACK when the frame was transmitted but no ACK was received,
* OT_ERROR_CHANNEL_ACCESS_FAILURE tx failed due to activity on the channel,
* OT_ERROR_ABORT when transmission was aborted for other reasons.
* @param[in] aChild The child to which the frame was transmitted.
*
*/
void HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, Child &aChild);
/**
* This callback method notifies that a requested frame change from `RequestFrameChange()` is processed.
*
* This callback indicates to the next layer that the indirect frame/message for the child can be safely
* updated.
*
* @param[in] aChild The child to update.
*
*/
void HandleFrameChangeDone(Child &aChild);
};
/**
* This constructor initializes the data poll handler object.
*
* @param[in] aInstance A reference to the OpenThread instance.
*
*/
explicit DataPollHandler(Instance &aInstance);
/**
* This method clears any state/info saved per child for indirect frame transmission.
*
*/
void Clear(void);
/**
* This method informs data poll handler that there is a new frame for a given child.
*
* After this call, the data poll handler can use the `Callbacks::PrepareFrameForChild()` method to request the
* frame to be prepared. A subsequent call to `Callbacks::PrepareFrameForChild()` should ensure to prepare the same
* frame (this is used for retransmissions of frame by data poll handler). If/When the frame transmission is
* finished, the data poll handler will invoke the `Callbacks::HandleSentFrameToChild()` to indicate the status of
* the frame transmission.
*
* @param[in] aChild The child which has a new frame.
*
*/
void HandleNewFrame(Child &aChild);
/**
* This method requests a frame change for a given child.
*
* Two types of frame change requests are supported:
*
* 1) "Purge Frame" which indicates that the previous frame should be purged and any ongoing indirect tx aborted.
* 2) "Replace Frame" which indicates that the previous frame needs to be replaced with a new higher priority one.
*
* If there is no ongoing indirect frame transmission to the child, the request will be handled immediately and the
* callback `HandleFrameChangeDone()` is called directly from this method itself. This callback notifies the next
* layer that the indirect frame/message for the child can be safely updated.
*
* If there is an ongoing indirect frame transmission to this child, the request can not be handled immediately.
* The following options can happen based on the request type:
*
* 1) In case of "purge" request, the ongoing indirect transmission is aborted and upon completion of the abort the
* callback `HandleFrameChangeDone()` is invoked.
*
* 2) In case of "replace" request, the ongoing indirect transmission is allowed to finish (current tx attempt).
* 2.a) If the tx attempt is successful, the `Callbacks::HandleSentFrameToChild()` in invoked which indicates
* the "replace" could not happen (in this case the `HandleFrameChangeDone()` is no longer called).
* 2.b) If the ongoing tx attempt is unsuccessful, then callback `HandleFrameChangeDone()` is invoked to allow
* the next layer to update the frame/message for the child.
*
* If there is a pending request, a subsequent call to this method is ignored except for the case where pending
* request is for "replace frame" and new one is for "purge frame" where the "purge" overrides the "replace"
* request.
*
* @param[in] aChange The frame change type.
* @param[in] aChild The child to process its frame change.
*
*/
void RequestFrameChange(FrameChange aChange, Child &aChild);
private:
// Callbacks from MAC
void HandleDataPoll(Mac::Frame &aFrame);
otError HandleFrameRequest(Mac::Frame &aFrame);
void HandleSentFrame(const Mac::Frame &aFrame, otError aError);
void HandleSentFrame(const Mac::Frame &aFrame, otError aError, Child &aChild);
void ProcessPendingPolls(void);
Child * mIndirectTxChild;
Callbacks mCallbacks;
};
/**
* @}
*
*/
} // namespace ot
#endif // DATA_POLL_HANDLER_HPP_
+13 -9
View File
@@ -50,6 +50,7 @@
#include "thread/link_quality.hpp"
#include "thread/mle_router.hpp"
#include "thread/thread_netif.hpp"
#include "thread/topology.hpp"
namespace ot {
namespace Mac {
@@ -1067,7 +1068,7 @@ void Mac::BeginTransmit(void)
sendFrame.SetChannel(mRadioChannel);
sendFrame.SetMaxCsmaBackoffs(kMaxCsmaBackoffsIndirect);
sendFrame.SetMaxFrameRetries(kMaxFrameRetriesIndirect);
SuccessOrExit(error = Get<IndirectSender>().HandleFrameRequest(sendFrame));
SuccessOrExit(error = Get<DataPollHandler>().HandleFrameRequest(sendFrame));
// If the frame is marked as a retransmission, then data sequence number is already set.
if (!sendFrame.IsARetransmission())
@@ -1328,7 +1329,7 @@ void Mac::HandleTransmitDone(Frame &aFrame, Frame *aAckFrame, otError aError)
mCounters.mTxData++;
otDumpDebgMac("TX", aFrame.GetHeader(), aFrame.GetLength());
FinishOperation();
Get<IndirectSender>().HandleSentFrame(aFrame, aError);
Get<DataPollHandler>().HandleSentFrame(aFrame, aError);
PerformNextOperation();
break;
#endif
@@ -1763,7 +1764,7 @@ void Mac::HandleReceivedFrame(Frame *aFrame, otError aError)
switch (aFrame->GetType())
{
case Frame::kFcfFrameMacCmd:
if (HandleMacCommand(*aFrame) == OT_ERROR_DROP)
if (HandleMacCommand(*aFrame)) // returns `true` when handled
{
ExitNow(error = OT_ERROR_NONE);
}
@@ -1833,9 +1834,9 @@ exit:
}
}
otError Mac::HandleMacCommand(Frame &aFrame)
bool Mac::HandleMacCommand(Frame &aFrame)
{
otError error = OT_ERROR_NONE;
bool didHandle = false;
uint8_t commandId;
aFrame.GetCommandId(commandId);
@@ -1850,11 +1851,15 @@ otError Mac::HandleMacCommand(Frame &aFrame)
{
StartOperation(kOperationTransmitBeacon);
}
ExitNow(error = OT_ERROR_DROP);
didHandle = true;
break;
case Frame::kMacCmdDataRequest:
mCounters.mRxDataPoll++;
#if OPENTHREAD_FTD
Get<DataPollHandler>().HandleDataPoll(aFrame);
didHandle = true;
#endif
break;
default:
@@ -1862,8 +1867,7 @@ otError Mac::HandleMacCommand(Frame &aFrame)
break;
}
exit:
return error;
return didHandle;
}
void Mac::SetPromiscuous(bool aPromiscuous)
+13 -2
View File
@@ -48,10 +48,11 @@
#include "mac/sub_mac.hpp"
#include "thread/key_manager.hpp"
#include "thread/link_quality.hpp"
#include "thread/topology.hpp"
namespace ot {
class Neighbor;
/**
* @addtogroup core-mac
*
@@ -496,6 +497,16 @@ public:
*/
bool IsEnergyScanInProgress(void) const { return (mOperation == kOperationEnergyScan) || (mPendingEnergyScan); }
#if OPENTHREAD_FTD
/**
* This method indicates whether the MAC layer is performing an indirect transmission (in middle of a tx).
*
* @returns TRUE if in middle of an indirect transmission, FALSE otherwise.
*
*/
bool IsPerformingIndirectTransmit(void) const { return (mOperation == kOperationTransmitDataIndirect); }
#endif
/**
* This method returns if the MAC layer is in transmit state.
*
@@ -649,7 +660,7 @@ private:
void PrepareBeacon(Frame &aFrame);
bool ShouldSendBeacon(void) const;
void BeginTransmit(void);
otError HandleMacCommand(Frame &aFrame);
bool HandleMacCommand(Frame &aFrame);
Frame * GetOperationFrame(void);
static void HandleTimer(Timer &aTimer);
+307 -304
View File
@@ -63,7 +63,8 @@ IndirectSender::IndirectSender(Instance &aInstance)
: InstanceLocator(aInstance)
, mEnabled(false)
, mSourceMatchController(aInstance)
, mIndirectStartingChild(NULL)
, mDataPollHandler(aInstance)
, mMessageNextOffset(0)
{
}
@@ -77,7 +78,7 @@ void IndirectSender::Stop(void)
mSourceMatchController.ResetMessageCount(*iter.GetChild());
}
mIndirectStartingChild = NULL;
mDataPollHandler.Clear();
exit:
mEnabled = false;
@@ -96,6 +97,8 @@ otError IndirectSender::AddMessageForSleepyChild(Message &aMessage, Child &aChil
aMessage.SetChildMask(childIndex);
mSourceMatchController.IncrementMessageCount(aChild);
RequestMessageUpdate(aChild);
exit:
return error;
}
@@ -110,10 +113,7 @@ otError IndirectSender::RemoveMessageFromSleepyChild(Message &aMessage, Child &a
aMessage.ClearChildMask(childIndex);
mSourceMatchController.DecrementMessageCount(aChild);
if (aChild.GetIndirectMessage() == &aMessage)
{
aChild.SetIndirectMessage(NULL);
}
RequestMessageUpdate(aChild);
exit:
return error;
@@ -121,11 +121,12 @@ exit:
void IndirectSender::ClearAllMessagesForSleepyChild(Child &aChild)
{
Message *message;
Message *nextMessage;
VerifyOrExit(aChild.GetIndirectMessageCount() > 0);
for (Message *message = Get<MeshForwarder>().mSendQueue.GetHead(); message; message = nextMessage)
for (message = Get<MeshForwarder>().mSendQueue.GetHead(); message; message = nextMessage)
{
nextMessage = message->GetNext();
@@ -146,113 +147,25 @@ void IndirectSender::ClearAllMessagesForSleepyChild(Child &aChild)
aChild.SetIndirectMessage(NULL);
mSourceMatchController.ResetMessageCount(aChild);
exit:
return;
}
otError IndirectSender::HandleFrameRequest(Mac::Frame &aFrame)
{
return Get<MeshForwarder>().HandleFrameRequest(aFrame);
}
void IndirectSender::HandleSentFrame(Mac::Frame &aFrame, otError aError)
{
Get<MeshForwarder>().HandleSentFrame(aFrame, aError);
}
void IndirectSender::HandleDataPoll(const Mac::Frame & aFrame,
const Mac::Address & aMacSource,
const otThreadLinkInfo &aLinkInfo)
{
Child * child;
uint16_t indirectMsgCount;
// Security Check: only process secure Data Poll frames.
VerifyOrExit(aLinkInfo.mLinkSecurity);
VerifyOrExit(Get<Mle::MleRouter>().GetRole() != OT_DEVICE_ROLE_DETACHED);
child = Get<ChildTable>().FindChild(aMacSource, ChildTable::kInStateValidOrRestoring);
VerifyOrExit(child != NULL);
child->SetLastHeard(TimerMilli::GetNow());
child->ResetLinkFailures();
indirectMsgCount = child->GetIndirectMessageCount();
otLogInfoMac("Rx data poll, src:0x%04x, qed_msgs:%d, rss:%d, ack-fp:%d", child->GetRloc16(), indirectMsgCount,
aLinkInfo.mRss, aFrame.IsAckedWithFramePending());
VerifyOrExit(aFrame.IsAckedWithFramePending());
if (!mSourceMatchController.IsEnabled() || (indirectMsgCount > 0))
{
child->SetDataRequestPending(true);
}
Get<MeshForwarder>().mScheduleTransmissionTask.Post();
mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
exit:
return;
}
otError IndirectSender::GetIndirectTransmission(void)
void IndirectSender::SetChildUseShortAddress(Child &aChild, bool aUseShortAddress)
{
otError error = OT_ERROR_NOT_FOUND;
VerifyOrExit(aChild.IsIndirectSourceMatchShort() != aUseShortAddress);
UpdateIndirectMessages();
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild);
!iter.IsDone(); iter++)
{
Child &child = *iter.GetChild();
if (!child.IsDataRequestPending())
{
continue;
}
Get<MeshForwarder>().mSendMessage = child.GetIndirectMessage();
if (Get<MeshForwarder>().mSendMessage == NULL)
{
Get<MeshForwarder>().mSendMessage = GetIndirectTransmission(child);
}
if (Get<MeshForwarder>().mSendMessage != NULL)
{
PrepareIndirectTransmission(*Get<MeshForwarder>().mSendMessage, child);
}
else
{
// A NULL `mSendMessage` triggers an empty frame to be sent to the child.
if (child.IsIndirectSourceMatchShort())
{
Get<MeshForwarder>().mMacSource.SetShort(Get<Mac::Mac>().GetShortAddress());
}
else
{
Get<MeshForwarder>().mMacSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
}
child.GetMacAddress(Get<MeshForwarder>().mMacDest);
}
// Remember the current child and move it to next one in the
// list after the indirect transmission has completed.
mIndirectStartingChild = &child;
Get<Mac::Mac>().RequestIndirectFrameTransmission();
ExitNow(error = OT_ERROR_NONE);
}
mSourceMatchController.SetSrcMatchAsShort(aChild, aUseShortAddress);
exit:
return error;
return;
}
Message *IndirectSender::GetIndirectTransmission(Child &aChild)
Message *IndirectSender::FindIndirectMessage(Child &aChild)
{
Message *message = NULL;
Message *message;
Message *next;
uint8_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
@@ -262,7 +175,8 @@ Message *IndirectSender::GetIndirectTransmission(Child &aChild)
if (message->GetChildMask(childIndex))
{
// Skip and remove the supervision message if there are other messages queued for the child.
// Skip and remove the supervision message if there are
// other messages queued for the child.
if ((message->GetType() == Message::kTypeSupervision) && (aChild.GetIndirectMessageCount() > 1))
{
@@ -277,68 +191,322 @@ Message *IndirectSender::GetIndirectTransmission(Child &aChild)
}
}
return message;
}
void IndirectSender::RequestMessageUpdate(Child &aChild)
{
Message *curMessage = aChild.GetIndirectMessage();
Message *newMessage;
// Purge the frame if the current message is no longer destined
// for the child. This check needs to be done first to cover the
// case where we have a pending "replace frame" request and while
// waiting for the callback, the current message is removed.
if ((curMessage != NULL) && !curMessage->GetChildMask(Get<ChildTable>().GetChildIndex(aChild)))
{
// Set the indirect message for this child to NULL to ensure
// it is not processed on `HandleSentFrameToChild()` callback.
aChild.SetIndirectMessage(NULL);
// Request a "frame purge" using `RequestFrameChange()` and
// wait for `HandleFrameChangeDone()` callback for completion
// of the request. Note that the callback may be directly
// called from the `RequestFrameChange()` itself when the
// request can be handled immediately.
aChild.SetWaitingForMessageUpdate(true);
mDataPollHandler.RequestFrameChange(DataPollHandler::kPurgeFrame, aChild);
ExitNow();
}
VerifyOrExit(!aChild.IsWaitingForMessageUpdate());
newMessage = FindIndirectMessage(aChild);
VerifyOrExit(curMessage != newMessage);
if (curMessage == NULL)
{
// Current message is NULL, but new message is not.
// We have a new indirect message.
UpdateIndirectMessage(aChild);
ExitNow();
}
// Current message and new message differ and are both non-NULL.
// We need to request the frame to be replaced. The current
// indirect message can be replaced only if it is the first
// fragment. If a next fragment frame for message is already
// prepared, we wait for the entire message to be delivered.
VerifyOrExit(aChild.GetIndirectFragmentOffset() == 0);
aChild.SetWaitingForMessageUpdate(true);
mDataPollHandler.RequestFrameChange(DataPollHandler::kReplaceFrame, aChild);
exit:
return;
}
void IndirectSender::HandleFrameChangeDone(Child &aChild)
{
VerifyOrExit(aChild.IsWaitingForMessageUpdate());
UpdateIndirectMessage(aChild);
exit:
return;
}
void IndirectSender::UpdateIndirectMessage(Child &aChild)
{
Message *message = FindIndirectMessage(aChild);
aChild.SetWaitingForMessageUpdate(false);
aChild.SetIndirectMessage(message);
aChild.SetIndirectFragmentOffset(0);
aChild.ResetIndirectTxAttempts();
aChild.SetIndirectTxSuccess(true);
if (message != NULL)
{
Mac::Address macAddr;
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message,
&aChild.GetMacAddress(macAddr), OT_ERROR_NONE);
mDataPollHandler.HandleNewFrame(aChild);
}
return message;
}
void IndirectSender::PrepareIndirectTransmission(Message &aMessage, const Child &aChild)
otError IndirectSender::PrepareFrameForChild(Mac::Frame &aFrame, Child &aChild)
{
if (aChild.GetIndirectTxAttempts() > 0)
otError error = OT_ERROR_NONE;
Message *message = aChild.GetIndirectMessage();
VerifyOrExit(mEnabled, error = OT_ERROR_ABORT);
if (message == NULL)
{
Get<MeshForwarder>().mSendMessageIsARetransmission = true;
Get<MeshForwarder>().mSendMessageFrameCounter = aChild.GetIndirectFrameCounter();
Get<MeshForwarder>().mSendMessageKeyId = aChild.GetIndirectKeyId();
Get<MeshForwarder>().mSendMessageDataSequenceNumber = aChild.GetIndirectDataSequenceNumber();
PrepareEmptyFrame(aFrame, aChild, /* aAckRequest */ true);
ExitNow();
}
aMessage.SetOffset(aChild.GetIndirectFragmentOffset());
switch (aMessage.GetType())
switch (message->GetType())
{
case Message::kTypeIp6:
{
Ip6::Header ip6Header;
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
Get<MeshForwarder>().mAddMeshHeader = false;
Get<MeshForwarder>().GetMacSourceAddress(ip6Header.GetSource(), Get<MeshForwarder>().mMacSource);
if (ip6Header.GetDestination().IsLinkLocal())
{
Get<MeshForwarder>().GetMacDestinationAddress(ip6Header.GetDestination(), Get<MeshForwarder>().mMacDest);
}
else
{
aChild.GetMacAddress(Get<MeshForwarder>().mMacDest);
}
mMessageNextOffset = PrepareDataFrame(aFrame, aChild, *message);
break;
}
case Message::kTypeSupervision:
aChild.GetMacAddress(Get<MeshForwarder>().mMacDest);
PrepareEmptyFrame(aFrame, aChild, kSupervisionMsgAckRequest);
mMessageNextOffset = message->GetLength();
break;
default:
assert(false);
break;
}
exit:
return error;
}
void IndirectSender::UpdateIndirectMessages(void)
uint16_t IndirectSender::PrepareDataFrame(Mac::Frame &aFrame, Child &aChild, Message &aMessage)
{
Ip6::Header ip6Header;
Mac::Address macSource, macDest;
uint16_t directTxOffset;
uint16_t nextOffset;
// Determine the MAC source and destination addresses.
aMessage.Read(0, sizeof(ip6Header), &ip6Header);
Get<MeshForwarder>().GetMacSourceAddress(ip6Header.GetSource(), macSource);
if (ip6Header.GetDestination().IsLinkLocal())
{
Get<MeshForwarder>().GetMacDestinationAddress(ip6Header.GetDestination(), macDest);
}
else
{
aChild.GetMacAddress(macDest);
}
// Prepare the data frame from previous child's indirect offset.
directTxOffset = aMessage.GetOffset();
aMessage.SetOffset(aChild.GetIndirectFragmentOffset());
nextOffset = Get<MeshForwarder>().PrepareDataFrame(aFrame, aMessage, macSource, macDest);
aMessage.SetOffset(directTxOffset);
// Set `FramePending` if there are more queued messages (excluding
// the current one being sent out) for the child (note `> 1` check).
// The case where the current message itself requires fragmentation
// is already checked and handled in `PrepareDataFrame()` method.
if (aChild.GetIndirectMessageCount() > 1)
{
aFrame.SetFramePending(true);
}
return nextOffset;
}
void IndirectSender::PrepareEmptyFrame(Mac::Frame &aFrame, Child &aChild, bool aAckRequest)
{
uint16_t fcf;
Mac::Address macSource, macDest;
aChild.GetMacAddress(macDest);
macSource.SetShort(Get<Mac::Mac>().GetShortAddress());
if (macSource.IsShortAddrInvalid() || macDest.IsExtended())
{
macSource.SetExtended(Get<Mac::Mac>().GetExtAddress());
}
fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfPanidCompression |
Mac::Frame::kFcfSecurityEnabled;
if (aAckRequest)
{
fcf |= Mac::Frame::kFcfAckRequest;
}
fcf |= (macDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt;
fcf |= (macSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt;
aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32);
aFrame.SetDstPanId(Get<Mac::Mac>().GetPanId());
aFrame.SetSrcPanId(Get<Mac::Mac>().GetPanId());
aFrame.SetDstAddr(macDest);
aFrame.SetSrcAddr(macSource);
aFrame.SetPayloadLength(0);
aFrame.SetFramePending(false);
}
void IndirectSender::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, Child &aChild)
{
Message *message = aChild.GetIndirectMessage();
VerifyOrExit(mEnabled);
switch (aError)
{
case OT_ERROR_NONE:
Get<Utils::ChildSupervisor>().UpdateOnSend(aChild);
break;
case OT_ERROR_NO_ACK:
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
case OT_ERROR_ABORT:
aChild.SetIndirectTxSuccess(false);
#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// We set the NextOffset to end of message, since there is no need to
// send any remaining fragments in the message to the child, if all tx
// attempts of current frame already failed.
if (message != NULL)
{
mMessageNextOffset = message->GetLength();
}
#endif
break;
default:
assert(false);
break;
}
if ((message != NULL) && (mMessageNextOffset < message->GetLength()))
{
aChild.SetIndirectFragmentOffset(mMessageNextOffset);
mDataPollHandler.HandleNewFrame(aChild);
ExitNow();
}
if (message != NULL)
{
// The indirect tx of this message to the child is done.
otError txError = aError;
uint8_t childIndex = Get<ChildTable>().GetChildIndex(aChild);
Mac::Address macDest;
aChild.SetIndirectMessage(NULL);
aChild.GetLinkInfo().AddMessageTxStatus(aChild.GetIndirectTxSuccess());
// Enable short source address matching after the first indirect
// message transmission attempt to the child. We intentionally do
// not check for successful tx here to address the scenario where
// the child does receive "Child ID Response" but parent misses the
// 15.4 ack from child. If the "Child ID Response" does not make it
// to the child, then the child will need to send a new "Child ID
// Request" which will cause the parent to switch to using long
// address mode for source address matching.
mSourceMatchController.SetSrcMatchAsShort(aChild, true);
#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
// disabled, all fragment frames of a larger message are
// sent even if the transmission of an earlier fragment fail.
// Note that `GetIndirectTxSuccess() tracks the tx success of
// the entire message to the child, while `txError = aError`
// represents the error status of the last fragment frame
// transmission.
if (!aChild.GetIndirectTxSuccess() && (txError == OT_ERROR_NONE))
{
txError = OT_ERROR_FAILED;
}
#endif
aFrame.GetDstAddr(macDest);
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *message, &macDest, txError);
if (message->GetType() == Message::kTypeIp6)
{
if (aChild.GetIndirectTxSuccess())
{
Get<MeshForwarder>().mIpCounters.mTxSuccess++;
}
else
{
Get<MeshForwarder>().mIpCounters.mTxFailure++;
}
}
if (message->GetChildMask(childIndex))
{
message->ClearChildMask(childIndex);
mSourceMatchController.DecrementMessageCount(aChild);
}
if (!message->GetDirectTransmission() && !message->IsChildPending())
{
Get<MeshForwarder>().mSendQueue.Dequeue(*message);
message->Free();
}
}
UpdateIndirectMessage(aChild);
exit:
if (mEnabled)
{
ClearMessagesForRemovedChildren();
}
}
void IndirectSender::ClearMessagesForRemovedChildren(void)
{
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptValidOrRestoring); !iter.IsDone();
iter++)
@@ -352,171 +520,6 @@ void IndirectSender::UpdateIndirectMessages(void)
}
}
void IndirectSender::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest)
{
Child *child;
child = Get<ChildTable>().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring);
VerifyOrExit(child != NULL);
child->SetDataRequestPending(false);
VerifyOrExit(Get<MeshForwarder>().mSendMessage != NULL);
if (Get<MeshForwarder>().mSendMessage == child->GetIndirectMessage())
{
// To ensure fairness in handling of data requests from sleepy
// children, once a message is completed for indirect transmission to a
// child (on both success or failure), the `mIndirectStartingChild` is
// updated to the next `Child` entry after the current one. Subsequent
// call to `ScheduleTransmissionTask()` will begin the iteration
// through the children list from this child.
ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild);
iter++;
mIndirectStartingChild = iter.GetChild();
switch (aError)
{
case OT_ERROR_NONE:
child->ResetIndirectTxAttempts();
break;
case OT_ERROR_NO_ACK:
child->IncrementIndirectTxAttempts();
// fall through
case OT_ERROR_CHANNEL_ACCESS_FAILURE:
case OT_ERROR_ABORT:
otLogInfoMac("Indirect tx to child %04x failed, attempt %d/%d, error:%s", child->GetRloc16(),
child->GetIndirectTxAttempts(), kMaxPollTriggeredTxAttempts, otThreadErrorToString(aError));
if (child->GetIndirectTxAttempts() < kMaxPollTriggeredTxAttempts)
{
// We save the frame counter, key id, and data sequence number of
// current frame so we use the same values for the retransmission
// of the frame following the receipt of a data request command (data
// poll) from the sleepy child.
child->SetIndirectDataSequenceNumber(aFrame.GetSequence());
if (aFrame.GetSecurityEnabled())
{
uint32_t frameCounter;
uint8_t keyId;
aFrame.GetFrameCounter(frameCounter);
child->SetIndirectFrameCounter(frameCounter);
aFrame.GetKeyId(keyId);
child->SetIndirectKeyId(keyId);
}
ExitNow();
}
child->ResetIndirectTxAttempts();
child->SetIndirectTxSuccess(false);
#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// We set the NextOffset to end of message, since there is no need to
// send any remaining fragments in the message to the child, if all tx
// attempts of current frame already failed.
Get<MeshForwarder>().mMessageNextOffset = Get<MeshForwarder>().mSendMessage->GetLength();
#endif
break;
default:
assert(false);
break;
}
}
if (Get<MeshForwarder>().mMessageNextOffset < Get<MeshForwarder>().mSendMessage->GetLength())
{
if (Get<MeshForwarder>().mSendMessage == child->GetIndirectMessage())
{
child->SetIndirectFragmentOffset(Get<MeshForwarder>().mMessageNextOffset);
}
}
else
{
otError txError = aError;
uint8_t childIndex;
if (Get<MeshForwarder>().mSendMessage == child->GetIndirectMessage())
{
child->SetIndirectFragmentOffset(0);
child->SetIndirectMessage(NULL);
child->GetLinkInfo().AddMessageTxStatus(child->GetIndirectTxSuccess());
// Enable short source address matching after the first indirect
// message transmission attempt to the child. We intentionally do
// not check for successful tx here to address the scenario where
// the child does receive "Child ID Response" but parent misses the
// 15.4 ack from child. If the "Child ID Response" does not make it
// to the child, then the child will need to send a new "Child ID
// Request" which will cause the parent to switch to using long
// address mode for source address matching.
mSourceMatchController.SetSrcMatchAsShort(*child, true);
#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
// disabled, all fragment frames of a larger message are
// sent even if the transmission of an earlier fragment fail.
// Note that `GetIndirectTxSuccess() tracks the tx success of
// the entire message to the child, while `txError = aError`
// represents the error status of the last fragment frame
// transmission.
if (!child->GetIndirectTxSuccess() && (txError == OT_ERROR_NONE))
{
txError = OT_ERROR_FAILED;
}
#endif
}
childIndex = Get<ChildTable>().GetChildIndex(*child);
if (Get<MeshForwarder>().mSendMessage->GetChildMask(childIndex))
{
Get<MeshForwarder>().mSendMessage->ClearChildMask(childIndex);
mSourceMatchController.DecrementMessageCount(*child);
}
if (!Get<MeshForwarder>().mSendMessage->GetDirectTransmission())
{
Get<MeshForwarder>().LogMessage(MeshForwarder::kMessageTransmit, *Get<MeshForwarder>().mSendMessage,
&aMacDest, txError);
if (Get<MeshForwarder>().mSendMessage->GetType() == Message::kTypeIp6)
{
if (Get<MeshForwarder>().mSendMessage->GetTxSuccess())
{
Get<MeshForwarder>().mIpCounters.mTxSuccess++;
}
else
{
Get<MeshForwarder>().mIpCounters.mTxFailure++;
}
}
}
}
if (aError == OT_ERROR_NONE)
{
Get<Utils::ChildSupervisor>().UpdateOnSend(*child);
}
exit:
return;
}
} // namespace ot
#endif // #if OPENTHREAD_FTD
+34 -40
View File
@@ -38,6 +38,7 @@
#include "common/locator.hpp"
#include "common/message.hpp"
#include "mac/data_poll_handler.hpp"
#include "mac/mac_frame.hpp"
#include "thread/src_match_controller.hpp"
@@ -61,12 +62,14 @@ class Child;
class IndirectSender : public InstanceLocator
{
friend class Instance;
friend class DataPollHandler::Callbacks;
public:
/**
* This class defines all the child info required for indirect transmission.
*
* `Child` class publicly inherits from this class.
*
*/
class ChildInfo
{
@@ -83,9 +86,6 @@ public:
uint16_t GetIndirectMessageCount(void) const { return mQueuedMessageCount; }
private:
bool IsDataRequestPending(void) const { return mDataRequestPendig; }
void SetDataRequestPending(bool aPending) { mDataRequestPendig = aPending; }
Message *GetIndirectMessage(void) { return mIndirectMessage; }
void SetIndirectMessage(Message *aMessage) { mIndirectMessage = aMessage; }
@@ -95,19 +95,6 @@ public:
bool GetIndirectTxSuccess(void) const { return mIndirectTxSuccess; }
void SetIndirectTxSuccess(bool aTxStatus) { mIndirectTxSuccess = aTxStatus; }
uint32_t GetIndirectFrameCounter(void) const { return mIndirectFrameCounter; }
void SetIndirectFrameCounter(uint32_t aFrameCounter) { mIndirectFrameCounter = aFrameCounter; }
uint8_t GetIndirectKeyId(void) const { return mIndirectKeyId; }
void SetIndirectKeyId(uint8_t aKeyId) { mIndirectKeyId = aKeyId; }
uint8_t GetIndirectTxAttempts(void) const { return mIndirectTxAttempts; }
void ResetIndirectTxAttempts(void) { mIndirectTxAttempts = 0; }
void IncrementIndirectTxAttempts(void) { mIndirectTxAttempts++; }
uint8_t GetIndirectDataSequenceNumber(void) const { return mIndirectDsn; }
void SetIndirectDataSequenceNumber(uint8_t aDsn) { mIndirectDsn = aDsn; }
bool IsIndirectSourceMatchShort(void) const { return mUseShortAddress; }
void SetIndirectSourceMatchShort(bool aShort) { mUseShortAddress = aShort; }
@@ -118,21 +105,21 @@ public:
void DecrementIndirectMessageCount(void) { mQueuedMessageCount--; }
void ResetIndirectMessageCount(void) { mQueuedMessageCount = 0; }
bool IsWaitingForMessageUpdate(void) const { return mWaitingForMessageUpdate; }
void SetWaitingForMessageUpdate(bool aNeedsUpdate) { mWaitingForMessageUpdate = aNeedsUpdate; }
const Mac::Address &GetMacAddress(Mac::Address &aMacAddress) const;
Message *mIndirectMessage; // Current indirect message.
uint16_t mIndirectFragmentOffset : 15; // 6LoWPAN fragment offset for the indirect message.
uint16_t mIndirectFragmentOffset : 14; // 6LoWPAN fragment offset for the indirect message.
bool mIndirectTxSuccess : 1; // Indicates tx success/failure of current indirect message.
uint16_t mQueuedMessageCount : 13; // Number of queued indirect messages for the child.
bool mWaitingForMessageUpdate : 1; // Indicates waiting for updating the indirect message.
uint16_t mQueuedMessageCount : 14; // Number of queued indirect messages for the child.
bool mUseShortAddress : 1; // Indicates whether to use short or extended address.
bool mSourceMatchPending : 1; // Indicates whether or not pending to add to src match table.
bool mDataRequestPendig : 1; // Indicates whether or not a Data Poll was received,
uint32_t mIndirectFrameCounter; // Frame counter for current indirect message (used fore retx).
uint8_t mIndirectKeyId; // Key Id for current indirect message (used for retx).
uint8_t mIndirectTxAttempts; // Number of data poll triggered tx attempts.
uint8_t mIndirectDsn; // MAC level Data Sequence Number (DSN) for retx attempts.
OT_STATIC_ASSERT(OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS < 8192, "mQueuedMessageCount cannot fit max required!");
OT_STATIC_ASSERT(OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS < (1UL << 14),
"mQueuedMessageCount cannot fit max required!");
};
/**
@@ -190,34 +177,41 @@ public:
*/
void ClearAllMessagesForSleepyChild(Child &aChild);
void HandleDataPoll(const Mac::Frame &aFrame, const Mac::Address &aMacSource, const otThreadLinkInfo &aLinkInfo);
otError GetIndirectTransmission(void);
void HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, const Mac::Address &aMacDest);
// Callbacks from MAC layer
void HandleSentFrame(Mac::Frame &aFrame, otError aError);
otError HandleFrameRequest(Mac::Frame &aFrame);
/**
* This method sets whether to use the extended or short address for a child.
*
* @param[in] aChild A reference to the child.
* @param[in] aUseShortAddress `true` to use short address, `false` to use extended address.
*
*/
void SetChildUseShortAddress(Child &aChild, bool aUseShortAddress);
private:
enum
{
/**
* Maximum number of tx attempts by `MeshForwarder` for an outbound indirect frame (for a sleepy child). The
* `MeshForwader` attempts occur following the reception of a new data request command (a new data poll) from
* the sleepy child.
* Indicates whether to set/enable 15.4 ack request in the MAC header of a supervision message.
*
*/
kMaxPollTriggeredTxAttempts = OPENTHREAD_CONFIG_MAX_TX_ATTEMPTS_INDIRECT_POLLS,
kSupervisionMsgAckRequest = (OPENTHREAD_CONFIG_SUPERVISION_MSG_NO_ACK_REQUEST == 0) ? true : false,
};
Message *GetIndirectTransmission(Child &aChild);
void PrepareIndirectTransmission(Message &aMessage, const Child &aChild);
void UpdateIndirectMessages(void);
// Callbacks from DataPollHandler
otError PrepareFrameForChild(Mac::Frame &aFrame, Child &aChild);
void HandleSentFrameToChild(const Mac::Frame &aFrame, otError aError, Child &aChild);
void HandleFrameChangeDone(Child &aChild);
bool mEnabled;
void UpdateIndirectMessage(Child &aChild);
Message *FindIndirectMessage(Child &aChild);
void RequestMessageUpdate(Child &aChild);
uint16_t PrepareDataFrame(Mac::Frame &aFrame, Child &aChild, Message &aMessage);
void PrepareEmptyFrame(Mac::Frame &aFrame, Child &aChild, bool aAckRequest);
void ClearMessagesForRemovedChildren(void);
bool mEnabled;
SourceMatchController mSourceMatchController;
Child * mIndirectStartingChild;
DataPollHandler mDataPollHandler;
uint16_t mMessageNextOffset;
};
/**
+55 -143
View File
@@ -61,7 +61,6 @@ MeshForwarder::MeshForwarder(Instance &aInstance)
, mUpdateTimer(aInstance, &MeshForwarder::HandleUpdateTimer, this)
, mMessageNextOffset(0)
, mSendMessage(NULL)
, mSendMessageIsARetransmission(false)
, mMeshSource()
, mMeshDest()
, mAddMeshHeader(false)
@@ -75,9 +74,6 @@ MeshForwarder::MeshForwarder(Instance &aInstance)
, mScanning(false)
#if OPENTHREAD_FTD
, mIndirectSender(aInstance)
, mSendMessageFrameCounter(0)
, mSendMessageKeyId(0)
, mSendMessageDataSequenceNumber(0)
#endif
, mDataPollSender(aInstance)
{
@@ -173,25 +169,15 @@ void MeshForwarder::ScheduleTransmissionTask(void)
{
VerifyOrExit(mSendBusy == false);
mSendMessageIsARetransmission = false;
mSendMessage = GetDirectTransmission();
VerifyOrExit(mSendMessage != NULL);
#if OPENTHREAD_FTD
if (mIndirectSender.GetIndirectTransmission() == OT_ERROR_NONE)
if (mSendMessage->GetOffset() == 0)
{
ExitNow();
mSendMessage->SetTxSuccess(true);
}
#endif // OPENTHREAD_FTD
if ((mSendMessage = GetDirectTransmission()) != NULL)
{
if (mSendMessage->GetOffset() == 0)
{
mSendMessage->SetTxSuccess(true);
}
Get<Mac::Mac>().RequestDirectFrameTransmission();
ExitNow();
}
Get<Mac::Mac>().RequestDirectFrameTransmission();
exit:
return;
@@ -491,16 +477,10 @@ otError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
otError error = OT_ERROR_NONE;
VerifyOrExit(mEnabled, error = OT_ERROR_ABORT);
VerifyOrExit(mSendMessage != NULL, error = OT_ERROR_ABORT);
mSendBusy = true;
if (mSendMessage == NULL)
{
SendEmptyFrame(aFrame, false);
aFrame.SetIsARetransmission(false);
ExitNow();
}
switch (mSendMessage->GetType())
{
case Message::kTypeIp6:
@@ -555,47 +535,7 @@ otError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
#endif
}
assert(error == OT_ERROR_NONE);
aFrame.SetIsARetransmission(mSendMessageIsARetransmission);
#if OPENTHREAD_FTD
{
Mac::Address macDest;
Child * child = NULL;
if (mSendMessageIsARetransmission)
{
// If this is the re-transmission of an indirect frame to a sleepy child, we
// ensure to use the same frame counter, key id, and data sequence number as
// the last attempt.
aFrame.SetSequence(mSendMessageDataSequenceNumber);
if (aFrame.GetSecurityEnabled())
{
aFrame.SetFrameCounter(mSendMessageFrameCounter);
aFrame.SetKeyId(mSendMessageKeyId);
}
}
aFrame.GetDstAddr(macDest);
// Set `FramePending` if there are more queued messages (excluding
// the current one being sent out) for the child (note `> 1` check).
// The case where the current message requires fragmentation is
// already checked and handled in `SendFragment()` method.
child = Get<ChildTable>().FindChild(macDest, ChildTable::kInStateValidOrRestoring);
if ((child != NULL) && !child->IsRxOnWhenIdle() && (child->GetIndirectMessageCount() > 1))
{
aFrame.SetFramePending(true);
}
}
#endif
aFrame.SetIsARetransmission(false);
exit:
return error;
@@ -993,83 +933,77 @@ void MeshForwarder::HandleSentFrame(Mac::Frame &aFrame, otError aError)
aFrame.GetDstAddr(macDest);
neighbor = UpdateNeighborOnSentFrame(aFrame, aError, macDest);
#if OPENTHREAD_FTD
mIndirectSender.HandleSentFrameToChild(aFrame, aError, macDest);
#endif
VerifyOrExit(mSendMessage != NULL);
assert(mSendMessage->GetDirectTransmission());
if (mSendMessage->GetDirectTransmission())
if (aError != OT_ERROR_NONE)
{
if (aError != OT_ERROR_NONE)
{
// If the transmission of any fragment frame fails,
// the overall message transmission is considered
// as failed
// If the transmission of any fragment frame fails,
// the overall message transmission is considered
// as failed
mSendMessage->SetTxSuccess(false);
mSendMessage->SetTxSuccess(false);
#if OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// We set the NextOffset to end of message to avoid sending
// any remaining fragments in the message.
// We set the NextOffset to end of message to avoid sending
// any remaining fragments in the message.
mMessageNextOffset = mSendMessage->GetLength();
mMessageNextOffset = mSendMessage->GetLength();
#endif
}
}
if (mMessageNextOffset < mSendMessage->GetLength())
if (mMessageNextOffset < mSendMessage->GetLength())
{
mSendMessage->SetOffset(mMessageNextOffset);
}
else
{
otError txError = aError;
mSendMessage->ClearDirectTransmission();
mSendMessage->SetOffset(0);
if (neighbor != NULL)
{
mSendMessage->SetOffset(mMessageNextOffset);
neighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess());
}
else
{
otError txError = aError;
mSendMessage->ClearDirectTransmission();
mSendMessage->SetOffset(0);
if (neighbor != NULL)
{
neighbor->GetLinkInfo().AddMessageTxStatus(mSendMessage->GetTxSuccess());
}
#if !OPENTHREAD_CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE
// When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
// disabled, all fragment frames of a larger message are
// sent even if the transmission of an earlier fragment fail.
// Note that `GetTxSuccess() tracks the tx success of the
// entire message, while `aError` represents the error
// status of the last fragment frame transmission.
// When `CONFIG_DROP_MESSAGE_ON_FRAGMENT_TX_FAILURE` is
// disabled, all fragment frames of a larger message are
// sent even if the transmission of an earlier fragment fail.
// Note that `GetTxSuccess() tracks the tx success of the
// entire message, while `aError` represents the error
// status of the last fragment frame transmission.
if (!mSendMessage->GetTxSuccess() && (txError == OT_ERROR_NONE))
{
txError = OT_ERROR_FAILED;
}
if (!mSendMessage->GetTxSuccess() && (txError == OT_ERROR_NONE))
{
txError = OT_ERROR_FAILED;
}
#endif
LogMessage(kMessageTransmit, *mSendMessage, &macDest, txError);
LogMessage(kMessageTransmit, *mSendMessage, &macDest, txError);
if (mSendMessage->GetType() == Message::kTypeIp6)
if (mSendMessage->GetType() == Message::kTypeIp6)
{
if (mSendMessage->GetTxSuccess())
{
if (mSendMessage->GetTxSuccess())
{
mIpCounters.mTxSuccess++;
}
else
{
mIpCounters.mTxFailure++;
}
mIpCounters.mTxSuccess++;
}
else
{
mIpCounters.mTxFailure++;
}
}
}
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
mSendBusy = true;
mDiscoverTimer.Start(static_cast<uint16_t>(Mac::kScanDurationDefault));
ExitNow();
}
if (mSendMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)
{
mSendBusy = true;
mDiscoverTimer.Start(static_cast<uint16_t>(Mac::kScanDurationDefault));
ExitNow();
}
if (mSendMessage->GetDirectTransmission() == false && mSendMessage->IsChildPending() == false)
@@ -1209,28 +1143,6 @@ void MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame)
break;
#if OPENTHREAD_FTD
case Mac::Frame::kFcfFrameMacCmd:
{
uint8_t commandId;
aFrame.GetCommandId(commandId);
if (commandId == Mac::Frame::kMacCmdDataRequest)
{
mIndirectSender.HandleDataPoll(aFrame, macSource, linkInfo);
}
else
{
error = OT_ERROR_DROP;
}
break;
}
#endif
case Mac::Frame::kFcfFrameBeacon:
break;
-4
View File
@@ -509,7 +509,6 @@ private:
uint16_t mMessageNextOffset;
Message *mSendMessage;
bool mSendMessageIsARetransmission;
Mac::Address mMacSource;
Mac::Address mMacDest;
@@ -534,9 +533,6 @@ private:
FragmentPriorityEntry mFragmentEntries[kNumFragmentPriorityEntries];
MessageQueue mResolvingQueue;
IndirectSender mIndirectSender;
uint32_t mSendMessageFrameCounter;
uint8_t mSendMessageKeyId;
uint8_t mSendMessageDataSequenceNumber;
#endif
DataPollSender mDataPollSender;
+5 -6
View File
@@ -50,6 +50,10 @@ otError MeshForwarder::SendMessage(Message &aMessage)
otError error = OT_ERROR_NONE;
Neighbor * neighbor;
aMessage.SetOffset(0);
aMessage.SetDatagramTag(0);
SuccessOrExit(error = mSendQueue.Enqueue(aMessage));
switch (aMessage.GetType())
{
case Message::kTypeIp6:
@@ -122,9 +126,7 @@ otError MeshForwarder::SendMessage(Message &aMessage)
case Message::kTypeSupervision:
{
Child *child = Get<Utils::ChildSupervisor>().GetDestination(aMessage);
VerifyOrExit(child != NULL, error = OT_ERROR_DROP);
VerifyOrExit(!child->IsRxOnWhenIdle(), error = OT_ERROR_DROP);
assert((child != NULL) && !child->IsRxOnWhenIdle());
mIndirectSender.AddMessageForSleepyChild(aMessage, *child);
break;
}
@@ -134,9 +136,6 @@ otError MeshForwarder::SendMessage(Message &aMessage)
break;
}
aMessage.SetOffset(0);
aMessage.SetDatagramTag(0);
SuccessOrExit(error = mSendQueue.Enqueue(aMessage));
mScheduleTransmissionTask.Post();
exit:
+3 -3
View File
@@ -2271,7 +2271,7 @@ otError MleRouter::HandleChildUpdateRequest(const Message & aMessage,
if (!(mode.GetMode() & ModeTlv::kModeRxOnWhenIdle) && (child->GetState() == Neighbor::kStateValid))
{
Get<SourceMatchController>().SetSrcMatchAsShort(*child, true);
Get<IndirectSender>().SetChildUseShortAddress(*child, true);
}
}
@@ -2860,7 +2860,7 @@ otError MleRouter::SendChildIdResponse(Child &aChild)
if (!aChild.IsRxOnWhenIdle())
{
Get<SourceMatchController>().SetSrcMatchAsShort(aChild, false);
Get<IndirectSender>().SetChildUseShortAddress(aChild, false);
}
#if OPENTHREAD_CONFIG_ENABLE_TIME_SYNC
@@ -3522,7 +3522,7 @@ void MleRouter::RestoreChildren(void)
child->SetDeviceMode(childInfo.mMode);
child->SetState(Neighbor::kStateRestored);
child->SetLastHeard(TimerMilli::GetNow());
Get<SourceMatchController>().SetSrcMatchAsShort(*child, true);
Get<IndirectSender>().SetChildUseShortAddress(*child, true);
numChildren++;
}
+1 -1
View File
@@ -374,7 +374,7 @@ private:
* This class represents a Thread Child.
*
*/
class Child : public Neighbor, public IndirectSender::ChildInfo
class Child : public Neighbor, public IndirectSender::ChildInfo, public DataPollHandler::ChildInfo
{
public:
enum