mirror of
https://github.com/espressif/openthread.git
synced 2026-09-16 06:00:13 +00:00
[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:
committed by
Jonathan Hui
parent
a222a82e04
commit
d4ce582f95
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <openthread/diag.h>
|
||||
#include <openthread/thread.h>
|
||||
#include <openthread/platform/diag.h>
|
||||
|
||||
#include "common/debug.hpp"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user