| /* |
| * Copyright (c) 2024, 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. |
| */ |
| |
| #define OTBR_LOG_TAG "NcpSpinel" |
| |
| #include "ncp_spinel.hpp" |
| |
| #include <stdarg.h> |
| |
| #include <algorithm> |
| |
| #include <openthread/dataset.h> |
| #include <openthread/thread.h> |
| #include <openthread/platform/dnssd.h> |
| |
| #include "common/code_utils.hpp" |
| #include "common/logging.hpp" |
| #include "lib/spinel/spinel.h" |
| #include "lib/spinel/spinel_decoder.hpp" |
| #include "lib/spinel/spinel_driver.hpp" |
| #include "lib/spinel/spinel_encoder.hpp" |
| #include "lib/spinel/spinel_helper.hpp" |
| #include "lib/spinel/spinel_prop_codec.hpp" |
| |
| namespace otbr { |
| namespace Host { |
| |
| static constexpr char kSpinelDataUnpackFormat[] = "CiiD"; |
| |
| NcpSpinel::NcpSpinel(void) |
| : mSpinelDriver(nullptr) |
| , mCmdTidsInUse(0) |
| , mCmdNextTid(1) |
| , mNcpBuffer(mTxBuffer, kTxBufferSize) |
| , mEncoder(mNcpBuffer) |
| , mIid(SPINEL_HEADER_INVALID_IID) |
| , mPropsObserver(nullptr) |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| , mPublisher(nullptr) |
| #endif |
| { |
| std::fill_n(mWaitingKeyTable, SPINEL_PROP_LAST_STATUS, sizeof(mWaitingKeyTable)); |
| memset(mCmdTable, 0, sizeof(mCmdTable)); |
| } |
| |
| void NcpSpinel::Init(ot::Spinel::SpinelDriver &aSpinelDriver, PropsObserver &aObserver) |
| { |
| mSpinelDriver = &aSpinelDriver; |
| mPropsObserver = &aObserver; |
| mIid = mSpinelDriver->GetIid(); |
| mSpinelDriver->SetFrameHandler(&HandleReceivedFrame, &HandleSavedFrame, this); |
| } |
| |
| void NcpSpinel::Deinit(void) |
| { |
| mSpinelDriver = nullptr; |
| mIp6AddressTableCallback = nullptr; |
| mNetifStateChangedCallback = nullptr; |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| mPublisher = nullptr; |
| #endif |
| } |
| |
| otbrError NcpSpinel::SpinelDataUnpack(const uint8_t *aDataIn, spinel_size_t aDataLen, const char *aPackFormat, ...) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| spinel_ssize_t unpacked; |
| va_list args; |
| |
| va_start(args, aPackFormat); |
| unpacked = spinel_datatype_vunpack(aDataIn, aDataLen, aPackFormat, args); |
| va_end(args); |
| |
| VerifyOrExit(unpacked > 0, error = OTBR_ERROR_PARSE); |
| |
| exit: |
| return error; |
| } |
| |
| void NcpSpinel::DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, AsyncTaskPtr aAsyncTask) |
| { |
| otError error = OT_ERROR_NONE; |
| EncodingFunc encodingFunc = [&aActiveOpDatasetTlvs](ot::Spinel::Encoder &aEncoder) { |
| return aEncoder.WriteData(aActiveOpDatasetTlvs.mTlvs, aActiveOpDatasetTlvs.mLength); |
| }; |
| |
| VerifyOrExit(mDatasetSetActiveTask == nullptr, error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = SetProperty(SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS, encodingFunc)); |
| mDatasetSetActiveTask = aAsyncTask; |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| mTaskRunner.Post([aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to set active dataset!"); }); |
| } |
| } |
| |
| void NcpSpinel::DatasetMgmtSetPending(std::shared_ptr<otOperationalDatasetTlvs> aPendingOpDatasetTlvsPtr, |
| AsyncTaskPtr aAsyncTask) |
| { |
| otError error = OT_ERROR_NONE; |
| EncodingFunc encodingFunc = [aPendingOpDatasetTlvsPtr](ot::Spinel::Encoder &aEncoder) { |
| return aEncoder.WriteData(aPendingOpDatasetTlvsPtr->mTlvs, aPendingOpDatasetTlvsPtr->mLength); |
| }; |
| |
| VerifyOrExit(mDatasetMgmtSetPendingTask == nullptr, error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = SetProperty(SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS, encodingFunc)); |
| mDatasetMgmtSetPendingTask = aAsyncTask; |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| mTaskRunner.Post([aAsyncTask, error] { aAsyncTask->SetResult(error, "Failed to set pending dataset!"); }); |
| } |
| } |
| |
| void NcpSpinel::Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask) |
| { |
| otError error = OT_ERROR_NONE; |
| EncodingFunc encodingFunc = [aEnable](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnable); }; |
| |
| VerifyOrExit(mIp6SetEnabledTask == nullptr, error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = SetProperty(SPINEL_PROP_NET_IF_UP, encodingFunc)); |
| mIp6SetEnabledTask = aAsyncTask; |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| mTaskRunner.Post( |
| [aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to enable the network interface!"); }); |
| } |
| return; |
| } |
| |
| otbrError NcpSpinel::Ip6Send(const uint8_t *aData, uint16_t aLength) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| EncodingFunc encodingFunc = [aData, aLength](ot::Spinel::Encoder &aEncoder) { |
| return aEncoder.WriteDataWithLen(aData, aLength); |
| }; |
| |
| SuccessOrExit(SetProperty(SPINEL_PROP_STREAM_NET, encodingFunc), error = OTBR_ERROR_OPENTHREAD); |
| |
| exit: |
| return error; |
| } |
| |
| void NcpSpinel::ThreadSetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask) |
| { |
| otError error = OT_ERROR_NONE; |
| EncodingFunc encodingFunc = [aEnable](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnable); }; |
| |
| VerifyOrExit(mThreadSetEnabledTask == nullptr, error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = SetProperty(SPINEL_PROP_NET_STACK_UP, encodingFunc)); |
| mThreadSetEnabledTask = aAsyncTask; |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| mTaskRunner.Post( |
| [aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to enable the Thread network!"); }); |
| } |
| return; |
| } |
| |
| void NcpSpinel::ThreadDetachGracefully(AsyncTaskPtr aAsyncTask) |
| { |
| otError error = OT_ERROR_NONE; |
| EncodingFunc encodingFunc = [](ot::Spinel::Encoder &) { return OT_ERROR_NONE; }; |
| |
| VerifyOrExit(mThreadDetachGracefullyTask == nullptr, error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = SetProperty(SPINEL_PROP_NET_LEAVE_GRACEFULLY, encodingFunc)); |
| mThreadDetachGracefullyTask = aAsyncTask; |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| mTaskRunner.Post([aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to detach gracefully!"); }); |
| } |
| return; |
| } |
| |
| void NcpSpinel::ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask) |
| { |
| otError error = OT_ERROR_NONE; |
| spinel_tid_t tid = GetNextTid(); |
| |
| VerifyOrExit(mThreadErasePersistentInfoTask == nullptr, error = OT_ERROR_BUSY); |
| |
| SuccessOrExit(error = mSpinelDriver->SendCommand(SPINEL_CMD_NET_CLEAR, SPINEL_PROP_LAST_STATUS, tid)); |
| |
| mWaitingKeyTable[tid] = SPINEL_PROP_LAST_STATUS; |
| mCmdTable[tid] = SPINEL_CMD_NET_CLEAR; |
| mThreadErasePersistentInfoTask = aAsyncTask; |
| |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| FreeTidTableItem(tid); |
| mTaskRunner.Post( |
| [aAsyncTask, error](void) { aAsyncTask->SetResult(error, "Failed to erase persistent info!"); }); |
| } |
| } |
| |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| void NcpSpinel::SrpServerSetAutoEnableMode(bool aEnabled) |
| { |
| otError error; |
| EncodingFunc encodingFunc = [aEnabled](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnabled); }; |
| |
| error = SetProperty(SPINEL_PROP_SRP_SERVER_AUTO_ENABLE_MODE, encodingFunc); |
| if (error != OT_ERROR_NONE) |
| { |
| otbrLogWarning("Failed to call SrpServerSetAutoEnableMode, %s", otThreadErrorToString(error)); |
| } |
| } |
| |
| void NcpSpinel::SrpServerSetEnabled(bool aEnabled) |
| { |
| otError error; |
| EncodingFunc encodingFunc = [aEnabled](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteBool(aEnabled); }; |
| |
| error = SetProperty(SPINEL_PROP_SRP_SERVER_ENABLED, encodingFunc); |
| if (error != OT_ERROR_NONE) |
| { |
| otbrLogWarning("Failed to call SrpServerSetEnabled, %s", otThreadErrorToString(error)); |
| } |
| } |
| |
| void NcpSpinel::DnssdSetState(Mdns::Publisher::State aState) |
| { |
| otError error; |
| otPlatDnssdState state = (aState == Mdns::Publisher::State::kReady) ? OT_PLAT_DNSSD_READY : OT_PLAT_DNSSD_STOPPED; |
| EncodingFunc encodingFunc = [state](ot::Spinel::Encoder &aEncoder) { return aEncoder.WriteUint8(state); }; |
| |
| error = SetProperty(SPINEL_PROP_DNSSD_STATE, encodingFunc); |
| if (error != OT_ERROR_NONE) |
| { |
| otbrLogWarning("Failed to call DnssdSetState, %s", otThreadErrorToString(error)); |
| } |
| } |
| #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| |
| void NcpSpinel::HandleReceivedFrame(const uint8_t *aFrame, |
| uint16_t aLength, |
| uint8_t aHeader, |
| bool &aSave, |
| void *aContext) |
| { |
| static_cast<NcpSpinel *>(aContext)->HandleReceivedFrame(aFrame, aLength, aHeader, aSave); |
| } |
| |
| void NcpSpinel::HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aShouldSaveFrame) |
| { |
| spinel_tid_t tid = SPINEL_HEADER_GET_TID(aHeader); |
| |
| if (tid == 0) |
| { |
| HandleNotification(aFrame, aLength); |
| } |
| else if (tid < kMaxTids) |
| { |
| HandleResponse(tid, aFrame, aLength); |
| } |
| else |
| { |
| otbrLogCrit("Received unexpected tid: %u", tid); |
| } |
| |
| aShouldSaveFrame = false; |
| } |
| |
| void NcpSpinel::HandleSavedFrame(const uint8_t *aFrame, uint16_t aLength, void *aContext) |
| { |
| /* Intentionally Empty */ |
| OT_UNUSED_VARIABLE(aFrame); |
| OT_UNUSED_VARIABLE(aLength); |
| OT_UNUSED_VARIABLE(aContext); |
| } |
| |
| void NcpSpinel::HandleNotification(const uint8_t *aFrame, uint16_t aLength) |
| { |
| spinel_prop_key_t key; |
| spinel_size_t len = 0; |
| uint8_t *data = nullptr; |
| uint32_t cmd; |
| uint8_t header; |
| otbrError error = OTBR_ERROR_NONE; |
| |
| SuccessOrExit(error = SpinelDataUnpack(aFrame, aLength, kSpinelDataUnpackFormat, &header, &cmd, &key, &data, &len)); |
| VerifyOrExit(SPINEL_HEADER_GET_TID(header) == 0, error = OTBR_ERROR_PARSE); |
| |
| switch (cmd) |
| { |
| case SPINEL_CMD_PROP_VALUE_IS: |
| HandleValueIs(key, data, static_cast<uint16_t>(len)); |
| break; |
| case SPINEL_CMD_PROP_VALUE_INSERTED: |
| HandleValueInserted(key, data, static_cast<uint16_t>(len)); |
| break; |
| case SPINEL_CMD_PROP_VALUE_REMOVED: |
| HandleValueRemoved(key, data, static_cast<uint16_t>(len)); |
| break; |
| } |
| |
| exit: |
| otbrLogResult(error, "%s", __FUNCTION__); |
| } |
| |
| void NcpSpinel::HandleResponse(spinel_tid_t aTid, const uint8_t *aFrame, uint16_t aLength) |
| { |
| spinel_prop_key_t key; |
| spinel_size_t len = 0; |
| uint8_t *data = nullptr; |
| uint32_t cmd; |
| uint8_t header; |
| otbrError error = OTBR_ERROR_NONE; |
| FailureHandler failureHandler = nullptr; |
| |
| SuccessOrExit(error = SpinelDataUnpack(aFrame, aLength, kSpinelDataUnpackFormat, &header, &cmd, &key, &data, &len)); |
| |
| switch (mCmdTable[aTid]) |
| { |
| case SPINEL_CMD_PROP_VALUE_SET: |
| { |
| error = HandleResponseForPropSet(aTid, key, data, len); |
| break; |
| } |
| case SPINEL_CMD_PROP_VALUE_INSERT: |
| { |
| error = HandleResponseForPropInsert(aTid, cmd, key, data, len); |
| break; |
| } |
| case SPINEL_CMD_PROP_VALUE_REMOVE: |
| { |
| error = HandleResponseForPropRemove(aTid, cmd, key, data, len); |
| break; |
| } |
| case SPINEL_CMD_NET_CLEAR: |
| { |
| spinel_status_t status = SPINEL_STATUS_OK; |
| |
| SuccessOrExit(error = SpinelDataUnpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| CallAndClear(mThreadErasePersistentInfoTask, ot::Spinel::SpinelStatusToOtError(status)); |
| break; |
| } |
| default: |
| break; |
| } |
| |
| exit: |
| if (error == OTBR_ERROR_INVALID_STATE) |
| { |
| otbrLogCrit("Received unexpected response with (cmd:%u, key:%u), waiting (cmd:%u, key:%u) for tid:%u", cmd, key, |
| mCmdTable[aTid], mWaitingKeyTable[aTid], aTid); |
| } |
| else if (error == OTBR_ERROR_PARSE) |
| { |
| otbrLogCrit("Error parsing response with tid:%u", aTid); |
| } |
| FreeTidTableItem(aTid); |
| } |
| |
| void NcpSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| |
| switch (aKey) |
| { |
| case SPINEL_PROP_LAST_STATUS: |
| { |
| spinel_status_t status = SPINEL_STATUS_OK; |
| |
| SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| |
| otbrLogInfo("NCP last status: %s", spinel_status_to_cstr(status)); |
| break; |
| } |
| |
| case SPINEL_PROP_NET_ROLE: |
| { |
| spinel_net_role_t role = SPINEL_NET_ROLE_DISABLED; |
| otDeviceRole deviceRole; |
| |
| SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT8_S, &role)); |
| |
| deviceRole = SpinelRoleToDeviceRole(role); |
| mPropsObserver->SetDeviceRole(deviceRole); |
| |
| otbrLogInfo("Device role changed to %s", otThreadDeviceRoleToString(deviceRole)); |
| break; |
| } |
| |
| case SPINEL_PROP_NET_LEAVE_GRACEFULLY: |
| { |
| CallAndClear(mThreadDetachGracefullyTask, OT_ERROR_NONE); |
| break; |
| } |
| |
| case SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS: |
| { |
| spinel_status_t status = SPINEL_STATUS_OK; |
| |
| SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| CallAndClear(mDatasetMgmtSetPendingTask, ot::Spinel::SpinelStatusToOtError(status)); |
| break; |
| } |
| |
| case SPINEL_PROP_IPV6_ADDRESS_TABLE: |
| { |
| std::vector<Ip6AddressInfo> addressInfoTable; |
| |
| VerifyOrExit(ParseIp6AddressTable(aBuffer, aLength, addressInfoTable) == OT_ERROR_NONE, |
| error = OTBR_ERROR_PARSE); |
| SafeInvoke(mIp6AddressTableCallback, addressInfoTable); |
| break; |
| } |
| |
| case SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE: |
| { |
| std::vector<Ip6Address> addressTable; |
| |
| VerifyOrExit(ParseIp6MulticastAddresses(aBuffer, aLength, addressTable) == OT_ERROR_NONE, |
| error = OTBR_ERROR_PARSE); |
| SafeInvoke(mIp6MulticastAddressTableCallback, addressTable); |
| break; |
| } |
| |
| case SPINEL_PROP_NET_IF_UP: |
| { |
| bool isUp; |
| SuccessOrExit(error = SpinelDataUnpack(aBuffer, aLength, SPINEL_DATATYPE_BOOL_S, &isUp)); |
| SafeInvoke(mNetifStateChangedCallback, isUp); |
| break; |
| } |
| |
| case SPINEL_PROP_THREAD_CHILD_TABLE: |
| case SPINEL_PROP_THREAD_ON_MESH_NETS: |
| case SPINEL_PROP_THREAD_OFF_MESH_ROUTES: |
| case SPINEL_PROP_THREAD_LEADER_NETWORK_DATA: |
| case SPINEL_PROP_IPV6_LL_ADDR: |
| break; |
| |
| case SPINEL_PROP_STREAM_NET: |
| { |
| const uint8_t *data; |
| uint16_t dataLen; |
| |
| SuccessOrExit(ParseIp6StreamNet(aBuffer, aLength, data, dataLen), error = OTBR_ERROR_PARSE); |
| SafeInvoke(mIp6ReceiveCallback, data, dataLen); |
| break; |
| } |
| |
| case SPINEL_PROP_INFRA_IF_SEND_ICMP6: |
| { |
| uint32_t infraIfIndex; |
| const otIp6Address *destAddress; |
| const uint8_t *data; |
| uint16_t dataLen; |
| |
| SuccessOrExit(ParseInfraIfIcmp6Nd(aBuffer, aLength, infraIfIndex, destAddress, data, dataLen), |
| error = OTBR_ERROR_PARSE); |
| SafeInvoke(mInfraIfIcmp6NdCallback, infraIfIndex, *destAddress, data, dataLen); |
| break; |
| } |
| |
| default: |
| otbrLogWarning("Received uncognized key: %u", aKey); |
| break; |
| } |
| |
| exit: |
| otbrLogResult(error, "NcpSpinel: %s", __FUNCTION__); |
| return; |
| } |
| |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| static std::string KeyNameFor(const otPlatDnssdKey &aKey) |
| { |
| std::string name(aKey.mName); |
| |
| if (aKey.mServiceType != nullptr) |
| { |
| // TODO: current code would not work with service instance labels that include a '.' |
| name += "."; |
| name += aKey.mServiceType; |
| } |
| return name; |
| } |
| #endif |
| |
| void NcpSpinel::HandleValueInserted(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| |
| VerifyOrExit(aBuffer != nullptr, error = OTBR_ERROR_INVALID_ARGS); |
| decoder.Init(aBuffer, aLength); |
| |
| switch (aKey) |
| { |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| case SPINEL_PROP_DNSSD_HOST: |
| { |
| Mdns::Publisher::AddressList addressList; |
| otPlatDnssdHost host; |
| otPlatDnssdRequestId requestId; |
| const uint8_t *callbackData; |
| uint16_t callbackDataSize; |
| std::vector<uint8_t> callbackDataCopy; |
| |
| SuccessOrExit(ot::Spinel::DecodeDnssdHost(decoder, host, requestId, callbackData, callbackDataSize)); |
| for (uint16_t i = 0; i < host.mAddressesLength; i++) |
| { |
| addressList.push_back(Ip6Address(host.mAddresses[i].mFields.m8)); |
| } |
| callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); |
| |
| mPublisher->PublishHost(host.mHostName, addressList, [this, requestId, callbackDataCopy](otbrError aError) { |
| OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); |
| }); |
| break; |
| } |
| case SPINEL_PROP_DNSSD_SERVICE: |
| { |
| otPlatDnssdService service; |
| Mdns::Publisher::SubTypeList subTypeList; |
| const char *subTypeArray[kMaxSubTypes]; |
| uint16_t subTypeCount; |
| Mdns::Publisher::TxtData txtData; |
| otPlatDnssdRequestId requestId; |
| const uint8_t *callbackData; |
| uint16_t callbackDataSize; |
| std::vector<uint8_t> callbackDataCopy; |
| |
| SuccessOrExit(ot::Spinel::DecodeDnssdService(decoder, service, subTypeArray, subTypeCount, requestId, |
| callbackData, callbackDataSize)); |
| for (uint16_t i = 0; i < subTypeCount; i++) |
| { |
| subTypeList.push_back(subTypeArray[i]); |
| } |
| txtData.assign(service.mTxtData, service.mTxtData + service.mTxtDataLength); |
| callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); |
| |
| mPublisher->PublishService(service.mHostName, service.mServiceInstance, service.mServiceType, subTypeList, |
| service.mPort, txtData, [this, requestId, callbackDataCopy](otbrError aError) { |
| OT_UNUSED_VARIABLE( |
| SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); |
| }); |
| break; |
| } |
| case SPINEL_PROP_DNSSD_KEY_RECORD: |
| { |
| otPlatDnssdKey key; |
| Mdns::Publisher::KeyData keyData; |
| otPlatDnssdRequestId requestId; |
| const uint8_t *callbackData; |
| uint16_t callbackDataSize; |
| std::vector<uint8_t> callbackDataCopy; |
| |
| SuccessOrExit(ot::Spinel::DecodeDnssdKey(decoder, key, requestId, callbackData, callbackDataSize)); |
| keyData.assign(key.mKeyData, key.mKeyData + key.mKeyDataLength); |
| callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); |
| |
| mPublisher->PublishKey(KeyNameFor(key), keyData, [this, requestId, callbackDataCopy](otbrError aError) { |
| OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); |
| }); |
| break; |
| } |
| #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| default: |
| error = OTBR_ERROR_DROPPED; |
| break; |
| } |
| |
| exit: |
| otbrLogResult(error, "HandleValueInserted, key:%u", aKey); |
| return; |
| } |
| |
| void NcpSpinel::HandleValueRemoved(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| |
| VerifyOrExit(aBuffer != nullptr, error = OTBR_ERROR_INVALID_ARGS); |
| decoder.Init(aBuffer, aLength); |
| |
| switch (aKey) |
| { |
| #if OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| case SPINEL_PROP_DNSSD_HOST: |
| { |
| otPlatDnssdHost host; |
| otPlatDnssdRequestId requestId; |
| const uint8_t *callbackData; |
| uint16_t callbackDataSize; |
| std::vector<uint8_t> callbackDataCopy; |
| |
| SuccessOrExit(ot::Spinel::DecodeDnssdHost(decoder, host, requestId, callbackData, callbackDataSize)); |
| callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); |
| |
| mPublisher->UnpublishHost(host.mHostName, [this, requestId, callbackDataCopy](otbrError aError) { |
| OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); |
| }); |
| break; |
| } |
| case SPINEL_PROP_DNSSD_SERVICE: |
| { |
| otPlatDnssdService service; |
| const char *subTypeArray[kMaxSubTypes]; |
| uint16_t subTypeCount; |
| otPlatDnssdRequestId requestId; |
| const uint8_t *callbackData; |
| uint16_t callbackDataSize; |
| std::vector<uint8_t> callbackDataCopy; |
| |
| SuccessOrExit(ot::Spinel::DecodeDnssdService(decoder, service, subTypeArray, subTypeCount, requestId, |
| callbackData, callbackDataSize)); |
| callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); |
| |
| mPublisher->UnpublishService( |
| service.mHostName, service.mServiceType, [this, requestId, callbackDataCopy](otbrError aError) { |
| OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); |
| }); |
| break; |
| } |
| case SPINEL_PROP_DNSSD_KEY_RECORD: |
| { |
| otPlatDnssdKey key; |
| otPlatDnssdRequestId requestId; |
| const uint8_t *callbackData; |
| uint16_t callbackDataSize; |
| std::vector<uint8_t> callbackDataCopy; |
| |
| SuccessOrExit(ot::Spinel::DecodeDnssdKey(decoder, key, requestId, callbackData, callbackDataSize)); |
| callbackDataCopy.assign(callbackData, callbackData + callbackDataSize); |
| |
| mPublisher->UnpublishKey(KeyNameFor(key), [this, requestId, callbackDataCopy](otbrError aError) { |
| OT_UNUSED_VARIABLE(SendDnssdResult(requestId, callbackDataCopy, OtbrErrorToOtError(aError))); |
| }); |
| break; |
| } |
| #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY |
| default: |
| error = OTBR_ERROR_DROPPED; |
| break; |
| } |
| |
| exit: |
| otbrLogResult(error, "HandleValueRemoved, key:%u", aKey); |
| return; |
| } |
| |
| otbrError NcpSpinel::HandleResponseForPropSet(spinel_tid_t aTid, |
| spinel_prop_key_t aKey, |
| const uint8_t *aData, |
| uint16_t aLength) |
| { |
| OTBR_UNUSED_VARIABLE(aData); |
| OTBR_UNUSED_VARIABLE(aLength); |
| |
| otbrError error = OTBR_ERROR_NONE; |
| spinel_status_t status = SPINEL_STATUS_OK; |
| |
| switch (mWaitingKeyTable[aTid]) |
| { |
| case SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS: |
| VerifyOrExit(aKey == SPINEL_PROP_THREAD_ACTIVE_DATASET_TLVS, error = OTBR_ERROR_INVALID_STATE); |
| CallAndClear(mDatasetSetActiveTask, OT_ERROR_NONE); |
| { |
| otOperationalDatasetTlvs datasetTlvs; |
| VerifyOrExit(ParseOperationalDatasetTlvs(aData, aLength, datasetTlvs) == OT_ERROR_NONE, |
| error = OTBR_ERROR_PARSE); |
| mPropsObserver->SetDatasetActiveTlvs(datasetTlvs); |
| } |
| break; |
| |
| case SPINEL_PROP_NET_IF_UP: |
| VerifyOrExit(aKey == SPINEL_PROP_NET_IF_UP, error = OTBR_ERROR_INVALID_STATE); |
| CallAndClear(mIp6SetEnabledTask, OT_ERROR_NONE); |
| { |
| bool isUp; |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_BOOL_S, &isUp)); |
| SafeInvoke(mNetifStateChangedCallback, isUp); |
| } |
| break; |
| |
| case SPINEL_PROP_NET_STACK_UP: |
| VerifyOrExit(aKey == SPINEL_PROP_NET_STACK_UP, error = OTBR_ERROR_INVALID_STATE); |
| CallAndClear(mThreadSetEnabledTask, OT_ERROR_NONE); |
| break; |
| |
| case SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS: |
| if (aKey == SPINEL_PROP_LAST_STATUS) |
| { // Failed case |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| CallAndClear(mDatasetMgmtSetPendingTask, ot::Spinel::SpinelStatusToOtError(status)); |
| } |
| else if (aKey != SPINEL_PROP_THREAD_MGMT_SET_PENDING_DATASET_TLVS) |
| { |
| ExitNow(error = OTBR_ERROR_INVALID_STATE); |
| } |
| break; |
| |
| case SPINEL_PROP_STREAM_NET: |
| break; |
| |
| case SPINEL_PROP_INFRA_IF_STATE: |
| VerifyOrExit(aKey == SPINEL_PROP_LAST_STATUS, error = OTBR_ERROR_INVALID_STATE); |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| otbrLogInfo("Infra If state update result: %s", spinel_status_to_cstr(status)); |
| break; |
| |
| case SPINEL_PROP_INFRA_IF_RECV_ICMP6: |
| VerifyOrExit(aKey == SPINEL_PROP_LAST_STATUS, error = OTBR_ERROR_INVALID_STATE); |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| otbrLogInfo("Infra If handle ICMP6 ND result: %s", spinel_status_to_cstr(status)); |
| break; |
| |
| case SPINEL_PROP_DNSSD_STATE: |
| VerifyOrExit(aKey == SPINEL_PROP_LAST_STATUS, error = OTBR_ERROR_INVALID_STATE); |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| otbrLogInfo("Update dnssd state result: %s", spinel_status_to_cstr(status)); |
| break; |
| |
| default: |
| VerifyOrExit(aKey == mWaitingKeyTable[aTid], error = OTBR_ERROR_INVALID_STATE); |
| break; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otbrError NcpSpinel::HandleResponseForPropInsert(spinel_tid_t aTid, |
| spinel_command_t aCmd, |
| spinel_prop_key_t aKey, |
| const uint8_t *aData, |
| uint16_t aLength) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| |
| switch (mWaitingKeyTable[aTid]) |
| { |
| case SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE: |
| if (aCmd == SPINEL_CMD_PROP_VALUE_IS) |
| { |
| spinel_status_t status = SPINEL_STATUS_OK; |
| |
| VerifyOrExit(aKey == SPINEL_PROP_LAST_STATUS, error = OTBR_ERROR_INVALID_STATE); |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| otbrLogInfo("Failed to subscribe to multicast address on NCP, error:%s", spinel_status_to_cstr(status)); |
| } |
| else |
| { |
| error = aCmd == SPINEL_CMD_PROP_VALUE_INSERTED ? OTBR_ERROR_NONE : OTBR_ERROR_INVALID_STATE; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| exit: |
| otbrLogResult(error, "HandleResponseForPropInsert, key:%u", mWaitingKeyTable[aTid]); |
| return error; |
| } |
| |
| otbrError NcpSpinel::HandleResponseForPropRemove(spinel_tid_t aTid, |
| spinel_command_t aCmd, |
| spinel_prop_key_t aKey, |
| const uint8_t *aData, |
| uint16_t aLength) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| |
| switch (mWaitingKeyTable[aTid]) |
| { |
| case SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE: |
| if (aCmd == SPINEL_CMD_PROP_VALUE_IS) |
| { |
| spinel_status_t status = SPINEL_STATUS_OK; |
| |
| VerifyOrExit(aKey == SPINEL_PROP_LAST_STATUS, error = OTBR_ERROR_INVALID_STATE); |
| SuccessOrExit(error = SpinelDataUnpack(aData, aLength, SPINEL_DATATYPE_UINT_PACKED_S, &status)); |
| otbrLogInfo("Failed to unsubscribe to multicast address on NCP, error:%s", spinel_status_to_cstr(status)); |
| } |
| else |
| { |
| error = aCmd == SPINEL_CMD_PROP_VALUE_REMOVED ? OTBR_ERROR_NONE : OTBR_ERROR_INVALID_STATE; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| exit: |
| otbrLogResult(error, "HandleResponseForPropRemove, key:%u", mWaitingKeyTable[aTid]); |
| return error; |
| } |
| |
| otbrError NcpSpinel::Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| EncodingFunc encodingFunc = [&aAddress](ot::Spinel::Encoder &aEncoder) { |
| return aEncoder.WriteIp6Address(aAddress); |
| }; |
| |
| if (aIsAdded) |
| { |
| SuccessOrExit(InsertProperty(SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE, encodingFunc), |
| error = OTBR_ERROR_OPENTHREAD); |
| } |
| else |
| { |
| SuccessOrExit(RemoveProperty(SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE, encodingFunc), |
| error = OTBR_ERROR_OPENTHREAD); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| spinel_tid_t NcpSpinel::GetNextTid(void) |
| { |
| spinel_tid_t tid = mCmdNextTid; |
| |
| while (((1 << tid) & mCmdTidsInUse) != 0) |
| { |
| tid = SPINEL_GET_NEXT_TID(tid); |
| |
| if (tid == mCmdNextTid) |
| { |
| // We looped back to `mCmdNextTid` indicating that all |
| // TIDs are in-use. |
| |
| ExitNow(tid = 0); |
| } |
| } |
| |
| mCmdTidsInUse |= (1 << tid); |
| mCmdNextTid = SPINEL_GET_NEXT_TID(tid); |
| |
| exit: |
| return tid; |
| } |
| |
| void NcpSpinel::FreeTidTableItem(spinel_tid_t aTid) |
| { |
| mCmdTidsInUse &= ~(1 << aTid); |
| |
| mCmdTable[aTid] = SPINEL_CMD_NOOP; |
| mWaitingKeyTable[aTid] = SPINEL_PROP_LAST_STATUS; |
| } |
| |
| otError NcpSpinel::SendCommand(spinel_command_t aCmd, spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc) |
| { |
| otError error = OT_ERROR_NONE; |
| spinel_tid_t tid = GetNextTid(); |
| uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid) | tid; |
| |
| VerifyOrExit(tid != 0, error = OT_ERROR_BUSY); |
| SuccessOrExit(error = mEncoder.BeginFrame(header, aCmd, aKey)); |
| SuccessOrExit(error = aEncodingFunc(mEncoder)); |
| SuccessOrExit(error = mEncoder.EndFrame()); |
| SuccessOrExit(error = SendEncodedFrame()); |
| |
| mCmdTable[tid] = aCmd; |
| mWaitingKeyTable[tid] = aKey; |
| exit: |
| if (error != OT_ERROR_NONE) |
| { |
| FreeTidTableItem(tid); |
| } |
| return error; |
| } |
| |
| otError NcpSpinel::SetProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc) |
| { |
| return SendCommand(SPINEL_CMD_PROP_VALUE_SET, aKey, aEncodingFunc); |
| } |
| |
| otError NcpSpinel::InsertProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc) |
| { |
| return SendCommand(SPINEL_CMD_PROP_VALUE_INSERT, aKey, aEncodingFunc); |
| } |
| |
| otError NcpSpinel::RemoveProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc) |
| { |
| return SendCommand(SPINEL_CMD_PROP_VALUE_REMOVE, aKey, aEncodingFunc); |
| } |
| |
| otError NcpSpinel::SendEncodedFrame(void) |
| { |
| otError error = OT_ERROR_NONE; |
| uint8_t frame[kTxBufferSize]; |
| uint16_t frameLength; |
| |
| SuccessOrExit(error = mNcpBuffer.OutFrameBegin()); |
| frameLength = mNcpBuffer.OutFrameGetLength(); |
| VerifyOrExit(mNcpBuffer.OutFrameRead(frameLength, frame) == frameLength, error = OT_ERROR_FAILED); |
| SuccessOrExit(error = mSpinelDriver->GetSpinelInterface()->SendFrame(frame, frameLength)); |
| |
| exit: |
| error = mNcpBuffer.OutFrameRemove(); |
| return error; |
| } |
| |
| otError NcpSpinel::ParseIp6AddressTable(const uint8_t *aBuf, |
| uint16_t aLength, |
| std::vector<Ip6AddressInfo> &aAddressTable) |
| { |
| otError error = OT_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| |
| VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS); |
| decoder.Init(aBuf, aLength); |
| |
| while (!decoder.IsAllReadInStruct()) |
| { |
| Ip6AddressInfo cur; |
| const otIp6Address *addr; |
| uint8_t prefixLength; |
| uint32_t preferredLifetime; |
| uint32_t validLifetime; |
| |
| SuccessOrExit(error = decoder.OpenStruct()); |
| SuccessOrExit(error = decoder.ReadIp6Address(addr)); |
| memcpy(&cur.mAddress, addr, sizeof(otIp6Address)); |
| SuccessOrExit(error = decoder.ReadUint8(prefixLength)); |
| cur.mPrefixLength = prefixLength; |
| SuccessOrExit(error = decoder.ReadUint32(preferredLifetime)); |
| cur.mPreferred = preferredLifetime ? true : false; |
| SuccessOrExit(error = decoder.ReadUint32(validLifetime)); |
| OTBR_UNUSED_VARIABLE(validLifetime); |
| SuccessOrExit((error = decoder.CloseStruct())); |
| |
| aAddressTable.push_back(cur); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpSpinel::ParseIp6MulticastAddresses(const uint8_t *aBuf, uint16_t aLen, std::vector<Ip6Address> &aAddressList) |
| { |
| otError error = OT_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| |
| VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS); |
| |
| decoder.Init(aBuf, aLen); |
| |
| while (!decoder.IsAllReadInStruct()) |
| { |
| const otIp6Address *addr; |
| |
| SuccessOrExit(error = decoder.OpenStruct()); |
| SuccessOrExit(error = decoder.ReadIp6Address(addr)); |
| aAddressList.emplace_back(Ip6Address(*addr)); |
| SuccessOrExit((error = decoder.CloseStruct())); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpSpinel::ParseIp6StreamNet(const uint8_t *aBuf, uint16_t aLen, const uint8_t *&aData, uint16_t &aDataLen) |
| { |
| otError error = OT_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| |
| VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS); |
| |
| decoder.Init(aBuf, aLen); |
| error = decoder.ReadDataWithLen(aData, aDataLen); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpSpinel::ParseOperationalDatasetTlvs(const uint8_t *aBuf, |
| uint16_t aLen, |
| otOperationalDatasetTlvs &aDatasetTlvs) |
| { |
| otError error = OT_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| const uint8_t *datasetTlvsData; |
| uint16_t datasetTlvsLen; |
| |
| decoder.Init(aBuf, aLen); |
| SuccessOrExit(error = decoder.ReadData(datasetTlvsData, datasetTlvsLen)); |
| VerifyOrExit(datasetTlvsLen <= sizeof(aDatasetTlvs.mTlvs), error = OT_ERROR_PARSE); |
| |
| memcpy(aDatasetTlvs.mTlvs, datasetTlvsData, datasetTlvsLen); |
| aDatasetTlvs.mLength = datasetTlvsLen; |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpSpinel::ParseInfraIfIcmp6Nd(const uint8_t *aBuf, |
| uint8_t aLen, |
| uint32_t &aInfraIfIndex, |
| const otIp6Address *&aAddr, |
| const uint8_t *&aData, |
| uint16_t &aDataLen) |
| { |
| otError error = OT_ERROR_NONE; |
| ot::Spinel::Decoder decoder; |
| |
| VerifyOrExit(aBuf != nullptr, error = OT_ERROR_INVALID_ARGS); |
| |
| decoder.Init(aBuf, aLen); |
| SuccessOrExit(error = decoder.ReadUint32(aInfraIfIndex)); |
| SuccessOrExit(error = decoder.ReadIp6Address(aAddr)); |
| SuccessOrExit(error = decoder.ReadDataWithLen(aData, aDataLen)); |
| |
| exit: |
| return error; |
| } |
| |
| otError NcpSpinel::SendDnssdResult(otPlatDnssdRequestId aRequestId, |
| const std::vector<uint8_t> &aCallbackData, |
| otError aError) |
| { |
| otError error; |
| EncodingFunc encodingFunc = [aRequestId, &aCallbackData, aError](ot::Spinel::Encoder &aEncoder) { |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(aEncoder.WriteUint8(aError)); |
| SuccessOrExit(aEncoder.WriteUint32(aRequestId)); |
| SuccessOrExit(aEncoder.WriteData(aCallbackData.data(), aCallbackData.size())); |
| |
| exit: |
| return error; |
| }; |
| |
| error = SetProperty(SPINEL_PROP_DNSSD_REQUEST_RESULT, encodingFunc); |
| if (error != OT_ERROR_NONE) |
| { |
| otbrLogWarning("Failed to SendDnssdResult, %s", otThreadErrorToString(error)); |
| } |
| |
| return error; |
| } |
| |
| otbrError NcpSpinel::SetInfraIf(uint32_t aInfraIfIndex, bool aIsRunning, const std::vector<Ip6Address> &aIp6Addresses) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| EncodingFunc encodingFunc = [aInfraIfIndex, aIsRunning, &aIp6Addresses](ot::Spinel::Encoder &aEncoder) { |
| otError error = OT_ERROR_NONE; |
| SuccessOrExit(error = aEncoder.WriteUint32(aInfraIfIndex)); |
| SuccessOrExit(error = aEncoder.WriteBool(aIsRunning)); |
| for (const Ip6Address &addr : aIp6Addresses) |
| { |
| SuccessOrExit(error = aEncoder.WriteIp6Address(reinterpret_cast<const otIp6Address &>(addr))); |
| } |
| |
| exit: |
| return error; |
| }; |
| |
| SuccessOrExit(SetProperty(SPINEL_PROP_INFRA_IF_STATE, encodingFunc), error = OTBR_ERROR_OPENTHREAD); |
| |
| exit: |
| return error; |
| } |
| |
| otbrError NcpSpinel::HandleIcmp6Nd(uint32_t aInfraIfIndex, |
| const Ip6Address &aIp6Address, |
| const uint8_t *aData, |
| uint16_t aDataLen) |
| { |
| otbrError error = OTBR_ERROR_NONE; |
| EncodingFunc encodingFunc = [aInfraIfIndex, &aIp6Address, aData, aDataLen](ot::Spinel::Encoder &aEncoder) { |
| otError error = OT_ERROR_NONE; |
| SuccessOrExit(error = aEncoder.WriteUint32(aInfraIfIndex)); |
| SuccessOrExit(error = aEncoder.WriteIp6Address(reinterpret_cast<const otIp6Address &>(aIp6Address))); |
| SuccessOrExit(error = aEncoder.WriteData(aData, aDataLen)); |
| exit: |
| return error; |
| }; |
| |
| SuccessOrExit(SetProperty(SPINEL_PROP_INFRA_IF_RECV_ICMP6, encodingFunc), error = OTBR_ERROR_OPENTHREAD); |
| |
| exit: |
| if (error != OTBR_ERROR_NONE) |
| { |
| otbrLogWarning("Failed to passthrough ICMP6 ND to NCP, %s", otbrErrorString(error)); |
| } |
| return error; |
| } |
| |
| otDeviceRole NcpSpinel::SpinelRoleToDeviceRole(spinel_net_role_t aRole) |
| { |
| otDeviceRole role = OT_DEVICE_ROLE_DISABLED; |
| |
| switch (aRole) |
| { |
| case SPINEL_NET_ROLE_DISABLED: |
| role = OT_DEVICE_ROLE_DISABLED; |
| break; |
| case SPINEL_NET_ROLE_DETACHED: |
| role = OT_DEVICE_ROLE_DETACHED; |
| break; |
| case SPINEL_NET_ROLE_CHILD: |
| role = OT_DEVICE_ROLE_CHILD; |
| break; |
| case SPINEL_NET_ROLE_ROUTER: |
| role = OT_DEVICE_ROLE_ROUTER; |
| break; |
| case SPINEL_NET_ROLE_LEADER: |
| role = OT_DEVICE_ROLE_LEADER; |
| break; |
| default: |
| otbrLogWarning("Unsupported spinel net role: %u", aRole); |
| break; |
| } |
| |
| return role; |
| } |
| |
| } // namespace Host |
| } // namespace otbr |