/* * * Copyright (c) 2024 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "GenericThreadBorderRouterDelegate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace chip { namespace app { namespace Clusters { namespace ThreadBorderRouterManagement { class ScopedThreadLock { public: ScopedThreadLock() { DeviceLayer::ThreadStackMgr().LockThreadStack(); } ~ScopedThreadLock() { DeviceLayer::ThreadStackMgr().UnlockThreadStack(); } }; CHIP_ERROR GenericOpenThreadBorderRouterDelegate::Init(AttributeChangeCallback * callback) { mpActivateDatasetCallback = nullptr; mpAttributeChangeCallback = callback; ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler, reinterpret_cast(this))); // When the Thread Border Router is reboot during SetActiveDataset, we need to revert the active dateset. RevertActiveDataset(); return CHIP_NO_ERROR; } CHIP_ERROR GenericOpenThreadBorderRouterDelegate::GetBorderAgentId(MutableByteSpan & borderAgentIdSpan) { otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); VerifyOrReturnError(otInst, CHIP_ERROR_INCORRECT_STATE); otBorderAgentId borderAgentId; if (borderAgentIdSpan.size() != sizeof(borderAgentId.mId)) { return CHIP_ERROR_INVALID_ARGUMENT; } otError otErr = OT_ERROR_NONE; { ScopedThreadLock threadLock; otErr = otBorderAgentGetId(otInst, &borderAgentId); } if (otErr == OT_ERROR_NONE) { CopySpanToMutableSpan(ByteSpan(borderAgentId.mId), borderAgentIdSpan); return CHIP_NO_ERROR; } return DeviceLayer::Internal::MapOpenThreadError(otErr); } uint16_t GenericOpenThreadBorderRouterDelegate::GetThreadVersion() { return otThreadGetVersion(); } bool GenericOpenThreadBorderRouterDelegate::GetInterfaceEnabled() { otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); VerifyOrReturnValue(otInst, false); ScopedThreadLock threadLock; return otIp6IsEnabled(otInst); } CHIP_ERROR GenericOpenThreadBorderRouterDelegate::GetDataset(Thread::OperationalDataset & dataset, DatasetType type) { otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); VerifyOrReturnError(otInst, CHIP_ERROR_INCORRECT_STATE); otError otErr = OT_ERROR_NONE; otOperationalDatasetTlvs datasetTlvs; { ScopedThreadLock threadLock; if (type == DatasetType::kActive) { otErr = otDatasetGetActiveTlvs(otInst, &datasetTlvs); } else { otErr = otDatasetGetPendingTlvs(otInst, &datasetTlvs); } } if (otErr == OT_ERROR_NONE) { return dataset.Init(ByteSpan(datasetTlvs.mTlvs, datasetTlvs.mLength)); } return DeviceLayer::Internal::MapOpenThreadError(otErr); } void GenericOpenThreadBorderRouterDelegate::SetActiveDataset(const Thread::OperationalDataset & activeDataset, uint32_t sequenceNum, ActivateDatasetCallback * callback) { // This function will never be invoked when there is an Active Dataset already configured. CHIP_ERROR err = SaveActiveDatasetConfigured(false); if (err == CHIP_NO_ERROR) { err = DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(activeDataset, nullptr); } if (err != CHIP_NO_ERROR) { callback->OnActivateDatasetComplete(sequenceNum, err); return; } mSequenceNum = sequenceNum; mpActivateDatasetCallback = callback; } void GenericOpenThreadBorderRouterDelegate::OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) { GenericOpenThreadBorderRouterDelegate * delegate = reinterpret_cast(arg); if (delegate) { if ((event->Type == DeviceLayer::DeviceEventType::kThreadConnectivityChange) && (event->ThreadConnectivityChange.Result == DeviceLayer::kConnectivity_Established) && delegate->mpActivateDatasetCallback) { delegate->mpActivateDatasetCallback->OnActivateDatasetComplete(delegate->mSequenceNum, CHIP_NO_ERROR); delegate->mpActivateDatasetCallback = nullptr; } } if (event->Type == DeviceLayer::DeviceEventType::kThreadStateChange) { if (event->ThreadStateChange.OpenThread.Flags & OT_CHANGED_THREAD_NETIF_STATE) { DeviceLayer::SystemLayer().ScheduleLambda( [delegate]() { delegate->mpAttributeChangeCallback->ReportAttributeChanged(Attributes::InterfaceEnabled::Id); }); } if (event->ThreadStateChange.OpenThread.Flags & OT_CHANGED_ACTIVE_DATASET) { DeviceLayer::SystemLayer().ScheduleLambda([delegate]() { delegate->mpAttributeChangeCallback->ReportAttributeChanged(Attributes::ActiveDatasetTimestamp::Id); }); } if (event->ThreadStateChange.OpenThread.Flags & OT_CHANGED_PENDING_DATASET) { DeviceLayer::SystemLayer().ScheduleLambda([delegate]() { delegate->mpAttributeChangeCallback->ReportAttributeChanged(Attributes::PendingDatasetTimestamp::Id); }); } } } CHIP_ERROR GenericOpenThreadBorderRouterDelegate::SaveActiveDatasetConfigured(bool configured) { VerifyOrReturnError(mStorage, CHIP_ERROR_INTERNAL); return mStorage->SyncSetKeyValue(kFailsafeActiveDatasetConfigured, &configured, sizeof(bool)); } CHIP_ERROR GenericOpenThreadBorderRouterDelegate::CommitActiveDataset() { return SaveActiveDatasetConfigured(DeviceLayer::ThreadStackMgrImpl().IsThreadAttached()); } CHIP_ERROR GenericOpenThreadBorderRouterDelegate::RevertActiveDataset() { // The FailSafe Timer is triggered and the previous command request should be handled, so reset the callback. mpActivateDatasetCallback = nullptr; bool activeDatasetConfigured = true; uint16_t activeDatasetConfiguredLen = sizeof(bool); VerifyOrReturnError(mStorage, CHIP_ERROR_INTERNAL); mStorage->SyncGetKeyValue(kFailsafeActiveDatasetConfigured, &activeDatasetConfigured, activeDatasetConfiguredLen); VerifyOrDie(activeDatasetConfiguredLen == sizeof(bool)); if (!activeDatasetConfigured) { // The active dataset should be no configured after calling this function, so we will try to attach an empty Thread dataset // and that will clear the one stored in the Thread stack since the SetActiveDataset operation fails and FailSafe timer is // triggered. Thread::OperationalDataset emptyDataset = {}; CHIP_ERROR err = DeviceLayer::ThreadStackMgrImpl().AttachToThreadNetwork(emptyDataset, nullptr); SaveActiveDatasetConfigured(false); return err; } return CHIP_NO_ERROR; } CHIP_ERROR GenericOpenThreadBorderRouterDelegate::SetPendingDataset(const Thread::OperationalDataset & pendingDataset) { otInstance * otInst = DeviceLayer::ThreadStackMgrImpl().OTInstance(); VerifyOrReturnError(otInst, CHIP_ERROR_INCORRECT_STATE); otOperationalDatasetTlvs datasetTlvs; memcpy(datasetTlvs.mTlvs, pendingDataset.AsByteSpan().data(), pendingDataset.AsByteSpan().size()); datasetTlvs.mLength = pendingDataset.AsByteSpan().size(); { ScopedThreadLock threadLock; VerifyOrReturnError(otDatasetSetPendingTlvs(otInst, &datasetTlvs) == OT_ERROR_NONE, CHIP_ERROR_INTERNAL); } return CHIP_NO_ERROR; } } // namespace ThreadBorderRouterManagement } // namespace Clusters } // namespace app } // namespace chip