/* * 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 full thread device specified Spinel interface to the OpenThread stack. */ #include "ncp_base.hpp" #include #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE #include #endif #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE #include #endif #include #include #include #include #include #include #include #include #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/instance.hpp" #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE #include "meshcop/commissioner.hpp" #endif #if OPENTHREAD_FTD namespace ot { namespace Ncp { otError NcpBase::EncodeChildInfo(const otChildInfo &aChildInfo) { otError error = OT_ERROR_NONE; uint8_t modeFlags; modeFlags = LinkFlagsToFlagByte(aChildInfo.mRxOnWhenIdle, aChildInfo.mFullThreadDevice, aChildInfo.mFullNetworkData); SuccessOrExit(error = mEncoder.WriteEui64(aChildInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(aChildInfo.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint32(aChildInfo.mTimeout)); SuccessOrExit(error = mEncoder.WriteUint32(aChildInfo.mAge)); SuccessOrExit(error = mEncoder.WriteUint8(aChildInfo.mNetworkDataVersion)); SuccessOrExit(error = mEncoder.WriteUint8(aChildInfo.mLinkQualityIn)); SuccessOrExit(error = mEncoder.WriteInt8(aChildInfo.mAverageRssi)); SuccessOrExit(error = mEncoder.WriteUint8(modeFlags)); SuccessOrExit(error = mEncoder.WriteInt8(aChildInfo.mLastRssi)); exit: return error; } // ---------------------------------------------------------------------------- // MARK: Property/Status Changed // ---------------------------------------------------------------------------- #if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE void NcpBase::HandleParentResponseInfo(otThreadParentResponseInfo *aInfo, void *aContext) { VerifyOrExit(aInfo && aContext); static_cast(aContext)->HandleParentResponseInfo(*aInfo); exit: return; } void NcpBase::HandleParentResponseInfo(const otThreadParentResponseInfo &aInfo) { VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(SPINEL_PROP_PARENT_RESPONSE_INFO)); SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_PARENT_RESPONSE_INFO)); SuccessOrExit(mEncoder.WriteEui64(aInfo.mExtAddr)); SuccessOrExit(mEncoder.WriteUint16(aInfo.mRloc16)); SuccessOrExit(mEncoder.WriteInt8(aInfo.mRssi)); SuccessOrExit(mEncoder.WriteInt8(aInfo.mPriority)); SuccessOrExit(mEncoder.WriteUint8(aInfo.mLinkQuality3)); SuccessOrExit(mEncoder.WriteUint8(aInfo.mLinkQuality2)); SuccessOrExit(mEncoder.WriteUint8(aInfo.mLinkQuality1)); SuccessOrExit(mEncoder.WriteBool(aInfo.mIsAttached)); SuccessOrExit(mEncoder.EndFrame()); exit: return; } #endif void NcpBase::HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo *aEntry) { GetNcpInstance()->HandleNeighborTableChanged(aEvent, *aEntry); } void NcpBase::HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo &aEntry) { otError error = OT_ERROR_NONE; unsigned int command = SPINEL_CMD_PROP_VALUE_REMOVED; spinel_prop_key_t property; switch (aEvent) { case OT_NEIGHBOR_TABLE_EVENT_CHILD_ADDED: command = SPINEL_CMD_PROP_VALUE_INSERTED; OT_FALL_THROUGH; case OT_NEIGHBOR_TABLE_EVENT_CHILD_REMOVED: property = SPINEL_PROP_THREAD_CHILD_TABLE; VerifyOrExit(!aEntry.mInfo.mChild.mIsStateRestoring); break; case OT_NEIGHBOR_TABLE_EVENT_ROUTER_ADDED: command = SPINEL_CMD_PROP_VALUE_INSERTED; OT_FALL_THROUGH; case OT_NEIGHBOR_TABLE_EVENT_ROUTER_REMOVED: property = SPINEL_PROP_THREAD_NEIGHBOR_TABLE; break; default: ExitNow(); } VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(property)); SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, command, property)); if (property == SPINEL_PROP_THREAD_CHILD_TABLE) { SuccessOrExit(error = EncodeChildInfo(aEntry.mInfo.mChild)); } else { SuccessOrExit(error = EncodeNeighborInfo(aEntry.mInfo.mRouter)); } SuccessOrExit(error = mEncoder.EndFrame()); exit: // If the frame can not be added (out of NCP buffer space), we remember // to send an async `LAST_STATUS(NOMEM)` when buffer space becomes // available. Also `mShouldEmitChildTableUpdate` flag is set to `true` so // that the entire child table is later emitted as `VALUE_IS` spinel frame // update from `ProcessThreadChangedFlags()`. if (error != OT_ERROR_NONE) { if (property == SPINEL_PROP_THREAD_CHILD_TABLE) { mShouldEmitChildTableUpdate = true; } mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); mUpdateChangedPropsTask.Post(); } } // ---------------------------------------------------------------------------- // MARK: Individual Property Handlers // ---------------------------------------------------------------------------- template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetLocalLeaderWeight(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetLeaderWeight(mInstance)); } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otChildInfo childInfo; uint16_t maxChildren; maxChildren = otThreadGetMaxAllowedChildren(mInstance); for (uint16_t index = 0; index < maxChildren; index++) { if ((otThreadGetChildInfoByIndex(mInstance, index, &childInfo) != OT_ERROR_NONE) || childInfo.mIsStateRestoring) { continue; } SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = EncodeChildInfo(childInfo)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otRouterInfo routerInfo; uint8_t maxRouterId; maxRouterId = otThreadGetMaxRouterId(mInstance); for (uint8_t routerId = 0; routerId <= maxRouterId; routerId++) { if ((otThreadGetRouterInfo(mInstance, routerId, &routerInfo) != OT_ERROR_NONE) || !routerInfo.mAllocated) { continue; } SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(routerInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(routerInfo.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint8(routerInfo.mRouterId)); SuccessOrExit(error = mEncoder.WriteUint8(routerInfo.mNextHop)); SuccessOrExit(error = mEncoder.WriteUint8(routerInfo.mPathCost)); SuccessOrExit(error = mEncoder.WriteUint8(routerInfo.mLinkQualityIn)); SuccessOrExit(error = mEncoder.WriteUint8(routerInfo.mLinkQualityOut)); SuccessOrExit(error = mEncoder.WriteUint8(routerInfo.mAge)); SuccessOrExit(error = mEncoder.WriteBool(routerInfo.mLinkEstablished)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otChildInfo childInfo; uint16_t maxChildren; otIp6Address ip6Address; otChildIp6AddressIterator iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; maxChildren = otThreadGetMaxAllowedChildren(mInstance); for (uint16_t childIndex = 0; childIndex < maxChildren; childIndex++) { if ((otThreadGetChildInfoByIndex(mInstance, childIndex, &childInfo) != OT_ERROR_NONE) || childInfo.mIsStateRestoring) { continue; } SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteEui64(childInfo.mExtAddress)); SuccessOrExit(error = mEncoder.WriteUint16(childInfo.mRloc16)); iterator = OT_CHILD_IP6_ADDRESS_ITERATOR_INIT; while (otThreadGetChildNextIp6Address(mInstance, childIndex, &iterator, &ip6Address) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.WriteIp6Address(ip6Address)); } SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otThreadIsRouterEligible(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { bool eligible; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(eligible)); error = otThreadSetRouterEligible(mInstance, eligible); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otLinkGetMaxFrameRetriesIndirect(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t maxFrameRetriesIndirect; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(maxFrameRetriesIndirect)); otLinkSetMaxFrameRetriesIndirect(mInstance, maxFrameRetriesIndirect); exit: return error; } #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUtf8(otThreadGetDomainName(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; const char *domainName; SuccessOrExit(error = mDecoder.ReadUtf8(domainName)); error = otThreadSetDomainName(mInstance, domainName); exit: return error; } #endif #if OPENTHREAD_CONFIG_DUA_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { const otIp6InterfaceIdentifier *iid = otThreadGetFixedDuaInterfaceIdentifier(mInstance); otError error = OT_ERROR_NONE; if (iid == nullptr) { // send empty response } else { for (uint8_t i : iid->mFields.m8) { SuccessOrExit(error = mEncoder.WriteUint8(i)); } } exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; if (mDecoder.GetRemainingLength() == 0) { SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, nullptr)); } else { otIp6InterfaceIdentifier iid; for (uint8_t &i : iid.mFields.m8) { SuccessOrExit(error = mDecoder.ReadUint8(i)); } SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, &iid)); } exit: return error; } #endif // OPENTHREAD_CONFIG_DUA_ENABLE #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { uint8_t state = SPINEL_THREAD_BACKBONE_ROUTER_STATE_DISABLED; switch (otBackboneRouterGetState(mInstance)) { case OT_BACKBONE_ROUTER_STATE_DISABLED: state = SPINEL_THREAD_BACKBONE_ROUTER_STATE_DISABLED; break; case OT_BACKBONE_ROUTER_STATE_SECONDARY: state = SPINEL_THREAD_BACKBONE_ROUTER_STATE_SECONDARY; break; case OT_BACKBONE_ROUTER_STATE_PRIMARY: state = SPINEL_THREAD_BACKBONE_ROUTER_STATE_PRIMARY; break; } return mEncoder.WriteUint8(state); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t state; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(state)); if (state) { otBackboneRouterSetEnabled(mInstance, true); } else { otBackboneRouterSetEnabled(mInstance, false); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otBackboneRouterConfig bbrConfig; otBackboneRouterGetConfig(mInstance, &bbrConfig); SuccessOrExit(error = mEncoder.WriteUint16(bbrConfig.mReregistrationDelay)); SuccessOrExit(error = mEncoder.WriteUint32(bbrConfig.mMlrTimeout)); SuccessOrExit(error = mEncoder.WriteUint8(bbrConfig.mSequenceNumber)); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; otBackboneRouterConfig bbrConfig; SuccessOrExit(error = mDecoder.ReadUint16(bbrConfig.mReregistrationDelay)); SuccessOrExit(error = mDecoder.ReadUint32(bbrConfig.mMlrTimeout)); SuccessOrExit(error = mDecoder.ReadUint8(bbrConfig.mSequenceNumber)); SuccessOrExit(error = otBackboneRouterSetConfig(mInstance, &bbrConfig)); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { return otBackboneRouterRegister(mInstance); } template <> otError NcpBase::HandlePropertyGet(void) { uint8_t jitter = otBackboneRouterGetRegistrationJitter(mInstance); return mEncoder.WriteUint8(jitter); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint8_t jitter; SuccessOrExit(error = mDecoder.ReadUint8(jitter)); otBackboneRouterSetRegistrationJitter(mInstance, jitter); exit: return error; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { Pskc pskc; otThreadGetPskc(mInstance, &pskc); return mEncoder.WriteData(pskc.m8, sizeof(spinel_net_pskc_t)); } template <> otError NcpBase::HandlePropertySet(void) { const uint8_t *ptr = nullptr; uint16_t len; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadData(ptr, len)); VerifyOrExit(len == sizeof(spinel_net_pskc_t), error = OT_ERROR_PARSE); error = otThreadSetPskc(mInstance, reinterpret_cast(ptr)); exit: return error; } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE template <> otError NcpBase::HandlePropertySet(void) { uint32_t partitionId = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(partitionId)); otThreadSetPreferredLeaderPartitionId(mInstance, partitionId); exit: return error; } #endif template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(static_cast(otThreadGetMaxAllowedChildren(mInstance))); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t maxChildren = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(maxChildren)); error = otThreadSetMaxAllowedChildren(mInstance, maxChildren); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetRouterUpgradeThreshold(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t threshold = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(threshold)); otThreadSetRouterUpgradeThreshold(mInstance, threshold); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetRouterDowngradeThreshold(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t threshold = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(threshold)); otThreadSetRouterDowngradeThreshold(mInstance, threshold); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetRouterSelectionJitter(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t jitter = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(jitter)); otThreadSetRouterSelectionJitter(mInstance, jitter); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otThreadGetContextIdReuseDelay(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t delay = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(delay)); otThreadSetContextIdReuseDelay(mInstance, delay); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otThreadGetNetworkIdTimeout(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t timeout = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(timeout)); otThreadSetNetworkIdTimeout(mInstance, timeout); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error; otOperationalDataset dataset; error = otDatasetCreateNewNetwork(mInstance, &dataset); if (error == OT_ERROR_NONE) { error = EncodeOperationalDataset(dataset); } else { error = mEncoder.OverwriteWithLastStatusError(ThreadErrorToSpinelStatus(error)); } return error; } #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { uint8_t state = SPINEL_MESHCOP_COMMISSIONER_STATE_DISABLED; switch (otCommissionerGetState(mInstance)) { case OT_COMMISSIONER_STATE_DISABLED: state = SPINEL_MESHCOP_COMMISSIONER_STATE_DISABLED; break; case OT_COMMISSIONER_STATE_PETITION: state = SPINEL_MESHCOP_COMMISSIONER_STATE_PETITION; break; case OT_COMMISSIONER_STATE_ACTIVE: state = SPINEL_MESHCOP_COMMISSIONER_STATE_ACTIVE; break; } return mEncoder.WriteUint8(state); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t state; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(state)); switch (state) { case SPINEL_MESHCOP_COMMISSIONER_STATE_DISABLED: error = otCommissionerStop(mInstance); break; case SPINEL_MESHCOP_COMMISSIONER_STATE_ACTIVE: error = otCommissionerStart(mInstance, nullptr, nullptr, nullptr); break; default: error = OT_ERROR_INVALID_ARGS; break; } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; uint16_t iter = 0; otJoinerInfo joinerInfo; while (otCommissionerGetNextJoinerInfo(mInstance, &iter, &joinerInfo) == OT_ERROR_NONE) { SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.OpenStruct()); // Joiner Id (any, EUI64 or a Joiner Discerner) struct switch (joinerInfo.mType) { case OT_JOINER_INFO_TYPE_ANY: break; case OT_JOINER_INFO_TYPE_EUI64: SuccessOrExit(error = mEncoder.WriteEui64(joinerInfo.mSharedId.mEui64)); break; case OT_JOINER_INFO_TYPE_DISCERNER: SuccessOrExit(error = mEncoder.WriteUint8(joinerInfo.mSharedId.mDiscerner.mLength)); SuccessOrExit(error = mEncoder.WriteUint64(joinerInfo.mSharedId.mDiscerner.mValue)); break; } SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.WriteUint32(joinerInfo.mExpirationTime)); SuccessOrExit(error = mEncoder.WriteUtf8(joinerInfo.mPskd.m8)); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; otJoinerDiscerner discerner; bool withDiscerner = false; const otExtAddress *eui64; uint32_t timeout; const char *psk; SuccessOrExit(error = mDecoder.OpenStruct()); switch (mDecoder.GetRemainingLengthInStruct()) { case 0: // Empty struct indicates any joiner eui64 = nullptr; break; case sizeof(spinel_eui64_t): SuccessOrExit(error = mDecoder.ReadEui64(eui64)); break; default: SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength)); SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue)); withDiscerner = true; break; } SuccessOrExit(error = mDecoder.CloseStruct()); SuccessOrExit(error = mDecoder.ReadUint32(timeout)); SuccessOrExit(error = mDecoder.ReadUtf8(psk)); if (withDiscerner) { error = otCommissionerAddJoinerWithDiscerner(mInstance, &discerner, psk, timeout); } else { error = otCommissionerAddJoiner(mInstance, eui64, psk, timeout); } exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; otJoinerDiscerner discerner; bool withDiscerner = false; const otExtAddress *eui64; SuccessOrExit(error = mDecoder.OpenStruct()); switch (mDecoder.GetRemainingLengthInStruct()) { case 0: // Empty struct indicates any joiner eui64 = nullptr; break; case sizeof(spinel_eui64_t): SuccessOrExit(error = mDecoder.ReadEui64(eui64)); break; default: SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength)); SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue)); withDiscerner = true; break; } SuccessOrExit(error = mDecoder.CloseStruct()); if (withDiscerner) { error = otCommissionerRemoveJoinerWithDiscerner(mInstance, &discerner); } else { error = otCommissionerRemoveJoiner(mInstance, eui64); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUtf8(otCommissionerGetProvisioningUrl(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; const char *url; SuccessOrExit(error = mDecoder.ReadUtf8(url)); error = otCommissionerSetProvisioningUrl(mInstance, url); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otCommissionerGetSessionId(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint32_t channelMask; uint8_t count; uint16_t period; const otIp6Address *address; SuccessOrExit(error = mDecoder.ReadUint32(channelMask)); SuccessOrExit(error = mDecoder.ReadUint8(count)); SuccessOrExit(error = mDecoder.ReadUint16(period)); SuccessOrExit(error = mDecoder.ReadIp6Address(address)); error = otCommissionerAnnounceBegin(mInstance, channelMask, count, period, address); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint32_t channelMask; uint8_t count; uint16_t period; uint16_t scanDuration; const otIp6Address *address; SuccessOrExit(error = mDecoder.ReadUint32(channelMask)); SuccessOrExit(error = mDecoder.ReadUint8(count)); SuccessOrExit(error = mDecoder.ReadUint16(period)); SuccessOrExit(error = mDecoder.ReadUint16(scanDuration)); SuccessOrExit(error = mDecoder.ReadIp6Address(address)); error = otCommissionerEnergyScan(mInstance, channelMask, count, period, scanDuration, address, &NcpBase::HandleCommissionerEnergyReport_Jump, this); exit: return error; } void NcpBase::HandleCommissionerEnergyReport_Jump(uint32_t aChannelMask, const uint8_t *aEnergyData, uint8_t aLength, void *aContext) { static_cast(aContext)->HandleCommissionerEnergyReport(aChannelMask, aEnergyData, aLength); } void NcpBase::HandleCommissionerEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyData, uint8_t aLength) { otError error = OT_ERROR_NONE; SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_MESHCOP_COMMISSIONER_ENERGY_SCAN_RESULT)); SuccessOrExit(error = mEncoder.WriteUint32(aChannelMask)); SuccessOrExit(error = mEncoder.WriteDataWithLen(aEnergyData, aLength)); SuccessOrExit(error = mEncoder.EndFrame()); exit: if (error != OT_ERROR_NONE) { mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); mUpdateChangedPropsTask.Post(); } } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint16_t panId; uint32_t channelMask; const otIp6Address *address; SuccessOrExit(error = mDecoder.ReadUint16(panId)); SuccessOrExit(error = mDecoder.ReadUint32(channelMask)); SuccessOrExit(error = mDecoder.ReadIp6Address(address)); error = otCommissionerPanIdQuery(mInstance, panId, channelMask, address, &NcpBase::HandleCommissionerPanIdConflict_Jump, this); exit: return error; } void NcpBase::HandleCommissionerPanIdConflict_Jump(uint16_t aPanId, uint32_t aChannelMask, void *aContext) { static_cast(aContext)->HandleCommissionerPanIdConflict(aPanId, aChannelMask); } void NcpBase::HandleCommissionerPanIdConflict(uint16_t aPanId, uint32_t aChannelMask) { otError error = OT_ERROR_NONE; SuccessOrExit(error = mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_INSERTED, SPINEL_PROP_MESHCOP_COMMISSIONER_PAN_ID_CONFLICT_RESULT)); SuccessOrExit(error = mEncoder.WriteUint16(aPanId)); SuccessOrExit(error = mEncoder.WriteUint32(aChannelMask)); SuccessOrExit(error = mEncoder.EndFrame()); exit: if (error != OT_ERROR_NONE) { mChangedPropsSet.AddLastStatus(SPINEL_STATUS_NOMEM); mUpdateChangedPropsTask.Post(); } } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; const uint8_t *tlvs; uint16_t length; SuccessOrExit(error = mDecoder.ReadDataWithLen(tlvs, length)); VerifyOrExit(length <= 255, error = OT_ERROR_INVALID_ARGS); error = otCommissionerSendMgmtGet(mInstance, tlvs, static_cast(length)); exit: return error; } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; const uint8_t *tlvs; uint16_t length; otCommissioningDataset dataset; SuccessOrExit(error = mDecoder.ReadDataWithLen(tlvs, length)); VerifyOrExit(length <= 255, error = OT_ERROR_INVALID_ARGS); memset(&dataset, 0, sizeof(otCommissioningDataset)); error = otCommissionerSendMgmtSet(mInstance, &dataset, tlvs, static_cast(length)); exit: return error; } otError NcpBase::HandlePropertySet_SPINEL_PROP_MESHCOP_COMMISSIONER_GENERATE_PSKC(uint8_t aHeader) { otError error = OT_ERROR_NONE; const char *passPhrase; const char *networkName; const uint8_t *extPanIdData; uint16_t length; otPskc pskc; SuccessOrExit(error = mDecoder.ReadUtf8(passPhrase)); SuccessOrExit(error = mDecoder.ReadUtf8(networkName)); SuccessOrExit(error = mDecoder.ReadDataWithLen(extPanIdData, length)); VerifyOrExit(length == sizeof(spinel_net_xpanid_t), error = OT_ERROR_PARSE); SuccessOrExit(error = otDatasetGeneratePskc(passPhrase, reinterpret_cast(networkName), reinterpret_cast(extPanIdData), &pskc)); SuccessOrExit( error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_MESHCOP_COMMISSIONER_GENERATE_PSKC)); SuccessOrExit(error = mEncoder.WriteData(pskc.m8, sizeof(pskc))); SuccessOrExit(error = mEncoder.EndFrame()); exit: return error; } // SPINEL_PROP_THREAD_COMMISSIONER_ENABLED is replaced by SPINEL_PROP_MESHCOP_COMMISSIONER_STATE. Please use the new // property. The old property/implementation remains for backward compatibility. template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otCommissionerGetState(mInstance) == OT_COMMISSIONER_STATE_ACTIVE); } otError NcpBase::HandlePropertySet_SPINEL_PROP_THREAD_COMMISSIONER_ENABLED(uint8_t aHeader) { bool enabled = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); if (!enabled) { error = otCommissionerStop(mInstance); } else { error = otCommissionerStart(mInstance, nullptr, nullptr, nullptr); } exit: return PrepareLastStatusResponse(aHeader, ThreadErrorToSpinelStatus(error)); } // SPINEL_PROP_THREAD_JOINERS is replaced by SPINEL_PROP_MESHCOP_COMMISSIONER_JOINERS. Please us the new property. // The old property/implementation remains for backward compatibility. template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; const otExtAddress *eui64 = nullptr; const char *pskd = nullptr; uint32_t joinerTimeout = 0; SuccessOrExit(error = mDecoder.ReadUtf8(pskd)); SuccessOrExit(error = mDecoder.ReadUint32(joinerTimeout)); if (mDecoder.ReadEui64(eui64) != OT_ERROR_NONE) { eui64 = nullptr; } error = otCommissionerAddJoiner(mInstance, eui64, pskd, joinerTimeout); exit: return error; } #endif // OPENTHREAD_CONFIG_COMMISSIONER_ENABLE template <> otError NcpBase::HandlePropertySet(void) { uint8_t weight; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(weight)); otThreadSetLocalLeaderWeight(mInstance, weight); exit: return error; } #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteEui64(mSteeringDataAddress); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadEui64(mSteeringDataAddress)); otThreadSetSteeringData(mInstance, &mSteeringDataAddress); exit: return error; } #endif // #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(mPreferredRouteId); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(mPreferredRouteId)); SuccessOrExit(error = otThreadSetPreferredRouterId(mInstance, mPreferredRouteId)); exit: return error; } template <> otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; uint8_t routerId; SuccessOrExit(error = mDecoder.ReadUint8(routerId)); error = otThreadReleaseRouterId(mInstance, routerId); // `INVALID_STATE` is returned when router ID was not allocated (i.e. not in the list) // in such a case, the "remove" operation can be considered successful. if (error == OT_ERROR_INVALID_STATE) { error = OT_ERROR_NONE; } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { otError error = OT_ERROR_NONE; otCacheEntryIterator iterator; otCacheEntryInfo entry; memset(&iterator, 0, sizeof(iterator)); for (uint8_t index = 0;; index++) { SuccessOrExit(otThreadGetNextCacheEntry(mInstance, &entry, &iterator)); SuccessOrExit(error = mEncoder.OpenStruct()); SuccessOrExit(error = mEncoder.WriteIp6Address(entry.mTarget)); SuccessOrExit(error = mEncoder.WriteUint16(entry.mRloc16)); SuccessOrExit(error = mEncoder.WriteUint8(index)); switch (entry.mState) { case OT_CACHE_ENTRY_STATE_CACHED: SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_ADDRESS_CACHE_ENTRY_STATE_CACHED)); break; case OT_CACHE_ENTRY_STATE_SNOOPED: SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_ADDRESS_CACHE_ENTRY_STATE_SNOOPED)); break; case OT_CACHE_ENTRY_STATE_QUERY: SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_ADDRESS_CACHE_ENTRY_STATE_QUERY)); break; case OT_CACHE_ENTRY_STATE_RETRY_QUERY: SuccessOrExit(error = mEncoder.WriteUint8(SPINEL_ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY)); break; } SuccessOrExit(error = mEncoder.OpenStruct()); if (entry.mState == OT_CACHE_ENTRY_STATE_CACHED) { SuccessOrExit(error = mEncoder.WriteBool(entry.mValidLastTrans)); SuccessOrExit(error = mEncoder.WriteUint32(entry.mLastTransTime)); SuccessOrExit(error = mEncoder.WriteIp6Address(entry.mMeshLocalEid)); } SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.OpenStruct()); if (entry.mState != OT_CACHE_ENTRY_STATE_CACHED) { SuccessOrExit(error = mEncoder.WriteBool(entry.mCanEvict)); SuccessOrExit(error = mEncoder.WriteUint16(entry.mRampDown ? 0 : entry.mTimeout)); SuccessOrExit(error = mEncoder.WriteUint16(entry.mRetryDelay)); } SuccessOrExit(error = mEncoder.CloseStruct()); SuccessOrExit(error = mEncoder.CloseStruct()); } exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChildSupervisionGetInterval(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint16_t interval; SuccessOrExit(error = mDecoder.ReadUint16(interval)); otChildSupervisionSetInterval(mInstance, interval); exit: return error; } #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint8(otChannelManagerGetRequestedChannel(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint8_t channel; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint8(channel)); otChannelManagerRequestChannelChange(mInstance, channel); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChannelManagerGetDelay(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint16_t delay; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint16(delay)); error = otChannelManagerSetDelay(mInstance, delay); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return EncodeChannelMask(otChannelManagerGetSupportedChannels(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t channelMask = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = DecodeChannelMask(channelMask)); otChannelManagerSetSupportedChannels(mInstance, channelMask); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return EncodeChannelMask(otChannelManagerGetFavoredChannels(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t channelMask = 0; otError error = OT_ERROR_NONE; SuccessOrExit(error = DecodeChannelMask(channelMask)); otChannelManagerSetFavoredChannels(mInstance, channelMask); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(false); } template <> otError NcpBase::HandlePropertySet(void) { bool skipQualityCheck = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(skipQualityCheck)); error = otChannelManagerRequestChannelSelect(mInstance, skipQualityCheck); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteBool(otChannelManagerGetAutoChannelSelectionEnabled(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { bool enabled = false; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadBool(enabled)); otChannelManagerSetAutoChannelSelectionEnabled(mInstance, enabled); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint32(otChannelManagerGetAutoChannelSelectionInterval(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { uint32_t interval; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadUint32(interval)); error = otChannelManagerSetAutoChannelSelectionInterval(mInstance, interval); exit: return error; } #endif // OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otNetworkTimeGetSyncPeriod(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint16_t timeSyncPeriod; SuccessOrExit(error = mDecoder.ReadUint16(timeSyncPeriod)); SuccessOrExit(error = otNetworkTimeSetSyncPeriod(mInstance, timeSyncPeriod)); exit: return error; } template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otNetworkTimeGetXtalThreshold(mInstance)); } template <> otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; uint16_t xtalThreshold; SuccessOrExit(error = mDecoder.ReadUint16(xtalThreshold)); SuccessOrExit(error = otNetworkTimeSetXtalThreshold(mInstance, xtalThreshold)); exit: return error; } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE } // namespace Ncp } // namespace ot #endif // OPENTHREAD_FTD