| /* |
| * 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/config.h> |
| |
| #include "ncp_base.hpp" |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| #include <openthread/border_router.h> |
| #endif |
| #if OPENTHREAD_ENABLE_CHANNEL_MONITOR |
| #include <openthread/channel_monitor.h> |
| #endif |
| #include <openthread/diag.h> |
| #include <openthread/icmp6.h> |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| #include <openthread/jam_detection.h> |
| #endif |
| #include <openthread/ncp.h> |
| #include <openthread/openthread.h> |
| #include <openthread/platform/misc.h> |
| #include <openthread/platform/radio.h> |
| #if OPENTHREAD_FTD |
| #include <openthread/thread_ftd.h> |
| #endif |
| |
| #include "common/code_utils.hpp" |
| #include "common/debug.hpp" |
| #include "common/instance.hpp" |
| #include "net/ip6.hpp" |
| |
| #if OPENTHREAD_MTD || OPENTHREAD_FTD |
| |
| namespace ot { |
| namespace Ncp { |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| static bool HasOnly1BitSet(uint32_t aValue) |
| { |
| return aValue != 0 && ((aValue & (aValue - 1)) == 0); |
| } |
| |
| static uint8_t IndexOfMSB(uint32_t aValue) |
| { |
| uint8_t index = 0; |
| |
| while (aValue >>= 1) |
| { |
| index++; |
| } |
| |
| return index; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| 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 |= (aConfig.mPreference << SPINEL_NET_FLAG_PREFERENCE_OFFSET); |
| |
| return flags; |
| } |
| |
| static uint8_t ExternalRoutePreferenceToFlagByte(int aPreference) |
| { |
| uint8_t flags; |
| |
| switch (aPreference) |
| { |
| case OT_ROUTE_PREFERENCE_LOW: |
| flags = SPINEL_ROUTE_PREFERENCE_LOW; |
| break; |
| |
| case OT_ROUTE_PREFERENCE_MED: |
| flags = SPINEL_ROUTE_PREFERENCE_MEDIUM; |
| break; |
| |
| case OT_ROUTE_PREFERENCE_HIGH: |
| flags = SPINEL_ROUTE_PREFERENCE_HIGH; |
| break; |
| |
| default: |
| flags = SPINEL_ROUTE_PREFERENCE_MEDIUM; |
| break; |
| } |
| |
| return flags; |
| } |
| |
| uint8_t NcpBase::LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aSecureDataRequests, bool aDeviceType, bool aNetworkData) |
| { |
| uint8_t flags(0); |
| |
| if (aRxOnWhenIdle) |
| { |
| flags |= SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE; |
| } |
| |
| if (aSecureDataRequests) |
| { |
| flags |= SPINEL_THREAD_MODE_SECURE_DATA_REQUEST; |
| } |
| |
| if (aDeviceType) |
| { |
| flags |= SPINEL_THREAD_MODE_FULL_FUNCTION_DEV; |
| } |
| |
| if (aNetworkData) |
| { |
| flags |= SPINEL_THREAD_MODE_FULL_NETWORK_DATA; |
| } |
| |
| return flags; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_DATA_POLL_PERIOD(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetPollPeriod(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_DATA_POLL_PERIOD(void) |
| { |
| uint32_t pollPeriod; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadUint32(pollPeriod)); |
| |
| otLinkSetPollPeriod(mInstance, pollPeriod); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_EXTENDED_ADDR(void) |
| { |
| return mEncoder.WriteEui64(*otLinkGetExtendedAddress(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_FREQ(void) |
| { |
| uint32_t freq_khz(0); |
| const uint8_t chan(otLinkGetChannel(mInstance)); |
| |
| if (chan == 0) |
| { |
| freq_khz = 868300; |
| } |
| else if (chan < 11) |
| { |
| freq_khz = 906000 - (2000 * 1) + 2000 * (chan); |
| } |
| else if (chan < 26) |
| { |
| freq_khz = 2405000 - (5000 * 11) + 5000 * (chan); |
| } |
| |
| return mEncoder.WriteUint32(freq_khz); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(void) |
| { |
| return EncodeChannelMask(mSupportedChannelMask); |
| } |
| |
| otError NcpBase::GetPropertyHandler_PHY_RSSI(void) |
| { |
| return mEncoder.WriteInt8(otPlatRadioGetRssi(mInstance)); |
| } |
| |
| otError NcpBase::CommandHandler_NET_SAVE(uint8_t aHeader) |
| { |
| return PrepareLastStatusResponse(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::CommandHandler_NET_CLEAR(uint8_t aHeader) |
| { |
| return PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(otInstanceErasePersistentInfo(mInstance))); |
| } |
| |
| otError NcpBase::CommandHandler_NET_RECALL(uint8_t aHeader) |
| { |
| return PrepareLastStatusResponse(aHeader, SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_SAVED(void) |
| { |
| return mEncoder.WriteBool(otDatasetIsCommissioned(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_IF_UP(void) |
| { |
| return mEncoder.WriteBool(otIp6IsEnabled(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_IF_UP(void) |
| { |
| bool enabled = false; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadBool(enabled)); |
| |
| error = otIp6SetEnabled(mInstance, enabled); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_STACK_UP(void) |
| { |
| return mEncoder.WriteBool(otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_STACK_UP(void) |
| { |
| bool enabled = false; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadBool(enabled)); |
| |
| // If the value has changed... |
| if ((enabled != false) != (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED)) |
| { |
| if (enabled != false) |
| { |
| error = otThreadSetEnabled(mInstance, true); |
| StartLegacy(); |
| } |
| else |
| { |
| error = otThreadSetEnabled(mInstance, false); |
| StopLegacy(); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_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); |
| } |
| |
| otError NcpBase::SetPropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_NETWORK_NAME(void) |
| { |
| return mEncoder.WriteUtf8(otThreadGetNetworkName(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_NETWORK_NAME(void) |
| { |
| const char *string = NULL; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(mDecoder.ReadUtf8(string)); |
| |
| error = otThreadSetNetworkName(mInstance, string); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_XPANID(void) |
| { |
| return mEncoder.WriteData(otThreadGetExtendedPanId(mInstance), sizeof(spinel_net_xpanid_t)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_XPANID(void) |
| { |
| const uint8_t *ptr = NULL; |
| 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, ptr); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_MASTER_KEY(void) |
| { |
| return mEncoder.WriteData(otThreadGetMasterKey(mInstance)->m8, OT_MASTER_KEY_SIZE); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_MASTER_KEY(void) |
| { |
| const uint8_t *ptr = NULL; |
| uint16_t len; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadData(ptr, len)); |
| |
| VerifyOrExit(len == OT_MASTER_KEY_SIZE, error = OT_ERROR_PARSE); |
| |
| error = otThreadSetMasterKey(mInstance, reinterpret_cast<const otMasterKey *>(ptr)); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetKeySequenceCounter(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_KEY_SEQUENCE_COUNTER(void) |
| { |
| uint32_t keySeqCounter; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadUint32(keySeqCounter)); |
| |
| otThreadSetKeySequenceCounter(mInstance, keySeqCounter); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_PARTITION_ID(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetPartitionId(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetKeySwitchGuardTime(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_KEY_SWITCH_GUARDTIME(void) |
| { |
| uint32_t keyGuardTime; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadUint32(keyGuardTime)); |
| |
| otThreadSetKeySwitchGuardTime(mInstance, keyGuardTime); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA_VERSION(void) |
| { |
| return mEncoder.WriteUint8(otNetDataGetVersion(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA_VERSION(void) |
| { |
| return mEncoder.WriteUint8(otNetDataGetStableVersion(mInstance)); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::GetPropertyHandler_THREAD_NETWORK_DATA(void) |
| { |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otBorderRouterGetNetData( |
| mInstance, |
| false, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| return mEncoder.WriteData(networkData, networkDataLen); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_STABLE_NETWORK_DATA(void) |
| { |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otBorderRouterGetNetData( |
| mInstance, |
| true, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| return mEncoder.WriteData(networkData, networkDataLen); |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| otError NcpBase::GetPropertyHandler_THREAD_LEADER_NETWORK_DATA(void) |
| { |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otNetDataGet( |
| mInstance, |
| false, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| return mEncoder.WriteData(networkData, networkDataLen); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_STABLE_LEADER_NETWORK_DATA(void) |
| { |
| uint8_t networkData[255]; |
| uint8_t networkDataLen = 255; |
| |
| otNetDataGet( |
| mInstance, |
| true, // Stable? |
| networkData, |
| &networkDataLen |
| ); |
| |
| return mEncoder.WriteData(networkData, networkDataLen); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_LEADER_RID(void) |
| { |
| return mEncoder.WriteUint8(otThreadGetLeaderRouterId(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_PARENT(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| otRouterInfo parentInfo; |
| |
| error = otThreadGetParentInfo(mInstance, &parentInfo); |
| |
| if (error == OT_ERROR_NONE) |
| { |
| SuccessOrExit(error = mEncoder.WriteEui64(parentInfo.mExtAddress)); // Parent's extended address |
| SuccessOrExit(error = mEncoder.WriteUint16(parentInfo.mRloc16)); |
| } |
| else |
| { |
| error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_NEIGHBOR_TABLE(void) |
| { |
| otError error = OT_ERROR_NONE; |
| otNeighborInfoIterator iter = OT_NEIGHBOR_INFO_ITERATOR_INIT; |
| otNeighborInfo neighInfo; |
| uint8_t modeFlags; |
| |
| while (otThreadGetNextNeighborInfo(mInstance, &iter, &neighInfo) == OT_ERROR_NONE) |
| { |
| modeFlags = LinkFlagsToFlagByte( |
| neighInfo.mRxOnWhenIdle, |
| neighInfo.mSecureDataRequest, |
| neighInfo.mFullFunction, |
| neighInfo.mFullNetworkData |
| ); |
| |
| SuccessOrExit(error = mEncoder.OpenStruct()); |
| |
| SuccessOrExit(error = mEncoder.WriteEui64(neighInfo.mExtAddress)); |
| SuccessOrExit(error = mEncoder.WriteUint16(neighInfo.mRloc16)); |
| SuccessOrExit(error = mEncoder.WriteUint32(neighInfo.mAge)); |
| SuccessOrExit(error = mEncoder.WriteUint8(neighInfo.mLinkQualityIn)); |
| SuccessOrExit(error = mEncoder.WriteInt8(neighInfo.mAverageRssi)); |
| SuccessOrExit(error = mEncoder.WriteUint8(modeFlags)); |
| SuccessOrExit(error = mEncoder.WriteBool(neighInfo.mIsChild)); |
| SuccessOrExit(error = mEncoder.WriteUint32(neighInfo.mLinkFrameCounter)); |
| SuccessOrExit(error = mEncoder.WriteUint32(neighInfo.mMleFrameCounter)); |
| SuccessOrExit(error = mEncoder.WriteInt8(neighInfo.mLastRssi)); |
| |
| SuccessOrExit(error = mEncoder.CloseStruct()); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| #if OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING |
| |
| otError NcpBase::GetPropertyHandler_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; |
| } |
| |
| #endif // OPENTHREAD_CONFIG_ENABLE_TX_ERROR_RATE_TRACKING |
| |
| otError NcpBase::GetPropertyHandler_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; |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_ASSISTING_PORTS(void) |
| { |
| uint8_t numEntries = 0; |
| const uint16_t *ports = otIp6GetUnsecurePorts(mInstance, &numEntries); |
| otError error = OT_ERROR_NONE; |
| |
| // First, we need to remove all of the current assisting ports. |
| for (; numEntries != 0; ports++, numEntries--) |
| { |
| SuccessOrExit(error = otIp6RemoveUnsecurePort(mInstance, *ports)); |
| } |
| |
| while (mDecoder.GetRemainingLengthInStruct() >= sizeof(uint16_t)) |
| { |
| uint16_t port; |
| |
| SuccessOrExit(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. |
| WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_THREAD_ASSISTING_PORTS); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ALLOW_LOCAL_NET_DATA_CHANGE(void) |
| { |
| return mEncoder.WriteBool(mAllowLocalNetworkDataChange); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::SetPropertyHandler_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 == true) && (value == false); |
| |
| mAllowLocalNetworkDataChange = value; |
| |
| exit: |
| |
| if (shouldRegisterWithLeader) |
| { |
| otBorderRouterRegister(mInstance); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| otError NcpBase::GetPropertyHandler_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.CloseStruct()); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| 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.CloseStruct()); |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| exit: |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| otError NcpBase::InsertPropertyHandler_THREAD_ON_MESH_NETS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| otBorderRouterConfig borderRouterConfig; |
| bool stable = false; |
| uint8_t flags = 0; |
| uint8_t prefixLength; |
| |
| memset(&borderRouterConfig, 0, sizeof(otBorderRouterConfig)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, 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); |
| |
| error = otBorderRouterAddOnMeshPrefix(mInstance, &borderRouterConfig); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_THREAD_ON_MESH_NETS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| otIp6Prefix ip6Prefix; |
| uint8_t prefixLength; |
| |
| memset(&ip6Prefix, 0, sizeof(otIp6Prefix)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, error = OT_ERROR_INVALID_STATE); |
| |
| SuccessOrExit(error = mDecoder.ReadIp6Address(ip6Prefix.mPrefix)); |
| SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); |
| |
| ip6Prefix.mLength = prefixLength; |
| |
| error = otBorderRouterRemoveOnMeshPrefix(mInstance, &ip6Prefix); |
| |
| // If prefix was not on the list, "remove" command can be considred |
| // successful. |
| |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_JOINER_FLAG(void) |
| { |
| return mEncoder.WriteBool(mDiscoveryScanJoinerFlag); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_JOINER_FLAG(void) |
| { |
| return mDecoder.ReadBool(mDiscoveryScanJoinerFlag); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING(void) |
| { |
| return mEncoder.WriteBool(mDiscoveryScanEnableFiltering); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_ENABLE_FILTERING(void) |
| { |
| return mDecoder.ReadBool(mDiscoveryScanEnableFiltering); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(void) |
| { |
| return mEncoder.WriteUint16(mDiscoveryScanPanId); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(void) |
| { |
| return mDecoder.ReadUint16(mDiscoveryScanPanId); |
| } |
| |
| otError NcpBase::EncodeOperationalDataset(const otOperationalDataset &aDataset) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| if (aDataset.mIsActiveTimestampSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP)); |
| SuccessOrExit(mEncoder.WriteUint64(aDataset.mActiveTimestamp)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsPendingTimestampSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_PENDING_TIMESTAMP)); |
| SuccessOrExit(mEncoder.WriteUint64(aDataset.mPendingTimestamp)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsMasterKeySet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_MASTER_KEY)); |
| SuccessOrExit(mEncoder.WriteData(aDataset.mMasterKey.m8, OT_MASTER_KEY_SIZE)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsNetworkNameSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_NETWORK_NAME)); |
| SuccessOrExit(mEncoder.WriteUtf8(aDataset.mNetworkName.m8)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsExtendedPanIdSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_XPANID)); |
| SuccessOrExit(mEncoder.WriteData(aDataset.mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsMeshLocalPrefixSet) |
| { |
| otIp6Address addr; |
| |
| memcpy(addr.mFields.m8, aDataset.mMeshLocalPrefix.m8, 8); |
| memset(addr.mFields.m8 + 8, 0, 8); // Zero out the last 8 bytes. |
| |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_IPV6_ML_PREFIX)); |
| SuccessOrExit(error = mEncoder.WriteIp6Address(addr)); // Mesh local prefix |
| SuccessOrExit(error = mEncoder.WriteUint8(64)); // Prefix length (in bits) |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsDelaySet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_DELAY_TIMER)); |
| SuccessOrExit(mEncoder.WriteUint32(aDataset.mDelay)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsPanIdSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_MAC_15_4_PANID)); |
| SuccessOrExit(mEncoder.WriteUint16(aDataset.mPanId)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsChannelSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(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(mEncoder.WriteUint8(static_cast<uint8_t>(aDataset.mChannel))); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsPSKcSet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_PSKC)); |
| SuccessOrExit(mEncoder.WriteData(aDataset.mPSKc.m8, sizeof(spinel_net_pskc_t))); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsSecurityPolicySet) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_SECURITY_POLICY)); |
| SuccessOrExit(mEncoder.WriteUint16(aDataset.mSecurityPolicy.mRotationTime)); |
| SuccessOrExit(mEncoder.WriteUint8(aDataset.mSecurityPolicy.mFlags)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| if (aDataset.mIsChannelMaskPage0Set) |
| { |
| SuccessOrExit(mEncoder.OpenStruct()); |
| SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN_SUPPORTED)); |
| SuccessOrExit(EncodeChannelMask(aDataset.mChannelMaskPage0)); |
| SuccessOrExit(mEncoder.CloseStruct()); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_ACTIVE_DATASET(void) |
| { |
| otOperationalDataset dataset; |
| |
| IgnoreReturnValue(otDatasetGetActive(mInstance, &dataset)); |
| return EncodeOperationalDataset(dataset); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_PENDING_DATASET(void) |
| { |
| otOperationalDataset dataset; |
| |
| IgnoreReturnValue(otDatasetGetPending(mInstance, &dataset)); |
| return EncodeOperationalDataset(dataset); |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const uint8_t *mlPrefix = otThreadGetMeshLocalPrefix(mInstance); |
| otIp6Address addr; |
| |
| VerifyOrExit(mlPrefix != NULL); // If `mlPrefix` is NULL send empty response. |
| |
| memcpy(addr.mFields.m8, mlPrefix, 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(64)); // Prefix length (in bits) |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_IPV6_ML_PREFIX(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const uint8_t *meshLocalPrefix; |
| uint8_t prefixLength; |
| |
| SuccessOrExit(error = mDecoder.ReadIp6Address(meshLocalPrefix)); |
| SuccessOrExit(error = mDecoder.ReadUint8(prefixLength)); |
| VerifyOrExit(prefixLength == 64, error = OT_ERROR_INVALID_ARGS); |
| |
| error = otThreadSetMeshLocalPrefix(mInstance, meshLocalPrefix); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ML_ADDR(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otIp6Address *ml64 = otThreadGetMeshLocalEid(mInstance); |
| |
| VerifyOrExit(ml64 != NULL); |
| SuccessOrExit(error = mEncoder.WriteIp6Address(*ml64)); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_LL_ADDR(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otIp6Address *address = otThreadGetLinkLocalIp6Address(mInstance); |
| |
| VerifyOrExit(address != NULL); |
| SuccessOrExit(error = mEncoder.WriteIp6Address(*address)); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_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; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_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.mPreferred = (preferredLifetime != 0); |
| netifAddr.mValid = (validLifetime != 0); |
| |
| error = otIp6AddUnicastAddress(mInstance, &netifAddr); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ROUTE_TABLE(void) |
| { |
| // TODO: Implement get route table |
| return mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_UNIMPLEMENTED); |
| } |
| |
| otError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(void) |
| { |
| return mEncoder.WriteBool(otIcmp6GetEchoMode(mInstance) != OT_ICMP6_ECHO_HANDLER_DISABLED); |
| } |
| |
| otError NcpBase::SetPropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_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; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_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; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_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); |
| } |
| |
| otError NcpBase::SetPropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(void) |
| { |
| // Note reverse logic: passthru enabled = filter disabled |
| return mEncoder.WriteBool(!otIp6IsReceiveFilterEnabled(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_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(ExternalRoutePreferenceToFlagByte(routeConfig.mPreference))); |
| SuccessOrExit(error = mEncoder.WriteBool(false)); // IsLocal |
| SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mNextHopIsThisDevice)); |
| SuccessOrExit(error = mEncoder.WriteUint16(routeConfig.mRloc16)); |
| |
| SuccessOrExit(error = mEncoder.CloseStruct()); |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| 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(ExternalRoutePreferenceToFlagByte(routeConfig.mPreference))); |
| SuccessOrExit(error = mEncoder.WriteBool(true)); // IsLocal |
| SuccessOrExit(error = mEncoder.WriteBool(routeConfig.mNextHopIsThisDevice)); |
| SuccessOrExit(error = mEncoder.WriteUint16(routeConfig.mRloc16)); |
| |
| SuccessOrExit(error = mEncoder.CloseStruct()); |
| } |
| #endif // OPENTHREAD_ENABLE_BORDER_ROUTER |
| |
| exit: |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_BORDER_ROUTER |
| 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; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_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 == true, 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); |
| |
| error = otBorderRouterAddRoute(mInstance, &routeConfig); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_THREAD_OFF_MESH_ROUTES(void) |
| { |
| otError error = OT_ERROR_NONE; |
| otIp6Prefix ip6Prefix; |
| uint8_t prefixLength; |
| |
| memset(&ip6Prefix, 0, sizeof(otIp6Prefix)); |
| |
| VerifyOrExit(mAllowLocalNetworkDataChange == true, 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_ENABLE_BORDER_ROUTER |
| |
| otError NcpBase::SetPropertyHandler_STREAM_NET(void) |
| { |
| const uint8_t *framePtr = NULL; |
| uint16_t frameLen = 0; |
| const uint8_t *metaPtr = NULL; |
| uint16_t metaLen = 0; |
| otMessage *message = NULL; |
| otError error = OT_ERROR_NONE; |
| |
| // STREAM_NET requires layer 2 security. |
| message = otIp6NewMessage(mInstance, true); |
| VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); |
| |
| 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... |
| OT_UNUSED_VARIABLE(metaPtr); |
| OT_UNUSED_VARIABLE(metaLen); |
| |
| SuccessOrExit(error = otMessageAppend(message, framePtr, frameLen)); |
| |
| error = otIp6Send(mInstance, message); |
| |
| // `otIp6Send()` takes ownership of `message` (in both success or |
| // failure cases). `message` is set to NULL so it is not freed at |
| // exit. |
| message = NULL; |
| |
| exit: |
| |
| if (message != NULL) |
| { |
| otMessageFree(message); |
| } |
| |
| if (error == OT_ERROR_NONE) |
| { |
| mInboundSecureIpFrameCounter++; |
| } |
| else |
| { |
| mDroppedInboundIpFrameCounter++; |
| } |
| |
| return error; |
| } |
| |
| #if OPENTHREAD_ENABLE_JAM_DETECTION |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_ENABLE(void) |
| { |
| return mEncoder.WriteBool(otJamDetectionIsEnabled(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECTED(void) |
| { |
| return mEncoder.WriteBool(otJamDetectionGetState(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_RSSI_THRESHOLD(void) |
| { |
| return mEncoder.WriteInt8(otJamDetectionGetRssiThreshold(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_WINDOW(void) |
| { |
| return mEncoder.WriteUint8(otJamDetectionGetWindow(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_BUSY(void) |
| { |
| return mEncoder.WriteUint8(otJamDetectionGetBusyPeriod(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_JAM_DETECT_HISTORY_BITMAP(void) |
| { |
| return mEncoder.WriteUint64(otJamDetectionGetHistoryBitmap(mInstance)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_JAM_DETECT_ENABLE(void) |
| { |
| bool enabled; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadBool(enabled)); |
| |
| if (enabled) |
| { |
| otJamDetectionStart(mInstance, &NcpBase::HandleJamStateChange_Jump, this); |
| } |
| else |
| { |
| otJamDetectionStop(mInstance); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_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; |
| } |
| |
| otError NcpBase::SetPropertyHandler_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; |
| } |
| |
| otError NcpBase::SetPropertyHandler_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_ENABLE_JAM_DETECTION |
| |
| #if OPENTHREAD_ENABLE_CHANNEL_MONITOR |
| |
| otError NcpBase::GetPropertyHandler_CHANNEL_MONITOR_SAMPLE_INTERVAL(void) |
| { |
| return mEncoder.WriteUint32(otChannelMonitorGetSampleInterval(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CHANNEL_MONITOR_RSSI_THRESHOLD(void) |
| { |
| return mEncoder.WriteInt8(otChannelMonitorGetRssiThreshold(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CHANNEL_MONITOR_SAMPLE_WINDOW(void) |
| { |
| return mEncoder.WriteUint32(otChannelMonitorGetSampleWindow(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CHANNEL_MONITOR_SAMPLE_COUNT(void) |
| { |
| return mEncoder.WriteUint32(otChannelMonitorGetSampleCount(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CHANNEL_MONITOR_CHANNEL_OCCUPANCY(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| for (uint8_t channel = OT_RADIO_CHANNEL_MIN; channel <= OT_RADIO_CHANNEL_MAX; channel++) |
| { |
| 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_ENABLE_CHANNEL_MONITOR |
| |
| otError NcpBase::GetPropertyHandler_MAC_CCA_FAILURE_RATE(void) |
| { |
| return mEncoder.WriteUint16(otLinkGetCcaFailureRate(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxTotal); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_ACK_REQ(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAckRequested); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_ACKED(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxAcked); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_NO_ACK_REQ(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxNoAckRequested); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_DATA(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxData); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_DATA_POLL(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxDataPoll); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_BEACON(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeacon); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_BEACON_REQ(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBeaconRequest); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_OTHER(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxOther); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_RETRY(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxRetry); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_ERR_CCA(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrCca); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_UNICAST(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxUnicast); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_PKT_BROADCAST(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxBroadcast); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_ERR_ABORT(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mTxErrAbort); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxTotal); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_DATA(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxData); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_DATA_POLL(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDataPoll); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_BEACON(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeacon); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_BEACON_REQ(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBeaconRequest); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_OTHER(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxOther); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_FILT_WL(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxAddressFiltered); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_FILT_DA(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDestAddrFiltered); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_DUP(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxDuplicated); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_UNICAST(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxUnicast); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_PKT_BROADCAST(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxBroadcast); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_ERR_EMPTY(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrNoFrame); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_ERR_UKWN_NBR(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrUnknownNeighbor); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_ERR_NVLD_SADDR(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrInvalidSrcAddr); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_ERR_SECURITY(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrSec); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_ERR_BAD_FCS(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrFcs); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_ERR_OTHER(void) |
| { |
| return mEncoder.WriteUint32(otLinkGetCounters(mInstance)->mRxErrOther); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_IP_SEC_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(mInboundSecureIpFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_IP_INSEC_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(mInboundInsecureIpFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_IP_DROPPED(void) |
| { |
| return mEncoder.WriteUint32(mDroppedInboundIpFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_IP_SEC_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(mOutboundSecureIpFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_IP_INSEC_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(mOutboundInsecureIpFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_IP_DROPPED(void) |
| { |
| return mEncoder.WriteUint32(mDroppedOutboundIpFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_TX_SPINEL_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(mTxSpinelFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_SPINEL_TOTAL(void) |
| { |
| return mEncoder.WriteUint32(mRxSpinelFrameCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_SPINEL_OUT_OF_ORDER_TID(void) |
| { |
| return mEncoder.WriteUint32(mRxSpinelOutOfOrderTidCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_RX_SPINEL_ERR(void) |
| { |
| return mEncoder.WriteUint32(mFramingErrorCounter); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_IP_TX_SUCCESS(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxSuccess); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_IP_RX_SUCCESS(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxSuccess); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_IP_TX_FAILURE(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mTxFailure); |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_IP_RX_FAILURE(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetIp6Counters(mInstance)->mRxFailure); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MSG_BUFFER_COUNTERS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| otBufferInfo bufferInfo; |
| |
| otMessageGetBufferInfo(mInstance, &bufferInfo); |
| |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mTotalBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mFreeBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.m6loSendMessages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.m6loSendBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.m6loReassemblyMessages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.m6loReassemblyBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mIp6Messages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mIp6Buffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mMplMessages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mMplBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mMleMessages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mMleBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mArpMessages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mArpBuffers)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mCoapMessages)); |
| SuccessOrExit(mEncoder.WriteUint16(bufferInfo.mCoapBuffers)); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_CNTR_ALL_MAC_COUNTERS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otMacCounters *counters = otLinkGetCounters(mInstance); |
| |
| if (counters == NULL) |
| { |
| error = mEncoder.OverwriteWithLastStatusError(SPINEL_STATUS_INVALID_COMMAND_FOR_PROP); |
| ExitNow(); |
| } |
| |
| // 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.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; |
| } |
| |
| #if OPENTHREAD_ENABLE_MAC_FILTER |
| |
| otError NcpBase::GetPropertyHandler_MAC_WHITELIST(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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_WHITELIST_ENABLED(void) |
| { |
| return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_WHITELIST); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_BLACKLIST(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; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_BLACKLIST_ENABLED(void) |
| { |
| return mEncoder.WriteBool(otLinkFilterGetAddressMode(mInstance) == OT_MAC_FILTER_ADDRESS_MODE_BLACKLIST); |
| } |
| |
| otError NcpBase::GetPropertyHandler_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; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_WHITELIST(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| // First, clear the address filter entries. |
| otLinkFilterClearAddresses(mInstance); |
| |
| while (mDecoder.GetRemainingLengthInStruct() > 0) |
| { |
| const otExtAddress *extAddress = NULL; |
| 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 whitelist, so we need to report |
| // those incomplete changes via an asynchronous |
| // change event. |
| |
| if (error != OT_ERROR_NONE) |
| { |
| WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_WHITELIST); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_WHITELIST_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_WHITELIST; |
| } |
| |
| error = otLinkFilterSetAddressMode(mInstance, mode); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_BLACKLIST(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| // First, clear the address filter entries. |
| otLinkFilterClearAddresses(mInstance); |
| |
| while (mDecoder.GetRemainingLengthInStruct() > 0) |
| { |
| const otExtAddress *extAddress = NULL; |
| |
| SuccessOrExit(error = mDecoder.OpenStruct()); |
| SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); |
| SuccessOrExit(error = mDecoder.CloseStruct()); |
| |
| error = otLinkFilterRemoveAddress(mInstance, extAddress); |
| |
| if (error == OT_ERROR_ALREADY) |
| { |
| error = OT_ERROR_NONE; |
| } |
| |
| SuccessOrExit(error); |
| } |
| |
| exit: |
| // If we had an error, we may have actually changed |
| // the state of the blacklist, so we need to report |
| // those incomplete changes via an asynchronous |
| // change event. |
| |
| if (error != OT_ERROR_NONE) |
| { |
| WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_BLACKLIST); |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_BLACKLIST_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_BLACKLIST; |
| } |
| |
| error = otLinkFilterSetAddressMode(mInstance, mode); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_FIXED_RSS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| // First, clear the address filter entries. |
| otLinkFilterClearRssIn(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 = NULL; |
| } |
| |
| SuccessOrExit(error = mDecoder.ReadInt8(rss)); |
| |
| SuccessOrExit(error = mDecoder.CloseStruct()); |
| |
| SuccessOrExit(error = otLinkFilterAddRssIn(mInstance, extAddress, 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) |
| { |
| WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_MAC_FIXED_RSS); |
| } |
| |
| return error; |
| } |
| #endif // OPENTHREAD_ENABLE_MAC_FILTER |
| |
| otError NcpBase::GetPropertyHandler_THREAD_MODE(void) |
| { |
| uint8_t numericMode; |
| otLinkModeConfig modeConfig = otThreadGetLinkMode(mInstance); |
| |
| numericMode = LinkFlagsToFlagByte( |
| modeConfig.mRxOnWhenIdle, |
| modeConfig.mSecureDataRequests, |
| modeConfig.mDeviceType, |
| modeConfig.mNetworkData |
| ); |
| |
| return mEncoder.WriteUint8(numericMode); |
| } |
| |
| otError NcpBase::SetPropertyHandler_THREAD_MODE(void) |
| { |
| uint8_t numericMode = 0; |
| otLinkModeConfig modeConfig; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(mDecoder.ReadUint8(numericMode)); |
| |
| modeConfig.mRxOnWhenIdle = ((numericMode & SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE) == SPINEL_THREAD_MODE_RX_ON_WHEN_IDLE); |
| modeConfig.mSecureDataRequests = |
| ((numericMode & SPINEL_THREAD_MODE_SECURE_DATA_REQUEST) == SPINEL_THREAD_MODE_SECURE_DATA_REQUEST); |
| modeConfig.mDeviceType = ((numericMode & SPINEL_THREAD_MODE_FULL_FUNCTION_DEV) == SPINEL_THREAD_MODE_FULL_FUNCTION_DEV); |
| modeConfig.mNetworkData = ((numericMode & SPINEL_THREAD_MODE_FULL_NETWORK_DATA) == SPINEL_THREAD_MODE_FULL_NETWORK_DATA); |
| |
| error = otThreadSetLinkMode(mInstance, modeConfig); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_CHILD_TIMEOUT(void) |
| { |
| return mEncoder.WriteUint32(otThreadGetChildTimeout(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_THREAD_RLOC16(void) |
| { |
| return mEncoder.WriteUint16(otThreadGetRloc16(mInstance)); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(void) |
| { |
| return mEncoder.WriteBool(mRequireJoinExistingNetwork); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NET_REQUIRE_JOIN_EXISTING(void) |
| { |
| return mDecoder.ReadBool(mRequireJoinExistingNetwork); |
| } |
| |
| otError NcpBase::SetPropertyHandler_STREAM_NET_INSECURE(void) |
| { |
| const uint8_t *framePtr = NULL; |
| uint16_t frameLen = 0; |
| const uint8_t *metaPtr = NULL; |
| uint16_t metaLen = 0; |
| otMessage *message = NULL; |
| otError error = OT_ERROR_NONE; |
| |
| // STREAM_NET_INSECURE packets are not secured at layer 2. |
| message = otIp6NewMessage(mInstance, false); |
| VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(mDecoder.ReadDataWithLen(framePtr, frameLen)); |
| SuccessOrExit(mDecoder.ReadData(metaPtr, metaLen)); |
| |
| // We ignore metadata for now. |
| // May later include TX power, allow retransmits, etc... |
| OT_UNUSED_VARIABLE(metaPtr); |
| OT_UNUSED_VARIABLE(metaLen); |
| |
| SuccessOrExit(error = otMessageAppend(message, framePtr, frameLen)); |
| |
| // Ensure the insecure message is forwarded using direct transmission. |
| otMessageSetDirectTransmission(message, true); |
| |
| error = otIp6Send(mInstance, message); |
| |
| // `otIp6Send()` takes ownership of `message` (in both success or |
| // failure cases). `message` is set to NULL so it is not freed at |
| // exit. |
| message = NULL; |
| |
| exit: |
| if (message != NULL) |
| { |
| otMessageFree(message); |
| } |
| |
| if (error == OT_ERROR_NONE) |
| { |
| mInboundInsecureIpFrameCounter++; |
| |
| } |
| else |
| { |
| mDroppedInboundIpFrameCounter++; |
| } |
| |
| return error; |
| } |
| |
| otError NcpBase::SetPropertyHandler_CNTR_RESET(void) |
| { |
| uint8_t value = 0; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadUint8(value)); |
| |
| VerifyOrExit(value == 1, error = OT_ERROR_INVALID_ARGS); |
| |
| // TODO: Implement counter reset! |
| error = OT_ERROR_NOT_IMPLEMENTED; |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_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_ENABLE_MAC_FILTER |
| |
| otError NcpBase::InsertPropertyHandler_MAC_WHITELIST(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otExtAddress *extAddress = NULL; |
| 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; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_MAC_BLACKLIST(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otExtAddress *extAddress = NULL; |
| |
| SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); |
| |
| error = otLinkFilterAddAddress(mInstance, extAddress); |
| |
| if (error == OT_ERROR_ALREADY) |
| { |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::InsertPropertyHandler_MAC_FIXED_RSS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otExtAddress *extAddress = NULL; |
| int8_t rss = OT_MAC_FILTER_FIXED_RSS_DISABLED; |
| |
| if (mDecoder.GetRemainingLength() > sizeof(int8_t)) |
| { |
| SuccessOrExit(mDecoder.ReadEui64(extAddress)); |
| } |
| |
| SuccessOrExit(mDecoder.ReadInt8(rss)); |
| |
| error = otLinkFilterAddRssIn(mInstance, extAddress, rss); |
| |
| exit: |
| return error; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_MAC_FILTER |
| |
| otError NcpBase::RemovePropertyHandler_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_ENABLE_MAC_FILTER |
| |
| otError NcpBase::RemovePropertyHandler_MAC_WHITELIST(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otExtAddress *extAddress = NULL; |
| |
| SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); |
| |
| error = otLinkFilterRemoveAddress(mInstance, extAddress); |
| |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_MAC_BLACKLIST(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otExtAddress *extAddress = NULL; |
| |
| SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); |
| |
| error = otLinkFilterRemoveAddress(mInstance, extAddress); |
| |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::RemovePropertyHandler_MAC_FIXED_RSS(void) |
| { |
| otError error = OT_ERROR_NONE; |
| const otExtAddress *extAddress = NULL; |
| |
| if (mDecoder.GetRemainingLength() > 0) |
| { |
| SuccessOrExit(error = mDecoder.ReadEui64(extAddress)); |
| } |
| |
| error = otLinkFilterRemoveRssIn(mInstance, extAddress); |
| |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| #endif // OPENTHREAD_ENABLE_MAC_FILTER |
| |
| #if OPENTHREAD_ENABLE_LEGACY |
| |
| void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) |
| { |
| mLegacyHandlers = aHandlers; |
| bool isEnabled; |
| |
| VerifyOrExit(mLegacyHandlers != NULL); |
| |
| isEnabled = (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED); |
| |
| if (isEnabled) |
| { |
| if (mLegacyHandlers->mStartLegacy) |
| { |
| mLegacyHandlers->mStartLegacy(); |
| } |
| } |
| else |
| { |
| if (mLegacyHandlers->mStopLegacy) |
| { |
| mLegacyHandlers->mStopLegacy(); |
| } |
| } |
| |
| if (mLegacyHandlers->mSetLegacyUlaPrefix) |
| { |
| mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); |
| } |
| |
| exit: |
| return; |
| } |
| |
| void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) |
| { |
| memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH); |
| mChangedPropsSet.AddProperty(SPINEL_PROP_NEST_LEGACY_ULA_PREFIX); |
| mUpdateChangedPropsTask.Post(); |
| } |
| |
| void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) |
| { |
| mLegacyNodeDidJoin = true; |
| mLegacyLastJoinedNode = *aExtAddr; |
| mChangedPropsSet.AddProperty(SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED); |
| mUpdateChangedPropsTask.Post(); |
| } |
| |
| otError NcpBase::GetPropertyHandler_NEST_LEGACY_ULA_PREFIX(void) |
| { |
| return mEncoder.WriteData(mLegacyUlaPrefix, sizeof(mLegacyUlaPrefix)); |
| } |
| |
| otError NcpBase::SetPropertyHandler_NEST_LEGACY_ULA_PREFIX(void) |
| { |
| const uint8_t *ptr = NULL; |
| uint16_t len; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadData(ptr, len)); |
| |
| VerifyOrExit(len <= sizeof(mLegacyUlaPrefix), error = OT_ERROR_PARSE); |
| |
| memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); |
| memcpy(mLegacyUlaPrefix, ptr, len); |
| |
| if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mSetLegacyUlaPrefix != NULL)) |
| { |
| mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_NEST_LEGACY_LAST_NODE_JOINED(void) |
| { |
| |
| if (!mLegacyNodeDidJoin) |
| { |
| memset(&mLegacyLastJoinedNode, 0, sizeof(mLegacyLastJoinedNode)); |
| } |
| |
| return mEncoder.WriteEui64(mLegacyLastJoinedNode); |
| } |
| |
| void NcpBase::StartLegacy(void) |
| { |
| mLegacyNodeDidJoin = false; |
| |
| if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStartLegacy != NULL)) |
| { |
| mLegacyHandlers->mStartLegacy(); |
| } |
| } |
| |
| void NcpBase::StopLegacy(void) |
| { |
| mLegacyNodeDidJoin = false; |
| |
| if ((mLegacyHandlers != NULL) && (mLegacyHandlers->mStopLegacy != NULL)) |
| { |
| mLegacyHandlers->mStopLegacy(); |
| } |
| } |
| |
| #endif // OPENTHREAD_ENABLE_LEGACY |
| |
| otError NcpBase::EncodeChannelMask(uint32_t aChannelMask) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| for (uint8_t i = 0; i < 32; i++) |
| { |
| if (0 != (aChannelMask & (1 << i))) |
| { |
| SuccessOrExit(error = mEncoder.WriteUint8(i)); |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::DecodeChannelMask(uint32_t &aChannelMask) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t channel; |
| |
| aChannelMask = 0; |
| |
| while (!mDecoder.IsAllReadInStruct()) |
| { |
| SuccessOrExit(error = mDecoder.ReadUint8(channel)); |
| VerifyOrExit(channel <= 31, error = OT_ERROR_INVALID_ARGS); |
| aChannelMask |= (1U << channel); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(void) |
| { |
| return EncodeChannelMask(mChannelMask); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(void) |
| { |
| uint32_t newMask = 0; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = DecodeChannelMask(newMask)); |
| VerifyOrExit((~mSupportedChannelMask & newMask) == 0, error = OT_ERROR_INVALID_ARGS); |
| |
| mChannelMask = newMask; |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_SCAN_PERIOD(void) |
| { |
| return mEncoder.WriteUint16(mScanPeriod); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SCAN_PERIOD(void) |
| { |
| return mDecoder.ReadUint16(mScanPeriod); |
| } |
| |
| otError NcpBase::GetPropertyHandler_MAC_SCAN_STATE(void) |
| { |
| uint8_t scanState = SPINEL_SCAN_STATE_IDLE; |
| |
| #if OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| scanState = (mCurScanChannel == kInvalidScanChannel) ? SPINEL_SCAN_STATE_IDLE : SPINEL_SCAN_STATE_ENERGY; |
| } |
| else |
| |
| #endif // OPENTHREAD_ENABLE_RAW_LINK_API |
| |
| { |
| if (otLinkIsActiveScanInProgress(mInstance)) |
| { |
| scanState = SPINEL_SCAN_STATE_BEACON; |
| |
| } |
| else if (otLinkIsEnergyScanInProgress(mInstance)) |
| { |
| scanState = SPINEL_SCAN_STATE_ENERGY; |
| } |
| else if (otThreadIsDiscoverInProgress(mInstance)) |
| { |
| scanState = SPINEL_SCAN_STATE_DISCOVER; |
| } |
| else |
| { |
| scanState = SPINEL_SCAN_STATE_IDLE; |
| } |
| } |
| |
| return mEncoder.WriteUint8(scanState); |
| } |
| |
| otError NcpBase::SetPropertyHandler_MAC_SCAN_STATE(void) |
| { |
| uint8_t state = 0; |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = mDecoder.ReadUint8(state)); |
| |
| switch (state) |
| { |
| case SPINEL_SCAN_STATE_IDLE: |
| error = OT_ERROR_NONE; |
| break; |
| |
| case SPINEL_SCAN_STATE_BEACON: |
| #if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| error = OT_ERROR_NOT_IMPLEMENTED; |
| } |
| else |
| #endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API |
| { |
| error = otLinkActiveScan( |
| mInstance, |
| mChannelMask, |
| mScanPeriod, |
| &HandleActiveScanResult_Jump, |
| this |
| ); |
| } |
| |
| SuccessOrExit(error); |
| break; |
| |
| case SPINEL_SCAN_STATE_ENERGY: |
| #if OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API |
| if (otLinkRawIsEnabled(mInstance)) |
| { |
| uint8_t scanChannel; |
| |
| // Make sure we aren't already scanning and that we have |
| // only 1 bit set for the channel mask. |
| VerifyOrExit(mCurScanChannel == kInvalidScanChannel, error = OT_ERROR_INVALID_STATE); |
| VerifyOrExit(HasOnly1BitSet(mChannelMask), error = OT_ERROR_INVALID_ARGS); |
| |
| scanChannel = IndexOfMSB(mChannelMask); |
| mCurScanChannel = (int8_t)scanChannel; |
| |
| error = otLinkRawEnergyScan( |
| mInstance, |
| scanChannel, |
| mScanPeriod, |
| LinkRawEnergyScanDone |
| ); |
| } |
| else |
| #endif // OPENTHREAD_RADIO || OPENTHREAD_ENABLE_RAW_LINK_API |
| { |
| error = otLinkEnergyScan( |
| mInstance, |
| mChannelMask, |
| mScanPeriod, |
| &HandleEnergyScanResult_Jump, |
| this |
| ); |
| } |
| |
| SuccessOrExit(error); |
| break; |
| |
| case SPINEL_SCAN_STATE_DISCOVER: |
| error = otThreadDiscover( |
| mInstance, |
| mChannelMask, |
| mDiscoveryScanPanId, |
| mDiscoveryScanJoinerFlag, |
| mDiscoveryScanEnableFiltering, |
| &HandleActiveScanResult_Jump, |
| this |
| ); |
| |
| SuccessOrExit(error); |
| break; |
| |
| default: |
| error = OT_ERROR_INVALID_ARGS; |
| break; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| 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->mIsJoinable) |
| { |
| flags |= SPINEL_BEACON_THREAD_FLAG_JOINABLE; |
| } |
| |
| 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(); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Outbound Datagram Handling |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleDatagramFromStack(otMessage *aMessage, void *aContext) |
| { |
| static_cast<NcpBase *>(aContext)->HandleDatagramFromStack(aMessage); |
| } |
| |
| void NcpBase::HandleDatagramFromStack(otMessage *aMessage) |
| { |
| VerifyOrExit(aMessage != NULL); |
| |
| SuccessOrExit(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()) |
| { |
| IgnoreReturnValue(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)) != NULL) |
| { |
| // 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; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Property/Status Changed |
| // ---------------------------------------------------------------------------- |
| |
| void NcpBase::HandleStateChanged(uint32_t aFlags, void *aContext) |
| { |
| NcpBase *ncp = static_cast<NcpBase *>(aContext); |
| |
| ncp->mThreadChangedFlags |= aFlags; |
| ncp->mUpdateChangedPropsTask.Post(); |
| } |
| |
| void NcpBase::ProcessThreadChangedFlags(void) |
| { |
| static const struct |
| { |
| uint32_t 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_SUBSRCRIBED, SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE }, |
| { OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED, 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_MASTER_KEY, SPINEL_PROP_NET_MASTER_KEY }, |
| { OT_CHANGED_PSKC, SPINEL_PROP_NET_PSKC }, |
| { OT_CHANGED_CHANNEL_MANAGER_NEW_CHANNEL, SPINEL_PROP_CHANNEL_MANAGER_NEW_CHANNEL }, |
| }; |
| |
| 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) |
| #if OPENTHREAD_ENABLE_LEGACY |
| && !mLegacyNodeDidJoin |
| #endif |
| ) |
| { |
| mThreadChangedFlags &= ~static_cast<uint32_t>(OT_CHANGED_THREAD_PARTITION_ID); |
| 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 (unsigned i = 0; i < OT_ARRAY_LENGTH(kFlags); i++) |
| { |
| uint32_t threadFlag = kFlags[i].mThreadFlag; |
| |
| if (mThreadChangedFlags & threadFlag) |
| { |
| spinel_prop_key_t propKey = kFlags[i].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_THREAD_RLOC_ADDED) |
| |
| mThreadChangedFlags = 0; |
| |
| exit: |
| return; |
| } |
| |
| } // namespace Ncp |
| } // namespace ot |
| |
| // ---------------------------------------------------------------------------- |
| // MARK: Legacy network APIs |
| // ---------------------------------------------------------------------------- |
| |
| void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| ncp->RegisterLegacyHandlers(aHandlers); |
| } |
| |
| #else |
| OT_UNUSED_VARIABLE(aHandlers); |
| #endif |
| } |
| |
| void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| ncp->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix); |
| } |
| |
| #else |
| OT_UNUSED_VARIABLE(aUlaPrefix); |
| #endif |
| } |
| |
| void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) |
| { |
| #if OPENTHREAD_ENABLE_LEGACY |
| ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); |
| |
| if (ncp != NULL) |
| { |
| ncp->HandleLegacyNodeDidJoin(aExtAddr); |
| } |
| |
| #else |
| OT_UNUSED_VARIABLE(aExtAddr); |
| #endif |
| } |
| |
| #endif // OPENTHREAD_MTD || OPENTHREAD_FTD |