| /* | |
| * 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 <openthread/border_router.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE | |
| #include <openthread/channel_monitor.h> | |
| #endif | |
| #include <openthread/child_supervision.h> | |
| #include <openthread/diag.h> | |
| #include <openthread/icmp6.h> | |
| #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE | |
| #include <openthread/jam_detection.h> | |
| #endif | |
| #include <openthread/ncp.h> | |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE | |
| #include <openthread/network_time.h> | |
| #endif | |
| #include <openthread/platform/misc.h> | |
| #include <openthread/platform/radio.h> | |
| #if OPENTHREAD_FTD | |
| #include <openthread/thread_ftd.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE | |
| #include <openthread/server.h> | |
| #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 <openthread/srp_client_buffers.h> | |
| #endif | |
| #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE | |
| #include <openthread/trel.h> | |
| #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<uint8_t>(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<SPINEL_PROP_THREAD_CSL_PERIOD>(void) | |
| { | |
| uint32_t cslPeriod; | |
| otError error = OT_ERROR_NONE; | |
| SuccessOrExit(error = mDecoder.ReadUint32(cslPeriod)); | |
| error = otLinkSetCslPeriod(mInstance, cslPeriod); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_PERIOD>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCslPeriod(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CSL_TIMEOUT>(void) | |
| { | |
| uint32_t cslTimeout; | |
| otError error = OT_ERROR_NONE; | |
| SuccessOrExit(error = mDecoder.ReadUint32(cslTimeout)); | |
| error = otLinkSetCslTimeout(mInstance, cslTimeout); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_TIMEOUT>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCslTimeout(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CSL_CHANNEL>(void) | |
| { | |
| uint8_t cslChannel; | |
| otError error = OT_ERROR_NONE; | |
| SuccessOrExit(error = mDecoder.ReadUint8(cslChannel)); | |
| error = otLinkSetCslChannel(mInstance, cslChannel); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_CHANNEL>(void) | |
| { | |
| return mEncoder.WriteUint8(otLinkGetCslChannel(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<SPINEL_PROP_THREAD_MLR_REQUEST>(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<NcpBase *>(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<uint8_t>(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<SPINEL_PROP_THREAD_BACKBONE_ROUTER_PRIMARY>(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<SPINEL_PROP_MAC_DATA_POLL_PERIOD>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetPollPeriod(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_DATA_POLL_PERIOD>(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<SPINEL_PROP_MAC_EXTENDED_ADDR>(void) | |
| { | |
| return mEncoder.WriteEui64(*otLinkGetExtendedAddress(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_MAX_RETRY_NUMBER_DIRECT>(void) | |
| { | |
| return mEncoder.WriteUint8(otLinkGetMaxFrameRetriesDirect(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_MAC_MAX_RETRY_NUMBER_DIRECT>(void) | |
| { | |
| uint8_t maxFrameRetriesDirect; | |
| otError error = OT_ERROR_NONE; | |
| SuccessOrExit(error = mDecoder.ReadUint8(maxFrameRetriesDirect)); | |
| otLinkSetMaxFrameRetriesDirect(mInstance, maxFrameRetriesDirect); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_CHAN_SUPPORTED>(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<SPINEL_PROP_NET_SAVED>(void) | |
| { | |
| return mEncoder.WriteBool(otDatasetIsCommissioned(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_IF_UP>(void) | |
| { | |
| return mEncoder.WriteBool(otIp6IsEnabled(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_IF_UP>(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<SPINEL_PROP_NET_STACK_UP>(void) | |
| { | |
| return mEncoder.WriteBool(otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_STACK_UP>(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<SPINEL_PROP_NET_ROLE>(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<SPINEL_PROP_NET_ROLE>(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<SPINEL_PROP_NET_NETWORK_NAME>(void) | |
| { | |
| return mEncoder.WriteUtf8(otThreadGetNetworkName(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_NETWORK_NAME>(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<SPINEL_PROP_NET_XPANID>(void) | |
| { | |
| return mEncoder.WriteData(otThreadGetExtendedPanId(mInstance)->m8, sizeof(spinel_net_xpanid_t)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_XPANID>(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<const otExtendedPanId *>(ptr)); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_NETWORK_KEY>(void) | |
| { | |
| otNetworkKey networkKey; | |
| otThreadGetNetworkKey(mInstance, &networkKey); | |
| return mEncoder.WriteData(networkKey.m8, OT_NETWORK_KEY_SIZE); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_NETWORK_KEY>(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<const otNetworkKey *>(ptr)); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetKeySequenceCounter(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_KEY_SEQUENCE_COUNTER>(void) | |
| { | |
| uint32_t keySeqCounter; | |
| otError error = OT_ERROR_NONE; | |
| SuccessOrExit(error = mDecoder.ReadUint32(keySeqCounter)); | |
| otThreadSetKeySequenceCounter(mInstance, keySeqCounter); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_PARTITION_ID>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetPartitionId(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetKeySwitchGuardTime(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_KEY_SWITCH_GUARDTIME>(void) | |
| { | |
| uint32_t keyGuardTime; | |
| otError error = OT_ERROR_NONE; | |
| SuccessOrExit(error = mDecoder.ReadUint32(keyGuardTime)); | |
| otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NETWORK_DATA_VERSION>(void) | |
| { | |
| return mEncoder.WriteUint8(otNetDataGetVersion(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_STABLE_NETWORK_DATA_VERSION>(void) | |
| { | |
| return mEncoder.WriteUint8(otNetDataGetStableVersion(mInstance)); | |
| } | |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_NETWORK_DATA>(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<SPINEL_PROP_THREAD_STABLE_NETWORK_DATA>(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<SPINEL_PROP_THREAD_LEADER_NETWORK_DATA>(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<SPINEL_PROP_THREAD_STABLE_LEADER_NETWORK_DATA>(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<SPINEL_PROP_THREAD_LEADER_RID>(void) | |
| { | |
| return mEncoder.WriteUint8(otThreadGetLeaderRouterId(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_LEADER_ADDR>(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<SPINEL_PROP_THREAD_PARENT>(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<SPINEL_PROP_THREAD_NEIGHBOR_TABLE>(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<SPINEL_PROP_THREAD_NEIGHBOR_TABLE_ERROR_RATES>(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<SPINEL_PROP_THREAD_ASSISTING_PORTS>(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<SPINEL_PROP_THREAD_ASSISTING_PORTS>(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<SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE>(void) | |
| { | |
| return mEncoder.WriteBool(mAllowLocalNetworkDataChange); | |
| } | |
| #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE>(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<SPINEL_PROP_THREAD_ON_MESH_NETS>(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<SPINEL_PROP_THREAD_ON_MESH_NETS>(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<SPINEL_PROP_THREAD_ON_MESH_NETS>(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 considered | |
| // 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<SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE>(void) | |
| { | |
| return mEncoder.WriteBool(mAllowLocalServerDataChange); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE>(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<SPINEL_PROP_SERVER_SERVICES>(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<uint8_t>(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<uint8_t>(dataLen); | |
| SuccessOrExit(error = otServerAddService(mInstance, &cfg)); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyRemove<SPINEL_PROP_SERVER_SERVICES>(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<uint8_t>(serviceDataLength))); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SERVER_SERVICES>(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<SPINEL_PROP_SERVER_LEADER_SERVICES>(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<SPINEL_PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG>(void) | |
| { | |
| return mEncoder.WriteBool(mDiscoveryScanJoinerFlag); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_JOINER_FLAG>(void) | |
| { | |
| return mDecoder.ReadBool(mDiscoveryScanJoinerFlag); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING>(void) | |
| { | |
| return mEncoder.WriteBool(mDiscoveryScanEnableFiltering); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING>(void) | |
| { | |
| return mDecoder.ReadBool(mDiscoveryScanEnableFiltering); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_PANID>(void) | |
| { | |
| return mEncoder.WriteUint16(mDiscoveryScanPanId); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_DISCOVERY_SCAN_PANID>(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<uint8_t>(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<const SecurityPolicy &>(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<SPINEL_PROP_THREAD_ACTIVE_DATASET>(void) | |
| { | |
| otOperationalDataset dataset; | |
| IgnoreError(otDatasetGetActive(mInstance, &dataset)); | |
| return EncodeOperationalDataset(dataset); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_PENDING_DATASET>(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<spinel_prop_key_t>(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<SecurityPolicy &>(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<uint8_t>(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<SPINEL_PROP_THREAD_ACTIVE_DATASET>(void) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otOperationalDataset dataset; | |
| SuccessOrExit(error = DecodeOperationalDataset(dataset)); | |
| error = otDatasetSetActive(mInstance, &dataset); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_PENDING_DATASET>(void) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| otOperationalDataset dataset; | |
| SuccessOrExit(error = DecodeOperationalDataset(dataset)); | |
| error = otDatasetSetPending(mInstance, &dataset); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MGMT_SET_ACTIVE_DATASET>(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<SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET>(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<SPINEL_PROP_THREAD_MGMT_GET_ACTIVE_DATASET>(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<SPINEL_PROP_THREAD_MGMT_GET_PENDING_DATASET>(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<SPINEL_PROP_MESHCOP_JOINER_STATE>(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<SPINEL_PROP_MESHCOP_JOINER_COMMISSIONING>(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<SPINEL_PROP_MESHCOP_JOINER_DISCERNER>(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<SPINEL_PROP_MESHCOP_JOINER_DISCERNER>(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<SPINEL_PROP_IPV6_ML_PREFIX>(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<SPINEL_PROP_IPV6_ML_PREFIX>(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<const otMeshLocalPrefix *>(meshLocalPrefix)); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ML_ADDR>(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<SPINEL_PROP_IPV6_LL_ADDR>(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<SPINEL_PROP_IPV6_ADDRESS_TABLE>(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<SPINEL_PROP_IPV6_ADDRESS_TABLE>(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<SPINEL_PROP_IPV6_ADDRESS_TABLE>(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<SPINEL_PROP_IPV6_ROUTE_TABLE>(void) | |
| { | |
| // TODO: Implement get route table | |
| return mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_UNIMPLEMENTED); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD>(void) | |
| { | |
| return mEncoder.WriteBool(otIcmp6GetEchoMode(mInstance) != OT_ICMP6_ECHO_HANDLER_DISABLED); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD>(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<SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE>(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<SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE>(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<SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE>(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<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD_MODE>(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<SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD_MODE>(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<SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU>(void) | |
| { | |
| // Note reverse logic: passthru enabled = filter disabled | |
| return mEncoder.WriteBool(!otIp6IsReceiveFilterEnabled(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_RLOC16_DEBUG_PASSTHRU>(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<SPINEL_PROP_THREAD_OFF_MESH_ROUTES>(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<SPINEL_PROP_THREAD_OFF_MESH_ROUTES>(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<SPINEL_PROP_THREAD_OFF_MESH_ROUTES>(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<SPINEL_PROP_STREAM_NET>(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<SPINEL_PROP_JAM_DETECT_ENABLE>(void) | |
| { | |
| return mEncoder.WriteBool(otJamDetectionIsEnabled(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECTED>(void) | |
| { | |
| return mEncoder.WriteBool(otJamDetectionGetState(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD>(void) | |
| { | |
| return mEncoder.WriteInt8(otJamDetectionGetRssiThreshold(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_WINDOW>(void) | |
| { | |
| return mEncoder.WriteUint8(otJamDetectionGetWindow(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_BUSY>(void) | |
| { | |
| return mEncoder.WriteUint8(otJamDetectionGetBusyPeriod(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_JAM_DETECT_HISTORY_BITMAP>(void) | |
| { | |
| return mEncoder.WriteUint64(otJamDetectionGetHistoryBitmap(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_JAM_DETECT_ENABLE>(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<SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD>(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<SPINEL_PROP_JAM_DETECT_WINDOW>(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<SPINEL_PROP_JAM_DETECT_BUSY>(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<NcpBase *>(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<SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT>(void) | |
| { | |
| return mEncoder.WriteUint16(otChildSupervisionGetCheckTimeout(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT>(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<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_INTERVAL>(void) | |
| { | |
| return mEncoder.WriteUint32(otChannelMonitorGetSampleInterval(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_RSSI_THRESHOLD>(void) | |
| { | |
| return mEncoder.WriteInt8(otChannelMonitorGetRssiThreshold(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_WINDOW>(void) | |
| { | |
| return mEncoder.WriteUint32(otChannelMonitorGetSampleWindow(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_SAMPLE_COUNT>(void) | |
| { | |
| return mEncoder.WriteUint32(otChannelMonitorGetSampleCount(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CHANNEL_MONITOR_CHANNEL_OCCUPANCY>(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<SPINEL_PROP_MAC_CCA_FAILURE_RATE>(void) | |
| { | |
| return mEncoder.WriteUint16(otLinkGetCcaFailureRate(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxTotal); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_ACK_REQ>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAckRequested); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_ACKED>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAcked); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_NO_ACK_REQ>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxNoAckRequested); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_DATA>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxData); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_DATA_POLL>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxDataPoll); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_BEACON>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeacon); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_BEACON_REQ>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeaconRequest); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_OTHER>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxOther); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_RETRY>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxRetry); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_ERR_CCA>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrCca); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_UNICAST>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxUnicast); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_PKT_BROADCAST>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBroadcast); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_ERR_ABORT>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrAbort); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxTotal); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_DATA>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxData); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_DATA_POLL>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDataPoll); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_BEACON>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeacon); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_BEACON_REQ>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeaconRequest); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_OTHER>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxOther); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_FILT_WL>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxAddressFiltered); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_FILT_DA>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDestAddrFiltered); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_DUP>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDuplicated); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_UNICAST>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxUnicast); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_PKT_BROADCAST>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBroadcast); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_EMPTY>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrNoFrame); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_UKWN_NBR>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrUnknownNeighbor); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_NVLD_SADDR>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrInvalidSrcAddr); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_SECURITY>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrSec); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_BAD_FCS>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrFcs); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_ERR_OTHER>(void) | |
| { | |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrOther); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_IP_SEC_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(mInboundSecureIpFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_IP_INSEC_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(mInboundInsecureIpFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_IP_DROPPED>(void) | |
| { | |
| return mEncoder.WriteUint32(mDroppedInboundIpFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_IP_SEC_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(mOutboundSecureIpFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_IP_INSEC_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(mOutboundInsecureIpFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_IP_DROPPED>(void) | |
| { | |
| return mEncoder.WriteUint32(mDroppedOutboundIpFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_TX_SPINEL_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(mTxSpinelFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_SPINEL_TOTAL>(void) | |
| { | |
| return mEncoder.WriteUint32(mRxSpinelFrameCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_SPINEL_OUT_OF_ORDER_TID>(void) | |
| { | |
| return mEncoder.WriteUint32(mRxSpinelOutOfOrderTidCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_RX_SPINEL_ERR>(void) | |
| { | |
| return mEncoder.WriteUint32(mFramingErrorCounter); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_TX_SUCCESS>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxSuccess); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_RX_SUCCESS>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxSuccess); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_TX_FAILURE>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxFailure); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_IP_RX_FAILURE>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxFailure); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MSG_BUFFER_COUNTERS>(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<SPINEL_PROP_CNTR_ALL_MAC_COUNTERS>(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<SPINEL_PROP_CNTR_ALL_MAC_COUNTERS>(void) | |
| { | |
| otLinkResetCounters(mInstance); | |
| return OT_ERROR_NONE; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_MLE_COUNTERS>(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<SPINEL_PROP_CNTR_MLE_COUNTERS>(void) | |
| { | |
| otThreadResetMleCounters(mInstance); | |
| return OT_ERROR_NONE; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_CNTR_ALL_IP_COUNTERS>(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<SPINEL_PROP_CNTR_MAC_RETRY_HISTOGRAM>(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<SPINEL_PROP_CNTR_MAC_RETRY_HISTOGRAM>(void) | |
| { | |
| otLinkResetTxRetrySuccessHistogram(mInstance); | |
| return OT_ERROR_NONE; | |
| } | |
| #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_CNTR_ALL_IP_COUNTERS>(void) | |
| { | |
| otThreadResetIp6Counters(mInstance); | |
| return OT_ERROR_NONE; | |
| } | |
| #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_ALLOWLIST>(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<SPINEL_PROP_MAC_ALLOWLIST_ENABLED>(void) | |
| { | |
| return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_DENYLIST>(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<SPINEL_PROP_MAC_DENYLIST_ENABLED>(void) | |
| { | |
| return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_MAC_FIXED_RSS>(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<SPINEL_PROP_MAC_ALLOWLIST>(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<SPINEL_PROP_MAC_ALLOWLIST_ENABLED>(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<SPINEL_PROP_MAC_DENYLIST>(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<SPINEL_PROP_MAC_DENYLIST_ENABLED>(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<SPINEL_PROP_MAC_FIXED_RSS>(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<SPINEL_PROP_THREAD_LINK_METRICS_QUERY>(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<SPINEL_PROP_THREAD_LINK_METRICS_PROBE>(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<SPINEL_PROP_THREAD_LINK_METRICS_MGMT_ENH_ACK>(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<otLinkMetricsEnhAckFlags>(controlFlags), | |
| controlFlags ? &linkMetrics : nullptr, | |
| &NcpBase::HandleLinkMetricsMgmtResponse_Jump, this, | |
| &NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump, this); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_LINK_METRICS_MGMT_FORWARD>(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<SPINEL_PROP_THREAD_MODE>(void) | |
| { | |
| uint8_t numericMode; | |
| otLinkModeConfig modeConfig = otThreadGetLinkMode(mInstance); | |
| numericMode = LinkFlagsToFlagByte(modeConfig.mRxOnWhenIdle, modeConfig.mDeviceType, modeConfig.mNetworkData); | |
| return mEncoder.WriteUint8(numericMode); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MODE>(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<SPINEL_PROP_THREAD_CHILD_TIMEOUT>(void) | |
| { | |
| return mEncoder.WriteUint32(otThreadGetChildTimeout(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_CHILD_TIMEOUT>(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<SPINEL_PROP_THREAD_RLOC16>(void) | |
| { | |
| return mEncoder.WriteUint16(otThreadGetRloc16(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING>(void) | |
| { | |
| return mEncoder.WriteBool(mRequireJoinExistingNetwork); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NET_REQUIRE_JOIN_EXISTING>(void) | |
| { | |
| return mDecoder.ReadBool(mRequireJoinExistingNetwork); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_STREAM_NET_INSECURE>(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<SPINEL_PROP_CNTR_RESET>(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<SPINEL_PROP_THREAD_ASSISTING_PORTS>(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<SPINEL_PROP_MAC_ALLOWLIST>(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<SPINEL_PROP_MAC_DENYLIST>(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<SPINEL_PROP_MAC_FIXED_RSS>(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<SPINEL_PROP_THREAD_ASSISTING_PORTS>(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<SPINEL_PROP_MAC_ALLOWLIST>(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<SPINEL_PROP_MAC_DENYLIST>(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<SPINEL_PROP_MAC_FIXED_RSS>(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<SPINEL_PROP_RCP_VERSION>(void) | |
| { | |
| return mEncoder.WriteUtf8(otGetRadioVersionString(mInstance)); | |
| } | |
| #endif | |
| #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SLAAC_ENABLED>(void) | |
| { | |
| return mEncoder.WriteBool(otIp6IsSlaacEnabled(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SLAAC_ENABLED>(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<SPINEL_PROP_SUPPORTED_RADIO_LINKS>(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<SPINEL_PROP_NEIGHBOR_TABLE_MULTI_RADIO_INFO>(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<SPINEL_PROP_SRP_CLIENT_START>(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<SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL>(void) | |
| { | |
| return mEncoder.WriteUint32(otSrpClientGetLeaseInterval(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_LEASE_INTERVAL>(void) | |
| { | |
| otError error; | |
| uint32_t interval; | |
| SuccessOrExit(error = mDecoder.ReadUint32(interval)); | |
| otSrpClientSetLeaseInterval(mInstance, interval); | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL>(void) | |
| { | |
| return mEncoder.WriteUint32(otSrpClientGetKeyLeaseInterval(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_KEY_LEASE_INTERVAL>(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<SPINEL_PROP_SRP_CLIENT_HOST_INFO>(void) | |
| { | |
| return EncodeSrpClientHostInfo(*otSrpClientGetHostInfo(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_HOST_NAME>(void) | |
| { | |
| const char *name = otSrpClientGetHostInfo(mInstance)->mName; | |
| return mEncoder.WriteUtf8(name != nullptr ? name : ""); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_HOST_NAME>(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<SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES>(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<SPINEL_PROP_SRP_CLIENT_HOST_ADDRESSES>(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<SPINEL_PROP_SRP_CLIENT_SERVICES>(void) | |
| { | |
| return EncodeSrpClientServices(otSrpClientGetServices(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertyInsert<SPINEL_PROP_SRP_CLIENT_SERVICES>(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<SPINEL_PROP_SRP_CLIENT_SERVICES>(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<otSrpClientService *>(service))); | |
| otSrpClientBuffersFreeService( | |
| mInstance, reinterpret_cast<otSrpClientBuffersServiceEntry *>(const_cast<otSrpClientService *>(service))); | |
| } | |
| else | |
| { | |
| error = otSrpClientRemoveService(mInstance, const_cast<otSrpClientService *>(service)); | |
| } | |
| exit: | |
| return error; | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_REMOVE>(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<SPINEL_PROP_SRP_CLIENT_HOST_SERVICES_CLEAR>(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<NcpBase *>(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<otSrpClientBuffersServiceEntry *>(const_cast<otSrpClientService *>(service))); | |
| } | |
| } | |
| #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE | |
| template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_SRP_CLIENT_SERVICE_KEY_ENABLED>(void) | |
| { | |
| return mEncoder.WriteBool(otSrpClientIsServiceKeyRecordEnabled(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_SRP_CLIENT_SERVICE_KEY_ENABLED>(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<SPINEL_PROP_DEBUG_TREL_TEST_MODE_ENABLE>(void) | |
| { | |
| return mEncoder.WriteBool(!otTrelIsFilterEnabled(mInstance)); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_DEBUG_TREL_TEST_MODE_ENABLE>(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<SPINEL_PROP_THREAD_NETWORK_TIME>(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<NcpBase *>(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<NcpBase *>(aContext)->HandleActiveScanResult(aResult); | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // MARK: Scan Results Glue | |
| // ---------------------------------------------------------------------------- | |
| void NcpBase::HandleActiveScanResult(otActiveScanResult *aResult) | |
| { | |
| otError error = OT_ERROR_NONE; | |
| if (aResult) | |
| { | |
| uint8_t flags = static_cast<uint8_t>(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<NcpBase *>(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<NcpBase *>(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, | |
| otLinkMetricsStatus aStatus, | |
| void *aContext) | |
| { | |
| static_cast<NcpBase *>(aContext)->HandleLinkMetricsReport(aSource, aMetricsValues, aStatus); | |
| } | |
| void NcpBase::HandleLinkMetricsReport(const otIp6Address *aSource, | |
| const otLinkMetricsValues *aMetricsValues, | |
| otLinkMetricsStatus 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, | |
| otLinkMetricsStatus aStatus, | |
| void *aContext) | |
| { | |
| static_cast<NcpBase *>(aContext)->HandleLinkMetricsMgmtResponse(aSource, aStatus); | |
| } | |
| void NcpBase::HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, otLinkMetricsStatus 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<NcpBase *>(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<NcpBase *>(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<SPINEL_PROP_THREAD_UDP_FORWARD_STREAM>(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<uint16_t>(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<NcpBase *>(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<NcpBase *>(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<SPINEL_PROP_PHY_PCAP_ENABLED>(void) | |
| { | |
| return mEncoder.WriteBool(mPcapEnabled); | |
| } | |
| template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_PHY_PCAP_ENABLED>(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<void *>(this)); | |
| } | |
| else | |
| { | |
| otLinkSetPcapCallback(mInstance, nullptr, nullptr); | |
| } | |
| exit: | |
| return error; | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // MARK: Property/Status Changed | |
| // ---------------------------------------------------------------------------- | |
| void NcpBase::HandleStateChanged(otChangedFlags aFlags, void *aContext) | |
| { | |
| NcpBase *ncp = static_cast<NcpBase *>(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<uint32_t>(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 |