blob: bd1f227cf7c3a585432b550e7c06c6259f00bd64 [file] [log] [blame]
/*
* 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
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#include <openthread/child_supervision.h>
#endif
#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)
{
uint16_t cslPeriod;
otError error = OT_ERROR_NONE;
SuccessOrExit(error = mDecoder.ReadUint16(cslPeriod));
error = otLinkCslSetPeriod(mInstance, cslPeriod);
exit:
return error;
}
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_PERIOD>(void)
{
return mEncoder.WriteUint16(otLinkCslGetPeriod(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 = otLinkCslSetTimeout(mInstance, cslTimeout);
exit:
return error;
}
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_TIMEOUT>(void)
{
return mEncoder.WriteUint32(otLinkCslGetTimeout(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 = otLinkCslSetChannel(mInstance, cslChannel);
exit:
return error;
}
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_THREAD_CSL_CHANNEL>(void)
{
return mEncoder.WriteUint8(otLinkCslGetChannel(mInstance));
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_THREAD_MLR_REQUEST>(void)
{
otError error = OT_ERROR_NONE;
otIp6Address addresses[kIp6AddressesNumMax];
uint8_t addressesCount = 0U;
bool timeoutPresent = false;
uint32_t timeout;
SuccessOrExit(error = mDecoder.OpenStruct());
while (mDecoder.GetRemainingLengthInStruct())
{
VerifyOrExit(addressesCount < kIp6AddressesNumMax, 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);
StartLegacy();
}
else
{
error = otThreadSetEnabled(mInstance, false);
StopLegacy();
}
}
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));
}
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 considred
// successful.
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
}
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
#if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE
template <> otError NcpBase::HandlePropertyGet<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)
{
SuccessOrExit(error = mEncoder.OpenStruct());
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP));
SuccessOrExit(error = mEncoder.WriteUint64(aDataset.mActiveTimestamp));
SuccessOrExit(error = mEncoder.CloseStruct());
}
if (aDataset.mComponents.mIsPendingTimestampPresent)
{
SuccessOrExit(error = mEncoder.OpenStruct());
SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_PENDING_TIMESTAMP));
SuccessOrExit(error = mEncoder.WriteUint64(aDataset.mPendingTimestamp));
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));
}
aDataset.mComponents.mIsActiveTimestampPresent = true;
break;
case SPINEL_PROP_DATASET_PENDING_TIMESTAMP:
if (!aAllowEmptyValues || !mDecoder.IsAllReadInStruct())
{
SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mPendingTimestamp));
}
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
#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_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;
}
#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE
#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 = {};
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 = {};
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 = {};
otLinkMetricsSeriesFlags seriesFlags = {};
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_LEGACY_ENABLE
void NcpBase::RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
{
mLegacyHandlers = aHandlers;
bool isEnabled;
VerifyOrExit(mLegacyHandlers != nullptr);
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();
}
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_NEST_LEGACY_ULA_PREFIX>(void)
{
return mEncoder.WriteData(mLegacyUlaPrefix, sizeof(mLegacyUlaPrefix));
}
template <> otError NcpBase::HandlePropertySet<SPINEL_PROP_NEST_LEGACY_ULA_PREFIX>(void)
{
const uint8_t *ptr = nullptr;
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 != nullptr) && (mLegacyHandlers->mSetLegacyUlaPrefix != nullptr))
{
mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix);
}
exit:
return error;
}
template <> otError NcpBase::HandlePropertyGet<SPINEL_PROP_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 != nullptr) && (mLegacyHandlers->mStartLegacy != nullptr))
{
mLegacyHandlers->mStartLegacy();
}
}
void NcpBase::StopLegacy(void)
{
mLegacyNodeDidJoin = false;
if ((mLegacyHandlers != nullptr) && (mLegacyHandlers->mStopLegacy != nullptr))
{
mLegacyHandlers->mStopLegacy();
}
}
#endif // OPENTHREAD_CONFIG_LEGACY_ENABLE
#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,
uint8_t aStatus,
void * aContext)
{
static_cast<NcpBase *>(aContext)->HandleLinkMetricsReport(aSource, aMetricsValues, aStatus);
}
void NcpBase::HandleLinkMetricsReport(const otIp6Address * aSource,
const otLinkMetricsValues *aMetricsValues,
uint8_t aStatus)
{
SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_THREAD_LINK_METRICS_QUERY_RESULT));
SuccessOrExit(mEncoder.WriteIp6Address(*aSource));
SuccessOrExit(mEncoder.WriteUint8(aStatus));
SuccessOrExit(EncodeLinkMetricsValues(aMetricsValues));
SuccessOrExit(mEncoder.EndFrame());
exit:
return;
}
void NcpBase::HandleLinkMetricsMgmtResponse_Jump(const otIp6Address *aSource, uint8_t aStatus, void *aContext)
{
static_cast<NcpBase *>(aContext)->HandleLinkMetricsMgmtResponse(aSource, aStatus);
}
void NcpBase::HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, uint8_t aStatus)
{
SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS,
SPINEL_PROP_THREAD_LINK_METRICS_MGMT_RESPONSE));
SuccessOrExit(mEncoder.WriteIp6Address(*aSource));
SuccessOrExit(mEncoder.WriteUint8(aStatus));
SuccessOrExit(mEncoder.EndFrame());
exit:
return;
}
void NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress aShortAddress,
const otExtAddress * aExtAddress,
const otLinkMetricsValues *aMetricsValues,
void * aContext)
{
static_cast<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)
#if OPENTHREAD_CONFIG_LEGACY_ENABLE
&& !mLegacyNodeDidJoin
#endif
)
{
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
// ----------------------------------------------------------------------------
// MARK: Legacy network APIs
// ----------------------------------------------------------------------------
void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers)
{
#if OPENTHREAD_CONFIG_LEGACY_ENABLE
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
if (ncp != nullptr)
{
ncp->RegisterLegacyHandlers(aHandlers);
}
#else
OT_UNUSED_VARIABLE(aHandlers);
#endif
}
void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix)
{
#if OPENTHREAD_CONFIG_LEGACY_ENABLE
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
if (ncp != nullptr)
{
ncp->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix);
}
#else
OT_UNUSED_VARIABLE(aUlaPrefix);
#endif
}
void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr)
{
#if OPENTHREAD_CONFIG_LEGACY_ENABLE
ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance();
if (ncp != nullptr)
{
ncp->HandleLegacyNodeDidJoin(aExtAddr);
}
#else
OT_UNUSED_VARIABLE(aExtAddr);
#endif
}
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD