diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 73dedfb80..7953a2b8d 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -675,6 +675,32 @@ public: */ otError GetRadioRegion(uint16_t *aRegionCode); +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + /** + * Enable/disable or update Enhanced-ACK Based Probing in radio for a specific Initiator. + * + * After Enhanced-ACK Based Probing is configured by a specific Probing Initiator, the Enhanced-ACK sent to that + * node should include Vendor-Specific IE containing Link Metrics data. This method informs the radio to start/stop + * to collect Link Metrics data and include Vendor-Specific IE that containing the data in Enhanced-ACK sent to that + * Probing Initiator. + * + * @param[in] aLinkMetrics This parameter specifies what metrics to query. Per spec 4.11.3.4.4.6, at most 2 + * metrics can be specified. The probing would be disabled if @p aLinkMetrics is + * bitwise 0. + * @param[in] aShortAddress The short address of the Probing Initiator. + * @param[in] aExtAddress The extended source address of the Probing Initiator. @p aExtAddress MUST NOT be + * nullptr. + * + * @retval OT_ERROR_NONE Successfully configured the Enhanced-ACK Based Probing. + * @retval OT_ERROR_INVALID_ARGS @p aExtAddress is nullptr. + * @retval OT_ERROR_NOT_FOUND The Initiator indicated by @p aShortAddress is not found when trying to clear. + * @retval OT_ERROR_NO_BUFS No more Initiator can be supported. + */ + otError ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics, + const otShortAddress aShortAddress, + const otExtAddress & aExtAddress); +#endif + /** * This method checks whether the spinel interface is radio-only. * diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index 2f673116f..2759bfd20 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -2406,5 +2406,41 @@ exit: return error; } +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +template +otError RadioSpinel::ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics, + const otShortAddress aShortAddress, + const otExtAddress & aExtAddress) +{ + otError error = OT_ERROR_NONE; + uint8_t flags = 0; + + if (aLinkMetrics.mPduCount) + { + flags |= SPINEL_THREAD_LINK_METRIC_PDU_COUNT; + } + + if (aLinkMetrics.mLqi) + { + flags |= SPINEL_THREAD_LINK_METRIC_LQI; + } + + if (aLinkMetrics.mLinkMargin) + { + flags |= SPINEL_THREAD_LINK_METRIC_LINK_MARGIN; + } + + if (aLinkMetrics.mRssi) + { + flags |= SPINEL_THREAD_LINK_METRIC_RSSI; + } + + error = Set(SPINEL_PROP_RCP_ENH_ACK_PROBING, SPINEL_DATATYPE_UINT16_S, SPINEL_DATATYPE_EUI64_S, + SPINEL_DATATYPE_UINT8_S, aShortAddress, aExtAddress.m8, flags); + + return error; +} +#endif + } // namespace Spinel } // namespace ot diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index edce57aef..726ca3e30 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1400,6 +1400,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_CHILD_SUPERVISION_INTERVAL, "CHILD_SUPERVISION_INTERVAL"}, {SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT, "CHILD_SUPERVISION_CHECK_TIMEOUT"}, {SPINEL_PROP_RCP_VERSION, "RCP_VERSION"}, + {SPINEL_PROP_RCP_ENH_ACK_PROBING, "ENH_ACK_PROBING"}, {SPINEL_PROP_PARENT_RESPONSE_INFO, "PARENT_RESPONSE_INFO"}, {SPINEL_PROP_SLAAC_ENABLED, "SLAAC_ENABLED"}, {SPINEL_PROP_SUPPORTED_RADIO_LINKS, "SUPPORTED_RADIO_LINKS"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index 845349b9f..8b6db66fa 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -377,7 +377,7 @@ * Please see section "Spinel definition compatibility guideline" for more details. * */ -#define SPINEL_RCP_API_VERSION 4 +#define SPINEL_RCP_API_VERSION 5 /** * @def SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION @@ -752,6 +752,7 @@ enum // @ref SPINEL_PROP_THREAD_LINK_METRICS_QUERY_RESULT // @ref SPINEL_PROP_THREAD_LINK_METRICS_MGMT_ENH_ACK // @ref SPINEL_PROP_THREAD_LINK_METRICS_MGMT_FORWARD +// @ref SPINEL_PROP_RCP_ENH_ACK_PROBING enum { SPINEL_THREAD_LINK_METRIC_PDU_COUNT = (1 << 0), @@ -4709,6 +4710,27 @@ enum */ SPINEL_PROP_RCP_TIMESTAMP = SPINEL_PROP_RCP_EXT__BEGIN + 2, + /// Configure Enhanced ACK probing + /** Format: `SEC` (Write-only). + * + * `S`: Short address + * `E`: Extended address + * `C`: List of requested metric ids encoded as bit fields in single byte + * + * +---------------+----+ + * | Metric | Id | + * +---------------+----+ + * | Received PDUs | 0 | + * | LQI | 1 | + * | Link margin | 2 | + * | RSSI | 3 | + * +---------------+----+ + * + * Enable/disable or update Enhanced-ACK Based Probing in radio for a specific Initiator. + * + */ + SPINEL_PROP_RCP_ENH_ACK_PROBING = SPINEL_PROP_RCP_EXT__BEGIN + 3, + SPINEL_PROP_RCP_EXT__END = 0x900, SPINEL_PROP_NEST__BEGIN = 0x3BC0, diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 65b3456dc..d2abea7bd 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -2541,6 +2541,40 @@ exit: } #endif +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +otError NcpBase::DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount) +{ + otError error = OT_ERROR_NONE; + uint8_t metrics = 0; + + SuccessOrExit(error = mDecoder.ReadUint8(metrics)); + + if (metrics & SPINEL_THREAD_LINK_METRIC_PDU_COUNT) + { + VerifyOrExit(aAllowPduCount, error = OT_ERROR_INVALID_ARGS); + aMetrics->mPduCount = true; + } + + if (metrics & SPINEL_THREAD_LINK_METRIC_LQI) + { + aMetrics->mLqi = true; + } + + if (metrics & SPINEL_THREAD_LINK_METRIC_LINK_MARGIN) + { + aMetrics->mLinkMargin = true; + } + + if (metrics & SPINEL_THREAD_LINK_METRIC_RSSI) + { + aMetrics->mRssi = true; + } + +exit: + return error; +} +#endif + } // namespace Ncp } // namespace ot diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index fc024c70c..28b020a02 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -389,8 +389,6 @@ protected: #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE - otError DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount); - otError EncodeLinkMetricsValues(const otLinkMetricsValues *aMetricsValues); #endif @@ -404,6 +402,10 @@ protected: #endif // OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE #endif // OPENTHREAD_MTD || OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + otError DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount); +#endif + otError CommandHandler_NOOP(uint8_t aHeader); otError CommandHandler_RESET(uint8_t aHeader); // Combined command handler for `VALUE_GET`, `VALUE_SET`, `VALUE_INSERT` and `VALUE_REMOVE`. diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 51fbcecfc..20084a5fb 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -479,7 +479,10 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_MAC_KEY), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_MAC_FRAME_COUNTER), +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_ENH_ACK_PROBING), #endif +#endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_UNSOL_UPDATE_FILTER), #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 0ed209669..44f4475ce 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -215,38 +215,6 @@ exit: } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -otError NcpBase::DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount) -{ - otError error = OT_ERROR_NONE; - uint8_t metrics = 0; - - SuccessOrExit(error = mDecoder.ReadUint8(metrics)); - - if (metrics & SPINEL_THREAD_LINK_METRIC_PDU_COUNT) - { - VerifyOrExit(aAllowPduCount, error = OT_ERROR_INVALID_ARGS); - aMetrics->mPduCount = true; - } - - if (metrics & SPINEL_THREAD_LINK_METRIC_LQI) - { - aMetrics->mLqi = true; - } - - if (metrics & SPINEL_THREAD_LINK_METRIC_LINK_MARGIN) - { - aMetrics->mLinkMargin = true; - } - - if (metrics & SPINEL_THREAD_LINK_METRIC_RSSI) - { - aMetrics->mRssi = true; - } - -exit: - return error; -} - otError NcpBase::EncodeLinkMetricsValues(const otLinkMetricsValues *aMetricsValues) { otError error = OT_ERROR_NONE; @@ -3155,7 +3123,7 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(controlFlags), controlFlags ? &linkMetrics : nullptr, @@ -3214,7 +3182,7 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + uint16_t shortAddress; + const otExtAddress *extAddress; + otLinkMetrics linkMetrics = {}; + + SuccessOrExit(error = mDecoder.ReadUint16(shortAddress)); + SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); + SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ true)); + + error = otPlatRadioConfigureEnhAckProbing(mInstance, linkMetrics, shortAddress, extAddress); + +exit: + return error; +} +#endif + } // namespace Ncp } // namespace ot diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index b14e4e2fd..410975272 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -619,6 +619,18 @@ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode) return sRadioSpinel.GetRadioRegion(aRegionCode); } +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +otError otPlatRadioConfigureEnhAckProbing(otInstance * aInstance, + otLinkMetrics aLinkMetrics, + const otShortAddress aShortAddress, + const otExtAddress * aExtAddress) +{ + OT_UNUSED_VARIABLE(aInstance); + + return sRadioSpinel.ConfigureEnhAckProbing(aLinkMetrics, aShortAddress, *aExtAddress); +} +#endif + otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration) { OT_UNUSED_VARIABLE(aInstance);