blob: 1032750458f57e66d1c3a9c6735e4fa49675e7cd [file] [log] [blame]
/*
* Copyright (c) 2024-2025 Project CHIP Authors
* All rights reserved.
*
* 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 "ThreadBROpenThreadUbus.h"
#include "UboxUtils.h"
#include <clusters/ThreadBorderRouterManagement/Attributes.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/Span.h>
#include <lib/support/ThreadOperationalDataset.h>
#include <platform/CHIPDeviceLayer.h>
#include <libubus.h>
#include <optional>
using namespace chip::ubus;
using namespace chip::app::Clusters::ThreadBorderRouterManagement::Attributes;
namespace chip {
static constexpr int kInvokeTimeout = 2000;
CHIP_ERROR OpenThreadUbusBorderRouterDelegate::Init(AttributeChangeCallback * attributeChangeCallback)
{
mAttributeChangeCallback = attributeChangeCallback;
mOtbr.SetResolvedCallback([](UbusWatch & watch, void * appState) {
auto * self = static_cast<decltype(this)>(appState);
ubus_invoke(&self->mUbusManager.Context(), watch.ObjectID(), "status", nullptr,
([](ubus_request * req, int type, blob_attr * msg) {
static_cast<decltype(this)>(req->priv)->OnDataReceived(msg, false);
}),
self, kInvokeTimeout);
});
mOtbr.SetNotificationCallback([](UbusWatch & watch, void * appState, ubus_request_data * req, const char * notification,
blob_attr * msg) { static_cast<decltype(this)>(appState)->OnDataReceived(msg, true); });
mUbusManager.Register(mOtbr);
return CHIP_NO_ERROR;
}
void OpenThreadUbusBorderRouterDelegate::GetBorderRouterName(MutableCharSpan & borderRouterName)
{
CopyCharSpanToMutableCharSpan("OpenThread BorderRouter"_span, borderRouterName);
}
CHIP_ERROR OpenThreadUbusBorderRouterDelegate::GetBorderAgentId(MutableByteSpan & borderAgentId)
{
return CopySpanToMutableSpan(mBorderAgentIDValid ? ByteSpan(mBorderAgentID) : ByteSpan(), borderAgentId);
}
uint16_t OpenThreadUbusBorderRouterDelegate::GetThreadVersion()
{
return /* Thread 1.4.0 */ 5;
}
bool OpenThreadUbusBorderRouterDelegate::GetInterfaceEnabled()
{
return !mActiveDataset.IsEmpty();
}
CHIP_ERROR OpenThreadUbusBorderRouterDelegate::GetDataset(Thread::OperationalDataset & dataset, DatasetType type)
{
VerifyOrReturnError(type == DatasetType::kActive, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(!mActiveDataset.IsEmpty(), CHIP_ERROR_NOT_FOUND);
dataset = mActiveDataset;
return CHIP_NO_ERROR;
}
using ErrorField = BlobMsgField<uint16_t, CHIP_CTST("Error")>;
void OpenThreadUbusBorderRouterDelegate::SetActiveDataset(const Thread::OperationalDataset & activeDataset, uint32_t sequenceNum,
ActivateDatasetCallback * callback)
{
CHIP_ERROR err = CHIP_ERROR_INTERNAL;
VerifyOrExit(activeDataset.IsCommissioned(), err = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrExit(mActiveDataset.IsEmpty(), err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(mActivateDatasetCallback == nullptr, err = CHIP_ERROR_BUSY);
VerifyOrExit(mOtbr.Resolved() && mBorderAgentIDValid, err = CHIP_ERROR_NOT_CONNECTED);
{
BlobMsgBuf buf;
buf.Add("dataset", activeDataset.AsByteSpan());
ChipLogDetail(AppServer, "SetActiveDataset invoking on %d", mOtbr.ObjectID());
VerifyOrExit(!ubus_invoke(&mUbusManager.Context(), mOtbr.ObjectID(), "provision", buf.head,
([](ubus_request * req, int type, blob_attr * msg) {
ErrorField otError;
VerifyOrReturn(BlobMsgParse(msg, otError) && otError.value_or(0) == 0);
*static_cast<decltype(err) *>(req->priv) = CHIP_NO_ERROR;
}),
&err, kInvokeTimeout),
err = CHIP_ERROR_INTERNAL);
}
mActiveDataset = activeDataset;
mActivateDatasetCallback = callback;
mActivateDatasetSequence = sequenceNum;
return;
exit:
callback->OnActivateDatasetComplete(sequenceNum, err);
}
void OpenThreadUbusBorderRouterDelegate::OnDataReceived(blob_attr * msg, bool notification)
{
BlobMsgField<ByteSpan, CHIP_CTST("BorderAgentId")> borderAgentID;
BlobMsgField<ByteSpan, CHIP_CTST("ActiveDataset")> activeDataset;
BlobMsgField<bool, CHIP_CTST("Attached")> attached;
BlobMsgParse(msg, borderAgentID, attached, activeDataset);
if (!mBorderAgentIDValid && borderAgentID.has_value() && borderAgentID->size() == sizeof(mBorderAgentID))
{
ChipLogProgress(AppServer, "Received OTBR BorderAgentId");
memcpy(mBorderAgentID, borderAgentID->data(), sizeof(mBorderAgentID));
mBorderAgentIDValid = true;
}
if (activeDataset.has_value())
{
Thread::OperationalDatasetView dataset;
if (dataset.Init(activeDataset.value()) == CHIP_NO_ERROR)
{
ChipLogProgress(AppServer, "Received OTBR ActiveDataset (size = %lu)",
static_cast<unsigned long>(activeDataset->size()));
mActiveDataset = dataset;
if (notification)
{
mAttributeChangeCallback->ReportAttributeChanged(ActiveDatasetTimestamp::Id);
}
}
}
if (attached.has_value())
{
ChipLogProgress(AppServer, "Received OTBR Attached = %d", attached.value());
if (attached.value() && mActivateDatasetCallback)
{
auto * callback = mActivateDatasetCallback;
mActivateDatasetCallback = nullptr;
callback->OnActivateDatasetComplete(mActivateDatasetSequence, CHIP_NO_ERROR);
}
}
}
} // namespace chip