mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[child-table] introducing ChildTable class (#2642)
This commit introduces a new class `ChildTable` as part of OpenThread core. This class provides methods for iterating through the child table and searching for a specific child (based on short/extended address and/or child state). This commit also adds a unit test for the newly added `ChildTable class.
This commit is contained in:
committed by
Jonathan Hui
parent
96ed7ccd24
commit
8e6be519da
@@ -124,6 +124,7 @@
|
||||
<ClCompile Include="..\..\src\core\net\udp6.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\address_resolver.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\announce_begin_server.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\child_table.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\energy_scan_server.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\key_manager.cpp" />
|
||||
@@ -208,6 +209,7 @@
|
||||
<ClInclude Include="..\..\src\core\net\dhcp6.hpp" />
|
||||
<ClInclude Include="..\..\src\core\net\dhcp6_client.hpp" />
|
||||
<ClInclude Include="..\..\src\core\net\dhcp6_server.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\child_table.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\key_manager.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\link_quality.hpp" />
|
||||
|
||||
@@ -204,6 +204,9 @@
|
||||
<ClCompile Include="..\..\src\core\thread\address_resolver.cpp">
|
||||
<Filter>Source Files\thread</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\core\thread\child_table.cpp">
|
||||
<Filter>Source Files\thread</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp">
|
||||
<Filter>Source Files\thread</Filter>
|
||||
</ClCompile>
|
||||
@@ -452,6 +455,9 @@
|
||||
<ClInclude Include="..\..\src\core\thread\announce_begin_server.hpp">
|
||||
<Filter>Header Files\thread</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\core\thread\child_table.hpp">
|
||||
<Filter>Header Files\thread</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp">
|
||||
<Filter>Header Files\thread</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
<ClCompile Include="..\..\src\core\net\udp6.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\address_resolver.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\announce_begin_server.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\child_table.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\energy_scan_server.cpp" />
|
||||
<ClCompile Include="..\..\src\core\thread\key_manager.cpp" />
|
||||
@@ -241,6 +242,7 @@
|
||||
<ClInclude Include="..\..\src\core\openthread-core-default-config.h" />
|
||||
<ClInclude Include="..\..\src\core\thread\address_resolver.hpp" />
|
||||
<ClInclude Include="..\..\src\core\meshcop\announce_begin_server.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\child_table.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\energy_scan_server.hpp" />
|
||||
<ClInclude Include="..\..\src\core\thread\key_manager.hpp" />
|
||||
|
||||
@@ -204,6 +204,9 @@
|
||||
<ClCompile Include="..\..\src\core\thread\address_resolver.cpp">
|
||||
<Filter>Source Files\thread</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\core\thread\child_table.cpp">
|
||||
<Filter>Source Files\thread</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\core\thread\data_poll_manager.cpp">
|
||||
<Filter>Source Files\thread</Filter>
|
||||
</ClCompile>
|
||||
@@ -449,6 +452,9 @@
|
||||
<ClInclude Include="..\..\src\core\thread\address_resolver.hpp">
|
||||
<Filter>Header Files\thread</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\core\thread\child_table.hpp">
|
||||
<Filter>Header Files\thread</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\core\thread\data_poll_manager.hpp">
|
||||
<Filter>Header Files\thread</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -172,6 +172,7 @@ SOURCES_COMMON = \
|
||||
net/udp6.cpp \
|
||||
thread/address_resolver.cpp \
|
||||
thread/announce_begin_server.cpp \
|
||||
thread/child_table.cpp \
|
||||
thread/data_poll_manager.cpp \
|
||||
thread/energy_scan_server.cpp \
|
||||
thread/key_manager.cpp \
|
||||
@@ -291,6 +292,7 @@ HEADERS_COMMON = \
|
||||
net/udp6.hpp \
|
||||
thread/address_resolver.hpp \
|
||||
thread/announce_begin_server.hpp \
|
||||
thread/child_table.hpp \
|
||||
thread/data_poll_manager.hpp \
|
||||
thread/energy_scan_server.hpp \
|
||||
thread/key_manager.hpp \
|
||||
|
||||
@@ -47,19 +47,16 @@ using namespace ot;
|
||||
|
||||
uint8_t otThreadGetMaxAllowedChildren(otInstance *aInstance)
|
||||
{
|
||||
uint8_t aNumChildren;
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
(void)instance.GetThreadNetif().GetMle().GetChildren(&aNumChildren);
|
||||
|
||||
return aNumChildren;
|
||||
return instance.GetThreadNetif().GetMle().GetChildTable().GetMaxChildrenAllowed();
|
||||
}
|
||||
|
||||
otError otThreadSetMaxAllowedChildren(otInstance *aInstance, uint8_t aMaxChildren)
|
||||
{
|
||||
Instance &instance = *static_cast<Instance *>(aInstance);
|
||||
|
||||
return instance.GetThreadNetif().GetMle().SetMaxAllowedChildren(aMaxChildren);
|
||||
return instance.GetThreadNetif().GetMle().GetChildTable().SetMaxChildrenAllowed(aMaxChildren);
|
||||
}
|
||||
|
||||
bool otThreadIsRouterRoleEnabled(otInstance *aInstance)
|
||||
|
||||
@@ -245,6 +245,11 @@ template <> Mle::MleRouter &Instance::Get(void)
|
||||
return GetThreadNetif().GetMle();
|
||||
}
|
||||
|
||||
template <> ChildTable &Instance::Get(void)
|
||||
{
|
||||
return GetThreadNetif().GetMle().GetChildTable();
|
||||
}
|
||||
|
||||
template <> Ip6::Netif &Instance::Get(void)
|
||||
{
|
||||
return GetThreadNetif();
|
||||
|
||||
@@ -542,8 +542,6 @@ void AddressResolver::HandleAddressError(Coap::Header &aHeader, Message &aMessag
|
||||
otError error = OT_ERROR_NONE;
|
||||
ThreadTargetTlv targetTlv;
|
||||
ThreadMeshLocalEidTlv mlIidTlv;
|
||||
Child * children;
|
||||
uint8_t numChildren;
|
||||
Mac::ExtAddress macAddr;
|
||||
Ip6::Address destination;
|
||||
|
||||
@@ -577,16 +575,14 @@ void AddressResolver::HandleAddressError(Coap::Header &aHeader, Message &aMessag
|
||||
}
|
||||
}
|
||||
|
||||
children = netif.GetMle().GetChildren(&numChildren);
|
||||
|
||||
memcpy(&macAddr, mlIidTlv.GetIid(), sizeof(macAddr));
|
||||
macAddr.m8[0] ^= 0x2;
|
||||
|
||||
for (int i = 0; i < numChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
Child &child = children[i];
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
if (child.GetState() != Neighbor::kStateValid || child.IsFullThreadDevice())
|
||||
if (child.IsFullThreadDevice())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -635,8 +631,6 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag
|
||||
ThreadTargetTlv targetTlv;
|
||||
ThreadMeshLocalEidTlv mlIidTlv;
|
||||
ThreadLastTransactionTimeTlv lastTransactionTimeTlv;
|
||||
Child * children;
|
||||
uint8_t numChildren;
|
||||
char stringBuffer[Ip6::Address::kIp6AddressStringSize];
|
||||
|
||||
VerifyOrExit(aHeader.GetType() == OT_COAP_TYPE_NON_CONFIRMABLE && aHeader.GetCode() == OT_COAP_CODE_POST);
|
||||
@@ -659,14 +653,11 @@ void AddressResolver::HandleAddressQuery(Coap::Header &aHeader, Message &aMessag
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
children = netif.GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (int i = 0; i < numChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
Child &child = children[i];
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
if (child.GetState() != Neighbor::kStateValid || child.IsFullThreadDevice() ||
|
||||
child.GetLinkFailures() >= Mle::kFailedChildTransmissions)
|
||||
if (child.IsFullThreadDevice() || child.GetLinkFailures() >= Mle::kFailedChildTransmissions)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018, 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 Thread child table.
|
||||
*/
|
||||
|
||||
#include "child_table.hpp"
|
||||
|
||||
#include "common/code_utils.hpp"
|
||||
#include "common/instance.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
ChildTable::Iterator::Iterator(Instance &aInstance, StateFilter aFilter)
|
||||
: InstanceLocator(aInstance)
|
||||
, mFilter(aFilter)
|
||||
, mStart(NULL)
|
||||
, mChild(NULL)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
ChildTable::Iterator::Iterator(Instance &aInstance, StateFilter aFilter, Child *aStartingChild)
|
||||
: InstanceLocator(aInstance)
|
||||
, mFilter(aFilter)
|
||||
, mStart(aStartingChild)
|
||||
, mChild(NULL)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void ChildTable::Iterator::Reset(void)
|
||||
{
|
||||
ChildTable &childTable = GetInstance().Get<ChildTable>();
|
||||
|
||||
if (mStart == NULL)
|
||||
{
|
||||
mStart = &childTable.mChildren[0];
|
||||
}
|
||||
|
||||
mChild = mStart;
|
||||
|
||||
if (!MatchesFilter(*mChild, mFilter))
|
||||
{
|
||||
Advance();
|
||||
}
|
||||
}
|
||||
|
||||
void ChildTable::Iterator::Advance(void)
|
||||
{
|
||||
ChildTable &childTable = GetInstance().Get<ChildTable>();
|
||||
Child * listStart = &childTable.mChildren[0];
|
||||
Child * listEnd = &childTable.mChildren[childTable.mMaxChildrenAllowed];
|
||||
|
||||
VerifyOrExit(mChild != NULL);
|
||||
|
||||
do
|
||||
{
|
||||
mChild++;
|
||||
|
||||
if (mChild >= listEnd)
|
||||
{
|
||||
mChild = listStart;
|
||||
}
|
||||
|
||||
VerifyOrExit(mChild != mStart, mChild = NULL);
|
||||
} while (!MatchesFilter(*mChild, mFilter));
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
ChildTable::ChildTable(Instance &aInstance)
|
||||
: InstanceLocator(aInstance)
|
||||
, mMaxChildrenAllowed(kMaxChildren)
|
||||
{
|
||||
memset(mChildren, 0, sizeof(mChildren));
|
||||
}
|
||||
|
||||
Child *ChildTable::GetChildAtIndex(uint8_t aChildIndex)
|
||||
{
|
||||
Child *child = NULL;
|
||||
|
||||
VerifyOrExit(aChildIndex < mMaxChildrenAllowed);
|
||||
child = &mChildren[aChildIndex];
|
||||
|
||||
exit:
|
||||
return child;
|
||||
}
|
||||
|
||||
Child *ChildTable::GetNewChild(void)
|
||||
{
|
||||
Child *child = mChildren;
|
||||
|
||||
for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++)
|
||||
{
|
||||
if (child->GetState() == Child::kStateInvalid)
|
||||
{
|
||||
memset(child, 0, sizeof(Child));
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
child = NULL;
|
||||
|
||||
exit:
|
||||
return child;
|
||||
}
|
||||
|
||||
Child *ChildTable::FindChild(uint16_t aRloc16, StateFilter aFilter)
|
||||
{
|
||||
Child *child = mChildren;
|
||||
|
||||
for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++)
|
||||
{
|
||||
if (MatchesFilter(*child, aFilter) && (child->GetRloc16() == aRloc16))
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
child = NULL;
|
||||
|
||||
exit:
|
||||
return child;
|
||||
}
|
||||
|
||||
Child *ChildTable::FindChild(const Mac::ExtAddress &aAddress, StateFilter aFilter)
|
||||
{
|
||||
Child *child = mChildren;
|
||||
|
||||
for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++)
|
||||
{
|
||||
if (MatchesFilter(*child, aFilter) && (child->GetExtAddress() == aAddress))
|
||||
{
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
child = NULL;
|
||||
|
||||
exit:
|
||||
return child;
|
||||
}
|
||||
|
||||
Child *ChildTable::FindChild(const Mac::Address &aAddress, StateFilter aFilter)
|
||||
{
|
||||
Child *child = NULL;
|
||||
|
||||
switch (aAddress.GetType())
|
||||
{
|
||||
case Mac::Address::kTypeShort:
|
||||
child = FindChild(aAddress.GetShort(), aFilter);
|
||||
break;
|
||||
|
||||
case Mac::Address::kTypeExtended:
|
||||
child = FindChild(aAddress.GetExtended(), aFilter);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
bool ChildTable::HasChildren(StateFilter aFilter) const
|
||||
{
|
||||
bool rval = false;
|
||||
const Child *child = mChildren;
|
||||
|
||||
for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++)
|
||||
{
|
||||
if (MatchesFilter(*child, aFilter))
|
||||
{
|
||||
ExitNow(rval = true);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rval;
|
||||
}
|
||||
|
||||
uint8_t ChildTable::GetNumChildren(StateFilter aFilter) const
|
||||
{
|
||||
uint8_t numChildren = 0;
|
||||
const Child *child = mChildren;
|
||||
|
||||
for (uint16_t num = mMaxChildrenAllowed; num != 0; num--, child++)
|
||||
{
|
||||
if (MatchesFilter(*child, aFilter))
|
||||
{
|
||||
numChildren++;
|
||||
}
|
||||
}
|
||||
|
||||
return numChildren;
|
||||
}
|
||||
|
||||
otError ChildTable::SetMaxChildrenAllowed(uint8_t aMaxChildren)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(aMaxChildren > 0 && aMaxChildren <= kMaxChildren, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(!HasChildren(kInStateAnyExceptInvalid), error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
mMaxChildrenAllowed = aMaxChildren;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
bool ChildTable::MatchesFilter(const Child &aChild, StateFilter aFilter)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
switch (aFilter)
|
||||
{
|
||||
case kInStateValid:
|
||||
rval = (aChild.GetState() == Child::kStateValid);
|
||||
break;
|
||||
|
||||
case kInStateValidOrRestoring:
|
||||
rval = aChild.IsStateValidOrRestoring();
|
||||
break;
|
||||
|
||||
case kInStateChildIdRequest:
|
||||
rval = (aChild.GetState() == Child::kStateChildIdRequest);
|
||||
break;
|
||||
|
||||
case kInStateValidOrAttaching:
|
||||
rval = aChild.IsStateValidOrAttaching();
|
||||
break;
|
||||
|
||||
case kInStateAnyExceptInvalid:
|
||||
rval = (aChild.GetState() != Child::kStateInvalid);
|
||||
break;
|
||||
|
||||
case kInStateAnyExceptValidOrRestoing:
|
||||
rval = !aChild.IsStateValidOrRestoring();
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
} // namespace ot
|
||||
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2018, 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 Thread child table.
|
||||
*/
|
||||
|
||||
#ifndef CHILD_TABLE_HPP_
|
||||
#define CHILD_TABLE_HPP_
|
||||
|
||||
#include "openthread-core-config.h"
|
||||
|
||||
#include "common/locator.hpp"
|
||||
#include "thread/topology.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
|
||||
/**
|
||||
* This class represents the Thread child table.
|
||||
*
|
||||
*/
|
||||
class ChildTable : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This enumeration defines child state filters used for finding a child or iterating through the child table.
|
||||
*
|
||||
* Each filter definition accepts a subset of `Child:State` values.
|
||||
*
|
||||
*/
|
||||
enum StateFilter
|
||||
{
|
||||
kInStateValid, ///< Accept child only in `Child::kStateValid`.
|
||||
kInStateValidOrRestoring, ///< Accept child with `Child::IsStateValidOrRestoring()` being `true`.
|
||||
kInStateChildIdRequest, ///< Accept child only in `Child:kStateChildIdRequest`.
|
||||
kInStateValidOrAttaching, ///< Accept child with `Child::IsStateValidOrAttaching()` being `true`.
|
||||
kInStateAnyExceptInvalid, ///< Accept child in any state except `Child:kStateInvalid`.
|
||||
kInStateAnyExceptValidOrRestoing, ///< Accept child in any state except `Child::IsStateValidOrRestoring()`.
|
||||
};
|
||||
|
||||
/**
|
||||
* This class represents an iterator for iterating through the child entries in the child table.
|
||||
*
|
||||
*/
|
||||
class Iterator : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* This constructor initializes an `Iterator` instance to start from beginning of the child table.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
* @param[in] aFilter A child state filter.
|
||||
*
|
||||
*/
|
||||
Iterator(Instance &aInstance, StateFilter aFilter);
|
||||
|
||||
/**
|
||||
* This constructor initializes an `Iterator` instance to start from a given child.
|
||||
*
|
||||
* This constructor allows the iterator to start from a given `Child` entry. The iterator will start from the
|
||||
* given child and will go through all entries in the child table (matching the filter) till it gets back to
|
||||
* the starting `Child` entry.
|
||||
*
|
||||
* If the given starting `Child` pointer is `NULL`, then the iterator starts from beginning of the child table.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
* @param[in] aFilter A child state filter.
|
||||
* @param[in] aStartingChild A pointer to a child. If non-NULL, the iterator starts from the given entry.
|
||||
*
|
||||
*/
|
||||
Iterator(Instance &aInstance, StateFilter aFilter, Child *aStartingChild);
|
||||
|
||||
/**
|
||||
* This method resets the iterator to start over.
|
||||
*
|
||||
*/
|
||||
void Reset(void);
|
||||
|
||||
/**
|
||||
* This method indicates whether there are no more `Child` entries in the list (iterator has reached end of
|
||||
* the list).
|
||||
*
|
||||
* @retval TRUE There are no more entries in the list (reached end of the list).
|
||||
* @retval FALSE The current entry is valid.
|
||||
*
|
||||
*/
|
||||
bool IsDone(void) const { return (mChild == NULL); }
|
||||
|
||||
/**
|
||||
* This method advances the iterator.
|
||||
*
|
||||
* The iterator is moved to point to the next `Child` entry matching the given state filter in the constructor.
|
||||
* If there are no more `Child` entries matching the given filter, the iterator becomes empty (i.e.,
|
||||
* `GetChild()` returns `NULL` and `IsDone()` returns `true`.
|
||||
*
|
||||
*/
|
||||
void Advance(void);
|
||||
|
||||
/**
|
||||
* This method gets the `Child` entry to which the iterator is currently pointing.
|
||||
*
|
||||
* @returns A pointer to the `Child` entry, or `NULL` if the iterator is done and/or empty.
|
||||
*
|
||||
*/
|
||||
Child *GetChild(void) { return mChild; }
|
||||
|
||||
private:
|
||||
StateFilter mFilter;
|
||||
Child * mStart;
|
||||
Child * mChild;
|
||||
};
|
||||
|
||||
/**
|
||||
* This constructor initializes a `ChildTable` instance.
|
||||
*
|
||||
* @param[in] aInstance A reference to the OpenThread instance.
|
||||
*
|
||||
*/
|
||||
explicit ChildTable(Instance &aIntsance);
|
||||
|
||||
/**
|
||||
* This method clears the child table.
|
||||
*
|
||||
*/
|
||||
void Clear(void) { memset(mChildren, 0, sizeof(mChildren)); }
|
||||
|
||||
/**
|
||||
* This method returns the child table index for a given `Child` instance.
|
||||
*
|
||||
* @param[in] aChild A reference to a `Child`
|
||||
*
|
||||
* @returns The index corresponding to @p aChild.
|
||||
*
|
||||
*/
|
||||
uint8_t GetChildIndex(const Child &aChild) const { return static_cast<uint8_t>(&aChild - mChildren); }
|
||||
|
||||
/**
|
||||
* This method returns a pointer to a `Child` entry at a given index, or `NULL` if the index is out of bounds,
|
||||
* i.e., index is larger or equal to maximum number of children allowed (@sa GetMaxChildrenAllowed()).
|
||||
*
|
||||
* @param[in] aChildIndex A child index.
|
||||
*
|
||||
* @returns A pointer to the `Child` corresponding to the given index, or `NULL` if the index is out of bounds.
|
||||
*
|
||||
*/
|
||||
Child *GetChildAtIndex(uint8_t aChildIndex);
|
||||
|
||||
/**
|
||||
* This method gets a new/unused `Child` entry from the child table.
|
||||
*
|
||||
* @note The returned child entry will be cleared (`memset` to zero).
|
||||
*
|
||||
* @returns A pointer to a new `Child` entry, or `NULL` if all `Child` entries are in use.
|
||||
*
|
||||
*/
|
||||
Child *GetNewChild(void);
|
||||
|
||||
/**
|
||||
* This method searches the child table for a `Child` with a given RLOC16 also matching a given state filter.
|
||||
*
|
||||
* @param[in] aRloc16 A RLCO16 address.
|
||||
* @param[in] aFilter A child state filter.
|
||||
*
|
||||
* @returns A pointer to the `Child` entry if one is found, or `NULL` otherwise.
|
||||
*
|
||||
*/
|
||||
Child *FindChild(uint16_t aRloc16, StateFilter aFilter);
|
||||
|
||||
/**
|
||||
* This method searches the child table for a `Child` with a given extended address also matching a given state
|
||||
* filter.
|
||||
*
|
||||
* @param[in] aAddress A reference to an extended address.
|
||||
* @param[in] aFilter A child state filter.
|
||||
*
|
||||
* @returns A pointer to the `Child` entry if one is found, or `NULL` otherwise.
|
||||
*
|
||||
*/
|
||||
Child *FindChild(const Mac::ExtAddress &aAddress, StateFilter aFilter);
|
||||
|
||||
/**
|
||||
* This method searches the child table for a `Child` with a given address also matching a given state filter.
|
||||
*
|
||||
* @param[in] aAddress A reference to a MAC address.
|
||||
* @param[in] aFilter A child state filter.
|
||||
*
|
||||
* @returns A pointer to the `Child` entry if one is found, or `NULL` otherwise.
|
||||
*
|
||||
*/
|
||||
Child *FindChild(const Mac::Address &aAddress, StateFilter aFilter);
|
||||
|
||||
/**
|
||||
* This method indicates whether the child table contains any child matching a given state filter.
|
||||
*
|
||||
* @param[in] aFilter A child state filter.
|
||||
*
|
||||
* @returns TRUE if the table contains at least one child table matching the given filter, FALSE otherwise.
|
||||
*
|
||||
*/
|
||||
bool HasChildren(StateFilter aFilter) const;
|
||||
|
||||
/**
|
||||
* This method returns the number of children in the child table matching a given state filter.
|
||||
*
|
||||
* @param[in] aFilter A child state filter.
|
||||
*
|
||||
* @returns Number of children matching the given state filer.
|
||||
*
|
||||
*/
|
||||
uint8_t GetNumChildren(StateFilter aFilter) const;
|
||||
|
||||
/**
|
||||
* This method returns the maximum number of children that can be supported (build-time constant).
|
||||
*
|
||||
* @note Number of children allowed (from `GetMaxChildrenAllowed()`) can be less than maximum number of supported
|
||||
* children.
|
||||
*
|
||||
* @returns The maximum number of children supported
|
||||
*
|
||||
*/
|
||||
uint8_t GetMaxChildren(void) const { return kMaxChildren; }
|
||||
|
||||
/**
|
||||
* This method get the maximum number of children allowed.
|
||||
*
|
||||
* @returns The maximum number of children allowed.
|
||||
*
|
||||
*/
|
||||
uint8_t GetMaxChildrenAllowed(void) const { return mMaxChildrenAllowed; }
|
||||
|
||||
/**
|
||||
* This method sets the maximum number of children allowed.
|
||||
*
|
||||
* The number of children allowed must be at least one and at most same as maximum supported children (@sa
|
||||
* GetMaxChildren()). It can be changed only if the child table is empty.
|
||||
*
|
||||
* @param[in] aMaxChildren Maximum number of children allowed.
|
||||
*
|
||||
* @retval OT_ERROR_NONE The number of allowed children changed successfully.
|
||||
* @retval OT_ERROR_INVALID_ARGS If @p aMaxChildren is not in the range [1, Max supported children].
|
||||
* @retval OT_ERROR_INVALID_STATE The child table is not empty.
|
||||
*
|
||||
*/
|
||||
otError SetMaxChildrenAllowed(uint8_t aMaxChildren);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kMaxChildren = OPENTHREAD_CONFIG_MAX_CHILDREN,
|
||||
};
|
||||
|
||||
static bool MatchesFilter(const Child &aChild, StateFilter aFilter);
|
||||
|
||||
uint8_t mMaxChildrenAllowed;
|
||||
Child mChildren[kMaxChildren];
|
||||
};
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
#if OPENTHREAD_MTD
|
||||
|
||||
class ChildTable : public InstanceLocator
|
||||
{
|
||||
public:
|
||||
enum StateFilter
|
||||
{
|
||||
kInStateValid,
|
||||
kInStateValidOrRestoring,
|
||||
kInStateChildIdRequest,
|
||||
kInStateValidOrAttaching,
|
||||
kInStateAnyExceptInvalid,
|
||||
};
|
||||
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
Iterator(Instance &, StateFilter) {}
|
||||
Iterator(Instance &, StateFilter, Child *) {}
|
||||
void Reset(void) {}
|
||||
bool IsDone(void) const { return true; }
|
||||
void Advance(void) {}
|
||||
Child *GetChild(void) { return NULL; }
|
||||
};
|
||||
|
||||
explicit ChildTable(Instance &aIntsance)
|
||||
: InstanceLocator(aIntsance)
|
||||
{
|
||||
}
|
||||
void Clear(void) {}
|
||||
|
||||
uint8_t GetChildIndex(const Child &) const { return 0; }
|
||||
Child * GetChildAtIndex(uint8_t) { return NULL; }
|
||||
|
||||
Child *GetNewChild(void) { return NULL; }
|
||||
|
||||
Child *FindChild(uint16_t, StateFilter) { return NULL; }
|
||||
Child *FindChild(const Mac::ExtAddress &, StateFilter) { return NULL; }
|
||||
Child *FindChild(const Mac::Address &, StateFilter) { return NULL; }
|
||||
|
||||
bool HasChildren(StateFilter) const { return false; }
|
||||
uint8_t GetNumChildren(StateFilter) const { return 0; }
|
||||
uint8_t GetMaxChildren(void) const { return 0; }
|
||||
uint8_t GetMaxChildrenAllowed(void) const { return 0; }
|
||||
otError SetMaxChildrenAllowed(uint8_t) { return OT_ERROR_INVALID_STATE; }
|
||||
};
|
||||
|
||||
#endif // OPENTHREAD_MTD
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#endif // CHILD_TABLE_HPP_
|
||||
@@ -121,7 +121,6 @@ otError KeyManager::SetMasterKey(const otMasterKey &aKey)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Router *routers;
|
||||
Child * children;
|
||||
uint8_t num;
|
||||
|
||||
VerifyOrExit(memcmp(&mMasterKey, &aKey, sizeof(mMasterKey)) != 0);
|
||||
@@ -147,13 +146,11 @@ otError KeyManager::SetMasterKey(const otMasterKey &aKey)
|
||||
}
|
||||
|
||||
// reset child frame counters
|
||||
children = GetNetif().GetMle().GetChildren(&num);
|
||||
|
||||
for (uint8_t i = 0; i < num; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
children[i].SetKeySequence(0);
|
||||
children[i].SetLinkFrameCounter(0);
|
||||
children[i].SetMleFrameCounter(0);
|
||||
iter.GetChild()->SetKeySequence(0);
|
||||
iter.GetChild()->SetLinkFrameCounter(0);
|
||||
iter.GetChild()->SetMleFrameCounter(0);
|
||||
}
|
||||
|
||||
GetNotifier().SetFlags(OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER | OT_CHANGED_MASTER_KEY);
|
||||
|
||||
@@ -82,7 +82,7 @@ MeshForwarder::MeshForwarder(Instance &aInstance)
|
||||
, mSendMessageFrameCounter(0)
|
||||
, mSendMessageKeyId(0)
|
||||
, mSendMessageDataSequenceNumber(0)
|
||||
, mStartChildIndex(0)
|
||||
, mIndirectStartingChild(NULL)
|
||||
#endif
|
||||
, mDataPollManager(aInstance)
|
||||
{
|
||||
@@ -148,14 +148,9 @@ exit:
|
||||
|
||||
void MeshForwarder::RemoveMessage(Message &aMessage)
|
||||
{
|
||||
Child * children;
|
||||
uint8_t numChildren;
|
||||
|
||||
children = GetNetif().GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (uint8_t i = 0; i < numChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
IgnoreReturnValue(RemoveMessageFromSleepyChild(aMessage, children[i]));
|
||||
IgnoreReturnValue(RemoveMessageFromSleepyChild(aMessage, *iter.GetChild()));
|
||||
}
|
||||
|
||||
if (mSendMessage == &aMessage)
|
||||
@@ -569,8 +564,9 @@ otError MeshForwarder::HandleFrameRequest(Mac::Frame &aFrame)
|
||||
// The case where the current message requires fragmentation is
|
||||
// already checked and handled in `SendFragment()` method.
|
||||
|
||||
if (((child = netif.GetMle().GetChild(macDest)) != NULL) && !child->IsRxOnWhenIdle() &&
|
||||
(child->GetIndirectMessageCount() > 1))
|
||||
child = netif.GetMle().GetChildTable().FindChild(macDest, ChildTable::kInStateValidOrRestoring);
|
||||
|
||||
if ((child != NULL) && !child->IsRxOnWhenIdle() && (child->GetIndirectMessageCount() > 1))
|
||||
{
|
||||
aFrame.SetFramePending(true);
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ private:
|
||||
uint32_t mSendMessageFrameCounter;
|
||||
uint8_t mSendMessageKeyId;
|
||||
uint8_t mSendMessageDataSequenceNumber;
|
||||
uint8_t mStartChildIndex;
|
||||
Child * mIndirectStartingChild;
|
||||
#endif
|
||||
|
||||
DataPollManager mDataPollManager;
|
||||
|
||||
@@ -44,13 +44,11 @@ namespace ot {
|
||||
|
||||
otError MeshForwarder::SendMessage(Message &aMessage)
|
||||
{
|
||||
ThreadNetif &netif = GetNetif();
|
||||
otError error = OT_ERROR_NONE;
|
||||
ThreadNetif &netif = GetNetif();
|
||||
ChildTable & childTable = netif.GetMle().GetChildTable();
|
||||
otError error = OT_ERROR_NONE;
|
||||
Neighbor * neighbor;
|
||||
|
||||
uint8_t numChildren;
|
||||
Child * child;
|
||||
|
||||
switch (aMessage.GetType())
|
||||
{
|
||||
case Message::kTypeIp6:
|
||||
@@ -73,30 +71,34 @@ otError MeshForwarder::SendMessage(Message &aMessage)
|
||||
|
||||
if (aMessage.GetSubType() != Message::kSubTypeMplRetransmission)
|
||||
{
|
||||
child = netif.GetMle().GetChildren(&numChildren);
|
||||
|
||||
if (ip6Header.GetDestination() == netif.GetMle().GetLinkLocalAllThreadNodesAddress() ||
|
||||
ip6Header.GetDestination() == netif.GetMle().GetRealmLocalAllThreadNodesAddress())
|
||||
{
|
||||
// destined for all sleepy children
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone();
|
||||
iter.Advance())
|
||||
{
|
||||
if (child->IsStateValidOrRestoring() && !child->IsRxOnWhenIdle())
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
if (!child.IsRxOnWhenIdle())
|
||||
{
|
||||
aMessage.SetChildMask(i);
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
aMessage.SetChildMask(childTable.GetChildIndex(child));
|
||||
mSourceMatchController.IncrementMessageCount(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// destined for some sleepy children which subscribed the multicast address.
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone();
|
||||
iter.Advance())
|
||||
{
|
||||
if (netif.GetMle().IsSleepyChildSubscribed(ip6Header.GetDestination(), *child))
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
if (netif.GetMle().IsSleepyChildSubscribed(ip6Header.GetDestination(), child))
|
||||
{
|
||||
aMessage.SetChildMask(i);
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
aMessage.SetChildMask(childTable.GetChildIndex(child));
|
||||
mSourceMatchController.IncrementMessageCount(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,9 +108,9 @@ otError MeshForwarder::SendMessage(Message &aMessage)
|
||||
!neighbor->IsRxOnWhenIdle() && !aMessage.GetDirectTransmission())
|
||||
{
|
||||
// destined for a sleepy child
|
||||
child = static_cast<Child *>(neighbor);
|
||||
aMessage.SetChildMask(netif.GetMle().GetChildIndex(*child));
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
Child &child = *static_cast<Child *>(neighbor);
|
||||
aMessage.SetChildMask(childTable.GetChildIndex(child));
|
||||
mSourceMatchController.IncrementMessageCount(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -120,13 +122,15 @@ otError MeshForwarder::SendMessage(Message &aMessage)
|
||||
}
|
||||
|
||||
case Message::kTypeSupervision:
|
||||
child = netif.GetChildSupervisor().GetDestination(aMessage);
|
||||
{
|
||||
Child *child = netif.GetChildSupervisor().GetDestination(aMessage);
|
||||
VerifyOrExit(child != NULL, error = OT_ERROR_DROP);
|
||||
VerifyOrExit(!child->IsRxOnWhenIdle(), error = OT_ERROR_DROP);
|
||||
|
||||
aMessage.SetChildMask(netif.GetMle().GetChildIndex(*child));
|
||||
aMessage.SetChildMask(childTable.GetChildIndex(*child));
|
||||
mSourceMatchController.IncrementMessageCount(*child);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
aMessage.SetDirectTransmission();
|
||||
@@ -192,7 +196,7 @@ void MeshForwarder::ClearChildIndirectMessages(Child &aChild)
|
||||
{
|
||||
nextMessage = message->GetNext();
|
||||
|
||||
message->ClearChildMask(GetNetif().GetMle().GetChildIndex(aChild));
|
||||
message->ClearChildMask(GetNetif().GetMle().GetChildTable().GetChildIndex(aChild));
|
||||
|
||||
if (!message->IsChildPending() && !message->GetDirectTransmission())
|
||||
{
|
||||
@@ -215,21 +219,15 @@ exit:
|
||||
|
||||
void MeshForwarder::UpdateIndirectMessages(void)
|
||||
{
|
||||
Child * children;
|
||||
uint8_t numChildren;
|
||||
|
||||
children = GetNetif().GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (uint8_t i = 0; i < numChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptValidOrRestoing); !iter.IsDone();
|
||||
iter.Advance())
|
||||
{
|
||||
Child *child = &children[i];
|
||||
|
||||
if (child->IsStateValidOrRestoring() || (child->GetIndirectMessageCount() == 0))
|
||||
if (iter.GetChild()->GetIndirectMessageCount() == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClearChildIndirectMessages(*child);
|
||||
ClearChildIndirectMessages(*iter.GetChild());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +253,7 @@ exit:
|
||||
otError MeshForwarder::RemoveMessageFromSleepyChild(Message &aMessage, Child &aChild)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint8_t childIndex = GetNetif().GetMle().GetChildIndex(aChild);
|
||||
uint8_t childIndex = GetNetif().GetMle().GetChildTable().GetChildIndex(aChild);
|
||||
|
||||
VerifyOrExit(aMessage.GetChildMask(childIndex) == true, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
@@ -350,14 +348,10 @@ void MeshForwarder::RemoveDataResponseMessages(void)
|
||||
|
||||
if (!(ip6Header.GetDestination().IsMulticast()))
|
||||
{
|
||||
Child * children;
|
||||
uint8_t numChildren;
|
||||
|
||||
children = GetNetif().GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (uint8_t i = 0; i < numChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone();
|
||||
iter.Advance())
|
||||
{
|
||||
IgnoreReturnValue(RemoveMessageFromSleepyChild(*message, children[i]));
|
||||
IgnoreReturnValue(RemoveMessageFromSleepyChild(*message, *iter.GetChild()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,32 +370,15 @@ otError MeshForwarder::GetIndirectTransmission(void)
|
||||
{
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
ThreadNetif &netif = GetNetif();
|
||||
uint8_t numChildren;
|
||||
uint8_t childIndex;
|
||||
uint8_t nextIndex;
|
||||
Child * children;
|
||||
|
||||
UpdateIndirectMessages();
|
||||
|
||||
children = netif.GetMle().GetChildren(&numChildren);
|
||||
|
||||
if (mStartChildIndex >= numChildren)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild);
|
||||
!iter.IsDone(); iter.Advance())
|
||||
{
|
||||
mStartChildIndex = 0;
|
||||
}
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
childIndex = mStartChildIndex;
|
||||
|
||||
for (uint8_t iterations = numChildren; iterations > 0; iterations--, childIndex = nextIndex)
|
||||
{
|
||||
Child &child = children[childIndex];
|
||||
|
||||
if ((nextIndex = childIndex + 1) == numChildren)
|
||||
{
|
||||
nextIndex = 0;
|
||||
}
|
||||
|
||||
if (!child.IsStateValidOrRestoring() || !child.IsDataRequestPending())
|
||||
if (!child.IsDataRequestPending())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -434,9 +411,9 @@ otError MeshForwarder::GetIndirectTransmission(void)
|
||||
child.GetMacAddress(mMacDest);
|
||||
}
|
||||
|
||||
// Record current child index, and move it to next index after this indirect transmission has completed.
|
||||
// Remember the current child and move it to next one in the list after the indirect transmission has completed.
|
||||
|
||||
mStartChildIndex = childIndex;
|
||||
mIndirectStartingChild = &child;
|
||||
|
||||
netif.GetMac().SendFrameRequest(mMacSender);
|
||||
ExitNow(error = OT_ERROR_NONE);
|
||||
@@ -450,7 +427,7 @@ Message *MeshForwarder::GetIndirectTransmission(Child &aChild)
|
||||
{
|
||||
Message *message = NULL;
|
||||
Message *next;
|
||||
uint8_t childIndex = GetNetif().GetMle().GetChildIndex(aChild);
|
||||
uint8_t childIndex = GetNetif().GetMle().GetChildTable().GetChildIndex(aChild);
|
||||
|
||||
for (message = mSendQueue.GetHead(); message; message = next)
|
||||
{
|
||||
@@ -569,7 +546,9 @@ void MeshForwarder::HandleDataRequest(const Mac::Address &aMacSource, const otTh
|
||||
|
||||
VerifyOrExit(netif.GetMle().GetRole() != OT_DEVICE_ROLE_DETACHED);
|
||||
|
||||
VerifyOrExit((child = netif.GetMle().GetChild(aMacSource)) != NULL);
|
||||
child = netif.GetMle().GetChildTable().FindChild(aMacSource, ChildTable::kInStateValidOrRestoring);
|
||||
VerifyOrExit(child != NULL);
|
||||
|
||||
child->SetLastHeard(TimerMilli::GetNow());
|
||||
child->ResetLinkFailures();
|
||||
indirectMsgCount = child->GetIndirectMessageCount();
|
||||
@@ -593,7 +572,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr
|
||||
ThreadNetif &netif = GetNetif();
|
||||
Child * child;
|
||||
|
||||
child = netif.GetMle().GetChild(aMacDest);
|
||||
child = netif.GetMle().GetChildTable().FindChild(aMacDest, ChildTable::kInStateValidOrRestoring);
|
||||
VerifyOrExit(child != NULL);
|
||||
|
||||
child->SetDataRequestPending(false);
|
||||
@@ -604,12 +583,14 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr
|
||||
{
|
||||
// To ensure fairness in handling of data requests from sleepy
|
||||
// children, once a message is completed for indirect transmission to a
|
||||
// child (no matter succeed or failed), the `mStartChildIndex` is updated to
|
||||
// the next index after the current child. Subsequent call to
|
||||
// `ScheduleTransmissionTask()` will begin the iteration through
|
||||
// the children list from this index.
|
||||
// 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.
|
||||
|
||||
mStartChildIndex++;
|
||||
ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring, mIndirectStartingChild);
|
||||
iter.Advance();
|
||||
mIndirectStartingChild = iter.GetChild();
|
||||
|
||||
if (aError == OT_ERROR_NONE)
|
||||
{
|
||||
@@ -685,7 +666,7 @@ void MeshForwarder::HandleSentFrameToChild(const Mac::Frame &aFrame, otError aEr
|
||||
mSourceMatchController.SetSrcMatchAsShort(*child, true);
|
||||
}
|
||||
|
||||
childIndex = netif.GetMle().GetChildIndex(*child);
|
||||
childIndex = netif.GetMle().GetChildTable().GetChildIndex(*child);
|
||||
|
||||
if (mSendMessage->GetChildMask(childIndex))
|
||||
{
|
||||
|
||||
+89
-256
@@ -64,7 +64,7 @@ MleRouter::MleRouter(Instance &aInstance)
|
||||
, mAddressRelease(OT_URI_PATH_ADDRESS_RELEASE, &MleRouter::HandleAddressRelease, this)
|
||||
, mRouterIdSequence(0)
|
||||
, mRouterIdSequenceLastUpdated(0)
|
||||
, mMaxChildrenAllowed(kMaxChildren)
|
||||
, mChildTable(aInstance)
|
||||
, mChildTableChangedCallback(NULL)
|
||||
, mChallengeTimeout(0)
|
||||
, mNextChildId(kMaxChildId)
|
||||
@@ -82,7 +82,6 @@ MleRouter::MleRouter(Instance &aInstance)
|
||||
{
|
||||
mDeviceMode |= ModeTlv::kModeFFD | ModeTlv::kModeFullNetworkData;
|
||||
|
||||
memset(mChildren, 0, sizeof(mChildren));
|
||||
memset(mRouters, 0, sizeof(mRouters));
|
||||
|
||||
SetRouterId(kInvalidRouterId);
|
||||
@@ -448,11 +447,11 @@ otError MleRouter::SetStateRouter(uint16_t aRloc16)
|
||||
}
|
||||
|
||||
// remove children that do not have matching RLOC16
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring() && GetRouterId(mChildren[i].GetRloc16()) != mRouterId)
|
||||
if (GetRouterId(iter.GetChild()->GetRloc16()) != mRouterId)
|
||||
{
|
||||
RemoveNeighbor(mChildren[i]);
|
||||
RemoveNeighbor(*iter.GetChild());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,11 +494,11 @@ otError MleRouter::SetStateLeader(uint16_t aRloc16)
|
||||
netif.GetAddressResolver().Clear();
|
||||
|
||||
// remove children that do not have matching RLOC16
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring() && GetRouterId(mChildren[i].GetRloc16()) != mRouterId)
|
||||
if (GetRouterId(iter.GetChild()->GetRloc16()) != mRouterId)
|
||||
{
|
||||
RemoveNeighbor(mChildren[i]);
|
||||
RemoveNeighbor(*iter.GetChild());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1104,51 +1103,6 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Child *MleRouter::NewChild(void)
|
||||
{
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].GetState() == Neighbor::kStateInvalid)
|
||||
{
|
||||
return &mChildren[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Child *MleRouter::FindChild(uint16_t aChildId)
|
||||
{
|
||||
Child *rval = NULL;
|
||||
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].GetState() != Neighbor::kStateInvalid && GetChildId(mChildren[i].GetRloc16()) == aChildId)
|
||||
{
|
||||
ExitNow(rval = &mChildren[i]);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rval;
|
||||
}
|
||||
|
||||
Child *MleRouter::FindChild(const Mac::ExtAddress &aAddress)
|
||||
{
|
||||
Child *rval = NULL;
|
||||
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].GetState() != Neighbor::kStateInvalid && mChildren[i].GetExtAddress() == aAddress)
|
||||
{
|
||||
ExitNow(rval = &mChildren[i]);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return rval;
|
||||
}
|
||||
|
||||
uint8_t MleRouter::LinkQualityToCost(uint8_t aLinkQuality)
|
||||
{
|
||||
switch (aLinkQuality)
|
||||
@@ -1762,11 +1716,11 @@ otError MleRouter::HandleParentRequest(const Message &aMessage, const Ip6::Messa
|
||||
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kChallenge, sizeof(challenge), challenge));
|
||||
VerifyOrExit(challenge.IsValid(), error = OT_ERROR_PARSE);
|
||||
|
||||
child = FindChild(macAddr);
|
||||
child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid);
|
||||
|
||||
if (child == NULL)
|
||||
{
|
||||
VerifyOrExit((child = NewChild()) != NULL);
|
||||
VerifyOrExit((child = GetChildTable().GetNewChild()) != NULL);
|
||||
|
||||
memset(child, 0, sizeof(*child));
|
||||
|
||||
@@ -1897,11 +1851,12 @@ void MleRouter::HandleStateUpdateTimer(void)
|
||||
}
|
||||
|
||||
// update children state
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
Child & child = *iter.GetChild();
|
||||
uint32_t timeout = 0;
|
||||
|
||||
switch (mChildren[i].GetState())
|
||||
switch (child.GetState())
|
||||
{
|
||||
case Neighbor::kStateInvalid:
|
||||
case Neighbor::kStateChildIdRequest:
|
||||
@@ -1911,7 +1866,7 @@ void MleRouter::HandleStateUpdateTimer(void)
|
||||
case Neighbor::kStateValid:
|
||||
case Neighbor::kStateRestored:
|
||||
case Neighbor::kStateChildUpdateRequest:
|
||||
timeout = TimerMilli::SecToMsec(mChildren[i].GetTimeout());
|
||||
timeout = TimerMilli::SecToMsec(child.GetTimeout());
|
||||
break;
|
||||
|
||||
case Neighbor::kStateParentResponse:
|
||||
@@ -1920,15 +1875,15 @@ void MleRouter::HandleStateUpdateTimer(void)
|
||||
break;
|
||||
}
|
||||
|
||||
if ((TimerMilli::GetNow() - mChildren[i].GetLastHeard()) >= timeout)
|
||||
if ((TimerMilli::GetNow() - child.GetLastHeard()) >= timeout)
|
||||
{
|
||||
otLogInfoMle(GetInstance(), "Child timeout expired");
|
||||
RemoveNeighbor(mChildren[i]);
|
||||
RemoveNeighbor(child);
|
||||
}
|
||||
else if ((mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER) &&
|
||||
(mChildren[i].GetState() == Neighbor::kStateRestored))
|
||||
(child.GetState() == Neighbor::kStateRestored))
|
||||
{
|
||||
SendChildUpdateRequest(mChildren[i]);
|
||||
SendChildUpdateRequest(child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2140,16 +2095,15 @@ otError MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffse
|
||||
// table is timed out and then trying to register its globally unique
|
||||
// IPv6 address as the new child.
|
||||
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone();
|
||||
iter.Advance())
|
||||
{
|
||||
Child &child = mChildren[i];
|
||||
|
||||
if (!child.IsStateValidOrRestoring() || (&child == &aChild))
|
||||
if (iter.GetChild() == &aChild)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IgnoreReturnValue(child.RemoveIp6Address(GetInstance(), address));
|
||||
IgnoreReturnValue(iter.GetChild()->RemoveIp6Address(GetInstance(), address));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2202,7 +2156,8 @@ otError MleRouter::HandleChildIdRequest(const Message & aMessage,
|
||||
// Find Child
|
||||
aMessageInfo.GetPeerAddr().ToExtAddress(macAddr);
|
||||
|
||||
VerifyOrExit((child = FindChild(macAddr)) != NULL, error = OT_ERROR_ALREADY);
|
||||
child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid);
|
||||
VerifyOrExit(child != NULL, error = OT_ERROR_ALREADY);
|
||||
|
||||
// Response
|
||||
SuccessOrExit(error = Tlv::GetTlv(aMessage, Tlv::kResponse, sizeof(response), response));
|
||||
@@ -2364,7 +2319,7 @@ otError MleRouter::HandleChildUpdateRequest(const Message & aMessage,
|
||||
|
||||
// Find Child
|
||||
aMessageInfo.GetPeerAddr().ToExtAddress(macAddr);
|
||||
child = FindChild(macAddr);
|
||||
child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid);
|
||||
|
||||
tlvs[tlvslength++] = Tlv::kSourceAddress;
|
||||
|
||||
@@ -2471,7 +2426,7 @@ otError MleRouter::HandleChildUpdateResponse(const Message & aMessage,
|
||||
// Find Child
|
||||
aMessageInfo.GetPeerAddr().ToExtAddress(macAddr);
|
||||
|
||||
child = FindChild(macAddr);
|
||||
child = GetChildTable().FindChild(macAddr, ChildTable::kInStateAnyExceptInvalid);
|
||||
|
||||
if (child == NULL)
|
||||
{
|
||||
@@ -2644,12 +2599,12 @@ void MleRouter::SynchronizeChildNetworkData(void)
|
||||
|
||||
VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER);
|
||||
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
Child & child = mChildren[i];
|
||||
Child & child = *iter.GetChild();
|
||||
uint8_t version;
|
||||
|
||||
if (child.GetState() != Neighbor::kStateValid || child.IsRxOnWhenIdle())
|
||||
if (child.IsRxOnWhenIdle())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -2907,7 +2862,7 @@ otError MleRouter::SendChildIdResponse(Child &aChild)
|
||||
{
|
||||
mNextChildId = kMinChildId;
|
||||
}
|
||||
} while (FindChild(mNextChildId) != NULL);
|
||||
} while (GetChildTable().FindChild(mNextChildId, ChildTable::kInStateAnyExceptInvalid) != NULL);
|
||||
|
||||
// allocate Child ID
|
||||
aChild.SetRloc16(netif.GetMac().GetShortAddress() | mNextChildId);
|
||||
@@ -2979,7 +2934,7 @@ otError MleRouter::SendChildUpdateRequest(Child &aChild)
|
||||
|
||||
if (!aChild.IsRxOnWhenIdle())
|
||||
{
|
||||
uint8_t childIndex = netif.GetMle().GetChildIndex(aChild);
|
||||
uint8_t childIndex = netif.GetMle().GetChildTable().GetChildIndex(aChild);
|
||||
|
||||
for (message = netif.GetMeshForwarder().GetSendQueue().GetHead(); message; message = message->GetNext())
|
||||
{
|
||||
@@ -3190,64 +3145,6 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
Child *MleRouter::GetChild(uint16_t aAddress)
|
||||
{
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetRloc16() == aAddress)
|
||||
{
|
||||
return &mChildren[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Child *MleRouter::GetChild(const Mac::ExtAddress &aAddress)
|
||||
{
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetExtAddress() == aAddress)
|
||||
{
|
||||
return &mChildren[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Child *MleRouter::GetChild(const Mac::Address &aAddress)
|
||||
{
|
||||
switch (aAddress.GetType())
|
||||
{
|
||||
case Mac::Address::kTypeShort:
|
||||
return GetChild(aAddress.GetShort());
|
||||
|
||||
case Mac::Address::kTypeExtended:
|
||||
return GetChild(aAddress.GetExtended());
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t MleRouter::GetChildIndex(const Child &aChild)
|
||||
{
|
||||
return static_cast<uint8_t>(&aChild - mChildren);
|
||||
}
|
||||
|
||||
Child *MleRouter::GetChildren(uint8_t *aNumChildren)
|
||||
{
|
||||
if (aNumChildren != NULL)
|
||||
{
|
||||
*aNumChildren = mMaxChildrenAllowed;
|
||||
}
|
||||
|
||||
return mChildren;
|
||||
}
|
||||
|
||||
bool MleRouter::IsMinimalChild(uint16_t aRloc16)
|
||||
{
|
||||
ThreadNetif &netif = GetNetif();
|
||||
@@ -3265,23 +3162,6 @@ bool MleRouter::IsMinimalChild(uint16_t aRloc16)
|
||||
return rval;
|
||||
}
|
||||
|
||||
otError MleRouter::SetMaxAllowedChildren(uint8_t aMaxChildren)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
// Ensure the value is between 1 and kMaxChildren
|
||||
VerifyOrExit(aMaxChildren > 0 && aMaxChildren <= kMaxChildren, error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
// Do not allow setting max children if MLE is running
|
||||
VerifyOrExit(mRole == OT_DEVICE_ROLE_DISABLED, error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
// Save the value
|
||||
mMaxChildrenAllowed = aMaxChildren;
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError MleRouter::RemoveNeighbor(const Mac::Address &aAddress)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
@@ -3394,13 +3274,8 @@ Neighbor *MleRouter::GetNeighbor(uint16_t aAddress)
|
||||
|
||||
case OT_DEVICE_ROLE_ROUTER:
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetRloc16() == aAddress)
|
||||
{
|
||||
ExitNow(rval = &mChildren[i]);
|
||||
}
|
||||
}
|
||||
rval = GetChildTable().FindChild(aAddress, ChildTable::kInStateValidOrRestoring);
|
||||
VerifyOrExit(rval == NULL);
|
||||
|
||||
if (IsActiveRouter(aAddress))
|
||||
{
|
||||
@@ -3436,13 +3311,8 @@ Neighbor *MleRouter::GetNeighbor(const Mac::ExtAddress &aAddress)
|
||||
|
||||
case OT_DEVICE_ROLE_ROUTER:
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring() && mChildren[i].GetExtAddress() == aAddress)
|
||||
{
|
||||
ExitNow(rval = &mChildren[i]);
|
||||
}
|
||||
}
|
||||
rval = GetChildTable().FindChild(aAddress, ChildTable::kInStateValidOrRestoring);
|
||||
VerifyOrExit(rval == NULL);
|
||||
|
||||
for (int i = 0; i <= kMaxRouterId; i++)
|
||||
{
|
||||
@@ -3518,14 +3388,9 @@ Neighbor *MleRouter::GetNeighbor(const Ip6::Address &aAddress)
|
||||
context.mContextId = 0xff;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
child = &mChildren[i];
|
||||
|
||||
if (!child->IsStateValidOrRestoring())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
child = iter.GetChild();
|
||||
|
||||
if (context.mContextId == 0 && aAddress.mFields.m16[4] == HostSwap16(0x0000) &&
|
||||
aAddress.mFields.m16[5] == HostSwap16(0x00ff) && aAddress.mFields.m16[6] == HostSwap16(0xfe00) &&
|
||||
@@ -3748,7 +3613,9 @@ otError MleRouter::GetChildInfoById(uint16_t aChildId, otChildInfo &aChildInfo)
|
||||
aChildId = GetChildId(aChildId);
|
||||
}
|
||||
|
||||
VerifyOrExit((child = FindChild(aChildId)) != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
child = GetChildTable().FindChild(aChildId, ChildTable::kInStateAnyExceptInvalid);
|
||||
VerifyOrExit(child != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
error = GetChildInfo(*child, aChildInfo);
|
||||
|
||||
exit:
|
||||
@@ -3758,9 +3625,12 @@ exit:
|
||||
otError MleRouter::GetChildInfoByIndex(uint8_t aChildIndex, otChildInfo &aChildInfo)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Child * child = NULL;
|
||||
|
||||
VerifyOrExit(aChildIndex < mMaxChildrenAllowed, error = OT_ERROR_INVALID_ARGS);
|
||||
error = GetChildInfo(mChildren[aChildIndex], aChildInfo);
|
||||
child = GetChildTable().GetChildAtIndex(aChildIndex);
|
||||
VerifyOrExit(child != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
error = GetChildInfo(*child, aChildInfo);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -3771,11 +3641,13 @@ otError MleRouter::GetChildNextIp6Address(uint8_t aChildIndex
|
||||
Ip6::Address & aAddress)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
Child * child = NULL;
|
||||
|
||||
VerifyOrExit(aChildIndex < mMaxChildrenAllowed, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(mChildren[aChildIndex].IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS);
|
||||
child = GetChildTable().GetChildAtIndex(aChildIndex);
|
||||
VerifyOrExit(child != NULL, error = OT_ERROR_INVALID_ARGS);
|
||||
VerifyOrExit(child->IsStateValidOrRestoring(), error = OT_ERROR_INVALID_ARGS);
|
||||
|
||||
error = mChildren[aChildIndex].GetNextIp6Address(GetInstance(), aIterator, aAddress);
|
||||
error = child->GetNextIp6Address(GetInstance(), aIterator, aAddress);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
@@ -3792,11 +3664,12 @@ void MleRouter::RestoreChildren(void)
|
||||
Child * child;
|
||||
const Settings::ChildInfo &childInfo = iter.GetChildInfo();
|
||||
|
||||
child = FindChild(*static_cast<const Mac::ExtAddress *>(&childInfo.mExtAddress));
|
||||
child = GetChildTable().FindChild(*static_cast<const Mac::ExtAddress *>(&childInfo.mExtAddress),
|
||||
ChildTable::kInStateAnyExceptInvalid);
|
||||
|
||||
if (child == NULL)
|
||||
{
|
||||
VerifyOrExit((child = NewChild()) != NULL, error = OT_ERROR_NO_BUFS);
|
||||
VerifyOrExit((child = GetChildTable().GetNewChild()) != NULL, error = OT_ERROR_NO_BUFS);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3817,7 +3690,7 @@ void MleRouter::RestoreChildren(void)
|
||||
|
||||
exit:
|
||||
|
||||
if (foundDuplicate || (numChildren > kMaxChildren) || (error != OT_ERROR_NONE))
|
||||
if (foundDuplicate || (numChildren > GetChildTable().GetMaxChildren()) || (error != OT_ERROR_NONE))
|
||||
{
|
||||
// If there is any error, e.g., there are more saved children
|
||||
// in non-volatile settings than could be restored or there are
|
||||
@@ -3852,7 +3725,8 @@ otError MleRouter::StoreChild(uint16_t aChildRloc16)
|
||||
Child * child;
|
||||
Settings::ChildInfo childInfo;
|
||||
|
||||
VerifyOrExit((child = FindChild(GetChildId(aChildRloc16))) != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
child = GetChildTable().FindChild(GetChildId(aChildRloc16), ChildTable::kInStateAnyExceptInvalid);
|
||||
VerifyOrExit(child != NULL, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
IgnoreReturnValue(RemoveStoredChild(aChildRloc16));
|
||||
|
||||
@@ -3874,12 +3748,9 @@ otError MleRouter::RefreshStoredChildren(void)
|
||||
|
||||
SuccessOrExit(error = GetInstance().GetSettings().DeleteChildInfo());
|
||||
|
||||
for (uint8_t i = 0; i < kMaxChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateAnyExceptInvalid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (mChildren[i].GetState() != Neighbor::kStateInvalid)
|
||||
{
|
||||
SuccessOrExit(error = StoreChild(mChildren[i].GetRloc16()));
|
||||
}
|
||||
SuccessOrExit(error = StoreChild(iter.GetChild()->GetRloc16()));
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -3959,15 +3830,22 @@ otError MleRouter::GetNextNeighborInfo(otNeighborInfoIterator &aIterator, otNeig
|
||||
|
||||
memset(&aNeighInfo, 0, sizeof(aNeighInfo));
|
||||
|
||||
// Non-negative iterator value gives the current index into mChildren array
|
||||
// Non-negative iterator value gives the Child index into child table
|
||||
|
||||
if (aIterator >= 0)
|
||||
{
|
||||
for (index = aIterator; index < mMaxChildrenAllowed; index++)
|
||||
for (index = aIterator;; index++)
|
||||
{
|
||||
if (mChildren[index].GetState() == Neighbor::kStateValid)
|
||||
Child *child = GetChildTable().GetChildAtIndex(static_cast<uint8_t>(index));
|
||||
|
||||
if (child == NULL)
|
||||
{
|
||||
neighbor = &mChildren[index];
|
||||
break;
|
||||
}
|
||||
|
||||
if (child->GetState() == Neighbor::kStateValid)
|
||||
{
|
||||
neighbor = child;
|
||||
aNeighInfo.mIsChild = true;
|
||||
index++;
|
||||
aIterator = index;
|
||||
@@ -4068,7 +3946,7 @@ otError MleRouter::CheckReachability(uint16_t aMeshSource, uint16_t aMeshDest, I
|
||||
else if (GetRouterId(aMeshDest) == mRouterId)
|
||||
{
|
||||
// mesh destination is a child of this device
|
||||
if (GetChild(aMeshDest))
|
||||
if (GetChildTable().FindChild(aMeshDest, ChildTable::kInStateValidOrRestoring))
|
||||
{
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
@@ -4294,26 +4172,9 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Header * aHeader,
|
||||
SendLinkRequest(NULL);
|
||||
|
||||
// send child id responses
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateChildIdRequest); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
switch (mChildren[i].GetState())
|
||||
{
|
||||
case Neighbor::kStateChildIdRequest:
|
||||
SendChildIdResponse(mChildren[i]);
|
||||
break;
|
||||
|
||||
case Neighbor::kStateParentResponse:
|
||||
case Neighbor::kStateLinkRequest:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case Neighbor::kStateInvalid:
|
||||
case Neighbor::kStateParentRequest:
|
||||
case Neighbor::kStateValid:
|
||||
case Neighbor::kStateRestored:
|
||||
case Neighbor::kStateChildUpdateRequest:
|
||||
break;
|
||||
}
|
||||
SendChildIdResponse(*iter.GetChild());
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -4535,7 +4396,6 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
|
||||
ConnectivityTlv &tlv = aTlv;
|
||||
uint8_t cost;
|
||||
uint8_t linkQuality;
|
||||
uint8_t numChildren = 0;
|
||||
int8_t parentPriority = kParentPriorityMedium;
|
||||
|
||||
if (mParentPriority != kParentPriorityUnspecified)
|
||||
@@ -4544,15 +4404,10 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].GetState() == Neighbor::kStateValid)
|
||||
{
|
||||
numChildren++;
|
||||
}
|
||||
}
|
||||
uint8_t numChildren = GetChildTable().GetNumChildren(ChildTable::kInStateValid);
|
||||
uint8_t maxAllowed = GetChildTable().GetMaxChildrenAllowed();
|
||||
|
||||
if ((mMaxChildrenAllowed - numChildren) < (mMaxChildrenAllowed / 3))
|
||||
if ((maxAllowed - numChildren) < (maxAllowed / 3))
|
||||
{
|
||||
parentPriority = kParentPriorityLow;
|
||||
}
|
||||
@@ -4877,29 +4732,14 @@ exit:
|
||||
|
||||
bool MleRouter::HasChildren(void)
|
||||
{
|
||||
bool hasChildren = false;
|
||||
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].GetState() == Neighbor::kStateRestored ||
|
||||
mChildren[i].GetState() >= Neighbor::kStateChildIdRequest)
|
||||
{
|
||||
ExitNow(hasChildren = true);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
return hasChildren;
|
||||
return GetChildTable().HasChildren(ChildTable::kInStateValidOrAttaching);
|
||||
}
|
||||
|
||||
void MleRouter::RemoveChildren(void)
|
||||
{
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (mChildren[i].IsStateValidOrRestoring())
|
||||
{
|
||||
RemoveNeighbor(mChildren[i]);
|
||||
}
|
||||
RemoveNeighbor(*iter.GetChild());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4910,13 +4750,7 @@ bool MleRouter::HasSmallNumberOfChildren(void)
|
||||
|
||||
VerifyOrExit(routerCount > mRouterDowngradeThreshold);
|
||||
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
{
|
||||
if (mChildren[i].GetState() == Neighbor::kStateValid)
|
||||
{
|
||||
numChildren++;
|
||||
}
|
||||
}
|
||||
numChildren = GetChildTable().GetNumChildren(ChildTable::kInStateValid);
|
||||
|
||||
return numChildren < (routerCount - mRouterDowngradeThreshold) * 3;
|
||||
|
||||
@@ -4976,21 +4810,18 @@ otError MleRouter::GetMaxChildTimeout(uint32_t &aTimeout) const
|
||||
|
||||
VerifyOrExit(mRole == OT_DEVICE_ROLE_ROUTER || mRole == OT_DEVICE_ROLE_LEADER, error = OT_ERROR_INVALID_STATE);
|
||||
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (mChildren[i].GetState() != Neighbor::kStateValid)
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
if (child.IsFullThreadDevice())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mChildren[i].IsFullThreadDevice())
|
||||
if (child.GetTimeout() > aTimeout)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mChildren[i].GetTimeout() > aTimeout)
|
||||
{
|
||||
aTimeout = mChildren[i].GetTimeout();
|
||||
aTimeout = child.GetTimeout();
|
||||
}
|
||||
|
||||
error = OT_ERROR_NONE;
|
||||
@@ -5029,14 +4860,16 @@ bool MleRouter::HasSleepyChildrenSubscribed(const Ip6::Address &aAddress)
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
for (uint8_t i = 0; i < mMaxChildrenAllowed; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (!mChildren[i].IsStateValidOrRestoring() || mChildren[i].IsRxOnWhenIdle())
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
if (child.IsRxOnWhenIdle())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsSleepyChildSubscribed(aAddress, mChildren[i]))
|
||||
if (IsSleepyChildSubscribed(aAddress, child))
|
||||
{
|
||||
ExitNow(rval = true);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "meshcop/meshcop_tlvs.hpp"
|
||||
#include "net/icmp6.hpp"
|
||||
#include "net/udp6.hpp"
|
||||
#include "thread/child_table.hpp"
|
||||
#include "thread/mle.hpp"
|
||||
#include "thread/mle_tlvs.hpp"
|
||||
#include "thread/thread_tlvs.hpp"
|
||||
@@ -378,66 +379,12 @@ public:
|
||||
otError RemoveNeighbor(Neighbor &aNeighbor);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to a Child object.
|
||||
* This method gets the `ChildTable` object.
|
||||
*
|
||||
* @param[in] aAddress The address of the Child.
|
||||
*
|
||||
* @returns A pointer to the Child object.
|
||||
* @returns A reference to the `ChildTable`.
|
||||
*
|
||||
*/
|
||||
Child *GetChild(uint16_t aAddress);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to a Child object.
|
||||
*
|
||||
* @param[in] aAddress A reference to the address of the Child.
|
||||
*
|
||||
* @returns A pointer to the Child object.
|
||||
*
|
||||
*/
|
||||
Child *GetChild(const Mac::ExtAddress &aAddress);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to a Child object.
|
||||
*
|
||||
* @param[in] aAddress A reference to the address of the Child.
|
||||
*
|
||||
* @returns A pointer to the Child corresponding to @p aAddress, NULL otherwise.
|
||||
*
|
||||
*/
|
||||
Child *GetChild(const Mac::Address &aAddress);
|
||||
|
||||
/**
|
||||
* This method returns a child index for the Child object.
|
||||
*
|
||||
* @param[in] aChild A reference to the Child object.
|
||||
*
|
||||
* @returns The index for the Child corresponding to @p aChild.
|
||||
*
|
||||
*/
|
||||
uint8_t GetChildIndex(const Child &aChild);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to a Child array.
|
||||
*
|
||||
* @param[out] aNumChildren A pointer to output the number of children.
|
||||
*
|
||||
* @returns A pointer to the Child array.
|
||||
*
|
||||
*/
|
||||
Child *GetChildren(uint8_t *aNumChildren);
|
||||
|
||||
/**
|
||||
* This method sets the max children allowed value for this Thread interface.
|
||||
*
|
||||
* @param[in] aMaxChildren The max children allowed value.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully set the max.
|
||||
* @retval OT_ERROR_INVALID_ARGS If @p aMaxChildren is not in the range [1, kMaxChildren].
|
||||
* @retval OT_ERROR_INVALID_STATE If MLE has already been started.
|
||||
*
|
||||
*/
|
||||
otError SetMaxAllowedChildren(uint8_t aMaxChildren);
|
||||
ChildTable &GetChildTable(void) { return mChildTable; }
|
||||
|
||||
/**
|
||||
* This method restores children information from non-volatile memory.
|
||||
@@ -882,10 +829,6 @@ private:
|
||||
|
||||
void HandlePartitionChange(void);
|
||||
|
||||
Child *NewChild(void);
|
||||
Child *FindChild(uint16_t aChildId);
|
||||
Child *FindChild(const Mac::ExtAddress &aMacAddr);
|
||||
|
||||
void SetChildStateToValid(Child &aChild);
|
||||
bool HasChildren(void);
|
||||
void RemoveChildren(void);
|
||||
@@ -914,8 +857,8 @@ private:
|
||||
uint8_t mRouterIdSequence;
|
||||
uint32_t mRouterIdSequenceLastUpdated;
|
||||
Router mRouters[kMaxRouterId + 1];
|
||||
uint8_t mMaxChildrenAllowed;
|
||||
Child mChildren[kMaxChildren];
|
||||
|
||||
ChildTable mChildTable;
|
||||
|
||||
otThreadChildTableCallback mChildTableChangedCallback;
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "utils/wrap_string.h"
|
||||
|
||||
#include "thread/child_table.hpp"
|
||||
#include "thread/mle.hpp"
|
||||
#include "thread/mle_tlvs.hpp"
|
||||
#include "thread/thread_tlvs.hpp"
|
||||
@@ -52,6 +53,7 @@ class MleRouter : public Mle
|
||||
public:
|
||||
explicit MleRouter(Instance &aInstance)
|
||||
: Mle(aInstance)
|
||||
, mChildTable(aInstance)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -83,21 +85,7 @@ public:
|
||||
otError RemoveNeighbor(const Mac::Address &) { return BecomeDetached(); }
|
||||
otError RemoveNeighbor(Neighbor &) { return BecomeDetached(); }
|
||||
|
||||
Child *GetChild(uint16_t) { return NULL; }
|
||||
Child *GetChild(const Mac::ExtAddress &) { return NULL; }
|
||||
Child *GetChild(const Mac::Address &) { return NULL; }
|
||||
|
||||
uint8_t GetChildIndex(const Child &) { return 0; }
|
||||
|
||||
Child *GetChildren(uint8_t *aNumChildren)
|
||||
{
|
||||
if (aNumChildren != NULL)
|
||||
{
|
||||
*aNumChildren = 0;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
ChildTable &GetChildTable(void) { return mChildTable; }
|
||||
|
||||
bool IsMinimalChild(uint16_t) { return false; }
|
||||
|
||||
@@ -176,6 +164,8 @@ private:
|
||||
OT_UNUSED_VARIABLE(aRoute);
|
||||
return OT_ERROR_NONE;
|
||||
}
|
||||
|
||||
ChildTable mChildTable;
|
||||
};
|
||||
|
||||
} // namespace Mle
|
||||
|
||||
@@ -235,43 +235,33 @@ otError NetworkDiagnostic::AppendChildTable(Message &aMessage)
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint8_t count = 0;
|
||||
uint8_t timeout = 0;
|
||||
uint8_t numChildren;
|
||||
const Child * children = netif.GetMle().GetChildren(&numChildren);
|
||||
ChildTableTlv tlv;
|
||||
ChildTableEntry entry;
|
||||
|
||||
tlv.Init();
|
||||
|
||||
for (int i = 0; i < numChildren; i++)
|
||||
{
|
||||
if (children[i].GetState() == Neighbor::kStateValid)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
count = netif.GetMle().GetChildTable().GetNumChildren(ChildTable::kInStateValid);
|
||||
tlv.SetLength(count * sizeof(ChildTableEntry));
|
||||
|
||||
SuccessOrExit(error = aMessage.Append(&tlv, sizeof(ChildTableTlv)));
|
||||
|
||||
for (int i = 0; i < numChildren; i++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (children[i].GetState() == Neighbor::kStateValid)
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
timeout = 0;
|
||||
|
||||
while (static_cast<uint32_t>(1 << timeout) < child.GetTimeout())
|
||||
{
|
||||
timeout = 0;
|
||||
|
||||
while (static_cast<uint32_t>(1 << timeout) < children[i].GetTimeout())
|
||||
{
|
||||
timeout++;
|
||||
}
|
||||
|
||||
entry.SetReserved(0);
|
||||
entry.SetTimeout(timeout + 4);
|
||||
entry.SetChildId(netif.GetMle().GetChildId(children[i].GetRloc16()));
|
||||
entry.SetMode(children[i].GetDeviceMode());
|
||||
|
||||
SuccessOrExit(error = aMessage.Append(&entry, sizeof(ChildTableEntry)));
|
||||
timeout++;
|
||||
}
|
||||
|
||||
entry.SetReserved(0);
|
||||
entry.SetTimeout(timeout + 4);
|
||||
entry.SetChildId(netif.GetMle().GetChildId(child.GetRloc16()));
|
||||
entry.SetMode(child.GetDeviceMode());
|
||||
|
||||
SuccessOrExit(error = aMessage.Append(&entry, sizeof(ChildTableEntry)));
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
@@ -218,17 +218,13 @@ exit:
|
||||
otError SourceMatchController::AddPendingEntries(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint8_t numChildren;
|
||||
Child * child;
|
||||
|
||||
child = GetNetif().GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValidOrRestoring); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (child->IsStateValidOrRestoring() && child->IsIndirectSourceMatchPending())
|
||||
if (iter.GetChild()->IsIndirectSourceMatchPending())
|
||||
{
|
||||
SuccessOrExit(error = AddAddress(*child));
|
||||
child->SetIndirectSourceMatchPending(false);
|
||||
SuccessOrExit(error = AddAddress(*iter.GetChild()));
|
||||
iter.GetChild()->SetIndirectSourceMatchPending(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,29 @@ void Neighbor::GenerateChallenge(void)
|
||||
Random::FillBuffer(mValidPending.mPending.mChallenge, sizeof(mValidPending.mPending.mChallenge));
|
||||
}
|
||||
|
||||
bool Child::IsStateValidOrAttaching(void) const
|
||||
{
|
||||
bool rval = false;
|
||||
|
||||
switch (GetState())
|
||||
{
|
||||
case kStateInvalid:
|
||||
case kStateParentRequest:
|
||||
case kStateParentResponse:
|
||||
break;
|
||||
|
||||
case kStateRestored:
|
||||
case kStateChildIdRequest:
|
||||
case kStateLinkRequest:
|
||||
case kStateChildUpdateRequest:
|
||||
case kStateValid:
|
||||
rval = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
void Child::ClearIp6Addresses(void)
|
||||
{
|
||||
memset(mMeshLocalIid, 0, sizeof(mMeshLocalIid));
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
void SetState(State aState) { mState = static_cast<uint8_t>(aState); }
|
||||
|
||||
/**
|
||||
* Check if the neighbor/child is being restored.
|
||||
* This method indicates whether the neighbor/child is being restored.
|
||||
*
|
||||
* @returns TRUE if the neighbor is being restored, FALSE otherwise.
|
||||
*
|
||||
@@ -95,7 +95,8 @@ public:
|
||||
bool IsStateRestoring(void) const { return (mState == kStateRestored) || (mState == kStateChildUpdateRequest); }
|
||||
|
||||
/**
|
||||
* Check if the neighbor/child is in valid state or if it is being restored.
|
||||
* This method indicates whether the neighbor/child is in valid state or if it is being restored.
|
||||
*
|
||||
* When in these states messages can be sent to and/or received from the neighbor/child.
|
||||
*
|
||||
* @returns TRUE if the neighbor is in valid, restored, or being restored states, FALSE otherwise.
|
||||
@@ -414,6 +415,17 @@ public:
|
||||
otChildIp6AddressIterator mIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method indicates if the child state is valid or being attached or being restored.
|
||||
*
|
||||
* The states `kStateRestored`, `kStateChildIdRequest`, `kStateChildUpdateRequest`, `kStateValid`, (and
|
||||
* `kStateLinkRequest) are considered as attached or being restored.
|
||||
*
|
||||
* @returns TRUE if the child is attached or being restored.
|
||||
*
|
||||
*/
|
||||
bool IsStateValidOrAttaching(void) const;
|
||||
|
||||
/**
|
||||
* This method clears the IPv6 address list for the child.
|
||||
*
|
||||
|
||||
@@ -68,14 +68,11 @@ Child *ChildSupervisor::GetDestination(const Message &aMessage) const
|
||||
{
|
||||
Child * child = NULL;
|
||||
uint8_t childIndex;
|
||||
uint8_t numChildren;
|
||||
|
||||
VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision);
|
||||
|
||||
aMessage.Read(0, sizeof(childIndex), &childIndex);
|
||||
child = GetNetif().GetMle().GetChildren(&numChildren);
|
||||
VerifyOrExit(childIndex < numChildren, child = NULL);
|
||||
child += childIndex;
|
||||
child = GetNetif().GetMle().GetChildTable().GetChildAtIndex(childIndex);
|
||||
|
||||
exit:
|
||||
return child;
|
||||
@@ -97,7 +94,7 @@ void ChildSupervisor::SendMessage(Child &aChild)
|
||||
// the destination of the message to be later retrieved using
|
||||
// `ChildSupervisor::GetDestination(message)`.
|
||||
|
||||
childIndex = netif.GetMle().GetChildIndex(aChild);
|
||||
childIndex = netif.GetMle().GetChildTable().GetChildIndex(aChild);
|
||||
SuccessOrExit(message->Append(&childIndex, sizeof(childIndex)));
|
||||
|
||||
SuccessOrExit(netif.SendMessage(*message));
|
||||
@@ -125,25 +122,17 @@ void ChildSupervisor::HandleTimer(Timer &aTimer)
|
||||
|
||||
void ChildSupervisor::HandleTimer(void)
|
||||
{
|
||||
Child * child;
|
||||
uint8_t numChildren;
|
||||
|
||||
VerifyOrExit(mSupervisionInterval != 0);
|
||||
|
||||
child = GetNetif().GetMle().GetChildren(&numChildren);
|
||||
|
||||
for (uint8_t i = 0; i < numChildren; i++, child++)
|
||||
for (ChildTable::Iterator iter(GetInstance(), ChildTable::kInStateValid); !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
if (child->GetState() != Child::kStateValid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Child &child = *iter.GetChild();
|
||||
|
||||
child->IncrementSecondsSinceLastSupervision();
|
||||
child.IncrementSecondsSinceLastSupervision();
|
||||
|
||||
if ((child->GetSecondsSinceLastSupervision() >= mSupervisionInterval) && (child->IsRxOnWhenIdle() == false))
|
||||
if ((child.GetSecondsSinceLastSupervision() >= mSupervisionInterval) && (child.IsRxOnWhenIdle() == false))
|
||||
{
|
||||
SendMessage(*child);
|
||||
SendMessage(child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,23 +144,15 @@ exit:
|
||||
|
||||
void ChildSupervisor::CheckState(void)
|
||||
{
|
||||
bool shouldRun = false;
|
||||
uint8_t numChildren = 0;
|
||||
Mle::MleRouter &mle = GetInstance().Get<Mle::MleRouter>();
|
||||
bool shouldRun = false;
|
||||
Mle::MleRouter &mle = GetInstance().Get<Mle::MleRouter>();
|
||||
|
||||
// Child Supervision should run if `mSupervisionInterval` is not
|
||||
// zero, Thread MLE operation is enabled, and there is at least one
|
||||
// "valid" child in the child table.
|
||||
|
||||
VerifyOrExit(mSupervisionInterval != 0);
|
||||
VerifyOrExit(mle.GetRole() != OT_DEVICE_ROLE_DISABLED);
|
||||
|
||||
for (Child *child = mle.GetChildren(&numChildren); numChildren > 0; child++, numChildren--)
|
||||
{
|
||||
VerifyOrExit(child->GetState() != Child::kStateValid, shouldRun = true);
|
||||
}
|
||||
|
||||
exit:
|
||||
shouldRun = ((mSupervisionInterval != 0) && (mle.GetRole() != OT_DEVICE_ROLE_DISABLED) &&
|
||||
mle.GetChildTable().HasChildren(ChildTable::kInStateValid));
|
||||
|
||||
if (shouldRun && !mTimer.IsRunning())
|
||||
{
|
||||
|
||||
@@ -85,6 +85,7 @@ endif # OPENTHREAD_ENABLE_BUILTIN_MBEDTLS
|
||||
check_PROGRAMS = \
|
||||
test-aes \
|
||||
test-child \
|
||||
test-child-table \
|
||||
test-heap \
|
||||
test-hmac-sha256 \
|
||||
test-lowpan \
|
||||
@@ -147,6 +148,9 @@ test_aes_SOURCES = test_platform.cpp test_aes.cpp
|
||||
test_child_LDADD = $(COMMON_LDADD)
|
||||
test_child_SOURCES = test_platform.cpp test_child.cpp
|
||||
|
||||
test_child_table_LDADD = $(COMMON_LDADD)
|
||||
test_child_table_SOURCES = test_platform.cpp test_child_table.cpp
|
||||
|
||||
test_heap_LDADD = $(COMMON_LDADD)
|
||||
test_heap_SOURCES = test_platform.cpp test_heap.cpp
|
||||
|
||||
@@ -212,6 +216,7 @@ PRETTY_FILES = \
|
||||
$(test_address_sanitizer_SOURCES) \
|
||||
$(test_aes_SOURCES) \
|
||||
$(test_child_SOURCES) \
|
||||
$(test_child_table_SOURCES) \
|
||||
$(test_heap_SOURCES) \
|
||||
$(test_hmac_sha256_SOURCES) \
|
||||
$(test_link_quality_SOURCES) \
|
||||
|
||||
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*/
|
||||
|
||||
#include "test_platform.h"
|
||||
|
||||
#include <openthread/config.h>
|
||||
#include <openthread/openthread.h>
|
||||
|
||||
#include "test_util.h"
|
||||
#include "common/instance.hpp"
|
||||
#include "thread/child_table.hpp"
|
||||
|
||||
namespace ot {
|
||||
|
||||
static ot::Instance *sInstance;
|
||||
|
||||
enum
|
||||
{
|
||||
kMaxChildren = OPENTHREAD_CONFIG_MAX_CHILDREN,
|
||||
};
|
||||
|
||||
struct TestChild
|
||||
{
|
||||
Child::State mState;
|
||||
uint16_t mRloc16;
|
||||
otExtAddress mExtAddress;
|
||||
};
|
||||
|
||||
const ChildTable::StateFilter kAllFilters[] = {
|
||||
ChildTable::kInStateValid,
|
||||
ChildTable::kInStateValidOrRestoring,
|
||||
ChildTable::kInStateChildIdRequest,
|
||||
ChildTable::kInStateValidOrAttaching,
|
||||
ChildTable::kInStateAnyExceptInvalid,
|
||||
};
|
||||
|
||||
// Checks whether a `Child` matches the `TestChild` struct.
|
||||
static bool ChildMatches(const Child &aChild, const TestChild &aTestChild)
|
||||
{
|
||||
return (aChild.GetState() == aTestChild.mState) && (aChild.GetRloc16() == aTestChild.mRloc16) &&
|
||||
(aChild.GetExtAddress() == static_cast<const Mac::ExtAddress &>(aTestChild.mExtAddress));
|
||||
}
|
||||
|
||||
// Checks whether a `Child::State` matches a `ChildTable::StateFilter`.
|
||||
static bool StateMatchesFilter(Child::State aState, ChildTable::StateFilter aFilter)
|
||||
{
|
||||
bool rval = false;
|
||||
Child child;
|
||||
|
||||
child.SetState(aState);
|
||||
|
||||
switch (aFilter)
|
||||
{
|
||||
case ChildTable::kInStateAnyExceptInvalid:
|
||||
rval = (aState != Child::kStateInvalid);
|
||||
break;
|
||||
|
||||
case ChildTable::kInStateValid:
|
||||
rval = (aState == Child::kStateValid);
|
||||
break;
|
||||
|
||||
case ChildTable::kInStateValidOrRestoring:
|
||||
rval = child.IsStateValidOrRestoring();
|
||||
break;
|
||||
|
||||
case ChildTable::kInStateChildIdRequest:
|
||||
rval = (aState == Child::kStateChildIdRequest);
|
||||
break;
|
||||
|
||||
case ChildTable::kInStateValidOrAttaching:
|
||||
rval = child.IsStateValidOrAttaching();
|
||||
break;
|
||||
|
||||
case ChildTable::kInStateAnyExceptValidOrRestoing:
|
||||
rval = !child.IsStateValidOrRestoring();
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Verifies that `ChildTable` contains a given list of `TestChild` entries.
|
||||
void VerifyChildTableContent(ChildTable &aTable, uint8_t aChildListLength, const TestChild *aChildList)
|
||||
{
|
||||
printf("Test ChildTable with %d entries", aChildListLength);
|
||||
|
||||
for (uint8_t k = 0; k < sizeof(kAllFilters) / sizeof(kAllFilters[0]); k++)
|
||||
{
|
||||
ChildTable::StateFilter filter = kAllFilters[k];
|
||||
|
||||
// Verify that we can find all children from given list by rloc or extended address.
|
||||
|
||||
for (uint8_t listIndex = 0; listIndex < aChildListLength; listIndex++)
|
||||
{
|
||||
Child * child;
|
||||
Mac::Address address;
|
||||
|
||||
if (!StateMatchesFilter(aChildList[listIndex].mState, filter))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
child = aTable.FindChild(aChildList[listIndex].mRloc16, filter);
|
||||
VerifyOrQuit(child != NULL, "FindChild(rloc) failed");
|
||||
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(rloc) returned incorrect child");
|
||||
|
||||
child = aTable.FindChild(static_cast<const Mac::ExtAddress &>(aChildList[listIndex].mExtAddress), filter);
|
||||
VerifyOrQuit(child != NULL, "FindChild(ExtAddress) failed");
|
||||
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(ExtAddress) returned incorrect child");
|
||||
|
||||
address.SetShort(aChildList[listIndex].mRloc16);
|
||||
child = aTable.FindChild(address, filter);
|
||||
VerifyOrQuit(child != NULL, "FindChild(address) failed");
|
||||
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child");
|
||||
|
||||
address.SetExtended(static_cast<const Mac::ExtAddress &>(aChildList[listIndex].mExtAddress));
|
||||
child = aTable.FindChild(address, filter);
|
||||
VerifyOrQuit(child != NULL, "FindChild(address) failed");
|
||||
VerifyOrQuit(ChildMatches(*child, aChildList[listIndex]), "FindChild(address) returned incorrect child");
|
||||
}
|
||||
|
||||
// Verify `ChildTable::Iterator` behavior when starting from different child entries.
|
||||
|
||||
for (uint8_t listIndex = 0; listIndex <= aChildListLength; listIndex++)
|
||||
{
|
||||
Child *startingChild = NULL;
|
||||
|
||||
if (listIndex < aChildListLength)
|
||||
{
|
||||
startingChild = aTable.FindChild(aChildList[listIndex].mRloc16, ChildTable::kInStateAnyExceptInvalid);
|
||||
VerifyOrQuit(startingChild != NULL, "FindChild() failed");
|
||||
}
|
||||
|
||||
// Test an iterator starting from `startingChild`.
|
||||
|
||||
{
|
||||
ChildTable::Iterator iter(*sInstance, filter, startingChild);
|
||||
bool childObserved[kMaxChildren];
|
||||
uint8_t numChildren = 0;
|
||||
|
||||
memset(childObserved, 0, sizeof(childObserved));
|
||||
|
||||
// Check if the first entry matches the `startingChild`
|
||||
|
||||
if ((startingChild != NULL) && StateMatchesFilter(startingChild->GetState(), filter))
|
||||
{
|
||||
VerifyOrQuit(!iter.IsDone(), "iterator IsDone() failed");
|
||||
VerifyOrQuit(iter.GetChild() != NULL, "iterator GetChild() failed");
|
||||
VerifyOrQuit(iter.GetChild() == startingChild,
|
||||
"Iterator failed to start from the given child entry");
|
||||
|
||||
iter.Advance();
|
||||
iter.Reset();
|
||||
VerifyOrQuit(iter.GetChild() == startingChild, "iterator Reset() failed");
|
||||
}
|
||||
|
||||
// Use the iterator and verify that each returned `Child` entry is in the expected list.
|
||||
|
||||
for (; !iter.IsDone(); iter.Advance())
|
||||
{
|
||||
Child * child = iter.GetChild();
|
||||
bool didFind = false;
|
||||
uint8_t childIndex;
|
||||
|
||||
VerifyOrQuit(child != NULL, "iter.GetChild() failed");
|
||||
|
||||
childIndex = aTable.GetChildIndex(*child);
|
||||
VerifyOrQuit(childIndex < aTable.GetMaxChildrenAllowed(), "Child Index is out of bound");
|
||||
VerifyOrQuit(aTable.GetChildAtIndex(childIndex) == child, "GetChildAtIndex() failed");
|
||||
|
||||
for (uint8_t index = 0; index < aChildListLength; index++)
|
||||
{
|
||||
if (ChildMatches(*iter.GetChild(), aChildList[index]))
|
||||
{
|
||||
childObserved[index] = true;
|
||||
numChildren++;
|
||||
didFind = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrQuit(didFind, "ChildTable::Iterator returned an entry not in the expected list");
|
||||
}
|
||||
|
||||
// Verify that when iterator is done, it points to `NULL`.
|
||||
|
||||
VerifyOrQuit(iter.GetChild() == NULL, "iterator GetChild() failed");
|
||||
|
||||
iter.Advance();
|
||||
VerifyOrQuit(iter.IsDone(), "iterator Advance() (after iterator is done) failed");
|
||||
VerifyOrQuit(iter.GetChild() == NULL, "iterator GetChild() failed");
|
||||
|
||||
// Verify that the number of children matches the number of entries we get from iterator.
|
||||
|
||||
VerifyOrQuit(aTable.GetNumChildren(filter) == numChildren, "GetNumChildren() failed");
|
||||
VerifyOrQuit(aTable.HasChildren(filter) == (numChildren != 0), "HasChildren() failed");
|
||||
|
||||
// Verify that there is no missing or extra entry between the expected list
|
||||
// and what was observed/returned by the iterator.
|
||||
|
||||
for (uint8_t index = 0; index < aChildListLength; index++)
|
||||
{
|
||||
if (StateMatchesFilter(aChildList[index].mState, filter))
|
||||
{
|
||||
VerifyOrQuit(childObserved[index], "iterator failed to return an expected entry");
|
||||
}
|
||||
else
|
||||
{
|
||||
VerifyOrQuit(!childObserved[index], "iterator returned an extra unexpected entry");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf(" -- PASS\n");
|
||||
}
|
||||
|
||||
void TestChildTable(void)
|
||||
{
|
||||
const TestChild testChildList[] = {
|
||||
{
|
||||
Child::kStateValid,
|
||||
0x8001,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x16}},
|
||||
},
|
||||
{
|
||||
Child::kStateParentRequest,
|
||||
0x8002,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x17}},
|
||||
},
|
||||
{
|
||||
Child::kStateValid,
|
||||
0x8003,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x18}},
|
||||
},
|
||||
{
|
||||
Child::kStateValid,
|
||||
0x8004,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x19}},
|
||||
},
|
||||
{
|
||||
Child::kStateRestored,
|
||||
0x8005,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x20}},
|
||||
},
|
||||
{
|
||||
Child::kStateValid,
|
||||
0x8006,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x21}},
|
||||
},
|
||||
{
|
||||
Child::kStateChildIdRequest,
|
||||
0x8007,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x22}},
|
||||
},
|
||||
{
|
||||
Child::kStateChildUpdateRequest,
|
||||
0x8008,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x23}},
|
||||
},
|
||||
{
|
||||
Child::kStateParentResponse,
|
||||
0x8009,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x24}},
|
||||
},
|
||||
{
|
||||
Child::kStateRestored,
|
||||
0x800a,
|
||||
{{0x10, 0x20, 0x03, 0x15, 0x10, 0x00, 0x60, 0x25}},
|
||||
},
|
||||
};
|
||||
|
||||
const uint8_t testListLength = sizeof(testChildList) / sizeof(testChildList[0]);
|
||||
|
||||
uint8_t testNumAllowedChildren = 2;
|
||||
|
||||
ChildTable *table;
|
||||
otError error;
|
||||
|
||||
sInstance = testInitInstance();
|
||||
VerifyOrQuit(sInstance != NULL, "Null instance");
|
||||
|
||||
table = &sInstance->Get<ChildTable>();
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
printf("Checking initial state after child table is constructed");
|
||||
|
||||
VerifyOrQuit(table->GetMaxChildrenAllowed() == table->GetMaxChildren(),
|
||||
"GetMaxChildrenAllowed() initial value is incorrect ");
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(kAllFilters) / sizeof(kAllFilters[0]); i++)
|
||||
{
|
||||
ChildTable::StateFilter filter = kAllFilters[i];
|
||||
|
||||
VerifyOrQuit(table->HasChildren(filter) == false, "HasChildren() failed after init");
|
||||
VerifyOrQuit(table->GetNumChildren(filter) == 0, "GetNumChildren() failed after init");
|
||||
}
|
||||
|
||||
printf(" -- PASS\n");
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
VerifyChildTableContent(*table, 0, testChildList);
|
||||
|
||||
VerifyOrQuit(table->GetMaxChildrenAllowed() >= testListLength,
|
||||
"Default child table size is too small for the unit test");
|
||||
|
||||
// Add the child entries from test list one by one and verify the table content
|
||||
for (uint8_t i = 0; i < testListLength; i++)
|
||||
{
|
||||
Child *child;
|
||||
|
||||
child = table->GetNewChild();
|
||||
VerifyOrQuit(child != NULL, "GetNewChild() failed");
|
||||
|
||||
child->SetState(testChildList[i].mState);
|
||||
child->SetRloc16(testChildList[i].mRloc16);
|
||||
child->SetExtAddress((static_cast<const Mac::ExtAddress &>(testChildList[i].mExtAddress)));
|
||||
|
||||
VerifyChildTableContent(*table, i + 1, testChildList);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Verify Child Table clear
|
||||
|
||||
table->Clear();
|
||||
|
||||
VerifyChildTableContent(*table, 0, testChildList);
|
||||
|
||||
// Add the child entries from test list in reverse order and verify the table content
|
||||
for (uint8_t i = testListLength; i > 0; i--)
|
||||
{
|
||||
Child *child;
|
||||
|
||||
child = table->GetNewChild();
|
||||
VerifyOrQuit(child != NULL, "GetNewChild() failed");
|
||||
|
||||
child->SetState(testChildList[i - 1].mState);
|
||||
child->SetRloc16(testChildList[i - 1].mRloc16);
|
||||
child->SetExtAddress((static_cast<const Mac::ExtAddress &>(testChildList[i - 1].mExtAddress)));
|
||||
|
||||
VerifyChildTableContent(*table, testListLength - i + 1, &testChildList[i - 1]);
|
||||
}
|
||||
|
||||
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
printf("Test Get/SetMaxChildrenAllowed");
|
||||
|
||||
error = table->SetMaxChildrenAllowed(kMaxChildren - 1);
|
||||
VerifyOrQuit(error == OT_ERROR_INVALID_STATE, "SetMaxChildrenAllowed() should fail when table is not empty");
|
||||
|
||||
table->Clear();
|
||||
error = table->SetMaxChildrenAllowed(kMaxChildren + 1);
|
||||
VerifyOrQuit(error == OT_ERROR_INVALID_ARGS, "SetMaxChildrenAllowed() did not fail with an invalid arg");
|
||||
|
||||
error = table->SetMaxChildrenAllowed(0);
|
||||
VerifyOrQuit(error == OT_ERROR_INVALID_ARGS, "SetMaxChildrenAllowed() did not fail with an invalid arg");
|
||||
|
||||
error = table->SetMaxChildrenAllowed(testNumAllowedChildren);
|
||||
VerifyOrQuit(error == OT_ERROR_NONE, "SetMaxChildrenAllowed() failed");
|
||||
VerifyOrQuit(table->GetMaxChildrenAllowed() == testNumAllowedChildren, "GetMaxChildrenAllowed() failed");
|
||||
|
||||
for (uint8_t num = 0; num < testNumAllowedChildren; num++)
|
||||
{
|
||||
Child *child = table->GetNewChild();
|
||||
|
||||
VerifyOrQuit(child != NULL, "GetNewChild() failed");
|
||||
child->SetState(Child::kStateValid);
|
||||
}
|
||||
|
||||
VerifyOrQuit(table->GetNewChild() == NULL, "GetNewChild() did not fail when table was full");
|
||||
|
||||
printf(" -- PASS\n");
|
||||
|
||||
testFreeInstance(sInstance);
|
||||
}
|
||||
|
||||
} // namespace ot
|
||||
|
||||
#ifdef ENABLE_TEST_MAIN
|
||||
int main(void)
|
||||
{
|
||||
ot::TestChildTable();
|
||||
printf("\nAll tests passed.\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user