Skip to content

Commit de24ab0

Browse files
committed
[nxp fromlist][border router] Add TBR Management cluster server and delagate implementation
This commit is created from Matter upstream PR project-chip#33872.
1 parent c2d9bbd commit de24ab0

12 files changed

+1270
-1
lines changed

src/app/chip_data_model.gni

+6
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,12 @@ template("chip_data_model") {
390390
"${_app_root}/clusters/${cluster}/thread-network-diagnostics-provider.cpp",
391391
"${_app_root}/clusters/${cluster}/thread-network-diagnostics-provider.h",
392392
]
393+
} else if (cluster == "thread-border-router-management-server") {
394+
sources += [
395+
"${_app_root}/clusters/${cluster}/thread-border-router-management-server.cpp",
396+
"${_app_root}/clusters/${cluster}/thread-border-router-management-server.h",
397+
"${_app_root}/clusters/${cluster}/thread-br-delegate.h",
398+
]
393399
} else {
394400
sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ]
395401
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
/*
2+
*
3+
* Copyright (c) 2024 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "thread-border-router-management-server.h"
19+
20+
#include "app-common/zap-generated/cluster-objects.h"
21+
#include "app-common/zap-generated/ids/Attributes.h"
22+
#include "app-common/zap-generated/ids/Clusters.h"
23+
#include "app-common/zap-generated/ids/Commands.h"
24+
#include "app/AttributeAccessInterfaceRegistry.h"
25+
#include "app/AttributeValueEncoder.h"
26+
#include "app/CommandHandler.h"
27+
#include "app/CommandHandlerInterface.h"
28+
#include "app/InteractionModelEngine.h"
29+
#include "app/MessageDef/StatusIB.h"
30+
#include "app/clusters/general-commissioning-server/general-commissioning-server.h"
31+
#include "app/data-model/Nullable.h"
32+
#include "lib/core/CHIPError.h"
33+
#include "lib/core/Optional.h"
34+
#include "lib/support/CodeUtils.h"
35+
#include "lib/support/Span.h"
36+
#include "lib/support/ThreadOperationalDataset.h"
37+
#include "platform/CHIPDeviceEvent.h"
38+
#include "platform/PlatformManager.h"
39+
#include "protocols/interaction_model/StatusCode.h"
40+
41+
namespace chip {
42+
namespace app {
43+
namespace Clusters {
44+
namespace ThreadBorderRouterManagement {
45+
46+
using Protocols::InteractionModel::Status;
47+
48+
static bool IsCommandOverCASESession(CommandHandlerInterface::HandlerContext & ctx)
49+
{
50+
Messaging::ExchangeContext * exchangeCtx = ctx.mCommandHandler.GetExchangeContext();
51+
return exchangeCtx && exchangeCtx->HasSessionHandle() && exchangeCtx->GetSessionHandle()->IsSecureSession() &&
52+
exchangeCtx->GetSessionHandle()->AsSecureSession()->GetSecureSessionType() == Transport::SecureSession::Type::kCASE;
53+
}
54+
55+
Status ServerInstance::HandleGetDatasetRequest(bool isOverCASESession, Delegate::DatasetType type,
56+
Thread::OperationalDataset & dataset)
57+
{
58+
VerifyOrDie(mDelegate);
59+
if (!isOverCASESession)
60+
{
61+
return Status::UnsupportedAccess;
62+
}
63+
64+
CHIP_ERROR err = mDelegate->GetDataset(dataset, type);
65+
if (err != CHIP_NO_ERROR)
66+
{
67+
return err == CHIP_IM_GLOBAL_STATUS(NotFound) ? StatusIB(err).mStatus : Status::Failure;
68+
}
69+
return Status::Success;
70+
}
71+
72+
Status ServerInstance::HandleSetActiveDatasetRequest(CommandHandler * commandHandler,
73+
const Commands::SetActiveDatasetRequest::DecodableType & req)
74+
{
75+
// The SetActiveDatasetRequest command SHALL be FailSafeArmed. Upon receiving this command, the Thread BR will set its
76+
// active dataset. If the dataset is set successfully, OnActivateDatasetComplete will be called with CHIP_NO_ERROR, prompting
77+
// the Thread BR to respond with a success status. If an error occurs while setting the active dataset, the Thread BR should
78+
// respond with a failure status. In this case, when the FailSafe timer expires, the active dataset set by this command will be
79+
// reverted. If the FailSafe timer expires before the Thread BR responds, the Thread BR will respond with a timeout status and
80+
// the active dataset should also be reverted.
81+
VerifyOrDie(mDelegate);
82+
VerifyOrReturnValue(mFailsafeContext.IsFailSafeArmed(commandHandler->GetAccessingFabricIndex()), Status::FailsafeRequired);
83+
84+
Thread::OperationalDataset activeDataset;
85+
Thread::OperationalDataset currentActiveDataset;
86+
uint64_t currentActiveDatasetTimestamp = 0;
87+
// If any of the parameters in the ActiveDataset is invalid, the command SHALL fail with a status code
88+
// of INVALID_COMMAND.
89+
VerifyOrReturnValue(activeDataset.Init(req.activeDataset) == CHIP_NO_ERROR, Status::InvalidCommand);
90+
91+
// If this command is invoked when the ActiveDatasetTimestamp attribute is not null, the command SHALL
92+
// fail with a status code of INVALID_IN_STATE.
93+
if ((mDelegate->GetDataset(currentActiveDataset, Delegate::DatasetType::kActive) == CHIP_NO_ERROR) &&
94+
(currentActiveDataset.GetActiveTimestamp(currentActiveDatasetTimestamp) == CHIP_NO_ERROR))
95+
{
96+
return Status::InvalidInState;
97+
}
98+
// If there is a back end command process, return status BUSY.
99+
if (mAsyncCommandHandle.Get())
100+
{
101+
return Status::Busy;
102+
}
103+
commandHandler->FlushAcksRightAwayOnSlowCommand();
104+
mAsyncCommandHandle = CommandHandler::Handle(commandHandler);
105+
mBreadcrumb = req.breadcrumb;
106+
mSetActiveDatasetSequenceNumber++;
107+
mDelegate->SetActiveDataset(activeDataset, mSetActiveDatasetSequenceNumber, this);
108+
return Status::Success;
109+
}
110+
111+
Status ServerInstance::HandleSetPendingDatasetRequest(const Commands::SetPendingDatasetRequest::DecodableType & req)
112+
{
113+
VerifyOrDie(mDelegate);
114+
if (!mDelegate->GetPanChangeSupported())
115+
{
116+
return Status::UnsupportedCommand;
117+
}
118+
Thread::OperationalDataset pendingDataset;
119+
// If any of the parameters in the PendingDataset is invalid, the command SHALL fail with a status code
120+
// of INVALID_COMMAND.
121+
ReturnErrorCodeIf(pendingDataset.Init(req.pendingDataset) != CHIP_NO_ERROR, Status::InvalidCommand);
122+
CHIP_ERROR err = mDelegate->SetPendingDataset(pendingDataset);
123+
return StatusIB(err).mStatus;
124+
}
125+
126+
void AddDatasetResponse(CommandHandlerInterface::HandlerContext & ctx, Status status, const Thread::OperationalDataset & dataset)
127+
{
128+
if (status != Status::Success)
129+
{
130+
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status);
131+
return;
132+
}
133+
Commands::DatasetResponse::Type response;
134+
response.dataset = dataset.AsByteSpan();
135+
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
136+
}
137+
138+
void ServerInstance::InvokeCommand(HandlerContext & ctxt)
139+
{
140+
switch (ctxt.mRequestPath.mCommandId)
141+
{
142+
case Commands::GetActiveDatasetRequest::Id:
143+
HandleCommand<Commands::GetActiveDatasetRequest::DecodableType>(ctxt, [this](HandlerContext & ctx, const auto & req) {
144+
Thread::OperationalDataset dataset;
145+
Status status = HandleGetActiveDatasetRequest(IsCommandOverCASESession(ctx), dataset);
146+
AddDatasetResponse(ctx, status, dataset);
147+
});
148+
break;
149+
case Commands::GetPendingDatasetRequest::Id:
150+
HandleCommand<Commands::GetPendingDatasetRequest::DecodableType>(ctxt, [this](HandlerContext & ctx, const auto & req) {
151+
Thread::OperationalDataset dataset;
152+
Status status = HandleGetPendingDatasetRequest(IsCommandOverCASESession(ctx), dataset);
153+
AddDatasetResponse(ctx, status, dataset);
154+
});
155+
break;
156+
case Commands::SetActiveDatasetRequest::Id:
157+
HandleCommand<Commands::SetActiveDatasetRequest::DecodableType>(ctxt, [this](HandlerContext & ctx, const auto & req) {
158+
mPath = ctx.mRequestPath;
159+
Status status = HandleSetActiveDatasetRequest(&ctx.mCommandHandler, req);
160+
if (status != Status::Success)
161+
{
162+
// If status is not Success, we should immediately report the status. Otherwise the async work will report the
163+
// status to the client.
164+
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, status);
165+
}
166+
});
167+
break;
168+
case Commands::SetPendingDatasetRequest::Id:
169+
HandleCommand<Commands::SetPendingDatasetRequest::DecodableType>(ctxt, [this](HandlerContext & ctx, const auto & req) {
170+
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, HandleSetPendingDatasetRequest(req));
171+
});
172+
break;
173+
default:
174+
break;
175+
}
176+
}
177+
178+
void ServerInstance::ReadFeatureMap(BitFlags<Feature> & outFeatureMap)
179+
{
180+
if (mDelegate->GetPanChangeSupported())
181+
{
182+
outFeatureMap.Set(Feature::kPANChange);
183+
}
184+
}
185+
186+
CHIP_ERROR ServerInstance::ReadBorderRouterName(MutableCharSpan & outBorderRouterName)
187+
{
188+
mDelegate->GetBorderRouterName(outBorderRouterName);
189+
VerifyOrReturnValue(outBorderRouterName.size() <= kBorderRouterNameMaxLength, CHIP_IM_GLOBAL_STATUS(Failure));
190+
return CHIP_NO_ERROR;
191+
}
192+
193+
CHIP_ERROR ServerInstance::ReadBorderAgentID(MutableByteSpan & outBorderAgentId)
194+
{
195+
VerifyOrReturnValue((mDelegate->GetBorderAgentId(outBorderAgentId) == CHIP_NO_ERROR) &&
196+
(outBorderAgentId.size() == kBorderAgentIdLength),
197+
CHIP_IM_GLOBAL_STATUS(Failure));
198+
return CHIP_NO_ERROR;
199+
}
200+
201+
Optional<uint64_t> ServerInstance::ReadActiveDatasetTimestamp()
202+
{
203+
uint64_t activeDatasetTimestampValue = 0;
204+
Thread::OperationalDataset activeDataset;
205+
if ((mDelegate->GetDataset(activeDataset, Delegate::DatasetType::kActive) == CHIP_NO_ERROR) &&
206+
(activeDataset.GetActiveTimestamp(activeDatasetTimestampValue) == CHIP_NO_ERROR))
207+
{
208+
return MakeOptional(activeDatasetTimestampValue);
209+
}
210+
return NullOptional;
211+
}
212+
213+
CHIP_ERROR ServerInstance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
214+
{
215+
if (aPath.mClusterId != ThreadBorderRouterManagement::Id)
216+
{
217+
return CHIP_ERROR_INVALID_ARGUMENT;
218+
}
219+
VerifyOrDie(mDelegate);
220+
CHIP_ERROR status = CHIP_NO_ERROR;
221+
switch (aPath.mAttributeId)
222+
{
223+
case Globals::Attributes::FeatureMap::Id: {
224+
BitFlags<Feature> featureMap;
225+
ReadFeatureMap(featureMap);
226+
status = aEncoder.Encode(featureMap);
227+
break;
228+
}
229+
case Attributes::BorderRouterName::Id: {
230+
char borderRouterNameBuf[kBorderRouterNameMaxLength] = { 0 };
231+
MutableCharSpan borderRouterName(borderRouterNameBuf);
232+
status = ReadBorderRouterName(borderRouterName);
233+
// If there are any internal errors, the status will be returned and the client will get an error report.
234+
if (status == CHIP_NO_ERROR)
235+
{
236+
status = aEncoder.Encode(borderRouterName);
237+
}
238+
break;
239+
}
240+
case Attributes::BorderAgentID::Id: {
241+
uint8_t borderAgentIDBuf[kBorderAgentIdLength] = { 0 };
242+
MutableByteSpan borderAgentID(borderAgentIDBuf);
243+
status = ReadBorderAgentID(borderAgentID);
244+
if (status == CHIP_NO_ERROR)
245+
{
246+
status = aEncoder.Encode(borderAgentID);
247+
}
248+
break;
249+
}
250+
case Attributes::ThreadVersion::Id: {
251+
uint16_t threadVersion = mDelegate->GetThreadVersion();
252+
status = aEncoder.Encode(threadVersion);
253+
break;
254+
}
255+
case Attributes::InterfaceEnabled::Id: {
256+
bool interfaceEnabled = mDelegate->GetInterfaceEnabled();
257+
status = aEncoder.Encode(interfaceEnabled);
258+
break;
259+
}
260+
case Attributes::ActiveDatasetTimestamp::Id: {
261+
Optional<uint64_t> activeDatasetTimestamp = ReadActiveDatasetTimestamp();
262+
status = activeDatasetTimestamp.HasValue() ? aEncoder.Encode(DataModel::MakeNullable(activeDatasetTimestamp.Value()))
263+
: aEncoder.EncodeNull();
264+
break;
265+
}
266+
default:
267+
break;
268+
}
269+
return status;
270+
}
271+
272+
void ServerInstance::CommitSavedBreadcrumb()
273+
{
274+
if (mBreadcrumb.HasValue())
275+
{
276+
GeneralCommissioning::SetBreadcrumb(mBreadcrumb.Value());
277+
}
278+
mBreadcrumb.ClearValue();
279+
}
280+
281+
void ServerInstance::OnActivateDatasetComplete(uint32_t sequenceNum, CHIP_ERROR error)
282+
{
283+
auto commandHandleRef = std::move(mAsyncCommandHandle);
284+
auto commandHandle = commandHandleRef.Get();
285+
if (commandHandle == nullptr)
286+
{
287+
return;
288+
}
289+
if (mSetActiveDatasetSequenceNumber != sequenceNum)
290+
{
291+
// Previous SetActiveDatasetRequest was handled.
292+
return;
293+
}
294+
if (error == CHIP_NO_ERROR)
295+
{
296+
// TODO: SPEC Issue #10022
297+
CommitSavedBreadcrumb();
298+
}
299+
else
300+
{
301+
ChipLogError(Zcl, "Failed on activating the active dataset for Thread BR: %" CHIP_ERROR_FORMAT, error.Format());
302+
}
303+
commandHandle->AddStatus(mPath, StatusIB(error).mStatus);
304+
}
305+
306+
void ServerInstance::ReportAttributeChanged(AttributeId attributeId)
307+
{
308+
MatterReportingAttributeChangeCallback(mServerEndpointId, Id, attributeId);
309+
}
310+
311+
void ServerInstance::OnFailSafeTimerExpired()
312+
{
313+
if (mDelegate)
314+
{
315+
mDelegate->RevertActiveDataset();
316+
}
317+
auto commandHandleRef = std::move(mAsyncCommandHandle);
318+
auto commandHandle = commandHandleRef.Get();
319+
if (commandHandle == nullptr)
320+
{
321+
return;
322+
}
323+
commandHandle->AddStatus(mPath, Status::Timeout);
324+
}
325+
326+
void ServerInstance::OnPlatformEventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
327+
{
328+
ServerInstance * _this = reinterpret_cast<ServerInstance *>(arg);
329+
if (event->Type == DeviceLayer::DeviceEventType::kFailSafeTimerExpired)
330+
{
331+
_this->OnFailSafeTimerExpired();
332+
}
333+
else if (event->Type == DeviceLayer::DeviceEventType::kCommissioningComplete)
334+
{
335+
_this->mDelegate->CommitActiveDataset();
336+
}
337+
}
338+
339+
CHIP_ERROR ServerInstance::Init()
340+
{
341+
ReturnErrorCodeIf(!mDelegate, CHIP_ERROR_INVALID_ARGUMENT);
342+
ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->RegisterCommandHandler(this));
343+
VerifyOrReturnError(registerAttributeAccessOverride(this), CHIP_ERROR_INCORRECT_STATE);
344+
ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(OnPlatformEventHandler, reinterpret_cast<intptr_t>(this)));
345+
return mDelegate->Init(this);
346+
}
347+
348+
} // namespace ThreadBorderRouterManagement
349+
} // namespace Clusters
350+
} // namespace app
351+
} // namespace chip
352+
353+
void MatterThreadBorderRouterManagementPluginServerInitCallback()
354+
{
355+
// Nothing to do, the server init routine will be done in Instance::Init()
356+
}

0 commit comments

Comments
 (0)