/* * Copyright (c) 2016-2017, 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 implements minimal thread device required Spinel interface to the OpenThread stack. */ #include "openthread-core-config.h" #include "ncp_base.hpp" #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE #include #endif #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE #include #endif #include #include #include #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE #include #endif #include #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE #include #endif #include #include #if OPENTHREAD_FTD #include #endif #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE #include #endif #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) #include "openthread/backbone_router.h" #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE #include #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE #include #endif #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/instance.hpp" #include "common/string.hpp" #include "net/ip6.hpp" #if OPENTHREAD_MTD || OPENTHREAD_FTD namespace ot { namespace Ncp { static uint8_t BorderRouterConfigToFlagByte(const otBorderRouterConfig &aConfig) { uint8_t flags = 0; if (aConfig.mPreferred) { flags |= SPINEL_NET_FLAG_PREFERRED; } if (aConfig.mSlaac) { flags |= SPINEL_NET_FLAG_SLAAC; } if (aConfig.mDhcp) { flags |= SPINEL_NET_FLAG_DHCP; } if (aConfig.mDefaultRoute) { flags |= SPINEL_NET_FLAG_DEFAULT_ROUTE; } if (aConfig.mConfigure) { flags |= SPINEL_NET_FLAG_CONFIGURE; } if (aConfig.mOnMesh) { flags |= SPINEL_NET_FLAG_ON_MESH; } flags |= (static_cast(aConfig.mPreference) << SPINEL_NET_FLAG_PREFERENCE_OFFSET); return flags; } static uint8_t BorderRouterConfigToFlagByteExtended(const otBorderRouterConfig &aConfig) { uint8_t flags = 0; if (aConfig.mNdDns) { flags |= SPINEL_NET_FLAG_EXT_DNS; } if (aConfig.mDp) { flags |= SPINEL_NET_FLAG_EXT_DP; } return flags; } static uint8_t ExternalRouteConfigToFlagByte(const otExternalRouteConfig &aConfig) { uint8_t flags = 0; switch (aConfig.mPreference) { case OT_ROUTE_PREFERENCE_LOW: flags |= SPINEL_ROUTE_PREFERENCE_LOW; break; case OT_ROUTE_PREFERENCE_HIGH: flags |= SPINEL_ROUTE_PREFERENCE_HIGH; break; case OT_ROUTE_PREFERENCE_MED: default: flags |= SPINEL_ROUTE_PREFERENCE_MEDIUM; break; } if (aConfig.mNat64) { flags |= SPINEL_ROUTE_FLAG_NAT64; } return flags; } uint8_t NcpBase::LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aDeviceType, bool aNetworkData) { uint8_t flags(0); if (aRxOnWhenIdle) { flags |= SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE; } if (aDeviceType) { flags |= SPINEL_THREAD_MODE_FULL_THREAD_DEV; } if (aNetworkData) { flags |= SPINEL_THREAD_MODE_FULL_NETWORK_DATA; } return flags; } otError NcpBase::EncodeNeighborInfo(const otNeighborInfo &aNeighborInfo) { otError error; uint8_t modeFlags; modeFlags = LinkFlagsToFlagByte(aNeighborInfo.mRxOnWhenIdle, aNeighborInfo.mFullThreadDevice, aNeighborInfo.mFullNetworkData); SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(aNeighborInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(aNeighborInfo.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint32(aNeighborInfo.mAge)); SuccessOrExit(error = mEncoder.WriteUint8(aNeighborInfo.mLinkQualityIn)); SuccessOrExit(error = mEncoder.WriteInt8(aNeighborInfo.mAverageRssi)); SuccessOrExit(error = mEncoder.WriteUint8(modeFlags)); SuccessOrExit(error = mEncoder.WriteBool(aNeighborInfo.mIsChild)); SuccessOrExit(error = mEncoder.WriteUint32(aNeighborInfo.mLinkFrameCounter)); SuccessOrExit(error = mEncoder.WriteUint32(aNeighborInfo.mMleFrameCounter)); SuccessOrExit(error = mEncoder.WriteInt8(aNeighborInfo.mLastRssi)); SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE otError NcpBase::EncodeLinkMetricsValues(const otLinkMetricsValues *aMetricsValues) { otError error = OT_ERROR_NONE; SuccessOrExit(error = mEncoder.OpenStruct()); if (aMetricsValues->mMetrics.mPduCount) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_PDU_COUNT)); SuccessOrExit(error = mEncoder.WriteUint32(aMetricsValues->mPduCountValue)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aMetricsValues->mMetrics.mLqi) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_LQI)); SuccessOrExit(error = mEncoder.WriteUint8(aMetricsValues->mLqiValue)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aMetricsValues->mMetrics.mLinkMargin) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_LINK_MARGIN)); SuccessOrExit(error = mEncoder.WriteUint8(aMetricsValues->mLinkMarginValue)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aMetricsValues->mMetrics.mRssi) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_THREAD_LINK_METRIC_RSSI)); SuccessOrExit(error = mEncoder.WriteInt8(aMetricsValues->mRssiValue)); SuccessOrExit(error = mEncoder.CloseStruct()); } SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE template <> otError NcpBase::HandlePropertySet(void) { uint16_t cslPeriod; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint16(cslPeriod)); error = otLinkCslSetPeriod(mInstance, cslPeriod); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otLinkCslGetPeriod(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t cslTimeout; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(cslTimeout)); error = otLinkCslSetTimeout(mInstance, cslTimeout); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkCslGetTimeout(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t cslChannel; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(cslChannel)); error = otLinkCslSetChannel(mInstance, cslChannel); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otLinkCslGetChannel(mInstance)); } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otIp6Address addresses[OT_IP6_MAX_MLR_ADDRESSES]; uint8_t addressesCount = 0U; bool timeoutPresent = false; uint32_t timeout; SuccessOrExit(error = mDecoder.OpenStruct()); while (mDecoder.GetRemainingLengthInStruct()) { VerifyOrExit(addressesCount < Ip6AddressesTlv::kMaxAddresses, error = OT_ERROR_NO_BUFS); SuccessOrExit(error = mDecoder.ReadIp6Address(addresses[addressesCount])); ++addressesCount; } SuccessOrExit(error = mDecoder.CloseStruct()); while (mDecoder.GetRemainingLengthInStruct()) { uint8_t paramId; SuccessOrExit(error = mDecoder.OpenStruct()); SuccessOrExit(error = mDecoder.ReadUint8(paramId)); switch (paramId) { case SPINEL_THREAD_MLR_PARAMID_TIMEOUT: SuccessOrExit(error = mDecoder.ReadUint32(timeout)); timeoutPresent = true; break; default: ExitNow(error = OT_ERROR_INVALID_ARGS); } SuccessOrExit(error = mDecoder.CloseStruct()); } SuccessOrExit(error = otIp6RegisterMulticastListeners(mInstance, addresses, addressesCount, timeoutPresent ? &timeout : nullptr, &NcpBase::HandleMlrRegResult_Jump, this)); exit: return error; } void NcpBase::HandleMlrRegResult_Jump(void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, uint8_t aFailedAddressNum) { static_cast(aContext)->HandleMlrRegResult(aError, aMlrStatus, aFailedAddresses, aFailedAddressNum); } void NcpBase::HandleMlrRegResult(otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, uint8_t aFailedAddressNum) { SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_MLR_RESPONSE)); SuccessOrExit(mEncoder.WriteUint8(static_cast(ThreadErrorToSpinelStatus(aError)))); SuccessOrExit(mEncoder.WriteUint8(aMlrStatus)); SuccessOrExit(mEncoder.OpenStruct()); if (aError == OT_ERROR_NONE) { for (size_t i = 0U; i < aFailedAddressNum; ++i) { SuccessOrExit(mEncoder.WriteIp6Address(aFailedAddresses[i])); } } SuccessOrExit(mEncoder.CloseStruct()); SuccessOrExit(mEncoder.EndFrame()); exit: return; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otBackboneRouterConfig bbrConfig; SuccessOrExit(error = otBackboneRouterGetPrimary(mInstance, &bbrConfig)); SuccessOrExit(error = mEncoder.WriteUint16(bbrConfig.mServer16)); SuccessOrExit(error = mEncoder.WriteUint16(bbrConfig.mReregistrationDelay)); SuccessOrExit(error = mEncoder.WriteUint32(bbrConfig.mMlrTimeout)); SuccessOrExit(error = mEncoder.WriteUint8(bbrConfig.mSequenceNumber)); exit: return error; } #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetPollPeriod(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t pollPeriod; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(pollPeriod)); error = otLinkSetPollPeriod(mInstance, pollPeriod); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteEui64(*otLinkGetExtendedAddress(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otLinkGetMaxFrameRetriesDirect(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t maxFrameRetriesDirect; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(maxFrameRetriesDirect)); otLinkSetMaxFrameRetriesDirect(mInstance, maxFrameRetriesDirect); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { uint32_t newMask = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = DecodeChannelMask(newMask)); error = otLinkSetSupportedChannelMask(mInstance, newMask); exit: return error; } otError NcpBase::CommandHandler_NET_CLEAR(uint8_t aHeader) { return PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance))); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otDatasetIsCommissioned(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otIp6IsEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { bool enabled = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); error = otIp6SetEnabled(mInstance, enabled); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED); } template <> otError NcpBase::HandlePropertySet(void) { bool enabled = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); // If the value has changed... if (enabled != (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED)) { if (enabled) { error = otThreadSetEnabled(mInstance, true); } else { error = otThreadSetEnabled(mInstance, false); } } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { spinel_net_role_t role(SPINEL_NET_ROLE_DETACHED); switch (otThreadGetDeviceRole(mInstance)) { case OT_DEVICE_ROLE_DISABLED: case OT_DEVICE_ROLE_DETACHED: role = SPINEL_NET_ROLE_DETACHED; break; case OT_DEVICE_ROLE_CHILD: role = SPINEL_NET_ROLE_CHILD; break; case OT_DEVICE_ROLE_ROUTER: role = SPINEL_NET_ROLE_ROUTER; break; case OT_DEVICE_ROLE_LEADER: role = SPINEL_NET_ROLE_LEADER; break; } return mEncoder.WriteUint8(role); } template <> otError NcpBase::HandlePropertySet(void) { unsigned int role = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUintPacked(role)); switch (role) { case SPINEL_NET_ROLE_DETACHED: error = otThreadBecomeDetached(mInstance); break; #if OPENTHREAD_FTD case SPINEL_NET_ROLE_ROUTER: error = otThreadBecomeRouter(mInstance); break; case SPINEL_NET_ROLE_LEADER: error = otThreadBecomeLeader(mInstance); break; #endif // OPENTHREAD_FTD case SPINEL_NET_ROLE_CHILD: error = otThreadBecomeChild(mInstance); break; } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUtf8(otThreadGetNetworkName(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { const char *string = nullptr; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUtf8(string)); error = otThreadSetNetworkName(mInstance, string); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteData(otThreadGetExtendedPanId(mInstance)->m8, sizeof(spinel_net_xpanid_t)); } template <> otError NcpBase::HandlePropertySet(void) { const uint8_t *ptr = nullptr; uint16_t len; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadData(ptr, len)); VerifyOrExit(len == sizeof(spinel_net_xpanid_t), error = OT_ERROR_PARSE); error = otThreadSetExtendedPanId(mInstance, reinterpret_cast(ptr)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otNetworkKey networkKey; otThreadGetNetworkKey(mInstance, &networkKey); return mEncoder.WriteData(networkKey.m8, OT_NETWORK_KEY_SIZE); } template <> otError NcpBase::HandlePropertySet(void) { const uint8_t *ptr = nullptr; uint16_t len; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadData(ptr, len)); VerifyOrExit(len == OT_NETWORK_KEY_SIZE, error = OT_ERROR_PARSE); error = otThreadSetNetworkKey(mInstance, reinterpret_cast(ptr)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetKeySequenceCounter(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t keySeqCounter; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(keySeqCounter)); otThreadSetKeySequenceCounter(mInstance, keySeqCounter); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetPartitionId(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetKeySwitchGuardTime(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t keyGuardTime; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(keyGuardTime)); otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otNetDataGetVersion(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otNetDataGetStableVersion(mInstance)); } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { uint8_t networkData[255]; uint8_t networkDataLen = 255; IgnoreError(otBorderRouterGetNetData(mInstance, false, // Stable? networkData, &networkDataLen)); return mEncoder.WriteData(networkData, networkDataLen); } template <> otError NcpBase::HandlePropertyGet(void) { uint8_t networkData[255]; uint8_t networkDataLen = 255; IgnoreError(otBorderRouterGetNetData(mInstance, true, // Stable? networkData, &networkDataLen)); return mEncoder.WriteData(networkData, networkDataLen); } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { uint8_t networkData[255]; uint8_t networkDataLen = 255; IgnoreError(otNetDataGet(mInstance, false, // Stable? networkData, &networkDataLen)); return mEncoder.WriteData(networkData, networkDataLen); } template <> otError NcpBase::HandlePropertyGet(void) { uint8_t networkData[255]; uint8_t networkDataLen = 255; IgnoreError(otNetDataGet(mInstance, true, // Stable? networkData, &networkDataLen)); return mEncoder.WriteData(networkData, networkDataLen); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetLeaderRouterId(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otIp6Address address; error = otThreadGetLeaderRloc(mInstance, &address); if (error == OT_ERROR_NONE) { error = mEncoder.WriteIp6Address(address); } else { error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error)); } return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otRouterInfo parentInfo; error = otThreadGetParentInfo(mInstance, &parentInfo); if (error == OT_ERROR_NONE) { if (parentInfo.mLinkEstablished) { int8_t averageRssi; int8_t lastRssi; IgnoreError(otThreadGetParentAverageRssi(mInstance, &averageRssi)); IgnoreError(otThreadGetParentLastRssi(mInstance, &lastRssi)); SuccessOrExit(error = mEncoder.WriteEui64(parentInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(parentInfo.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint32(parentInfo.mAge)); SuccessOrExit(error = mEncoder.WriteInt8(averageRssi)); SuccessOrExit(error = mEncoder.WriteInt8(lastRssi)); SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mLinkQualityIn)); SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mLinkQualityOut)); SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mVersion)); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mCslClockAccuracy)); SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mCslUncertainty)); #endif } else { SuccessOrExit(error = mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_ITEM_NOT_FOUND)); } } else { error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error)); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; otNeighborInfo neighInfo; while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE) { SuccessOrExit(error = EncodeNeighborInfo(neighInfo)); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; otNeighborInfo neighInfo; while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(neighInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mFrameErrorRate)); SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mMessageErrorRate)); SuccessOrExit(error = mEncoder.WriteInt8(neighInfo.mAverageRssi)); SuccessOrExit(error = mEncoder.WriteInt8(neighInfo.mLastRssi)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; uint8_t numEntries = 0; const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries); for (; numEntries != 0; ports++, numEntries--) { SuccessOrExit(error = mEncoder.WriteUint16(*ports)); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; // First, we need to remove all of the current assisting ports. otIp6RemoveAllUnsecurePorts(mInstance); while (mDecoder.GetRemainingLengthInStruct() >= sizeof(uint16_t)) { uint16_t port; SuccessOrExit(error = mDecoder.ReadUint16(port)); SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, port)); } exit: if (error != OT_ERROR_NONE) { // We had an error, but we've actually changed // the state of these ports, so we need to report // those incomplete changes via an asynchronous // change event. IgnoreError( WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_THREAD_ASSISTING_PORTS)); } return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(mAllowLocalNetworkDataChange); } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE template <> otError NcpBase::HandlePropertySet(void) { bool value = false; otError error = OT_ERROR_NONE; bool shouldRegisterWithLeader = false; SuccessOrExit(error = mDecoder.ReadBool(value)); // Register any net data changes on transition from `true` to `false`. shouldRegisterWithLeader = mAllowLocalNetworkDataChange && !value; mAllowLocalNetworkDataChange = value; exit: if (shouldRegisterWithLeader) { IgnoreError(otBorderRouterRegister(mInstance)); } return error; } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otBorderRouterConfig borderRouterConfig; otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT; // Fill from non-local network data first while (otNetDataGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(borderRouterConfig.mPrefix.mPrefix)); SuccessOrExit(error = mEncoder.WriteUint8(borderRouterConfig.mPrefix.mLength)); SuccessOrExit(error = mEncoder.WriteBool(borderRouterConfig.mStable)); SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByte(borderRouterConfig))); SuccessOrExit(error = mEncoder.WriteBool(false)); // isLocal SuccessOrExit(error = mEncoder.WriteUint16(borderRouterConfig.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByteExtended(borderRouterConfig))); SuccessOrExit(error = mEncoder.CloseStruct()); } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE iter = OT_NETWORK_DATA_ITERATOR_INIT; // Fill from local network data last while (otBorderRouterGetNextOnMeshPrefix(mInstance, &iter, &borderRouterConfig) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(borderRouterConfig.mPrefix.mPrefix)); SuccessOrExit(error = mEncoder.WriteUint8(borderRouterConfig.mPrefix.mLength)); SuccessOrExit(error = mEncoder.WriteBool(borderRouterConfig.mStable)); SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByte(borderRouterConfig))); SuccessOrExit(error = mEncoder.WriteBool(true)); // isLocal SuccessOrExit(error = mEncoder.WriteUint16(borderRouterConfig.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint8(BorderRouterConfigToFlagByteExtended(borderRouterConfig))); SuccessOrExit(error = mEncoder.CloseStruct()); } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE exit: return error; } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; otBorderRouterConfig borderRouterConfig; bool stable = false; bool isLocal; uint8_t flags = 0; uint8_t flagsExtended = 0; uint8_t prefixLength; uint16_t rloc16; memset(&borderRouterConfig, 0, sizeof(otBorderRouterConfig)); VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE); SuccessOrExit(error = mDecoder.ReadIp6Address(borderRouterConfig.mPrefix.mPrefix)); SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); SuccessOrExit(error = mDecoder.ReadBool(stable)); SuccessOrExit(error = mDecoder.ReadUint8(flags)); borderRouterConfig.mPrefix.mLength = prefixLength; borderRouterConfig.mStable = stable; borderRouterConfig.mPreference = ((flags & SPINEL_NET_FLAG_PREFERENCE_MASK) >> SPINEL_NET_FLAG_PREFERENCE_OFFSET); borderRouterConfig.mPreferred = ((flags & SPINEL_NET_FLAG_PREFERRED) != 0); borderRouterConfig.mSlaac = ((flags & SPINEL_NET_FLAG_SLAAC) != 0); borderRouterConfig.mDhcp = ((flags & SPINEL_NET_FLAG_DHCP) != 0); borderRouterConfig.mConfigure = ((flags & SPINEL_NET_FLAG_CONFIGURE) != 0); borderRouterConfig.mDefaultRoute = ((flags & SPINEL_NET_FLAG_DEFAULT_ROUTE) != 0); borderRouterConfig.mOnMesh = ((flags & SPINEL_NET_FLAG_ON_MESH) != 0); // A new field 'TLV flags extended' has been added to the SPINEL_PROP_THREAD_ON_MESH_NETS property. // To correctly handle a new field for INSERT command, the additional fields 'isLocal' and 'rloc16' are read and // ignored. if ((mDecoder.ReadBool(isLocal) == OT_ERROR_NONE) && (mDecoder.ReadUint16(rloc16) == OT_ERROR_NONE) && (mDecoder.ReadUint8(flagsExtended) == OT_ERROR_NONE)) { borderRouterConfig.mNdDns = ((flagsExtended & SPINEL_NET_FLAG_EXT_DNS) != 0); borderRouterConfig.mDp = ((flagsExtended & SPINEL_NET_FLAG_EXT_DP) != 0); } error = otBorderRouterAddOnMeshPrefix(mInstance, &borderRouterConfig); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; otIp6Prefix ip6Prefix; uint8_t prefixLength; memset(&ip6Prefix, 0, sizeof(otIp6Prefix)); VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE); SuccessOrExit(error = mDecoder.ReadIp6Address(ip6Prefix.mPrefix)); SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); ip6Prefix.mLength = prefixLength; error = otBorderRouterRemoveOnMeshPrefix(mInstance, &ip6Prefix); // If prefix was not on the list, "remove" command can be considred // successful. if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } exit: return error; } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(mAllowLocalServerDataChange); } template <> otError NcpBase::HandlePropertySet(void) { bool value = false; otError error = OT_ERROR_NONE; bool shouldRegisterWithLeader = false; SuccessOrExit(error = mDecoder.ReadBool(value)); // Register any server data changes on transition from `true` to `false`. shouldRegisterWithLeader = mAllowLocalServerDataChange && !value; mAllowLocalServerDataChange = value; exit: if (shouldRegisterWithLeader) { IgnoreError(otServerRegister(mInstance)); } return error; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; otServiceConfig cfg; bool stable; const uint8_t *data; uint16_t dataLen; VerifyOrExit(mAllowLocalServerDataChange, error = OT_ERROR_INVALID_STATE); SuccessOrExit(error = mDecoder.ReadUint32(cfg.mEnterpriseNumber)); SuccessOrExit(error = mDecoder.ReadDataWithLen(data, dataLen)); VerifyOrExit((dataLen <= sizeof(cfg.mServiceData)), error = OT_ERROR_INVALID_ARGS); memcpy(cfg.mServiceData, data, dataLen); static_assert((sizeof(cfg.mServiceData) <= UINT8_MAX), "Cannot handle full range of buffer length"); cfg.mServiceDataLength = static_cast(dataLen); SuccessOrExit(error = mDecoder.ReadBool(stable)); cfg.mServerConfig.mStable = stable; SuccessOrExit(error = mDecoder.ReadDataWithLen(data, dataLen)); VerifyOrExit((dataLen <= sizeof(cfg.mServerConfig.mServerData)), error = OT_ERROR_INVALID_ARGS); memcpy(cfg.mServerConfig.mServerData, data, dataLen); static_assert((sizeof(cfg.mServerConfig.mServerData) <= UINT8_MAX), "Cannot handle full range of buffer length"); cfg.mServerConfig.mServerDataLength = static_cast(dataLen); SuccessOrExit(error = otServerAddService(mInstance, &cfg)); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; uint32_t enterpriseNumber; const uint8_t *serviceData; uint16_t serviceDataLength; VerifyOrExit(mAllowLocalServerDataChange, error = OT_ERROR_INVALID_STATE); SuccessOrExit(error = mDecoder.ReadUint32(enterpriseNumber)); SuccessOrExit(error = mDecoder.ReadDataWithLen(serviceData, serviceDataLength)); VerifyOrExit(serviceDataLength <= UINT8_MAX, error = OT_ERROR_INVALID_ARGS); SuccessOrExit(error = otServerRemoveService(mInstance, enterpriseNumber, serviceData, static_cast(serviceDataLength))); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otServiceConfig cfg; while (otServerGetNextService(mInstance, &iterator, &cfg) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint32(cfg.mEnterpriseNumber)); SuccessOrExit(error = mEncoder.WriteDataWithLen(cfg.mServiceData, cfg.mServiceDataLength)); SuccessOrExit(error = mEncoder.WriteBool(cfg.mServerConfig.mStable)); SuccessOrExit( error = mEncoder.WriteDataWithLen(cfg.mServerConfig.mServerData, cfg.mServerConfig.mServerDataLength)); SuccessOrExit(error = mEncoder.WriteUint16(cfg.mServerConfig.mRloc16)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; otServiceConfig cfg; while (otNetDataGetNextService(mInstance, &iterator, &cfg) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint8(cfg.mServiceId)); SuccessOrExit(error = mEncoder.WriteUint32(cfg.mEnterpriseNumber)); SuccessOrExit(error = mEncoder.WriteDataWithLen(cfg.mServiceData, cfg.mServiceDataLength)); SuccessOrExit(error = mEncoder.WriteBool(cfg.mServerConfig.mStable)); SuccessOrExit( error = mEncoder.WriteDataWithLen(cfg.mServerConfig.mServerData, cfg.mServerConfig.mServerDataLength)); SuccessOrExit(error = mEncoder.WriteUint16(cfg.mServerConfig.mRloc16)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(mDiscoveryScanJoinerFlag); } template <> otError NcpBase::HandlePropertySet(void) { return mDecoder.ReadBool(mDiscoveryScanJoinerFlag); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(mDiscoveryScanEnableFiltering); } template <> otError NcpBase::HandlePropertySet(void) { return mDecoder.ReadBool(mDiscoveryScanEnableFiltering); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(mDiscoveryScanPanId); } template <> otError NcpBase::HandlePropertySet(void) { return mDecoder.ReadUint16(mDiscoveryScanPanId); } otError NcpBase::EncodeOperationalDataset(const otOperationalDataset &aDataset) { otError error = OT_ERROR_NONE; if (aDataset.mComponents.mIsActiveTimestampPresent) { const otTimestamp &activeTimestamp = aDataset.mActiveTimestamp; SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP)); SuccessOrExit(error = mEncoder.WriteUint64(activeTimestamp.mSeconds)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsPendingTimestampPresent) { const otTimestamp &pendingTimestamp = aDataset.mPendingTimestamp; SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_PENDING_TIMESTAMP)); SuccessOrExit(error = mEncoder.WriteUint64(pendingTimestamp.mSeconds)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsNetworkKeyPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_NETWORK_KEY)); SuccessOrExit(error = mEncoder.WriteData(aDataset.mNetworkKey.m8, OT_NETWORK_KEY_SIZE)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsNetworkNamePresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_NETWORK_NAME)); SuccessOrExit(error = mEncoder.WriteUtf8(aDataset.mNetworkName.m8)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsExtendedPanIdPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_XPANID)); SuccessOrExit(error = mEncoder.WriteData(aDataset.mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsMeshLocalPrefixPresent) { otIp6Address addr; memcpy(addr.mFields.m8, aDataset.mMeshLocalPrefix.m8, 8); memset(addr.mFields.m8 + 8, 0, 8); // Zero out the last 8 bytes. SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_IPV6_ML_PREFIX)); SuccessOrExit(error = mEncoder.WriteIp6Address(addr)); // Mesh local prefix SuccessOrExit(error = mEncoder.WriteUint8(OT_IP6_PREFIX_BITSIZE)); // Prefix length (in bits) SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsDelayPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_DELAY_TIMER)); SuccessOrExit(error = mEncoder.WriteUint32(aDataset.mDelay)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsPanIdPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_MAC_15_4_PANID)); SuccessOrExit(error = mEncoder.WriteUint16(aDataset.mPanId)); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsChannelPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN)); // The channel is stored in Dataset as `uint16_t` (to accommodate // larger number of channels in sub-GHz band), however the current // definition of `SPINEL_PROP_PHY_CHAN` property limits the channel // to a `uint8_t`. SuccessOrExit(error = mEncoder.WriteUint8(static_cast(aDataset.mChannel))); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsPskcPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_NET_PSKC)); SuccessOrExit(error = mEncoder.WriteData(aDataset.mPskc.m8, sizeof(spinel_net_pskc_t))); SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsSecurityPolicyPresent) { uint8_t flags[2]; static_cast(aDataset.mSecurityPolicy).GetFlags(flags, sizeof(flags)); SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_SECURITY_POLICY)); SuccessOrExit(error = mEncoder.WriteUint16(aDataset.mSecurityPolicy.mRotationTime)); SuccessOrExit(error = mEncoder.WriteUint8(flags[0])); if (otThreadGetVersion() >= OT_THREAD_VERSION_1_2) { SuccessOrExit(error = mEncoder.WriteUint8(flags[1])); } SuccessOrExit(error = mEncoder.CloseStruct()); } if (aDataset.mComponents.mIsChannelMaskPresent) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN_SUPPORTED)); SuccessOrExit(error = EncodeChannelMask(aDataset.mChannelMask)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otOperationalDataset dataset; IgnoreError(otDatasetGetActive(mInstance, &dataset)); return EncodeOperationalDataset(dataset); } template <> otError NcpBase::HandlePropertyGet(void) { otOperationalDataset dataset; IgnoreError(otDatasetGetPending(mInstance, &dataset)); return EncodeOperationalDataset(dataset); } otError NcpBase::DecodeOperationalDataset(otOperationalDataset &aDataset, const uint8_t **aTlvs, uint8_t *aTlvsLength, const otIp6Address **aDestIpAddress, bool aAllowEmptyValues) { otError error = OT_ERROR_NONE; memset(&aDataset, 0, sizeof(otOperationalDataset)); if (aTlvs != nullptr) { *aTlvs = nullptr; } if (aTlvsLength != nullptr) { *aTlvsLength = 0; } if (aDestIpAddress != nullptr) { *aDestIpAddress = nullptr; } while (!mDecoder.IsAllReadInStruct()) { unsigned int propKey; SuccessOrExit(error = mDecoder.OpenStruct()); SuccessOrExit(error = mDecoder.ReadUintPacked(propKey)); switch (static_cast(propKey)) { case SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mActiveTimestamp.mSeconds)); aDataset.mActiveTimestamp.mTicks = 0; aDataset.mActiveTimestamp.mAuthoritative = false; } aDataset.mComponents.mIsActiveTimestampPresent = true; break; case SPINEL_PROP_DATASET_PENDING_TIMESTAMP: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mPendingTimestamp.mSeconds)); aDataset.mPendingTimestamp.mTicks = 0; aDataset.mPendingTimestamp.mAuthoritative = false; } aDataset.mComponents.mIsPendingTimestampPresent = true; break; case SPINEL_PROP_NET_NETWORK_KEY: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const uint8_t *key; uint16_t len; SuccessOrExit(error = mDecoder.ReadData(key, len)); VerifyOrExit(len == OT_NETWORK_KEY_SIZE, error = OT_ERROR_INVALID_ARGS); memcpy(aDataset.mNetworkKey.m8, key, len); } aDataset.mComponents.mIsNetworkKeyPresent = true; break; case SPINEL_PROP_NET_NETWORK_NAME: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const char *name; size_t len; SuccessOrExit(error = mDecoder.ReadUtf8(name)); len = strlen(name); VerifyOrExit(len <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS); memcpy(aDataset.mNetworkName.m8, name, len + 1); } aDataset.mComponents.mIsNetworkNamePresent = true; break; case SPINEL_PROP_NET_XPANID: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const uint8_t *xpanid; uint16_t len; SuccessOrExit(error = mDecoder.ReadData(xpanid, len)); VerifyOrExit(len == OT_EXT_PAN_ID_SIZE, error = OT_ERROR_INVALID_ARGS); memcpy(aDataset.mExtendedPanId.m8, xpanid, len); } aDataset.mComponents.mIsExtendedPanIdPresent = true; break; case SPINEL_PROP_IPV6_ML_PREFIX: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const otIp6Address *addr; uint8_t prefixLen; SuccessOrExit(error = mDecoder.ReadIp6Address(addr)); SuccessOrExit(error = mDecoder.ReadUint8(prefixLen)); VerifyOrExit(prefixLen == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS); memcpy(aDataset.mMeshLocalPrefix.m8, addr, OT_MESH_LOCAL_PREFIX_SIZE); } aDataset.mComponents.mIsMeshLocalPrefixPresent = true; break; case SPINEL_PROP_DATASET_DELAY_TIMER: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUint32(aDataset.mDelay)); } aDataset.mComponents.mIsDelayPresent = true; break; case SPINEL_PROP_MAC_15_4_PANID: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUint16(aDataset.mPanId)); } aDataset.mComponents.mIsPanIdPresent = true; break; case SPINEL_PROP_PHY_CHAN: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { uint8_t channel; SuccessOrExit(error = mDecoder.ReadUint8(channel)); aDataset.mChannel = channel; } aDataset.mComponents.mIsChannelPresent = true; break; case SPINEL_PROP_NET_PSKC: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const uint8_t *psk; uint16_t len; SuccessOrExit(error = mDecoder.ReadData(psk, len)); VerifyOrExit(len == OT_PSKC_MAX_SIZE, error = OT_ERROR_INVALID_ARGS); memcpy(aDataset.mPskc.m8, psk, OT_PSKC_MAX_SIZE); } aDataset.mComponents.mIsPskcPresent = true; break; case SPINEL_PROP_DATASET_SECURITY_POLICY: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { uint8_t flags[2]; uint8_t flagsLength = 1; SuccessOrExit(error = mDecoder.ReadUint16(aDataset.mSecurityPolicy.mRotationTime)); SuccessOrExit(error = mDecoder.ReadUint8(flags[0])); if (otThreadGetVersion() >= OT_THREAD_VERSION_1_2 && mDecoder.GetRemainingLengthInStruct() > 0) { SuccessOrExit(error = mDecoder.ReadUint8(flags[1])); ++flagsLength; } static_cast(aDataset.mSecurityPolicy).SetFlags(flags, flagsLength); } aDataset.mComponents.mIsSecurityPolicyPresent = true; break; case SPINEL_PROP_PHY_CHAN_SUPPORTED: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { uint8_t channel; aDataset.mChannelMask = 0; while (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUint8(channel)); VerifyOrExit(channel <= 31, error = OT_ERROR_INVALID_ARGS); aDataset.mChannelMask |= (1UL << channel); } } aDataset.mComponents.mIsChannelMaskPresent = true; break; case SPINEL_PROP_DATASET_RAW_TLVS: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const uint8_t *tlvs; uint16_t len; SuccessOrExit(error = mDecoder.ReadData(tlvs, len)); VerifyOrExit(len <= 255, error = OT_ERROR_INVALID_ARGS); if (aTlvs != nullptr) { *aTlvs = tlvs; } if (aTlvsLength != nullptr) { *aTlvsLength = static_cast(len); } } break; case SPINEL_PROP_DATASET_DEST_ADDRESS: if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct()) { const otIp6Address *addr; SuccessOrExit(error = mDecoder.ReadIp6Address(addr)); if (aDestIpAddress != nullptr) { *aDestIpAddress = addr; } } break; default: break; } SuccessOrExit(error = mDecoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otOperationalDataset dataset; SuccessOrExit(error = DecodeOperationalDataset(dataset)); error = otDatasetSetActive(mInstance, &dataset); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otOperationalDataset dataset; SuccessOrExit(error = DecodeOperationalDataset(dataset)); error = otDatasetSetPending(mInstance, &dataset); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otOperationalDataset dataset; const uint8_t *extraTlvs; uint8_t extraTlvsLength; SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength)); error = otDatasetSendMgmtActiveSet(mInstance, &dataset, extraTlvs, extraTlvsLength, /* aCallback */ nullptr, /* aContext */ nullptr); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otOperationalDataset dataset; const uint8_t *extraTlvs; uint8_t extraTlvsLength; SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength)); error = otDatasetSendMgmtPendingSet(mInstance, &dataset, extraTlvs, extraTlvsLength, /* aCallback */ nullptr, /* aContext */ nullptr); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otOperationalDataset dataset; const uint8_t *extraTlvs; uint8_t extraTlvsLength; const otIp6Address *destIpAddress; SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength, &destIpAddress, true)); error = otDatasetSendMgmtActiveGet(mInstance, &dataset.mComponents, extraTlvs, extraTlvsLength, destIpAddress); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otOperationalDataset dataset; const uint8_t *extraTlvs; uint8_t extraTlvsLength; const otIp6Address *destIpAddress; SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength, &destIpAddress, true)); error = otDatasetSendMgmtPendingGet(mInstance, &dataset.mComponents, extraTlvs, extraTlvsLength, destIpAddress); exit: return error; } #if OPENTHREAD_CONFIG_JOINER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { spinel_meshcop_joiner_state_t state = SPINEL_MESHCOP_JOINER_STATE_IDLE; switch (otJoinerGetState(mInstance)) { case OT_JOINER_STATE_IDLE: state = SPINEL_MESHCOP_JOINER_STATE_IDLE; break; case OT_JOINER_STATE_DISCOVER: state = SPINEL_MESHCOP_JOINER_STATE_DISCOVER; break; case OT_JOINER_STATE_CONNECT: state = SPINEL_MESHCOP_JOINER_STATE_CONNECTING; break; case OT_JOINER_STATE_CONNECTED: state = SPINEL_MESHCOP_JOINER_STATE_CONNECTED; break; case OT_JOINER_STATE_ENTRUST: state = SPINEL_MESHCOP_JOINER_STATE_ENTRUST; break; case OT_JOINER_STATE_JOINED: state = SPINEL_MESHCOP_JOINER_STATE_JOINED; break; } return mEncoder.WriteUint8(state); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool action = false; const char *psk = nullptr; const char *provisioningUrl = nullptr; const char *vendorName = nullptr; const char *vendorModel = nullptr; const char *vendorSwVersion = nullptr; const char *vendorData = nullptr; SuccessOrExit(error = mDecoder.ReadBool(action)); if (!action) { otJoinerStop(mInstance); ExitNow(); } SuccessOrExit(error = mDecoder.ReadUtf8(psk)); // Parse optional fields if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUtf8(provisioningUrl)); } if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUtf8(vendorName)); } if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUtf8(vendorModel)); } if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUtf8(vendorSwVersion)); } if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadUtf8(vendorData)); } // Use OpenThread default values for vendor name, mode, sw version if // not specified or an empty string is given. if ((vendorName == nullptr) || (vendorName[0] == 0)) { vendorName = PACKAGE_NAME; } if ((vendorModel == nullptr) || (vendorModel[0] == 0)) { vendorModel = OPENTHREAD_CONFIG_PLATFORM_INFO; } if ((vendorSwVersion == nullptr) || (vendorSwVersion[0] == 0)) { vendorSwVersion = PACKAGE_VERSION; } error = otJoinerStart(mInstance, psk, provisioningUrl, vendorName, vendorModel, vendorSwVersion, vendorData, &NcpBase::HandleJoinerCallback_Jump, this); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error; const otJoinerDiscerner *discerner = otJoinerGetDiscerner(mInstance); if (discerner == nullptr) { SuccessOrExit(error = mEncoder.WriteUint8(0)); } else { SuccessOrExit(error = mEncoder.WriteUint8(discerner->mLength)); SuccessOrExit(error = mEncoder.WriteUint64(discerner->mValue)); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otJoinerDiscerner discerner; SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength)); if (discerner.mLength == 0) { // Clearing any previously set Joiner Discerner error = otJoinerSetDiscerner(mInstance, nullptr); ExitNow(); } SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue)); error = otJoinerSetDiscerner(mInstance, &discerner); exit: return error; } #endif // OPENTHREAD_CONFIG_JOINER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otMeshLocalPrefix *mlPrefix = otThreadGetMeshLocalPrefix(mInstance); otIp6Address addr; VerifyOrExit(mlPrefix != nullptr); // If `mlPrefix` is nullptr send empty response. memcpy(addr.mFields.m8, mlPrefix->m8, 8); // Zero out the last 8 bytes. memset(addr.mFields.m8 + 8, 0, 8); SuccessOrExit(error = mEncoder.WriteIp6Address(addr)); // Mesh local prefix SuccessOrExit(error = mEncoder.WriteUint8(OT_IP6_PREFIX_BITSIZE)); // Prefix length (in bits) exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; const otIp6Address *meshLocalPrefix; uint8_t prefixLength; SuccessOrExit(error = mDecoder.ReadIp6Address(meshLocalPrefix)); SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); VerifyOrExit(prefixLength == OT_IP6_PREFIX_BITSIZE, error = OT_ERROR_INVALID_ARGS); error = otThreadSetMeshLocalPrefix(mInstance, reinterpret_cast(meshLocalPrefix)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otIp6Address *ml64 = otThreadGetMeshLocalEid(mInstance); VerifyOrExit(ml64 != nullptr); SuccessOrExit(error = mEncoder.WriteIp6Address(*ml64)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otIp6Address *address = otThreadGetLinkLocalIp6Address(mInstance); VerifyOrExit(address != nullptr); SuccessOrExit(error = mEncoder.WriteIp6Address(*address)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; for (const otNetifAddress *address = otIp6GetUnicastAddresses(mInstance); address; address = address->mNext) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(address->mAddress)); SuccessOrExit(error = mEncoder.WriteUint8(address->mPrefixLength)); SuccessOrExit(error = mEncoder.WriteUint32(address->mPreferred ? 0xffffffff : 0)); SuccessOrExit(error = mEncoder.WriteUint32(address->mValid ? 0xffffffff : 0)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; otNetifAddress netifAddr; uint32_t preferredLifetime; uint32_t validLifetime; SuccessOrExit(error = mDecoder.ReadIp6Address(netifAddr.mAddress)); SuccessOrExit(error = mDecoder.ReadUint8(netifAddr.mPrefixLength)); SuccessOrExit(error = mDecoder.ReadUint32(preferredLifetime)); SuccessOrExit(error = mDecoder.ReadUint32(validLifetime)); netifAddr.mAddressOrigin = OT_ADDRESS_ORIGIN_MANUAL; netifAddr.mPreferred = (preferredLifetime != 0); netifAddr.mValid = (validLifetime != 0); error = otIp6AddUnicastAddress(mInstance, &netifAddr); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; const otIp6Address *addrPtr; SuccessOrExit(error = mDecoder.ReadIp6Address(addrPtr)); error = otIp6RemoveUnicastAddress(mInstance, addrPtr); // If address was not on the list, "remove" command is successful. if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { // TODO: Implement get route table return mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_UNIMPLEMENTED); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otIcmp6GetEchoMode(mInstance) != OT_ICMP6_ECHO_HANDLER_DISABLED); } template <> otError NcpBase::HandlePropertySet(void) { bool enabled = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); otIcmp6SetEchoMode(mInstance, enabled ? OT_ICMP6_ECHO_HANDLER_ALL : OT_ICMP6_ECHO_HANDLER_DISABLED); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otNetifMulticastAddress *address; for (address = otIp6GetMulticastAddresses(mInstance); address; address = address->mNext) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(address->mAddress)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; const otIp6Address *addrPtr; SuccessOrExit(error = mDecoder.ReadIp6Address(addrPtr)); error = otIp6SubscribeMulticastAddress(mInstance, addrPtr); if (error == OT_ERROR_ALREADY) { error = OT_ERROR_NONE; } exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; const otIp6Address *addrPtr; SuccessOrExit(error = mDecoder.ReadIp6Address(addrPtr)); error = otIp6UnsubscribeMulticastAddress(mInstance, addrPtr); // If the address was not on the list, "remove" command is successful, // and we respond with a `SPINEL_STATUS_OK` status. if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { spinel_ipv6_icmp_ping_offload_mode_t mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_DISABLED; switch (otIcmp6GetEchoMode(mInstance)) { case OT_ICMP6_ECHO_HANDLER_DISABLED: mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_DISABLED; break; case OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY: mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_UNICAST_ONLY; break; case OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY: mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_MULTICAST_ONLY; break; case OT_ICMP6_ECHO_HANDLER_ALL: mode = SPINEL_IPV6_ICMP_PING_OFFLOAD_ALL; break; }; return mEncoder.WriteUint8(mode); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otIcmp6EchoMode mode = OT_ICMP6_ECHO_HANDLER_DISABLED; uint8_t spinelMode; SuccessOrExit(error = mDecoder.ReadUint8(spinelMode)); switch (spinelMode) { case SPINEL_IPV6_ICMP_PING_OFFLOAD_DISABLED: mode = OT_ICMP6_ECHO_HANDLER_DISABLED; break; case SPINEL_IPV6_ICMP_PING_OFFLOAD_UNICAST_ONLY: mode = OT_ICMP6_ECHO_HANDLER_UNICAST_ONLY; break; case SPINEL_IPV6_ICMP_PING_OFFLOAD_MULTICAST_ONLY: mode = OT_ICMP6_ECHO_HANDLER_MULTICAST_ONLY; break; case SPINEL_IPV6_ICMP_PING_OFFLOAD_ALL: mode = OT_ICMP6_ECHO_HANDLER_ALL; break; }; otIcmp6SetEchoMode(mInstance, mode); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { // Note reverse logic: passthru enabled = filter disabled return mEncoder.WriteBool(!otIp6IsReceiveFilterEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { bool enabled = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); // Note reverse logic: passthru enabled = filter disabled otIp6SetReceiveFilterEnabled(mInstance, !enabled); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otExternalRouteConfig routeConfig; otNetworkDataIterator iter = OT_NETWORK_DATA_ITERATOR_INIT; while (otNetDataGetNextRoute(mInstance, &iter, &routeConfig) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(routeConfig.mPrefix.mPrefix)); SuccessOrExit(error = mEncoder.WriteUint8(routeConfig.mPrefix.mLength)); SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mStable)); SuccessOrExit(error = mEncoder.WriteUint8(ExternalRouteConfigToFlagByte(routeConfig))); SuccessOrExit(error = mEncoder.WriteBool(false)); // IsLocal SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mNextHopIsThisDevice)); SuccessOrExit(error = mEncoder.WriteUint16(routeConfig.mRloc16)); SuccessOrExit(error = mEncoder.CloseStruct()); } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE iter = OT_NETWORK_DATA_ITERATOR_INIT; while (otBorderRouterGetNextRoute(mInstance, &iter, &routeConfig) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(routeConfig.mPrefix.mPrefix)); SuccessOrExit(error = mEncoder.WriteUint8(routeConfig.mPrefix.mLength)); SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mStable)); SuccessOrExit(error = mEncoder.WriteUint8(ExternalRouteConfigToFlagByte(routeConfig))); SuccessOrExit(error = mEncoder.WriteBool(true)); // IsLocal SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mNextHopIsThisDevice)); SuccessOrExit(error = mEncoder.WriteUint16(routeConfig.mRloc16)); SuccessOrExit(error = mEncoder.CloseStruct()); } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE exit: return error; } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE static int FlagByteToExternalRoutePreference(uint8_t aFlags) { int route_preference = 0; switch (aFlags & SPINEL_NET_FLAG_PREFERENCE_MASK) { case SPINEL_ROUTE_PREFERENCE_HIGH: route_preference = OT_ROUTE_PREFERENCE_HIGH; break; case SPINEL_ROUTE_PREFERENCE_MEDIUM: route_preference = OT_ROUTE_PREFERENCE_MED; break; case SPINEL_ROUTE_PREFERENCE_LOW: route_preference = OT_ROUTE_PREFERENCE_LOW; break; } return route_preference; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; otExternalRouteConfig routeConfig; bool stable = false; uint8_t flags = 0; uint8_t prefixLength; memset(&routeConfig, 0, sizeof(otExternalRouteConfig)); VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE); SuccessOrExit(error = mDecoder.ReadIp6Address(routeConfig.mPrefix.mPrefix)); SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); SuccessOrExit(error = mDecoder.ReadBool(stable)); SuccessOrExit(error = mDecoder.ReadUint8(flags)); routeConfig.mPrefix.mLength = prefixLength; routeConfig.mStable = stable; routeConfig.mPreference = FlagByteToExternalRoutePreference(flags); routeConfig.mNat64 = ((flags & SPINEL_ROUTE_FLAG_NAT64) != 0); error = otBorderRouterAddRoute(mInstance, &routeConfig); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; otIp6Prefix ip6Prefix; uint8_t prefixLength; memset(&ip6Prefix, 0, sizeof(otIp6Prefix)); VerifyOrExit(mAllowLocalNetworkDataChange, error = OT_ERROR_INVALID_STATE); SuccessOrExit(error = mDecoder.ReadIp6Address(ip6Prefix.mPrefix)); SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); ip6Prefix.mLength = prefixLength; error = otBorderRouterRemoveRoute(mInstance, &ip6Prefix); // If the route prefix was not on the list, "remove" command is successful. if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } exit: return error; } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE template <> otError NcpBase::HandlePropertySet(void) { const uint8_t *framePtr = nullptr; uint16_t frameLen = 0; const uint8_t *metaPtr = nullptr; uint16_t metaLen = 0; otMessage *message = nullptr; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen)); SuccessOrExit(error = mDecoder.ReadData(metaPtr, metaLen)); // We ignore metadata for now. // May later include TX power, allow retransmits, etc... // STREAM_NET requires layer 2 security. message = otIp6NewMessageFromBuffer(mInstance, framePtr, frameLen, nullptr); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); error = otIp6Send(mInstance, message); exit: if (error == OT_ERROR_NONE) { mInboundSecureIpFrameCounter++; } else { mDroppedInboundIpFrameCounter++; } return error; } #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otJamDetectionIsEnabled(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otJamDetectionGetState(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteInt8(otJamDetectionGetRssiThreshold(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otJamDetectionGetWindow(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otJamDetectionGetBusyPeriod(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint64(otJamDetectionGetHistoryBitmap(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { bool enabled; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); if (enabled) { IgnoreError(otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this)); } else { IgnoreError(otJamDetectionStop(mInstance)); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { int8_t threshold = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadInt8(threshold)); error = otJamDetectionSetRssiThreshold(mInstance, threshold); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { uint8_t window = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(window)); error = otJamDetectionSetWindow(mInstance, window); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { uint8_t busy = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(busy)); error = otJamDetectionSetBusyPeriod(mInstance, busy); exit: return error; } void NcpBase::HandleJamStateChange_Jump(bool aJamState, void *aContext) { static_cast(aContext)->HandleJamStateChange(aJamState); } void NcpBase::HandleJamStateChange(bool aJamState) { OT_UNUSED_VARIABLE(aJamState); mChangedPropsSet.AddProperty(SPINEL_PROP_JAM_DETECTED); mUpdateChangedPropsTask.Post(); } #endif // OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChildSupervisionGetCheckTimeout(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint16_t timeout; SuccessOrExit(error = mDecoder.ReadUint16(timeout)); otChildSupervisionSetCheckTimeout(mInstance, timeout); exit: return error; } #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otChannelMonitorGetSampleInterval(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteInt8(otChannelMonitorGetRssiThreshold(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otChannelMonitorGetSampleWindow(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otChannelMonitorGetSampleCount(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; uint32_t channelMask = otLinkGetSupportedChannelMask(mInstance); uint8_t channelNum = sizeof(channelMask) * CHAR_BIT; for (uint8_t channel = 0; channel < channelNum; channel++) { if (!((1UL << channel) & channelMask)) { continue; } SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint8(channel)); SuccessOrExit(error = mEncoder.WriteUint16(otChannelMonitorGetChannelOccupancy(mInstance, channel))); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } #endif // OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otLinkGetCcaFailureRate(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxTotal); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAckRequested); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAcked); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxNoAckRequested); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxData); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxDataPoll); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeacon); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeaconRequest); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxOther); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxRetry); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrCca); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxUnicast); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBroadcast); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrAbort); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxTotal); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxData); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDataPoll); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeacon); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeaconRequest); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxOther); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxAddressFiltered); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDestAddrFiltered); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDuplicated); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxUnicast); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBroadcast); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrNoFrame); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrUnknownNeighbor); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrInvalidSrcAddr); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrSec); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrFcs); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrOther); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mInboundSecureIpFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mInboundInsecureIpFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mDroppedInboundIpFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mOutboundSecureIpFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mOutboundInsecureIpFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mDroppedOutboundIpFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mTxSpinelFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mRxSpinelFrameCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mRxSpinelOutOfOrderTidCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(mFramingErrorCounter); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxSuccess); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxSuccess); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxFailure); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxFailure); } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otBufferInfo bufferInfo; otMessageGetBufferInfo(mInstance, &bufferInfo); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mTotalBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mFreeBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendQueue.mNumMessages)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loSendQueue.mNumBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyQueue.mNumMessages)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.m6loReassemblyQueue.mNumBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Queue.mNumMessages)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mIp6Queue.mNumBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplQueue.mNumMessages)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMplQueue.mNumBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleQueue.mNumMessages)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mMleQueue.mNumBuffers)); SuccessOrExit(error = mEncoder.WriteUint16(0)); // Write zero for ARP for backward compatibility. SuccessOrExit(error = mEncoder.WriteUint16(0)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapQueue.mNumMessages)); SuccessOrExit(error = mEncoder.WriteUint16(bufferInfo.mCoapQueue.mNumBuffers)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otMacCounters *counters = otLinkGetCounters(mInstance); // Encode Tx related counters SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxTotal)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxUnicast)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxBroadcast)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxAckRequested)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxAcked)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxNoAckRequested)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxData)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxDataPoll)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxBeacon)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxBeaconRequest)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxOther)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxRetry)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxErrCca)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxErrAbort)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxErrBusyChannel)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxDirectMaxRetryExpiry)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxIndirectMaxRetryExpiry)); SuccessOrExit(error = mEncoder.CloseStruct()); // Encode Rx related counters SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxTotal)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxUnicast)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxBroadcast)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxData)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxDataPoll)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxBeacon)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxBeaconRequest)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxOther)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxAddressFiltered)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxDestAddrFiltered)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxDuplicated)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrNoFrame)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrUnknownNeighbor)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrInvalidSrcAddr)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrSec)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrFcs)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxErrOther)); SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otLinkResetCounters(mInstance); return OT_ERROR_NONE; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otMleCounters *counters = otThreadGetMleCounters(mInstance); OT_ASSERT(counters != nullptr); SuccessOrExit(error = mEncoder.WriteUint16(counters->mDisabledRole)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mDetachedRole)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mChildRole)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mRouterRole)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mLeaderRole)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mAttachAttempts)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mPartitionIdChanges)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mBetterPartitionAttachAttempts)); SuccessOrExit(error = mEncoder.WriteUint16(counters->mParentChanges)); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otThreadResetMleCounters(mInstance); return OT_ERROR_NONE; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otIpCounters *counters = otThreadGetIp6Counters(mInstance); OT_ASSERT(counters != nullptr); // Encode Tx related counters SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxSuccess)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mTxFailure)); SuccessOrExit(error = mEncoder.CloseStruct()); // Encode Rx related counters SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxSuccess)); SuccessOrExit(error = mEncoder.WriteUint32(counters->mRxFailure)); SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const uint32_t *histogramDirect; const uint32_t *histogramIndirect; uint8_t histogramDirectEntries; uint8_t histogramIndirectEntries; histogramDirect = otLinkGetTxDirectRetrySuccessHistogram(mInstance, &histogramDirectEntries); histogramIndirect = otLinkGetTxIndirectRetrySuccessHistogram(mInstance, &histogramIndirectEntries); OT_ASSERT((histogramDirectEntries == 0) || (histogramDirect != nullptr)); OT_ASSERT((histogramIndirectEntries == 0) || (histogramIndirect != nullptr)); // Encode direct message retries histogram SuccessOrExit(error = mEncoder.OpenStruct()); for (uint8_t i = 0; i < histogramDirectEntries; i++) { SuccessOrExit(error = mEncoder.WriteUint32(histogramDirect[i])); } SuccessOrExit(error = mEncoder.CloseStruct()); // Encode indirect message retries histogram SuccessOrExit(error = mEncoder.OpenStruct()); for (uint8_t i = 0; i < histogramIndirectEntries; i++) { SuccessOrExit(error = mEncoder.WriteUint32(histogramIndirect[i])); } SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otLinkResetTxRetrySuccessHistogram(mInstance); return OT_ERROR_NONE; } #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE template <> otError NcpBase::HandlePropertySet(void) { otThreadResetIp6Counters(mInstance); return OT_ERROR_NONE; } #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otMacFilterEntry entry; otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT; otError error = OT_ERROR_NONE; while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(entry.mExtAddress)); SuccessOrExit(error = mEncoder.WriteInt8(entry.mRssIn)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST); } template <> otError NcpBase::HandlePropertyGet(void) { otMacFilterEntry entry; otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT; otError error = OT_ERROR_NONE; while (otLinkFilterGetNextAddress(mInstance, &iterator, &entry) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(entry.mExtAddress)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST); } template <> otError NcpBase::HandlePropertyGet(void) { otMacFilterEntry entry; otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT; otError error = OT_ERROR_NONE; while (otLinkFilterGetNextRssIn(mInstance, &iterator, &entry) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(entry.mExtAddress)); SuccessOrExit(error = mEncoder.WriteInt8(entry.mRssIn)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; // First, clear the address filter entries. otLinkFilterClearAddresses(mInstance); while (mDecoder.GetRemainingLengthInStruct() > 0) { const otExtAddress *extAddress = nullptr; int8_t rss; SuccessOrExit(error = mDecoder.OpenStruct()); SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadInt8(rss)); } else { rss = OT_MAC_FILTER_FIXED_RSS_DISABLED; } SuccessOrExit(error = mDecoder.CloseStruct()); error = otLinkFilterAddAddress(mInstance, extAddress); if (error == OT_ERROR_ALREADY) { error = OT_ERROR_NONE; } SuccessOrExit(error); if (rss != OT_MAC_FILTER_FIXED_RSS_DISABLED) { SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, extAddress, rss)); } } exit: // If we had an error, we may have actually changed // the state of the allowlist, so we need to report // those incomplete changes via an asynchronous // change event. if (error != OT_ERROR_NONE) { IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_ALLOWLIST)); } return error; } template <> otError NcpBase::HandlePropertySet(void) { bool enabled; otError error = OT_ERROR_NONE; otMacFilterAddressMode mode = OT_MAC_FILTER_ADDRESS_MODE_DISABLED; SuccessOrExit(error = mDecoder.ReadBool(enabled)); if (enabled) { mode = OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST; } otLinkFilterSetAddressMode(mInstance, mode); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; // First, clear the address filter entries. otLinkFilterClearAddresses(mInstance); while (mDecoder.GetRemainingLengthInStruct() > 0) { const otExtAddress *extAddress = nullptr; SuccessOrExit(error = mDecoder.OpenStruct()); SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); SuccessOrExit(error = mDecoder.CloseStruct()); SuccessOrExit(error = otLinkFilterAddAddress(mInstance, extAddress)); } exit: // If we had an error, we may have actually changed // the state of the denylist, so we need to report // those incomplete changes via an asynchronous // change event. if (error != OT_ERROR_NONE) { IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_DENYLIST)); } return error; } template <> otError NcpBase::HandlePropertySet(void) { bool enabled; otError error = OT_ERROR_NONE; otMacFilterAddressMode mode = OT_MAC_FILTER_ADDRESS_MODE_DISABLED; SuccessOrExit(error = mDecoder.ReadBool(enabled)); if (enabled) { mode = OT_MAC_FILTER_ADDRESS_MODE_DENYLIST; } otLinkFilterSetAddressMode(mInstance, mode); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; // First, clear the address filter entries. otLinkFilterClearAllRssIn(mInstance); while (mDecoder.GetRemainingLengthInStruct() > 0) { const otExtAddress *extAddress; int8_t rss; SuccessOrExit(error = mDecoder.OpenStruct()); if (mDecoder.GetRemainingLengthInStruct() > sizeof(otExtAddress)) { SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); } else { extAddress = nullptr; } SuccessOrExit(error = mDecoder.ReadInt8(rss)); SuccessOrExit(error = mDecoder.CloseStruct()); if (extAddress != nullptr) { SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, extAddress, rss)); } else { otLinkFilterSetDefaultRssIn(mInstance, rss); } } exit: // If we had an error, we may have actually changed // the state of the RssIn filter, so we need to report // those incomplete changes via an asynchronous // change event. if (error != OT_ERROR_NONE) { IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_FIXED_RSS)); } return error; } #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; struct otIp6Address address; uint8_t seriesId; otLinkMetrics linkMetrics = {false, false, false, false, false}; SuccessOrExit(error = mDecoder.ReadIp6Address(address)); SuccessOrExit(error = mDecoder.ReadUint8(seriesId)); SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ true)); error = otLinkMetricsQuery(mInstance, &address, seriesId, &linkMetrics, &NcpBase::HandleLinkMetricsReport_Jump, this); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; struct otIp6Address address; uint8_t seriesId; uint8_t length; SuccessOrExit(error = mDecoder.ReadIp6Address(address)); SuccessOrExit(error = mDecoder.ReadUint8(seriesId)); SuccessOrExit(error = mDecoder.ReadUint8(length)); error = otLinkMetricsSendLinkProbe(mInstance, &address, seriesId, length); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; struct otIp6Address address; uint8_t controlFlags; otLinkMetrics linkMetrics = {false, false, false, false, false}; SuccessOrExit(error = mDecoder.ReadIp6Address(address)); SuccessOrExit(error = mDecoder.ReadUint8(controlFlags)); SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ false)); error = otLinkMetricsConfigEnhAckProbing(mInstance, &address, static_cast(controlFlags), controlFlags ? &linkMetrics : nullptr, &NcpBase::HandleLinkMetricsMgmtResponse_Jump, this, &NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump, this); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; struct otIp6Address address; uint8_t seriesId; uint8_t types; otLinkMetrics linkMetrics = {false, false, false, false, false}; otLinkMetricsSeriesFlags seriesFlags = {false, false, false, false}; SuccessOrExit(error = mDecoder.ReadIp6Address(address)); SuccessOrExit(error = mDecoder.ReadUint8(seriesId)); SuccessOrExit(error = mDecoder.ReadUint8(types)); SuccessOrExit(error = DecodeLinkMetrics(&linkMetrics, /* aAllowPduCount */ true)); if (types & SPINEL_THREAD_FRAME_TYPE_MLE_LINK_PROBE) { seriesFlags.mLinkProbe = true; } if (types & SPINEL_THREAD_FRAME_TYPE_MAC_DATA) { seriesFlags.mMacData = true; } if (types & SPINEL_THREAD_FRAME_TYPE_MAC_DATA_REQUEST) { seriesFlags.mMacDataRequest = true; } if (types & SPINEL_THREAD_FRAME_TYPE_MAC_ACK) { seriesFlags.mMacAck = true; } error = otLinkMetricsConfigForwardTrackingSeries(mInstance, &address, seriesId, seriesFlags, &linkMetrics, &NcpBase::HandleLinkMetricsMgmtResponse_Jump, this); exit: return error; } #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { uint8_t numericMode; otLinkModeConfig modeConfig = otThreadGetLinkMode(mInstance); numericMode = LinkFlagsToFlagByte(modeConfig.mRxOnWhenIdle, modeConfig.mDeviceType, modeConfig.mNetworkData); return mEncoder.WriteUint8(numericMode); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t numericMode = 0; otLinkModeConfig modeConfig; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(numericMode)); modeConfig.mRxOnWhenIdle = ((numericMode & SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE) == SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE); modeConfig.mDeviceType = ((numericMode & SPINEL_THREAD_MODE_FULL_THREAD_DEV) == SPINEL_THREAD_MODE_FULL_THREAD_DEV); modeConfig.mNetworkData = ((numericMode & SPINEL_THREAD_MODE_FULL_NETWORK_DATA) == SPINEL_THREAD_MODE_FULL_NETWORK_DATA); error = otThreadSetLinkMode(mInstance, modeConfig); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetChildTimeout(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t timeout = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(timeout)); otThreadSetChildTimeout(mInstance, timeout); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otThreadGetRloc16(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(mRequireJoinExistingNetwork); } template <> otError NcpBase::HandlePropertySet(void) { return mDecoder.ReadBool(mRequireJoinExistingNetwork); } template <> otError NcpBase::HandlePropertySet(void) { const uint8_t *framePtr = nullptr; uint16_t frameLen = 0; const uint8_t *metaPtr = nullptr; uint16_t metaLen = 0; otMessage *message = nullptr; otError error = OT_ERROR_NONE; otMessageSettings msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL}; SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen)); SuccessOrExit(error = mDecoder.ReadData(metaPtr, metaLen)); // We ignore metadata for now. // May later include TX power, allow retransmits, etc... // STREAM_NET_INSECURE packets are not secured at layer 2. message = otIp6NewMessageFromBuffer(mInstance, framePtr, frameLen, &msgSettings); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); // Ensure the insecure message is forwarded using direct transmission. otMessageSetDirectTransmission(message, true); error = otIp6Send(mInstance, message); exit: if (error == OT_ERROR_NONE) { mInboundInsecureIpFrameCounter++; } else { mDroppedInboundIpFrameCounter++; } return error; } template <> otError NcpBase::HandlePropertySet(void) { otLinkResetCounters(mInstance); #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE otLinkResetTxRetrySuccessHistogram(mInstance); #endif otThreadResetIp6Counters(mInstance); otThreadResetMleCounters(mInstance); ResetCounters(); return OT_ERROR_NONE; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; uint16_t port; SuccessOrExit(error = mDecoder.ReadUint16(port)); error = otIp6AddUnsecurePort(mInstance, port); exit: return error; } #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; const otExtAddress *extAddress; int8_t rss = OT_MAC_FILTER_FIXED_RSS_DISABLED; SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); if (!mDecoder.IsAllRead()) { SuccessOrExit(error = mDecoder.ReadInt8(rss)); } error = otLinkFilterAddAddress(mInstance, extAddress); if (error == OT_ERROR_ALREADY) { error = OT_ERROR_NONE; } SuccessOrExit(error); if (rss != OT_MAC_FILTER_FIXED_RSS_DISABLED) { error = otLinkFilterAddRssIn(mInstance, extAddress, rss); } exit: return error; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; const otExtAddress *extAddress; SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); error = otLinkFilterAddAddress(mInstance, extAddress); if (error == OT_ERROR_ALREADY) { error = OT_ERROR_NONE; } exit: return error; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; const otExtAddress *extAddress = nullptr; int8_t rss = OT_MAC_FILTER_FIXED_RSS_DISABLED; if (mDecoder.GetRemainingLength() > sizeof(int8_t)) { SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); } SuccessOrExit(error = mDecoder.ReadInt8(rss)); if (extAddress != nullptr) { error = otLinkFilterAddRssIn(mInstance, extAddress, rss); } else { otLinkFilterSetDefaultRssIn(mInstance, rss); } exit: return error; } #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; uint16_t port; SuccessOrExit(error = mDecoder.ReadUint16(port)); error = otIp6RemoveUnsecurePort(mInstance, port); // If unsecure port was not on the list, "remove" command is successful. if (error == OT_ERROR_NOT_FOUND) { error = OT_ERROR_NONE; } exit: return error; } #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; const otExtAddress *extAddress = nullptr; SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); otLinkFilterRemoveAddress(mInstance, extAddress); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; const otExtAddress *extAddress = nullptr; SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); otLinkFilterRemoveAddress(mInstance, extAddress); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; const otExtAddress *extAddress = nullptr; if (mDecoder.GetRemainingLength() > 0) { SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); } if (extAddress != nullptr) { otLinkFilterRemoveRssIn(mInstance, extAddress); } else { otLinkFilterClearDefaultRssIn(mInstance); } exit: return error; } #endif // OPENTHREAD_CONFIG_MAC_FILTER_ENABLE #if OPENTHREAD_PLATFORM_POSIX template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUtf8(otGetRadioVersionString(mInstance)); } #endif #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otIp6IsSlaacEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool enabled; SuccessOrExit(error = mDecoder.ReadBool(enabled)); otIp6SetSlaacEnabled(mInstance, enabled); exit: return error; } #endif // OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otError error; #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_RADIO_LINK_IEEE_802_15_4)); #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_RADIO_LINK_TREL_UDP6)); #endif exit: return error; } #if OPENTHREAD_CONFIG_MULTI_RADIO otError NcpBase::EncodeNeighborMultiRadioInfo(uint32_t aSpinelRadioLink, const otRadioLinkInfo &aInfo) { otError error; SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUintPacked(aSpinelRadioLink)); SuccessOrExit(error = mEncoder.WriteUint8(aInfo.mPreference)); SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; otNeighborInfo neighInfo; otMultiRadioNeighborInfo multiRadioInfo; while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(neighInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mRloc16)); if (otMultiRadioGetNeighborInfo(mInstance, &neighInfo.mExtAddress, &multiRadioInfo) == OT_ERROR_NONE) { if (multiRadioInfo.mSupportsIeee802154) { SuccessOrExit(error = EncodeNeighborMultiRadioInfo(SPINEL_RADIO_LINK_IEEE_802_15_4, multiRadioInfo.mIeee802154Info)); } if (multiRadioInfo.mSupportsTrelUdp6) { SuccessOrExit( error = EncodeNeighborMultiRadioInfo(SPINEL_RADIO_LINK_TREL_UDP6, multiRadioInfo.mTrelUdp6Info)); } } SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } #endif // OPENTHREAD_CONFIG_MULTI_RADIO // ---------------------------------------------------------------------------- // SRP Client // ---------------------------------------------------------------------------- #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool start; bool callbackEnabled; otSockAddr serverAddr; SuccessOrExit(error = mDecoder.ReadBool(start)); if (!start) { otSrpClientStop(mInstance); ExitNow(); } SuccessOrExit(error = mDecoder.ReadIp6Address(serverAddr.mAddress)); SuccessOrExit(error = mDecoder.ReadUint16(serverAddr.mPort)); SuccessOrExit(error = mDecoder.ReadBool(callbackEnabled)); SuccessOrExit(error = otSrpClientStart(mInstance, &serverAddr)); mSrpClientCallbackEnabled = callbackEnabled; exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otSrpClientGetLeaseInterval(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error; uint32_t interval; SuccessOrExit(error = mDecoder.ReadUint32(interval)); otSrpClientSetLeaseInterval(mInstance, interval); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otSrpClientGetKeyLeaseInterval(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error; uint32_t interval; SuccessOrExit(error = mDecoder.ReadUint32(interval)); otSrpClientSetKeyLeaseInterval(mInstance, interval); exit: return error; } static spinel_srp_client_item_state_t SrpClientItemStateToSpinel(otSrpClientItemState aItemState) { spinel_srp_client_item_state_t state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED; switch (aItemState) { case OT_SRP_CLIENT_ITEM_STATE_TO_ADD: state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_ADD; break; case OT_SRP_CLIENT_ITEM_STATE_ADDING: state = SPINEL_SRP_CLIENT_ITEM_STATE_ADDING; break; case OT_SRP_CLIENT_ITEM_STATE_TO_REFRESH: state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_REFRESH; break; case OT_SRP_CLIENT_ITEM_STATE_REFRESHING: state = SPINEL_SRP_CLIENT_ITEM_STATE_REFRESHING; break; case OT_SRP_CLIENT_ITEM_STATE_TO_REMOVE: state = SPINEL_SRP_CLIENT_ITEM_STATE_TO_REMOVE; break; case OT_SRP_CLIENT_ITEM_STATE_REMOVING: state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVING; break; case OT_SRP_CLIENT_ITEM_STATE_REGISTERED: state = SPINEL_SRP_CLIENT_ITEM_STATE_REGISTERED; break; case OT_SRP_CLIENT_ITEM_STATE_REMOVED: state = SPINEL_SRP_CLIENT_ITEM_STATE_REMOVED; break; } return state; } otError NcpBase::EncodeSrpClientHostInfo(const otSrpClientHostInfo &aHostInfo) { otError error; SuccessOrExit(error = mEncoder.WriteUtf8(aHostInfo.mName != nullptr ? aHostInfo.mName : "")); SuccessOrExit(error = mEncoder.WriteUint8(SrpClientItemStateToSpinel(aHostInfo.mState))); SuccessOrExit(error = mEncoder.OpenStruct()); for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) { SuccessOrExit(error = mEncoder.WriteIp6Address(aHostInfo.mAddresses[index])); } SuccessOrExit(error = mEncoder.CloseStruct()); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return EncodeSrpClientHostInfo(*otSrpClientGetHostInfo(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { const char *name = otSrpClientGetHostInfo(mInstance)->mName; return mEncoder.WriteUtf8(name != nullptr ? name : ""); } template <> otError NcpBase::HandlePropertySet(void) { otError error; const char *name; uint16_t size; char *hostNameBuffer; SuccessOrExit(error = mDecoder.ReadUtf8(name)); hostNameBuffer = otSrpClientBuffersGetHostNameString(mInstance, &size); VerifyOrExit(StringLength(name, size) < size, error = OT_ERROR_INVALID_ARGS); // We first make sure we can set the name, and if so // we copy it to the persisted buffer and set // the host name again now with the persisted buffer. // This ensures that we do not overwrite a previous // buffer with a host name that cannot be set. SuccessOrExit(error = otSrpClientSetHostName(mInstance, name)); strcpy(hostNameBuffer, name); SuccessOrAssert(error = otSrpClientSetHostName(mInstance, hostNameBuffer)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(mInstance); for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) { SuccessOrExit(error = mEncoder.WriteIp6Address(hostInfo->mAddresses[index])); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error; otIp6Address addresses[kSrpClientMaxHostAddresses]; uint8_t numAddresses = 0; otIp6Address *hostAddressArray; uint8_t hostAddressArrayLength; hostAddressArray = otSrpClientBuffersGetHostAddressesArray(mInstance, &hostAddressArrayLength); OT_ASSERT(hostAddressArrayLength <= kSrpClientMaxHostAddresses); while (!mDecoder.IsAllReadInStruct()) { VerifyOrExit(numAddresses < kSrpClientMaxHostAddresses, error = OT_ERROR_NO_BUFS); SuccessOrExit(error = mDecoder.ReadIp6Address(addresses[numAddresses])); numAddresses++; } // We first make sure we can set the addresses, and if so we copy // the address list into `hostAddressArray` and set it again. This // ensures that we do not overwrite a previous list before we know // it is safe to set/change the address list. SuccessOrExit(error = otSrpClientSetHostAddresses(mInstance, addresses, numAddresses)); memcpy(hostAddressArray, addresses, sizeof(addresses)); SuccessOrAssert(error = otSrpClientSetHostAddresses(mInstance, hostAddressArray, numAddresses)); exit: return error; } otError NcpBase::EncodeSrpClientServices(const otSrpClientService *aServices) { otError error = OT_ERROR_NONE; for (; aServices != nullptr; aServices = aServices->mNext) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteUtf8(aServices->mName)); SuccessOrExit(error = mEncoder.WriteUtf8(aServices->mInstanceName)); SuccessOrExit(error = mEncoder.WriteUint16(aServices->mPort)); SuccessOrExit(error = mEncoder.WriteUint16(aServices->mPriority)); SuccessOrExit(error = mEncoder.WriteUint16(aServices->mWeight)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return EncodeSrpClientServices(otSrpClientGetServices(mInstance)); } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; otSrpClientBuffersServiceEntry *entry = nullptr; const char *serviceName; const char *instanceName; char *stringBuffer; uint16_t size; entry = otSrpClientBuffersAllocateService(mInstance); VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS); stringBuffer = otSrpClientBuffersGetServiceEntryServiceNameString(entry, &size); SuccessOrExit(error = mDecoder.ReadUtf8(serviceName)); VerifyOrExit(StringLength(serviceName, size) < size, error = OT_ERROR_INVALID_ARGS); strcpy(stringBuffer, serviceName); stringBuffer = otSrpClientBuffersGetServiceEntryInstanceNameString(entry, &size); SuccessOrExit(error = mDecoder.ReadUtf8(instanceName)); VerifyOrExit(StringLength(instanceName, size) < size, error = OT_ERROR_INVALID_ARGS); strcpy(stringBuffer, instanceName); SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mPort)); SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mPriority)); SuccessOrExit(error = mDecoder.ReadUint16(entry->mService.mWeight)); SuccessOrExit(error = otSrpClientAddService(mInstance, &entry->mService)); entry = nullptr; exit: if (entry != nullptr) { otSrpClientBuffersFreeService(mInstance, entry); } return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; const char *serviceName; const char *instanceName; bool toClear = false; const otSrpClientService *service; SuccessOrExit(error = mDecoder.ReadUtf8(serviceName)); SuccessOrExit(error = mDecoder.ReadUtf8(instanceName)); if (!mDecoder.IsAllReadInStruct()) { SuccessOrExit(error = mDecoder.ReadBool(toClear)); } for (service = otSrpClientGetServices(mInstance); service != nullptr; service = service->mNext) { if ((strcmp(serviceName, service->mName) == 0) || (strcmp(instanceName, service->mInstanceName) == 0)) { break; } } VerifyOrExit(service != nullptr, error = OT_ERROR_NOT_FOUND); if (toClear) { SuccessOrExit(error = otSrpClientClearService(mInstance, const_cast(service))); otSrpClientBuffersFreeService( mInstance, reinterpret_cast(const_cast(service))); } else { error = otSrpClientRemoveService(mInstance, const_cast(service)); } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool removeKeyLease; bool sendUnregToServer; SuccessOrExit(error = mDecoder.ReadBool(removeKeyLease)); SuccessOrExit(error = mDecoder.ReadBool(sendUnregToServer)); error = otSrpClientRemoveHostAndServices(mInstance, removeKeyLease, sendUnregToServer); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otSrpClientClearHostAndServices(mInstance); return OT_ERROR_NONE; } static spinel_srp_client_error_t SrpClientErrorToSpinelError(otError aError) { spinel_srp_client_error_t error = SPINEL_SRP_CLIENT_ERROR_FAILED; switch (aError) { case OT_ERROR_NONE: error = SPINEL_SRP_CLIENT_ERROR_NONE; break; case OT_ERROR_PARSE: error = SPINEL_SRP_CLIENT_ERROR_PARSE; break; case OT_ERROR_NOT_FOUND: error = SPINEL_SRP_CLIENT_ERROR_NOT_FOUND; break; case OT_ERROR_NOT_IMPLEMENTED: error = SPINEL_SRP_CLIENT_ERROR_NOT_IMPLEMENTED; break; case OT_ERROR_SECURITY: error = SPINEL_SRP_CLIENT_ERROR_SECURITY; break; case OT_ERROR_DUPLICATED: error = SPINEL_SRP_CLIENT_ERROR_DUPLICATED; break; case OT_ERROR_RESPONSE_TIMEOUT: error = SPINEL_SRP_CLIENT_ERROR_RESPONSE_TIMEOUT; break; case OT_ERROR_INVALID_ARGS: error = SPINEL_SRP_CLIENT_ERROR_INVALID_ARGS; break; case OT_ERROR_NO_BUFS: error = SPINEL_SRP_CLIENT_ERROR_NO_BUFS; break; case OT_ERROR_FAILED: default: error = SPINEL_SRP_CLIENT_ERROR_FAILED; break; } return error; } void NcpBase::HandleSrpClientCallback(otError aError, const otSrpClientHostInfo *aHostInfo, const otSrpClientService *aServices, const otSrpClientService *aRemovedServices, void *aContext) { static_cast(aContext)->HandleSrpClientCallback(aError, aHostInfo, aServices, aRemovedServices); } void NcpBase::HandleSrpClientCallback(otError aError, const otSrpClientHostInfo *aHostInfo, const otSrpClientService *aServices, const otSrpClientService *aRemovedServices) { otError error = OT_ERROR_NONE; const otSrpClientService *service; const otSrpClientService *next; VerifyOrExit(mSrpClientCallbackEnabled); SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_SRP_CLIENT_EVENT)); SuccessOrExit(error = mEncoder.WriteUint16(SrpClientErrorToSpinelError(aError))); SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = EncodeSrpClientHostInfo(*aHostInfo)); SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = EncodeSrpClientServices(aServices)); SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = EncodeSrpClientServices(aRemovedServices)); SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.EndFrame()); exit: if (error != OT_ERROR_NONE) { // Emit a NONMEM status if we fail to send the event. mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); mUpdateChangedPropsTask.Post(); } for (service = aRemovedServices; service != nullptr; service = next) { next = service->mNext; otSrpClientBuffersFreeService( mInstance, reinterpret_cast(const_cast(service))); } } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otSrpClientIsServiceKeyRecordEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool enabled; SuccessOrExit(error = mDecoder.ReadBool(enabled)); otSrpClientSetServiceKeyRecordEnabled(mInstance, enabled); exit: return error; } #endif #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(!otTrelIsFilterEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool testMode; SuccessOrExit(error = mDecoder.ReadBool(testMode)); // Note that `TEST_MODE` being `true` indicates that the TREL // interface should be enabled and functional, so filtering // should be disabled. otTrelSetFilterEnabled(mInstance, !testMode); exit: return error; } #endif #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otNetworkTimeStatus networkTimeStatus; uint64_t time; networkTimeStatus = otNetworkTimeGet(mInstance, &time); SuccessOrExit(error = mEncoder.WriteUint64(time)); SuccessOrExit(error = mEncoder.WriteInt8((int8_t)networkTimeStatus)); exit: return error; } void NcpBase::HandleTimeSyncUpdate(void *aContext) { static_cast(aContext)->HandleTimeSyncUpdate(); } void NcpBase::HandleTimeSyncUpdate(void) { mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_NETWORK_TIME); mUpdateChangedPropsTask.Post(); } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE void NcpBase::HandleActiveScanResult_Jump(otActiveScanResult *aResult, void *aContext) { static_cast(aContext)->HandleActiveScanResult(aResult); } // ---------------------------------------------------------------------------- // MARK: Scan Results Glue // ---------------------------------------------------------------------------- void NcpBase::HandleActiveScanResult(otActiveScanResult *aResult) { otError error = OT_ERROR_NONE; if (aResult) { uint8_t flags = static_cast(aResult->mVersion << SPINEL_BEACON_THREAD_FLAG_VERSION_SHIFT); if (aResult->mIsNative) { flags |= SPINEL_BEACON_THREAD_FLAG_NATIVE; } SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_MAC_SCAN_BEACON)); SuccessOrExit(error = mEncoder.WriteUint8(aResult->mChannel)); SuccessOrExit(error = mEncoder.WriteInt8(aResult->mRssi)); SuccessOrExit(error = mEncoder.OpenStruct()); // "mac-layer data" SuccessOrExit(error = mEncoder.WriteEui64(aResult->mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(0xffff)); // short address, not given SuccessOrExit(error = mEncoder.WriteUint16(aResult->mPanId)); SuccessOrExit(error = mEncoder.WriteUint8(aResult->mLqi)); SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.OpenStruct()); // "net-layer data" SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROTOCOL_TYPE_THREAD)); // type SuccessOrExit(error = mEncoder.WriteUint8(flags)); SuccessOrExit(error = mEncoder.WriteUtf8(aResult->mNetworkName.m8)); SuccessOrExit(error = mEncoder.WriteDataWithLen(aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE)); SuccessOrExit(error = mEncoder.WriteDataWithLen(aResult->mSteeringData.m8, aResult->mSteeringData.mLength)); SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.EndFrame()); } else { // We are finished with the scan, send an unsolicited // scan state update. mChangedPropsSet.AddProperty(SPINEL_PROP_MAC_SCAN_STATE); mUpdateChangedPropsTask.Post(); } exit: if (error != OT_ERROR_NONE) { // We ran out of buffer adding a scan result so remember to send // an async `LAST_STATUS(NOMEM)` when buffer space becomes // available. mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); mUpdateChangedPropsTask.Post(); } } void NcpBase::HandleEnergyScanResult_Jump(otEnergyScanResult *aResult, void *aContext) { static_cast(aContext)->HandleEnergyScanResult(aResult); } void NcpBase::HandleEnergyScanResult(otEnergyScanResult *aResult) { otError error = OT_ERROR_NONE; if (aResult) { SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)); SuccessOrExit(error = mEncoder.WriteUint8(aResult->mChannel)); SuccessOrExit(error = mEncoder.WriteInt8(aResult->mMaxRssi)); SuccessOrExit(error = mEncoder.EndFrame()); } else { // We are finished with the scan, send an unsolicited // scan state update. mChangedPropsSet.AddProperty(SPINEL_PROP_MAC_SCAN_STATE); mUpdateChangedPropsTask.Post(); } exit: if (error != OT_ERROR_NONE) { mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); mUpdateChangedPropsTask.Post(); } } #if OPENTHREAD_CONFIG_JOINER_ENABLE void NcpBase::HandleJoinerCallback_Jump(otError aError, void *aContext) { static_cast(aContext)->HandleJoinerCallback(aError); } void NcpBase::HandleJoinerCallback(otError aError) { switch (aError) { case OT_ERROR_NONE: mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_SUCCESS); break; case OT_ERROR_SECURITY: mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_SECURITY); break; case OT_ERROR_NOT_FOUND: mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_NO_PEERS); break; case OT_ERROR_RESPONSE_TIMEOUT: mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_RSP_TIMEOUT); break; default: mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_FAILURE); break; } mUpdateChangedPropsTask.Post(); } #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE void NcpBase::HandleLinkMetricsReport_Jump(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus, void *aContext) { static_cast(aContext)->HandleLinkMetricsReport(aSource, aMetricsValues, aStatus); } void NcpBase::HandleLinkMetricsReport(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus) { SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_LINK_METRICS_QUERY_RESULT)); SuccessOrExit(mEncoder.WriteIp6Address(*aSource)); SuccessOrExit(mEncoder.WriteUint8(aStatus)); SuccessOrExit(EncodeLinkMetricsValues(aMetricsValues)); SuccessOrExit(mEncoder.EndFrame()); exit: return; } void NcpBase::HandleLinkMetricsMgmtResponse_Jump(const otIp6Address *aSource, uint8_t aStatus, void *aContext) { static_cast(aContext)->HandleLinkMetricsMgmtResponse(aSource, aStatus); } void NcpBase::HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, uint8_t aStatus) { SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_LINK_METRICS_MGMT_RESPONSE)); SuccessOrExit(mEncoder.WriteIp6Address(*aSource)); SuccessOrExit(mEncoder.WriteUint8(aStatus)); SuccessOrExit(mEncoder.EndFrame()); exit: return; } void NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress aShortAddress, const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, void *aContext) { static_cast(aContext)->HandleLinkMetricsEnhAckProbingIeReport(aShortAddress, aExtAddress, aMetricsValues); } void NcpBase::HandleLinkMetricsEnhAckProbingIeReport(otShortAddress aShortAddress, const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues) { SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_LINK_METRICS_MGMT_ENH_ACK_IE)); SuccessOrExit(mEncoder.WriteUint16(aShortAddress)); SuccessOrExit(mEncoder.WriteEui64(*aExtAddress)); SuccessOrExit(EncodeLinkMetricsValues(aMetricsValues)); SuccessOrExit(mEncoder.EndFrame()); exit: return; } #endif // ---------------------------------------------------------------------------- // MARK: Outbound Datagram Handling // ---------------------------------------------------------------------------- void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext) { static_cast(aContext)->HandleDatagramFromStack(aMessage); } void NcpBase::HandleDatagramFromStack(otMessage *aMessage) { VerifyOrExit(aMessage != nullptr); // Do not forward frames larger than SPINEL payload size. VerifyOrExit(otMessageGetLength(aMessage) <= SPINEL_FRAME_MAX_COMMAND_PAYLOAD_SIZE, otMessageFree(aMessage)); otMessageQueueEnqueue(&mMessageQueue, aMessage); // If there is no queued spinel command response, try to write/send // the datagram message immediately. If there is a queued response // or if currently out of buffer space, the IPv6 datagram message // will be sent from `HandleFrameRemovedFromNcpBuffer()` when buffer // space becomes available and after any pending spinel command // response. if (IsResponseQueueEmpty()) { IgnoreError(SendQueuedDatagramMessages()); } exit: return; } otError NcpBase::SendDatagramMessage(otMessage *aMessage) { otError error = OT_ERROR_NONE; uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; bool isSecure = otMessageIsLinkSecurityEnabled(aMessage); spinel_prop_key_t propKey = isSecure ? SPINEL_PROP_STREAM_NET : SPINEL_PROP_STREAM_NET_INSECURE; SuccessOrExit(error = mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, propKey)); SuccessOrExit(error = mEncoder.WriteUint16(otMessageGetLength(aMessage))); SuccessOrExit(error = mEncoder.WriteMessage(aMessage)); // Append any metadata (rssi, lqi, channel, etc) here! SuccessOrExit(error = mEncoder.EndFrame()); if (isSecure) { mOutboundSecureIpFrameCounter++; } else { mOutboundInsecureIpFrameCounter++; } exit: return error; } otError NcpBase::SendQueuedDatagramMessages(void) { otError error = OT_ERROR_NONE; otMessage *message; while ((message = otMessageQueueGetHead(&mMessageQueue)) != nullptr) { // Since an `otMessage` instance can be in one queue at a time, // it is first dequeued from `mMessageQueue` before attempting // to include it in a spinel frame by calling `SendDatagramMessage()` // If forming of the spinel frame fails, the message is enqueued // back at the front of `mMessageQueue`. otMessageQueueDequeue(&mMessageQueue, message); error = SendDatagramMessage(message); if (error != OT_ERROR_NONE) { otMessageQueueEnqueueAtHead(&mMessageQueue, message); } SuccessOrExit(error); } exit: return error; } #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE template <> otError NcpBase::HandlePropertySet(void) { const uint8_t *framePtr = nullptr; uint16_t frameLen = 0; const otIp6Address *peerAddr; uint16_t peerPort; uint16_t sockPort; otMessage *message; otError error = OT_ERROR_NONE; otMessageSettings msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL}; message = otIp6NewMessage(mInstance, &msgSettings); VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen)); SuccessOrExit(error = mDecoder.ReadUint16(peerPort)); SuccessOrExit(error = mDecoder.ReadIp6Address(peerAddr)); SuccessOrExit(error = mDecoder.ReadUint16(sockPort)); SuccessOrExit(error = otMessageAppend(message, framePtr, static_cast(frameLen))); otUdpForwardReceive(mInstance, message, peerPort, peerAddr, sockPort); // `otUdpForwardReceive()` takes ownership of `message` (in both success // or failure cases). `message` is set to nullptr so it is not freed at // exit. message = nullptr; exit: if (message != nullptr) { otMessageFree(message); } return error; } void NcpBase::HandleUdpForwardStream(otMessage *aMessage, uint16_t aPeerPort, otIp6Address *aPeerAddr, uint16_t aSockPort, void *aContext) { static_cast(aContext)->HandleUdpForwardStream(aMessage, aPeerPort, *aPeerAddr, aSockPort); } void NcpBase::HandleUdpForwardStream(otMessage *aMessage, uint16_t aPeerPort, otIp6Address &aPeerAddr, uint16_t aPort) { uint16_t length = otMessageGetLength(aMessage); uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_THREAD_UDP_FORWARD_STREAM)); SuccessOrExit(mEncoder.WriteUint16(length)); SuccessOrExit(mEncoder.WriteMessage(aMessage)); SuccessOrExit(mEncoder.WriteUint16(aPeerPort)); SuccessOrExit(mEncoder.WriteIp6Address(aPeerAddr)); SuccessOrExit(mEncoder.WriteUint16(aPort)); SuccessOrExit(mEncoder.EndFrame()); // The `aMessage` is owned by the outbound frame and NCP buffer // after frame was finished/ended successfully. It will be freed // when the frame is successfully sent and removed. aMessage = nullptr; exit: if (aMessage != nullptr) { otMessageFree(aMessage); } } #endif // OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE // ---------------------------------------------------------------------------- // MARK: Pcap frame handling // ---------------------------------------------------------------------------- void NcpBase::HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx, void *aContext) { static_cast(aContext)->HandlePcapFrame(aFrame, aIsTx); } void NcpBase::HandlePcapFrame(const otRadioFrame *aFrame, bool aIsTx) { uint16_t flags = 0; uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; VerifyOrExit(mPcapEnabled); if (aIsTx) { flags |= SPINEL_MD_FLAG_TX; } SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW)); SuccessOrExit(mEncoder.WriteUint16(aFrame->mLength)); SuccessOrExit(mEncoder.WriteData(aFrame->mPsdu, aFrame->mLength)); // Append metadata (rssi, etc) SuccessOrExit(mEncoder.WriteInt8(aFrame->mInfo.mRxInfo.mRssi)); // RSSI SuccessOrExit(mEncoder.WriteInt8(-128)); // Noise floor (Currently unused) SuccessOrExit(mEncoder.WriteUint16(flags)); // Flags SuccessOrExit(mEncoder.OpenStruct()); // PHY-data // Empty for now SuccessOrExit(mEncoder.CloseStruct()); SuccessOrExit(mEncoder.OpenStruct()); // Vendor-data // Empty for now SuccessOrExit(mEncoder.CloseStruct()); SuccessOrExit(mEncoder.EndFrame()); exit: return; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(mPcapEnabled); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; bool enabled; SuccessOrExit(error = mDecoder.ReadBool(enabled)); VerifyOrExit(enabled != mPcapEnabled); mPcapEnabled = enabled; if (mPcapEnabled) { otLinkSetPcapCallback(mInstance, &NcpBase::HandlePcapFrame, static_cast(this)); } else { otLinkSetPcapCallback(mInstance, nullptr, nullptr); } exit: return error; } // ---------------------------------------------------------------------------- // MARK: Property/Status Changed // ---------------------------------------------------------------------------- void NcpBase::HandleStateChanged(otChangedFlags aFlags, void *aContext) { NcpBase *ncp = static_cast(aContext); ncp->mThreadChangedFlags |= aFlags; ncp->mUpdateChangedPropsTask.Post(); } void NcpBase::ProcessThreadChangedFlags(void) { static const struct { otChangedFlags mThreadFlag; spinel_prop_key_t mPropKey; } kFlags[] = { {OT_CHANGED_IP6_ADDRESS_ADDED, SPINEL_PROP_IPV6_ADDRESS_TABLE}, {OT_CHANGED_IP6_ADDRESS_REMOVED, SPINEL_PROP_IPV6_ADDRESS_TABLE}, {OT_CHANGED_THREAD_ROLE, SPINEL_PROP_NET_ROLE}, {OT_CHANGED_THREAD_LL_ADDR, SPINEL_PROP_IPV6_LL_ADDR}, {OT_CHANGED_THREAD_ML_ADDR, SPINEL_PROP_IPV6_ML_ADDR}, {OT_CHANGED_THREAD_PARTITION_ID, SPINEL_PROP_NET_PARTITION_ID}, {OT_CHANGED_THREAD_KEY_SEQUENCE_COUNTER, SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER}, {OT_CHANGED_THREAD_NETDATA, SPINEL_PROP_THREAD_LEADER_NETWORK_DATA}, {OT_CHANGED_THREAD_CHILD_ADDED, SPINEL_PROP_THREAD_CHILD_TABLE}, {OT_CHANGED_THREAD_CHILD_REMOVED, SPINEL_PROP_THREAD_CHILD_TABLE}, {OT_CHANGED_IP6_MULTICAST_SUBSCRIBED, SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE}, {OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED, SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE}, {OT_CHANGED_THREAD_CHANNEL, SPINEL_PROP_PHY_CHAN}, {OT_CHANGED_THREAD_PANID, SPINEL_PROP_MAC_15_4_PANID}, {OT_CHANGED_THREAD_NETWORK_NAME, SPINEL_PROP_NET_NETWORK_NAME}, {OT_CHANGED_THREAD_EXT_PANID, SPINEL_PROP_NET_XPANID}, {OT_CHANGED_THREAD_RLOC_ADDED, SPINEL_PROP_IPV6_ADDRESS_TABLE}, {OT_CHANGED_THREAD_RLOC_REMOVED, SPINEL_PROP_IPV6_ADDRESS_TABLE}, {OT_CHANGED_NETWORK_KEY, SPINEL_PROP_NET_NETWORK_KEY}, {OT_CHANGED_PSKC, SPINEL_PROP_NET_PSKC}, {OT_CHANGED_CHANNEL_MANAGER_NEW_CHANNEL, SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL}, {OT_CHANGED_SUPPORTED_CHANNEL_MASK, SPINEL_PROP_PHY_CHAN_SUPPORTED}, }; VerifyOrExit(mThreadChangedFlags != 0); // If thread role has changed, check for possible "join" error. if ((mThreadChangedFlags & OT_CHANGED_THREAD_ROLE) != 0) { if (mRequireJoinExistingNetwork) { switch (otThreadGetDeviceRole(mInstance)) { case OT_DEVICE_ROLE_DETACHED: case OT_DEVICE_ROLE_DISABLED: break; default: mRequireJoinExistingNetwork = false; mChangedPropsSet.AddProperty(SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING); break; } if ((otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_LEADER) && otThreadIsSingleton(mInstance)) { mThreadChangedFlags &= ~static_cast(OT_CHANGED_THREAD_PARTITION_ID); IgnoreError(otThreadSetEnabled(mInstance, false)); mChangedPropsSet.AddProperty(SPINEL_PROP_NET_STACK_UP); mChangedPropsSet.AddLastStatus(SPINEL_STATUS_JOIN_FAILURE); } } } // Convert OT_CHANGED flags to corresponding NCP property update. for (auto &flag : kFlags) { uint32_t threadFlag = flag.mThreadFlag; if (mThreadChangedFlags & threadFlag) { spinel_prop_key_t propKey = flag.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) { mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_ON_MESH_NETS); mChangedPropsSet.AddProperty(SPINEL_PROP_THREAD_OFF_MESH_ROUTES); } mThreadChangedFlags &= ~threadFlag; VerifyOrExit(mThreadChangedFlags != 0); } } // Clear any remaining ThreadFlag that has no matching // NCP property update (e.g., OT_CHANGED_SECURITY_POLICY) mThreadChangedFlags = 0; exit: return; } } // namespace Ncp } // namespace ot #endif // OPENTHREAD_MTD || OPENTHREAD_FTD