mirror of
https://github.com/espressif/openthread.git
synced 2026-08-11 05:07:47 +00:00
[child-table] emit child entry inserted/removed from NCP (#2280)
This commit adds new public OpenThread API to set a callback for user to be notified when a child table entry is being added or removed. This callback provides info about the child entry and is always invoked before the `otStateChangedCallback`. This commit also updates the `NcpBase` implementation to use the new callback and emit a `VALUE_INSERTED` or `VALUE_REMOVED` async spinel frame to host whenever child table gets updated. If the async spinel frame can not be sent (running out of NCP buffer space), an async `LAST_STATUS(NOMEM)` spinel frame is sent and the entire child table is sent later (when buffer becomes available) as an async `VALUE_IS` spinel message.
This commit is contained in:
committed by
Jonathan Hui
parent
50942427de
commit
7392200e9e
@@ -465,6 +465,46 @@ OTAPI int8_t OTCALL otThreadGetParentPriority(otInstance *aInstance);
|
||||
*/
|
||||
OTAPI otError OTCALL otThreadSetParentPriority(otInstance *aInstance, int8_t aParentPriority);
|
||||
|
||||
/**
|
||||
* This enumeration defines the constants used in `otThreadChildTableCallback` to indicate whether a child is added or
|
||||
* removed.
|
||||
*
|
||||
*/
|
||||
typedef enum otThreadChildTableEvent
|
||||
{
|
||||
OT_THREAD_CHILD_TABLE_EVENT_CHILD_ADDED, ///< A child is being added.
|
||||
OT_THREAD_CHILD_TABLE_EVENT_CHILD_REMOVED, ///< A child is being removed.
|
||||
} otThreadChildTableEvent;
|
||||
|
||||
/**
|
||||
* This function pointer is called to notify that a child is being added to or removed from child table.
|
||||
*
|
||||
* @param[in] aEvent A event flag indicating whether a child is being added or removed.
|
||||
* @param[in] aChildInfo A pointer to child information structure.
|
||||
*
|
||||
*/
|
||||
typedef void (*otThreadChildTableCallback)(otThreadChildTableEvent aEvent, const otChildInfo *aChildInfo);
|
||||
|
||||
/**
|
||||
* This function gets the child table callback function.
|
||||
*
|
||||
* @returns The callback function pointer.
|
||||
*
|
||||
*/
|
||||
otThreadChildTableCallback otThreadGetChildTableCallback(otInstance *aInstance);
|
||||
|
||||
/**
|
||||
* This function sets the child table callback function.
|
||||
*
|
||||
* The provided callback (if non-NULL) will be invoked when a child entry is being added/removed to/from the child
|
||||
* table. Subsequent calls to this method will overwrite the previous callback. Note that this callback in invoked
|
||||
* while the child table is being updated and always before the `otStateChangedCallback`.
|
||||
*
|
||||
* @param[in] aCallback A pointer to callback handler function.
|
||||
*
|
||||
*/
|
||||
void otThreadSetChildTableCallback(otInstance *aInstance, otThreadChildTableCallback aCallback);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -289,4 +289,14 @@ otError otThreadSetParentPriority(otInstance *aInstance, const int8_t aParentPri
|
||||
return aInstance->mThreadNetif.GetMle().SetAssignParentPriority(aParentPriority);
|
||||
}
|
||||
|
||||
otThreadChildTableCallback otThreadGetChildTableCallback(otInstance *aInstance)
|
||||
{
|
||||
return aInstance->mThreadNetif.GetMle().GetChildTableChangedCallback();
|
||||
}
|
||||
|
||||
void otThreadSetChildTableCallback(otInstance *aInstance, otThreadChildTableCallback aCallback)
|
||||
{
|
||||
aInstance->mThreadNetif.GetMle().SetChildTableChangedCallback(aCallback);
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_FTD
|
||||
|
||||
@@ -65,6 +65,7 @@ MleRouter::MleRouter(otInstance &aInstance):
|
||||
mRouterIdSequence(0),
|
||||
mRouterIdSequenceLastUpdated(0),
|
||||
mMaxChildrenAllowed(kMaxChildren),
|
||||
mChildTableChangedCallback(NULL),
|
||||
mChallengeTimeout(0),
|
||||
mNextChildId(kMaxChildId),
|
||||
mNetworkIdTimeout(kNetworkIdTimeout),
|
||||
@@ -3228,9 +3229,9 @@ otError MleRouter::RemoveNeighbor(Neighbor &aNeighbor)
|
||||
case OT_DEVICE_ROLE_LEADER:
|
||||
if (aNeighbor.IsStateValidOrRestoring() && !IsActiveRouter(aNeighbor.GetRloc16()))
|
||||
{
|
||||
SignalChildUpdated(OT_THREAD_CHILD_TABLE_EVENT_CHILD_REMOVED, static_cast<Child &>(aNeighbor));
|
||||
aNeighbor.SetState(Neighbor::kStateInvalid);
|
||||
netif.GetMeshForwarder().UpdateIndirectMessages();
|
||||
netif.SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_REMOVED);
|
||||
netif.GetNetworkDataLeader().SendServerDataNotification(aNeighbor.GetRloc16());
|
||||
RemoveStoredChild(aNeighbor.GetRloc16());
|
||||
}
|
||||
@@ -4701,8 +4702,8 @@ void MleRouter::SetChildStateToValid(Child &aChild)
|
||||
VerifyOrExit(aChild.GetState() != Neighbor::kStateValid);
|
||||
|
||||
aChild.SetState(Neighbor::kStateValid);
|
||||
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_ADDED);
|
||||
StoreChild(aChild.GetRloc16());
|
||||
SignalChildUpdated(OT_THREAD_CHILD_TABLE_EVENT_CHILD_ADDED, aChild);
|
||||
|
||||
exit:
|
||||
return;
|
||||
@@ -4732,7 +4733,7 @@ void MleRouter::RemoveChildren(void)
|
||||
switch (mChildren[i].GetState())
|
||||
{
|
||||
case Neighbor::kStateValid:
|
||||
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_REMOVED);
|
||||
SignalChildUpdated(OT_THREAD_CHILD_TABLE_EVENT_CHILD_REMOVED, mChildren[i]);
|
||||
|
||||
// fall-through
|
||||
|
||||
@@ -4857,6 +4858,31 @@ exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void MleRouter::SignalChildUpdated(otThreadChildTableEvent aEvent, Child &aChild)
|
||||
{
|
||||
if (mChildTableChangedCallback != NULL)
|
||||
{
|
||||
otChildInfo childInfo;
|
||||
otError error;
|
||||
|
||||
error = GetChildInfo(aChild, childInfo);
|
||||
assert(error == OT_ERROR_NONE);
|
||||
|
||||
mChildTableChangedCallback(aEvent, &childInfo);
|
||||
}
|
||||
|
||||
switch (aEvent)
|
||||
{
|
||||
case OT_THREAD_CHILD_TABLE_EVENT_CHILD_ADDED:
|
||||
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_ADDED);
|
||||
break;
|
||||
|
||||
case OT_THREAD_CHILD_TABLE_EVENT_CHILD_REMOVED:
|
||||
GetNetif().SetStateChangedFlags(OT_CHANGED_THREAD_CHILD_REMOVED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Mle
|
||||
} // namespace ot
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
|
||||
#include "utils/wrap_string.h"
|
||||
|
||||
#include <openthread/thread_ftd.h>
|
||||
|
||||
#include "coap/coap.hpp"
|
||||
#include "coap/coap_header.hpp"
|
||||
#include "common/timer.hpp"
|
||||
@@ -709,6 +711,25 @@ public:
|
||||
*/
|
||||
otError GetMaxChildTimeout(uint32_t &aTimeout) const;
|
||||
|
||||
/**
|
||||
* This method sets the "child table changed" callback function.
|
||||
*
|
||||
* The provided callback (if non-NULL) will be invoked when a child entry is being added/remove to/from the child
|
||||
* table. Subsequent calls to this method will overwrite the previous callback.
|
||||
*
|
||||
* @param[in] aCallback A pointer to callback handler function.
|
||||
*
|
||||
*/
|
||||
void SetChildTableChangedCallback(otThreadChildTableCallback aCallback) { mChildTableChangedCallback = aCallback; }
|
||||
|
||||
/**
|
||||
* This method gets the "child table changed" callback function.
|
||||
*
|
||||
* @returns The callback function pointer.
|
||||
*
|
||||
*/
|
||||
otThreadChildTableCallback GetChildTableChangedCallback(void) const { return mChildTableChangedCallback; }
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
@@ -804,6 +825,8 @@ private:
|
||||
static void HandleStateUpdateTimer(Timer &aTimer);
|
||||
void HandleStateUpdateTimer(void);
|
||||
|
||||
void SignalChildUpdated(otThreadChildTableEvent aEvent, Child &aChild);
|
||||
|
||||
static MleRouter &GetOwner(const Context &aContext);
|
||||
|
||||
TrickleTimer mAdvertiseTimer;
|
||||
@@ -818,6 +841,8 @@ private:
|
||||
uint8_t mMaxChildrenAllowed;
|
||||
Child mChildren[kMaxChildren];
|
||||
|
||||
otThreadChildTableCallback mChildTableChangedCallback;
|
||||
|
||||
uint8_t mChallengeTimeout;
|
||||
uint8_t mChallenge[8];
|
||||
uint16_t mNextChildId;
|
||||
|
||||
@@ -528,6 +528,7 @@ NcpBase::NcpBase(otInstance *aInstance):
|
||||
mRequireJoinExistingNetwork(false),
|
||||
mIsRawStreamEnabled(false),
|
||||
mDisableStreamWrite(false),
|
||||
mShouldEmitChildTableUpdate(false),
|
||||
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
||||
mCurTransmitTID(0),
|
||||
mCurReceiveChannel(OPENTHREAD_CONFIG_DEFAULT_CHANNEL),
|
||||
@@ -559,6 +560,9 @@ NcpBase::NcpBase(otInstance *aInstance):
|
||||
otIp6SetReceiveFilterEnabled(mInstance, true);
|
||||
otLinkSetPcapCallback(mInstance, &NcpBase::HandleRawFrame, static_cast<void *>(this));
|
||||
otIcmp6SetEchoEnabled(mInstance, false);
|
||||
#if OPENTHREAD_FTD
|
||||
otThreadSetChildTableCallback(mInstance, &NcpBase::HandleChildTableChanged);
|
||||
#endif
|
||||
#if OPENTHREAD_ENABLE_LEGACY
|
||||
mLegacyNodeDidJoin = false;
|
||||
mLegacyHandlers = NULL;
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
#else
|
||||
#include <openthread/platform/radio.h>
|
||||
#endif
|
||||
#if OPENTHREAD_FTD
|
||||
#include <openthread/thread_ftd.h>
|
||||
#endif
|
||||
#include <openthread/message.h>
|
||||
#include <openthread/ncp.h>
|
||||
#include <openthread/types.h>
|
||||
@@ -216,6 +219,11 @@ private:
|
||||
static void HandleNetifStateChanged(uint32_t aFlags, void *aContext);
|
||||
void ProcessThreadChangedFlags(void);
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
static void HandleChildTableChanged(otThreadChildTableEvent aEvent, const otChildInfo *aChildInfo);
|
||||
void HandleChildTableChanged(otThreadChildTableEvent aEvent, const otChildInfo &aChildInfo);
|
||||
#endif
|
||||
|
||||
static void HandleDatagramFromStack(otMessage *aMessage, void *aContext);
|
||||
void HandleDatagramFromStack(otMessage *aMessage);
|
||||
|
||||
@@ -235,6 +243,11 @@ private:
|
||||
void SendDoneTask(void);
|
||||
|
||||
otError GetPropertyHandler_ChannelMaskHelper(uint32_t channel_mask);
|
||||
|
||||
#if OPENTHREAD_FTD
|
||||
otError EncodeChildInfo(const otChildInfo &aChildInfo);
|
||||
#endif
|
||||
|
||||
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
|
||||
|
||||
#if OPENTHREAD_FTD && OPENTHREAD_ENABLE_TMF_PROXY
|
||||
@@ -658,6 +671,7 @@ private:
|
||||
bool mRequireJoinExistingNetwork;
|
||||
bool mIsRawStreamEnabled;
|
||||
bool mDisableStreamWrite;
|
||||
bool mShouldEmitChildTableUpdate;
|
||||
|
||||
#if OPENTHREAD_ENABLE_RAW_LINK_API
|
||||
uint8_t mCurTransmitTID;
|
||||
|
||||
+83
-19
@@ -55,6 +55,88 @@
|
||||
namespace ot {
|
||||
namespace Ncp {
|
||||
|
||||
otError NcpBase::EncodeChildInfo(const otChildInfo &aChildInfo)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint8_t modeFlags;
|
||||
|
||||
modeFlags = LinkFlagsToFlagByte(
|
||||
aChildInfo.mRxOnWhenIdle,
|
||||
aChildInfo.mSecureDataRequest,
|
||||
aChildInfo.mFullFunction,
|
||||
aChildInfo.mFullNetworkData
|
||||
);
|
||||
|
||||
SuccessOrExit(error = mEncoder.WriteEui64(aChildInfo.mExtAddress));
|
||||
SuccessOrExit(error = mEncoder.WriteUint16(aChildInfo.mRloc16));
|
||||
SuccessOrExit(error = mEncoder.WriteUint32(aChildInfo.mTimeout));
|
||||
SuccessOrExit(error = mEncoder.WriteUint32(aChildInfo.mAge));
|
||||
SuccessOrExit(error = mEncoder.WriteUint8(aChildInfo.mNetworkDataVersion));
|
||||
SuccessOrExit(error = mEncoder.WriteUint8(aChildInfo.mLinkQualityIn));
|
||||
SuccessOrExit(error = mEncoder.WriteInt8(aChildInfo.mAverageRssi));
|
||||
SuccessOrExit(error = mEncoder.WriteUint8(modeFlags));
|
||||
SuccessOrExit(error = mEncoder.WriteInt8(aChildInfo.mLastRssi));
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// MARK: Property/Status Changed
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void NcpBase::HandleChildTableChanged(otThreadChildTableEvent aEvent, const otChildInfo *aChildInfo)
|
||||
{
|
||||
GetNcpInstance()->HandleChildTableChanged(aEvent, *aChildInfo);
|
||||
}
|
||||
|
||||
void NcpBase::HandleChildTableChanged(otThreadChildTableEvent aEvent, const otChildInfo &aChildInfo)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0;
|
||||
unsigned int command = 0;
|
||||
|
||||
VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(SPINEL_PROP_THREAD_CHILD_TABLE));
|
||||
|
||||
switch (aEvent)
|
||||
{
|
||||
case OT_THREAD_CHILD_TABLE_EVENT_CHILD_ADDED:
|
||||
command = SPINEL_CMD_PROP_VALUE_INSERTED;
|
||||
break;
|
||||
|
||||
case OT_THREAD_CHILD_TABLE_EVENT_CHILD_REMOVED:
|
||||
command = SPINEL_CMD_PROP_VALUE_REMOVED;
|
||||
break;
|
||||
|
||||
default:
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
SuccessOrExit(error = mEncoder.BeginFrame(header, command, SPINEL_PROP_THREAD_CHILD_TABLE));
|
||||
SuccessOrExit(error = EncodeChildInfo(aChildInfo));
|
||||
SuccessOrExit(error = mEncoder.EndFrame());
|
||||
|
||||
exit:
|
||||
|
||||
// If the frame can not be added (out of NCP buffer space), we remember
|
||||
// to send an async `LAST_STATUS(NOMEM)` when buffer space becomes
|
||||
// available. Also `mShouldEmitChildTableUpdate` flag is set to `true` so
|
||||
// that the entire child table is later emitted as `VALUE_IS` spinel frame
|
||||
// update from `ProcessThreadChangedFlags()`.
|
||||
|
||||
if (error != OT_ERROR_NONE)
|
||||
{
|
||||
mShouldEmitChildTableUpdate = true;
|
||||
|
||||
mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM);
|
||||
mUpdateChangedPropsTask.Post();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// MARK: Individual Property Handlers
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
otError NcpBase::GetPropertyHandler_THREAD_LOCAL_LEADER_WEIGHT(void)
|
||||
{
|
||||
return mEncoder.WriteUint8(otThreadGetLocalLeaderWeight(mInstance));
|
||||
@@ -70,7 +152,6 @@ otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(void)
|
||||
otError error = OT_ERROR_NONE;
|
||||
otChildInfo childInfo;
|
||||
uint8_t maxChildren;
|
||||
uint8_t modeFlags;
|
||||
|
||||
maxChildren = otThreadGetMaxAllowedChildren(mInstance);
|
||||
|
||||
@@ -81,25 +162,8 @@ otError NcpBase::GetPropertyHandler_THREAD_CHILD_TABLE(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
modeFlags = LinkFlagsToFlagByte(
|
||||
childInfo.mRxOnWhenIdle,
|
||||
childInfo.mSecureDataRequest,
|
||||
childInfo.mFullFunction,
|
||||
childInfo.mFullNetworkData
|
||||
);
|
||||
|
||||
SuccessOrExit(error = mEncoder.OpenStruct());
|
||||
|
||||
SuccessOrExit(error = mEncoder.WriteEui64(childInfo.mExtAddress));
|
||||
SuccessOrExit(error = mEncoder.WriteUint16(childInfo.mRloc16));
|
||||
SuccessOrExit(error = mEncoder.WriteUint32(childInfo.mTimeout));
|
||||
SuccessOrExit(error = mEncoder.WriteUint32(childInfo.mAge));
|
||||
SuccessOrExit(error = mEncoder.WriteUint8(childInfo.mNetworkDataVersion));
|
||||
SuccessOrExit(error = mEncoder.WriteUint8(childInfo.mLinkQualityIn));
|
||||
SuccessOrExit(error = mEncoder.WriteInt8(childInfo.mAverageRssi));
|
||||
SuccessOrExit(error = mEncoder.WriteUint8(modeFlags));
|
||||
SuccessOrExit(error = mEncoder.WriteInt8(childInfo.mLastRssi));
|
||||
|
||||
SuccessOrExit(error = EncodeChildInfo(childInfo));
|
||||
SuccessOrExit(error = mEncoder.CloseStruct());
|
||||
}
|
||||
|
||||
|
||||
@@ -2707,7 +2707,26 @@ void NcpBase::ProcessThreadChangedFlags(void)
|
||||
|
||||
if (mThreadChangedFlags & threadFlag)
|
||||
{
|
||||
mChangedPropsSet.AddProperty(kFlags[i].mPropKey);
|
||||
spinel_prop_key_t propKey = kFlags[i].mPropKey;
|
||||
bool shouldAddProperty = true;
|
||||
|
||||
// Child table changes are reported using the `HandleChildAdded()` and
|
||||
// `HandleChildRemoved()` callbacks emitting spinel `VALUE_INSERTED` and
|
||||
// `VALUE_REMOVED` async spinel frames. If the spinel frames could not be
|
||||
// added (e.g., out of NCP buffer) from the above callbacks, the flag
|
||||
// `mShouldEmitChildTableUpdate` is set to `true` so that the entire
|
||||
// child table is emitted as an unsolicited `VALUE_IS` update.
|
||||
|
||||
if (propKey == SPINEL_PROP_THREAD_CHILD_TABLE)
|
||||
{
|
||||
shouldAddProperty = mShouldEmitChildTableUpdate;
|
||||
mShouldEmitChildTableUpdate = false;
|
||||
}
|
||||
|
||||
if (shouldAddProperty)
|
||||
{
|
||||
mChangedPropsSet.AddProperty(propKey);
|
||||
}
|
||||
|
||||
if (threadFlag == OT_CHANGED_THREAD_NETDATA)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user