diff --git a/include/openthread-types.h b/include/openthread-types.h index f2bd481e1..9dca52b36 100644 --- a/include/openthread-types.h +++ b/include/openthread-types.h @@ -395,21 +395,41 @@ typedef enum } otDeviceRole; /** - * This structure holds diagnostics information for a Thread Router + * This structure holds diagnostic information for a Thread Child * */ typedef struct { - otExtAddress mExtAddress; ///< IEEE 802.15.4 Extended Address - uint16_t mRloc16; ///< RLOC16 - uint8_t mRouterId; ///< Router ID - uint8_t mNextHop; ///< Next hop to router - uint8_t mPathCost; ///< Path cost to router - uint8_t mLinkQualityIn; ///< Link Quality In - uint8_t mLinkQualityOut; ///< Link Quality Out - uint8_t mAge; ///< Time last heard - bool mAllocated : 1; ///< Router ID allocated or not - bool mLinkEstablished : 1; ///< Link established with Router ID or not + otExtAddress mExtAddress; ///< IEEE 802.15.4 Extended Address + uint32_t mTimeout; ///< Timeout + uint16_t mRloc16; ///< RLOC16 + uint8_t mChildId; ///< Router ID + uint8_t mNetworkDataVersion; ///< Network Data Version + uint8_t mAge; ///< Time last heard + uint8_t mLinkQualityIn; ///< Link Quality In + int8_t mAverageRssi; ///< Average RSSI + bool mRxOnWhenIdle : 1; ///< rx-on-when-idle + bool mSecureDataRequest : 1; ///< Secure Data Requests + bool mFullFunction : 1; ///< Full Function Device + bool mFullNetworkData : 1; ///< Full Network Data +} otChildInfo; + +/** + * This structure holds diagnostic information for a Thread Router + * + */ +typedef struct +{ + otExtAddress mExtAddress; ///< IEEE 802.15.4 Extended Address + uint16_t mRloc16; ///< RLOC16 + uint8_t mRouterId; ///< Router ID + uint8_t mNextHop; ///< Next hop to router + uint8_t mPathCost; ///< Path cost to router + uint8_t mLinkQualityIn; ///< Link Quality In + uint8_t mLinkQualityOut; ///< Link Quality Out + uint8_t mAge; ///< Time last heard + bool mAllocated : 1; ///< Router ID allocated or not + bool mLinkEstablished : 1; ///< Link established with Router ID or not } otRouterInfo; /** diff --git a/include/openthread.h b/include/openthread.h index 863d1b7f2..e2196373f 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -854,6 +854,24 @@ ThreadError otBecomeLeader(void); * */ +/** + * The function retains diagnostic information for an attached Child by its Child ID or RLOC16. + * + * @param[in] aChildId The Child ID or RLOC16 for the attached child. + * @param[out] aChildInfo A pointer to where the child information is placed. + * + */ +ThreadError otGetChildInfoById(uint16_t aChildId, otChildInfo *aChildInfo); + +/** + * The function retains diagnostic information for an attached Child by the internal table index. + * + * @param[in] aChildIndex The table index. + * @param[out] aChildInfo A pointer to where the child information is placed. + * + */ +ThreadError otGetChildInfoByIndex(uint8_t aChildIndex, otChildInfo *aChildInfo); + /** * Get the device role. * diff --git a/src/cli/README.md b/src/cli/README.md index a51a7e79d..45abe862b 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -8,6 +8,7 @@ OpenThread test scripts use the CLI to execute test cases. ## OpenThread Command List * [channel](#channel) +* [child](#child) * [childtimeout](#childtimeout) * [contextreusedelay](#contextreusedelay) * [counter](#counter) @@ -56,6 +57,34 @@ Set the IEEE 802.15.4 Channel value. Done ``` +### child list + +List attached Child IDs + +```bash +> child list +1 2 3 6 7 8 +Done +``` + +### child \ + +Print diagnostic information for an attached Thread Child. The `id` may be a Child ID or an RLOC16. + +```bash +> child 1 +Child ID: 1 +Rloc: 9c01 +Ext Addr: e2b3540590b0fd87 +Mode: rsn +Net Data: 184 +Timeout: 100 +Age: 0 +LQI: 3 +RSSI: -20 +Done +``` + ### childtimeout Get the Thread Child Timeout value. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index d96715d4a..dc93d83c4 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -51,6 +51,7 @@ const struct Command Interpreter::sCommands[] = { { "help", &ProcessHelp }, { "channel", &ProcessChannel }, + { "child", &ProcessChild }, { "childtimeout", &ProcessChildTimeout }, { "contextreusedelay", &ProcessContextIdReuseDelay }, { "counter", &ProcessCounters }, @@ -186,6 +187,78 @@ exit: AppendResult(error); } +void Interpreter::ProcessChild(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + otChildInfo childInfo; + long value; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + + if (strcmp(argv[0], "list") == 0) + { + for (int i = 0; ; i++) + { + if (otGetChildInfoByIndex(i, &childInfo) != kThreadError_None) + { + sServer->OutputFormat("\r\n"); + ExitNow(); + } + + if (childInfo.mTimeout > 0) + { + sServer->OutputFormat("%d ", childInfo.mChildId); + } + } + } + + SuccessOrExit(error = ParseLong(argv[0], value)); + SuccessOrExit(error = otGetChildInfoById(value, &childInfo)); + + sServer->OutputFormat("Child ID: %d\r\n", childInfo.mChildId); + sServer->OutputFormat("Rloc: %04x\r\n", childInfo.mRloc16); + sServer->OutputFormat("Ext Addr: "); + + for (int j = 0; j < sizeof(childInfo.mExtAddress); j++) + { + sServer->OutputFormat("%02x", childInfo.mExtAddress.m8[j]); + } + + sServer->OutputFormat("\r\n"); + sServer->OutputFormat("Mode: "); + + if (childInfo.mRxOnWhenIdle) + { + sServer->OutputFormat("r"); + } + + if (childInfo.mSecureDataRequest) + { + sServer->OutputFormat("s"); + } + + if (childInfo.mFullFunction) + { + sServer->OutputFormat("d"); + } + + if (childInfo.mFullNetworkData) + { + sServer->OutputFormat("n"); + } + + sServer->OutputFormat("\r\n"); + + sServer->OutputFormat("Net Data: %d\r\n", childInfo.mNetworkDataVersion); + sServer->OutputFormat("Timeout: %d\r\n", childInfo.mTimeout); + sServer->OutputFormat("Age: %d\r\n", childInfo.mAge); + sServer->OutputFormat("LQI: %d\r\n", childInfo.mLinkQualityIn); + sServer->OutputFormat("RSSI: %d\r\n", childInfo.mAverageRssi); + +exit: + AppendResult(error); +} + void Interpreter::ProcessChildTimeout(int argc, char *argv[]) { ThreadError error = kThreadError_None; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 825073ab8..43b3ce627 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -88,6 +88,7 @@ private: static void ProcessHelp(int argc, char *argv[]); static void ProcessChannel(int argc, char *argv[]); + static void ProcessChild(int argc, char *argv[]); static void ProcessChildTimeout(int argc, char *argv[]); static void ProcessContextIdReuseDelay(int argc, char *argv[]); static void ProcessCounters(int argc, char *argv[]); diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index 678b3182d..ce23da20a 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -454,6 +454,30 @@ ThreadError otBecomeLeader(void) return sThreadNetif->GetMle().BecomeLeader(); } +ThreadError otGetChildInfoById(uint16_t aChildId, otChildInfo *aChildInfo) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(aChildInfo != NULL, error = kThreadError_InvalidArgs); + + error = sThreadNetif->GetMle().GetChildInfoById(aChildId, *aChildInfo); + +exit: + return error; +} + +ThreadError otGetChildInfoByIndex(uint8_t aChildIndex, otChildInfo *aChildInfo) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(aChildInfo != NULL, error = kThreadError_InvalidArgs); + + error = sThreadNetif->GetMle().GetChildInfoByIndex(aChildIndex, *aChildInfo); + +exit: + return error; +} + otDeviceRole otGetDeviceRole(void) { otDeviceRole rval = kDeviceRoleDisabled; diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index 52f304989..4b487bfbc 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -976,6 +976,23 @@ Child *MleRouter::NewChild(void) return NULL; } +Child *MleRouter::FindChild(uint16_t aChildId) +{ + Child *rval = NULL; + + for (int i = 0; i < kMaxChildren; i++) + { + if (mChildren[i].mState != Neighbor::kStateInvalid && + GetChildId(mChildren[i].mValid.mRloc16) == aChildId) + { + ExitNow(rval = &mChildren[i]); + } + } + +exit: + return rval; +} + Child *MleRouter::FindChild(const Mac::ExtAddress &aAddress) { Child *rval = NULL; @@ -2252,6 +2269,56 @@ Router *MleRouter::GetRouters(uint8_t *aNumRouters) return mRouters; } +ThreadError MleRouter::GetChildInfoById(uint16_t aChildId, otChildInfo &aChildInfo) +{ + ThreadError error = kThreadError_None; + Child *child; + + if ((aChildId & ~kChildIdMask) != 0) + { + aChildId = GetChildId(aChildId); + } + + VerifyOrExit((child = FindChild(aChildId)) != NULL, error = kThreadError_NotFound); + GetChildInfo(*child, aChildInfo); + +exit: + return error; +} + +ThreadError MleRouter::GetChildInfoByIndex(uint8_t aChildIndex, otChildInfo &aChildInfo) +{ + ThreadError error = kThreadError_None; + + VerifyOrExit(aChildIndex < kMaxChildren, error = kThreadError_InvalidArgs); + GetChildInfo(mChildren[aChildIndex], aChildInfo); + +exit: + return error; +} + +void MleRouter::GetChildInfo(Child &aChild, otChildInfo &aChildInfo) +{ + memset(&aChildInfo, 0, sizeof(aChildInfo)); + + if (aChild.mState == Neighbor::kStateValid) + { + memcpy(&aChildInfo.mExtAddress, &aChild.mMacAddr, sizeof(aChildInfo.mExtAddress)); + aChildInfo.mTimeout = aChild.mTimeout; + aChildInfo.mRloc16 = aChild.mValid.mRloc16; + aChildInfo.mChildId = GetChildId(aChild.mValid.mRloc16); + aChildInfo.mNetworkDataVersion = aChild.mNetworkDataVersion; + aChildInfo.mAge = Timer::MsecToSec(Timer::GetNow() - aChild.mLastHeard); + aChildInfo.mLinkQualityIn = aChild.mLinkInfo.GetLinkQuality(); + aChildInfo.mAverageRssi = aChild.mLinkInfo.GetAverageRss(); + + aChildInfo.mRxOnWhenIdle = (aChild.mMode & ModeTlv::kModeRxOnWhenIdle) != 0; + aChildInfo.mSecureDataRequest = (aChild.mMode & ModeTlv::kModeSecureDataRequest) != 0; + aChildInfo.mFullFunction = (aChild.mMode & ModeTlv::kModeFFD) != 0; + aChildInfo.mFullNetworkData = (aChild.mMode & ModeTlv::kModeFullNetworkData) != 0; + } +} + ThreadError MleRouter::GetRouterInfo(uint16_t aRouterId, otRouterInfo &aRouterInfo) { ThreadError error = kThreadError_None; diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index dba9c269d..009d75c15 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -279,6 +279,24 @@ public: */ Neighbor *GetNeighbor(const Ip6::Address &aAddress); + /** + * This method retains diagnotsic information for an attached child by Child ID or RLOC16. + * + * @param[in] aChildId The Child ID or RLOC16 for an attached child. + * @param[out] aChildInfo The child information. + * + */ + ThreadError GetChildInfoById(uint16_t aChildId, otChildInfo &aChildInfo); + + /** + * This method retains diagnotsic information for an attached child by the internal table index. + * + * @param[in] aChildIndex The table index. + * @param[out] aChildInfo The child information. + * + */ + ThreadError GetChildInfoByIndex(uint8_t aChildIndex, otChildInfo &aChildInfo); + /** * This method returns a pointer to a Router array. * @@ -340,6 +358,7 @@ private: ThreadError AppendChildAddresses(Message &aMessage, Child &aChild); ThreadError AppendRoute(Message &aMessage); uint8_t GetLinkCost(uint8_t aRouterId); + void GetChildInfo(Child &aChild, otChildInfo &aChildInfo); ThreadError HandleDetachStart(void); ThreadError HandleChildStart(otMleAttachFilter aFilter); ThreadError HandleLinkRequest(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo); @@ -388,6 +407,7 @@ private: static uint8_t LqiToCost(uint8_t aLqi); Child *NewChild(void); + Child *FindChild(uint16_t aChildId); Child *FindChild(const Mac::ExtAddress &aMacAddr); int AllocateRouterId(void);