diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 84ccdb794..5371a06f7 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (47) +#define OPENTHREAD_API_VERSION (48) /** * @addtogroup api-instance diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index 7bad904ef..134936fa5 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -67,10 +67,13 @@ typedef struct int8_t mLastRssi; ///< Last observed RSSI uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature. uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature. + uint16_t mQueuedMessageCnt; ///< Number of queued messages for the child. + uint8_t mVersion; ///< MLE version bool mRxOnWhenIdle : 1; ///< rx-on-when-idle bool mFullThreadDevice : 1; ///< Full Thread Device bool mFullNetworkData : 1; ///< Full Network Data bool mIsStateRestoring : 1; ///< Is in restoring state + bool mIsCslSynced : 1; ///< Is child CSL synchronized } otChildInfo; #define OT_CHILD_IP6_ADDRESS_ITERATOR_INIT 0 ///< Initializer for otChildIP6AddressIterator diff --git a/src/cli/README.md b/src/cli/README.md index d0cd2fc0c..899811480 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -403,10 +403,10 @@ Print table of attached children. ```bash > child table -| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|S|D|N| Extended MAC | -+-----+--------+------------+------------+-------+------+-+-+-+-+------------------+ -| 1 | 0xe001 | 240 | 44 | 3 | 237 |1|1|1|1| d28d7f875888fccb | -| 2 | 0xe002 | 240 | 27 | 3 | 237 |0|1|0|1| e2b3540590b0fd87 | +| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC | ++-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+ +| 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 4ecede68435358ac | +| 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | a672a601d2ce37d8 | Done ``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index e9dfbdc7c..c86585eea 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -782,8 +782,10 @@ otError Interpreter::ProcessChild(uint8_t aArgsLength, char *aArgs[]) if (isTable) { - OutputLine("| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N| Extended MAC |"); - OutputLine("+-----+--------+------------+------------+-------+------+-+-+-+------------------+"); + OutputLine( + "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC |"); + OutputLine( + "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+"); } maxChildren = otThreadGetMaxAllowedChildren(mInstance); @@ -806,6 +808,9 @@ otError Interpreter::ProcessChild(uint8_t aArgsLength, char *aArgs[]) OutputFormat("|%1d", childInfo.mRxOnWhenIdle); OutputFormat("|%1d", childInfo.mFullThreadDevice); OutputFormat("|%1d", childInfo.mFullNetworkData); + OutputFormat("|%3d", childInfo.mVersion); + OutputFormat("| %1d ", childInfo.mIsCslSynced); + OutputFormat("| %5d ", childInfo.mQueuedMessageCnt); OutputFormat("| "); OutputExtAddress(childInfo.mExtAddress); OutputLine(" |"); diff --git a/src/core/mac/data_poll_handler.hpp b/src/core/mac/data_poll_handler.hpp index 5930e3471..730494917 100644 --- a/src/core/mac/data_poll_handler.hpp +++ b/src/core/mac/data_poll_handler.hpp @@ -105,7 +105,6 @@ public: void SetIndirectKeyId(uint8_t aKeyId) { mIndirectKeyId = aKeyId; } uint8_t GetIndirectTxAttempts(void) const { return mIndirectTxAttempts; } - void SetIndirectTxAttemptsToMax(void) { mIndirectTxAttempts = kMaxPollTriggeredTxAttempts; } void ResetIndirectTxAttempts(void) { mIndirectTxAttempts = 0; } void IncrementIndirectTxAttempts(void) { mIndirectTxAttempts++; } diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index c8893dc4e..7216f082d 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -97,7 +97,7 @@ void CslTxScheduler::Clear(void) { for (Child &child : Get().Iterate(Child::kInStateAnyExceptInvalid)) { - child.SetCslTxAttempts(0); + child.ResetCslTxAttempts(); child.SetCslSynchronized(false); child.SetCslChannel(0); child.SetCslTimeout(0); @@ -128,8 +128,7 @@ void CslTxScheduler::RescheduleCslTx(void) uint32_t delay; uint32_t cslTxDelay; - if (!child.IsCslSynchronized() || child.GetIndirectMessageCount() == 0 || - child.GetCslTxAttempts() >= kMaxCslTriggeredTxAttempts) + if (!child.IsCslSynchronized() || child.GetIndirectMessageCount() == 0) { continue; } @@ -233,12 +232,20 @@ void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, otError aError, aChild.ResetCslTxAttempts(); aChild.ResetIndirectTxAttempts(); break; + case OT_ERROR_NO_ACK: aChild.IncrementCslTxAttempts(); otLogInfoMac("CSL tx to child %04x failed, attempt %d/%d", aChild.GetRloc16(), aChild.GetCslTxAttempts(), kMaxCslTriggeredTxAttempts); + if (aChild.GetCslTxAttempts() >= kMaxCslTriggeredTxAttempts) + { + // CSL transmission attempts reach max, consider child out of sync + aChild.SetCslSynchronized(false); + aChild.ResetCslTxAttempts(); + } + // Fall through case OT_ERROR_CHANNEL_ACCESS_FAILURE: case OT_ERROR_ABORT: diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index 7b12af4cd..d7509bd09 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -79,9 +79,8 @@ public: { public: uint8_t GetCslTxAttempts(void) const { return mCslTxAttempts; } - void SetCslTxAttempts(uint8_t aCslTxAttempts) { mCslTxAttempts = aCslTxAttempts; } void IncrementCslTxAttempts(void) { mCslTxAttempts++; } - void ResetCslTxAttempts(void) { SetCslTxAttempts(0); } + void ResetCslTxAttempts(void) { mCslTxAttempts = 0; } bool IsCslSynchronized(void) const { return mCslSynchronized && mCslPeriod > 0; } void SetCslSynchronized(bool aCslSynchronized) { mCslSynchronized = aCslSynchronized; } diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index fd806f892..dd0375433 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -321,14 +321,15 @@ void IndirectSender::UpdateIndirectMessage(Child &aChild) aChild.SetIndirectFragmentOffset(0); aChild.SetIndirectTxSuccess(true); +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + mCslTxScheduler.Update(); +#endif + if (message != nullptr) { Mac::Address childAddress; mDataPollHandler.HandleNewFrame(aChild); -#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - mCslTxScheduler.Update(); -#endif aChild.GetMacAddress(childAddress); Get().LogMessage(MeshForwarder::kMessagePrepareIndirect, *message, &childAddress, OT_ERROR_NONE); diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index a4aee28da..582d197ce 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -2482,6 +2482,14 @@ void MleRouter::HandleChildUpdateRequest(const Message & aMessage, childDidChange = true; +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + if (child->IsRxOnWhenIdle()) + { + // Clear CSL synchronization state + child->SetCslSynchronized(false); + } +#endif + // The `IndirectSender::HandleChildModeChange()` needs to happen // after "Child Update" message is fully parsed to ensure that // any registered IPv6 addresses included in the "Child Update" diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index bbf832926..cd6995369 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -211,10 +211,17 @@ void Child::Info::SetFrom(const Child &aChild) mLastRssi = aChild.GetLinkInfo().GetLastRss(); mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate(); mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate(); + mQueuedMessageCnt = aChild.GetIndirectMessageCount(); + mVersion = aChild.GetVersion(); mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); mFullThreadDevice = aChild.IsFullThreadDevice(); mFullNetworkData = aChild.IsFullNetworkData(); mIsStateRestoring = aChild.IsStateRestoring(); +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + mIsCslSynced = aChild.IsCslSynchronized(); +#else + mIsCslSynced = false; +#endif } const Ip6::Address *Child::AddressIterator::GetAddress(void) const diff --git a/tests/scripts/expect/cli-child.exp b/tests/scripts/expect/cli-child.exp index 6d4512163..9ac2a5ca4 100755 --- a/tests/scripts/expect/cli-child.exp +++ b/tests/scripts/expect/cli-child.exp @@ -46,9 +46,9 @@ expect "Done" switch_node 1 send "child table\n" -expect "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N| Extended MAC |" -expect "+-----+--------+------------+------------+-------+------+-+-+-+------------------+" -expect -re "\\| +(\\d+) \\| 0x$rloc \\| +\\d+ \\| +\\d+ \\| +\\d+ \\| +\\d+ \\|\\d\\|\\d\\|\\d\\| $extaddr \\|" +expect "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC |" +expect "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+" +expect -re "\\| +(\\d+) \\| 0x$rloc \\| +\\d+ \\| +\\d+ \\| +\\d+ \\| +\\d+ \\|\\d\\|\\d\\|\\d\\| *\\d+\\| \\d \\| +\\d+ \\| $extaddr \\|" set child_id $expect_out(1,string) expect "Done" send "child list\n"